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

Reply via email to