[dmd-internals] Backtraces under Linux

Sean Kelly sean at invisibleduck.org
Thu Aug 5 16:11:35 PDT 2010


The blocker for adopting this code as-is is that it depends on Phobos.  -L--export-dynamic still has to be added to linux/bin/dmd.conf in the install package, and I'm working on a demangler.  I'd just copy the one from Phobos, but it works purely with strings using array concatenation and I want to avoid hitting the GC so hard when an exception is thrown.  I've discovered that writing such a demangler is kind of a pain because things have to be reordered during demangling (where the qualified name is for function vs. non-function types, for example).  Anyway, almost done, but it's taking the backseat to other work ATM.  Thanks for the reminder!

On Aug 4, 2010, at 5:43 AM, Jason House wrote:

> I just want to ensure this post to digitalmars.D doesn't get lost.
> 
> It looks like a change to dmd.conf (similar to OSX?) and a patch to druntime. Sean replied to the start of the thread, but not this revised post.
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=114487
> 
> module backtrace;
> 
> //
> // Provides support for a readable backtrace on a program crash.
> //
> // Everything is private - you build this into a library and
> // link to the library, and bingo (via static this).
> //
> // It works by registering a stacktrace handler with the runtime,
> // which, unlike the default one, provides demangled symbols
> // rather than just a list of addresses.
> //
> 
> private {
> 
>     import core.stdc.signal;
>     import core.stdc.stdlib : free;
>     import core.stdc.string : strlen;
>     import core.runtime;
>     import std.demangle;
>     import std.string;
> 
>     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 signal_handler(int sig) {
>         string name() {
>             switch (sig) {
>             case SIGSEGV: return "SIGSEGV";
>             case SIGFPE:  return "SIGFPE";
>             case SIGILL:  return "SIGILL";
>             case SIGABRT: return "SIGABRT";
>             default:      return "";
>             }
>         }
> 
>         throw new Error(format("Got signal %s %s", sig, name));
>     }
> 
>     shared static this() {
>         // set up shared signal handlers for fatal thread-specific signals
>         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
>         Runtime.traceHandler = &traceHandler;
>     }
> 
>     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;
>     }
> }
> 
> 
> 
> Ok,
> 
> I've added -L--export-dynamic to dmd.conf and done a copy/modify of 
> DefaultTraceInfo from runtime.d to include demangling and catch certain 
> signals. My backtraces now look gorgeous under Linux.
> 
> I've attached the source for the updated module.
> 
> Thanks,
> Dave
> 
> On 04/08/10 12:39, Sean Kelly wrote:
> > Graham St Jack Wrote:
> >>
> >> Sean, is there any chance of you rolling some of the good stuff in this
> >> code into druntime?
> >>
> >> In particular, it would be very nice to get symbol names in a stacktrace
> >> rather than a bunch of addresses, and even nicer to get a stacktrace on
> >> a SIGSEGV.
> >
> > You can get the symbol names by adding -L--export-dynamic to your DFLAGS in dmd.conf.  That option is on by default on OSX and I didn't realize it was different on Linux.  I'll sort out demangling as well, it's just a bit more work.  I like the stack trace code provided, but it uses a lot of modules that aren't available to druntime so it would be difficult to use directly.
> 
> 
> 
> 
> module backtrace;
> 
> //
> // Provides support for a readable backtrace on a program crash.
> //
> // Everything is private - you build this into a library and
> // link to the library, and bingo (via static this).
> //
> // It works by registering a stacktrace handler with the runtime,
> // which, unlike the default one, provides demangled symbols
> // rather than just a list of addresses.
> //
> 
> private {
> 
>     import core.stdc.signal;
>     import core.stdc.stdlib : free;
>     import core.stdc.string : strlen;
>     import core.runtime;
>     import std.demangle;
>     import std.string;
> 
>     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 signal_handler(int sig) {
>         string name() {
>             switch (sig) {
>             case SIGSEGV: return "SIGSEGV";
>             case SIGFPE:  return "SIGFPE";
>             case SIGILL:  return "SIGILL";
>             case SIGABRT: return "SIGABRT";
>             default:      return "";
>             }
>         }
> 
>         throw new Error(format("Got signal %s %s", sig, name));
>     }
> 
>     shared static this() {
>         // set up shared signal handlers for fatal thread-specific signals
>         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
>         Runtime.traceHandler = &traceHandler;
>     }
> 
>     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;
>     }
> }
> 
> 
> 
> Ok,
> 
> I've added -L--export-dynamic to dmd.conf and done a copy/modify of 
> DefaultTraceInfo from runtime.d to include demangling and catch certain 
> signals. My backtraces now look gorgeous under Linux.
> 
> I've attached the source for the updated module.
> 
> Thanks,
> Dave
> 
> On 04/08/10 12:39, Sean Kelly wrote:
> > Graham St Jack Wrote:
> >>
> >> Sean, is there any chance of you rolling some of the good stuff in this
> >> code into druntime?
> >>
> >> In particular, it would be very nice to get symbol names in a stacktrace
> >> rather than a bunch of addresses, and even nicer to get a stacktrace on
> >> a SIGSEGV.
> >
> > You can get the symbol names by adding -L--export-dynamic to your DFLAGS in dmd.conf.  That option is on by default on OSX and I didn't realize it was different on Linux.  I'll sort out demangling as well, it's just a bit more work.  I like the stack trace code provided, but it uses a lot of modules that aren't available to druntime so it would be difficult to use directly.
> 
> 
> 
> 
> module backtrace;
> 
> //
> // Provides support for a readable backtrace on a program crash.
> //
> // Everything is private - you build this into a library and
> // link to the library, and bingo (via static this).
> //
> // It works by registering a stacktrace handler with the runtime,
> // which, unlike the default one, provides demangled symbols
> // rather than just a list of addresses.
> //
> 
> private {
> 
>     import core.stdc.signal;
>     import core.stdc.stdlib : free;
>     import core.stdc.string : strlen;
>     import core.runtime;
>     import std.demangle;
>     import std.string;
> 
>     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 signal_handler(int sig) {
>         string name() {
>             switch (sig) {
>             case SIGSEGV: return "SIGSEGV";
>             case SIGFPE:  return "SIGFPE";
>             case SIGILL:  return "SIGILL";
>             case SIGABRT: return "SIGABRT";
>             default:      return "";
>             }
>         }
> 
>         throw new Error(format("Got signal %s %s", sig, name));
>     }
> 
>     shared static this() {
>         // set up shared signal handlers for fatal thread-specific signals
>         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
>         Runtime.traceHandler = &traceHandler;
>     }
> 
>     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;
>     }
> }
> 
> 
> 
> Ok,
> 
> I've added -L--export-dynamic to dmd.conf and done a copy/modify of 
> DefaultTraceInfo from runtime.d to include demangling and catch certain 
> signals. My backtraces now look gorgeous under Linux.
> 
> I've attached the source for the updated module.
> 
> Thanks,
> Dave
> 
> On 04/08/10 12:39, Sean Kelly wrote:
> > Graham St Jack Wrote:
> >>
> >> Sean, is there any chance of you rolling some of the good stuff in this
> >> code into druntime?
> >>
> >> In particular, it would be very nice to get symbol names in a stacktrace
> >> rather than a bunch of addresses, and even nicer to get a stacktrace on
> >> a SIGSEGV.
> >
> > You can get the symbol names by adding -L--export-dynamic to your DFLAGS in dmd.conf.  That option is on by default on OSX and I didn't realize it was different on Linux.  I'll sort out demangling as well, it's just a bit more work.  I like the stack trace code provided, but it uses a lot of modules that aren't available to druntime so it would be difficult to use directly.
> 
> 
> 
> 
> module backtrace;
> 
> //
> // Provides support for a readable backtrace on a program crash.
> //
> // Everything is private - you build this into a library and
> // link to the library, and bingo (via static this).
> //
> // It works by registering a stacktrace handler with the runtime,
> // which, unlike the default one, provides demangled symbols
> // rather than just a list of addresses.
> //
> 
> private {
> 
>     import core.stdc.signal;
>     import core.stdc.stdlib : free;
>     import core.stdc.string : strlen;
>     import core.runtime;
>     import std.demangle;
>     import std.string;
> 
>     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 signal_handler(int sig) {
>         string name() {
>             switch (sig) {
>             case SIGSEGV: return "SIGSEGV";
>             case SIGFPE:  return "SIGFPE";
>             case SIGILL:  return "SIGILL";
>             case SIGABRT: return "SIGABRT";
>             default:      return "";
>             }
>         }
> 
>         throw new Error(format("Got signal %s %s", sig, name));
>     }
> 
>     shared static this() {
>         // set up shared signal handlers for fatal thread-specific signals
>         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
>         Runtime.traceHandler = &traceHandler;
>     }
> 
>     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;
>     }
> }
> 
> 
> _______________________________________________
> dmd-internals mailing list
> dmd-internals at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-internals

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/dmd-internals/attachments/20100805/6f2741e2/attachment-0001.html>


More information about the dmd-internals mailing list