[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