[dmd-internals] Backtraces under Linux

Jason House jason.james.house at gmail.com
Wed Aug 4 05:43:38 PDT 2010


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;
    }
}


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


More information about the dmd-internals mailing list