changeset 50:dfa1e219eafe

New version of backtrace based on the one in druntime. Note, you must have -L--export-dynamic in dmd.conf for symbols to be resolved.
author daveb
date Wed, 04 Aug 2010 16:36:07 +0930
parents 1b4c9ba58673
children 0eaf39fda206
files doodle/core/backtrace.d
diffstat 1 files changed, 58 insertions(+), 217 deletions(-) [+]
line wrap: on
line diff
--- a/doodle/core/backtrace.d	Tue Aug 03 17:37:21 2010 +0930
+++ b/doodle/core/backtrace.d	Wed Aug 04 16:36:07 2010 +0930
@@ -12,201 +12,19 @@
 //
 
 private {
-    import core.sys.posix.unistd;
-    import core.sys.posix.fcntl;
-    import core.sys.posix.sys.stat;
-    import core.sys.posix.sys.mman;
-
-    import core.stdc.string;
-    import core.stdc.signal;
-
-    import core.runtime;
-
-    import std.stdio;
-    import std.string;
-    import std.demangle;
-    import std.algorithm;
-
-    immutable int EI_NIDENT  = 16;
-
-    immutable int SHT_SYMTAB = 2;
-    immutable int SHT_STRTAB = 3;
-
-    immutable int STT_FUNC   = 2;
-
-    alias ushort Elf32_Half;
-    alias uint   Elf32_Word;
-    alias uint   Elf32_Addr;
-    alias uint   Elf32_Off;
-    alias ushort Elf32_Section;
-
-    struct Elf32_Ehdr {
-        ubyte[EI_NIDENT] e_ident;             /* Magic number and other info */
-        Elf32_Half    e_type;                 /* Object file type */
-        Elf32_Half    e_machine;              /* Architecture */
-        Elf32_Word    e_version;              /* Object file version */
-        Elf32_Addr    e_entry;                /* Entry point virtual address */
-        Elf32_Off     e_phoff;                /* Program header table file offset */
-        Elf32_Off     e_shoff;                /* Section header table file offset */
-        Elf32_Word    e_flags;                /* Processor-specific flags */
-        Elf32_Half    e_ehsize;               /* ELF header size in bytes */
-        Elf32_Half    e_phentsize;            /* Program header table entry size */
-        Elf32_Half    e_phnum;                /* Program header table entry count */
-        Elf32_Half    e_shentsize;            /* Section header table entry size */
-        Elf32_Half    e_shnum;                /* Section header table entry count */
-        Elf32_Half    e_shstrndx;             /* Section header string table index */
-    };
-
-    struct Elf32_Shdr {
-        Elf32_Word    sh_name;                /* Section name (string tbl index) */
-        Elf32_Word    sh_type;                /* Section type */
-        Elf32_Word    sh_flags;               /* Section flags */
-        Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
-        Elf32_Off     sh_offset;              /* Section file offset */
-        Elf32_Word    sh_size;                /* Section size in bytes */
-        Elf32_Word    sh_link;                /* Link to another section */
-        Elf32_Word    sh_info;                /* Additional section information */
-        Elf32_Word    sh_addralign;           /* Section alignment */
-        Elf32_Word    sh_entsize;             /* Entry size if section holds table */
-    };
-
-    struct Elf32_Sym {
-        Elf32_Word    st_name;                /* Symbol name (string tbl index) */
-        Elf32_Addr    st_value;               /* Symbol value */
-        Elf32_Word    st_size;                /* Symbol size */
-        ubyte         st_info;                /* Symbol type and binding */
-        ubyte         st_other;               /* Symbol visibility */
-        Elf32_Section st_shndx;               /* Section index */
-    };
-
-    extern (C) int backtrace(void**, size_t);
-
-    struct Symbol {
-        this(void * a, size_t s, char * n) {
-            address = a;
-            size = s;
-            name = n;
-        }
-        void * address;
-        size_t size;
-        char * name;
-        void * end_address() { return address + size; }
-    };
-
-    // Newton-Raphson
-    Symbol nr_lookup(Symbol[] symbols, void * addr, int depth = 0) {
-        if (symbols.length == 0 ||
-            addr < symbols[0].address ||
-            addr >= symbols[$-1].end_address)
-        {
-            throw new Exception("Symbol not found");
-        }
-        else if (symbols.length == 1) {
-            return symbols[0];
-        }
-        else {
-            void * begin_addr = symbols[0].address;
-            void * end_addr = symbols[$-1].end_address;
 
-            int i = ((addr - begin_addr) * symbols.length) / (end_addr - begin_addr);
-            if (!(depth % 2) && i < symbols.length - 1) { ++i; }        // Some wiggle to force convergence
-
-            if (addr < symbols[i].address) {
-                return nr_lookup(symbols[0..i], addr, depth + 1);
-            }
-            else {
-                return nr_lookup(symbols[i..$], addr, depth + 1);
-            }
-        }
-    }
-
-    int generate(void*[] addresses, scope int delegate(ref char[]) dg) {
-        static char[] get_exe() {
-            char buf[1024];
-            ssize_t s = readlink("/proc/self/exe", buf.ptr, buf.length);
-            if (s == -1) { throw new Exception(""); }
-            return buf[0..s];
-        }
-
-        int fd = open(toStringz(get_exe()), O_RDONLY);
-        if (fd == -1) { throw new Exception(""); }
-        scope(exit) close(fd);
-
-        stat_t st;
-        if (fstat(fd, &st) == -1) { throw new Exception(""); }
-
-        void * contents = mmap(null, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
-        if (!contents) { throw new Exception(""); }
-        scope(exit) munmap(contents, st.st_size);
-
-        Elf32_Ehdr * elf_hdr = cast(Elf32_Ehdr *)(contents);
-
-        Elf32_Shdr * sec_hdr = cast(Elf32_Shdr *)(contents + elf_hdr.e_shoff);
-
-        int symtab_idx = 0;
-        for (int i = 0; i < elf_hdr.e_shnum; ++i) {
-            if (sec_hdr[i].sh_type == SHT_SYMTAB) {
-                symtab_idx = i;
-                break;
-            }
-        }
-        if (symtab_idx == 0) { throw new Exception(""); }
-
-        int strtab_idx = sec_hdr[symtab_idx].sh_link;
-        if (strtab_idx == 0) { throw new Exception(""); }        // No associated string table
+    import core.stdc.signal;
+    import core.stdc.stdlib : free;
+    import core.stdc.string : strlen;
+    import core.runtime;
+    import std.demangle;
+    import std.string;
 
-        if (sec_hdr[strtab_idx].sh_type != SHT_STRTAB) { throw new Exception(""); } // invalid string table
-
-        Elf32_Sym * sym_ent = cast(Elf32_Sym *)(contents + sec_hdr[symtab_idx].sh_offset);
-        char * strtab = cast(char *)(contents + sec_hdr[strtab_idx].sh_offset);
-        int num_syms = sec_hdr[symtab_idx].sh_size / sec_hdr[symtab_idx].sh_entsize;
-
-        if (num_syms == 0) { throw new Exception(""); }      // No symbols
-
-        Symbol symbols[];
-
-        for (int i = 0; i < num_syms; ++i) {
-            if ((sym_ent[i].st_info & 0xf) != STT_FUNC) {
-                continue;
-            }
-
-            if (sym_ent[i].st_shndx == 0) {
-                continue;
-            }
-
-            void * address = cast(void *)(sym_ent[i].st_value);       // inclusive
-            size_t size    = sym_ent[i].st_size;
-            char * name    = strtab + sym_ent[i].st_name;
-
-            symbols ~= Symbol(address, size, name);
-        }
-
-        sort!("a.address < b.address")(symbols);
-
-        int ret;
-        foreach (address; addresses) {
-            string str = format("[0x%0.8x] ", address);
-            try {
-                Symbol symbol = nr_lookup(symbols, address);
-                char[] s1 = symbol.name[0..strlen(symbol.name)];
-                str ~= format("%s", demangle(s1.idup));
-            }
-            catch (Exception e) {
-                str ~= "<unknown>";
-            }
-            char[] cstr = str.dup;
-            ret = dg(cstr);
-            if (ret) {
-                break;
-            }
-        }
-
-        return ret;
-    }
-
+    extern (C) int    backtrace(void**, size_t);
+    extern (C) char** backtrace_symbols(void**, int);
 
     // signal handler for otherwise-fatal thread-specific signals 
-    extern (C) void arghh(int sig) {
+    extern (C) void signal_handler(int sig) {
         string name() {
             switch (sig) {
             case SIGSEGV: return "SIGSEGV";
@@ -222,40 +40,63 @@
 
     shared static this() {
         // set up shared signal handlers for fatal thread-specific signals
-        //writeln("setting up shared signal handlers for ABRT, FPE, ILL, SEGV");
-        signal(SIGABRT, &arghh);
-        signal(SIGFPE,  &arghh);
-        signal(SIGILL,  &arghh);
-        signal(SIGSEGV, &arghh);
+        signal(SIGABRT, &signal_handler);
+        signal(SIGFPE,  &signal_handler);
+        signal(SIGILL,  &signal_handler);
+        signal(SIGSEGV, &signal_handler);
     }
 
     static this() {
         // register our trace handler for each thread
-        //writeln("installing our traceHandler");
         Runtime.traceHandler = &traceHandler;
     }
 
-    // Captures backtrace info at the point of construction, stripping
-    // off the bits that concern itself and the ininteresting early stuff.
-    // Also provides opApply to traverse a nice text representation of the backtrace.
-    class TraceInfo : Throwable.TraceInfo {
-        void*[256] callstack;
-        int numframes;
-
-        this() {
-            numframes = backtrace(callstack.ptr, callstack.length);
-        }
-
-        override string toString() const {
-            return "Why does dmd require an override of this?";
-        }
-
-        override int opApply(scope int delegate(ref char[]) dg) {
-            return generate(callstack[4..numframes-5], dg);
-        }
-    }
-
     Throwable.TraceInfo traceHandler(void * ptr = null) {
         return new TraceInfo;
     }
+
+    class TraceInfo : Throwable.TraceInfo {
+        this() {
+            immutable MAXFRAMES = 128;
+            void*[MAXFRAMES] callstack;
+
+            numframes = backtrace(callstack.ptr, MAXFRAMES);
+            framelist = backtrace_symbols(callstack.ptr, numframes);
+        }
+
+        ~this() {
+            free(framelist);
+        }
+
+        override string toString() const { return null; }   // Why does toString require overriding?
+
+        override int opApply(scope int delegate(ref char[]) dg) {
+            // NOTE: The first 5 frames with the current implementation are
+            //       inside core.runtime and the object code, so eliminate
+            //       these for readability.
+            immutable FIRSTFRAME = 5;
+            int ret = 0;
+
+            for(int i = FIRSTFRAME; i < numframes; ++i) {
+                char[] text = framelist[i][0 .. strlen(framelist[i])];
+
+                int a = text.lastIndexOf('(');
+                int b = text.lastIndexOf('+');
+
+                if (a != -1 && b != -1) {
+                    ++a;
+                    text = format("%s%s%s", text[0..a], demangle(text[a..b].idup), text[b..$]).dup;
+                }
+
+                ret = dg(text);
+                if (ret)
+                    break;
+            }
+            return ret;
+        }
+
+    private:
+        int    numframes; 
+        char** framelist;
+    }
 }