Wolfgang Thallergcc16 merge934c3885 months agoHistory
397 lines · 11.9 KB · D
RawDownload
/** * ... * * Copyright: Copyright Benjamin Thaut 2010 - 2013. * License: Distributed under the *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). *    (See accompanying file LICENSE) * Authors:   Benjamin Thaut, Sean Kelly * Source:    $(DRUNTIMESRC core/sys/windows/_stacktrace.d) */ module core.sys.windows.stacktrace;version (Windows): import core.demangle;import core.stdc.stdlib;import core.stdc.string;import core.sys.windows.dbghelp;import core.sys.windows.imagehlp /+: ADDRESS_MODE+/;import core.sys.windows.winbase;import core.sys.windows.windef; //debug=PRINTF;debug(PRINTF) import core.stdc.stdio;  extern(Windows) void RtlCaptureContext(CONTEXT* ContextRecord) @nogc;extern(Windows) DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR pBuffer, DWORD nSize); extern(Windows) alias RtlCaptureStackBackTraceFunc = USHORT function(ULONG FramesToSkip, ULONG FramesToCapture, PVOID *BackTrace, PULONG BackTraceHash) @nogc; private __gshared RtlCaptureStackBackTraceFunc RtlCaptureStackBackTrace;private __gshared CRITICAL_SECTION mutex; // cannot use core.sync.mutex.Mutex unfortunately (cyclic dependency...)private __gshared immutable bool initialized;  class StackTrace : Throwable.TraceInfo{public:    /**     * Constructor     * Params:     *  skip = The number of stack frames to skip.     *  context = The context to receive the stack trace from. Can be null.     */    this(size_t skip, CONTEXT* context) @nogc    {        if (context is null)        {            version (Win64)                enum INTERNALFRAMES = 3;            else version (Win32)                enum INTERNALFRAMES = 2;             skip += INTERNALFRAMES; //skip the stack frames within the StackTrace class        }        else        {            //When a exception context is given the first stack frame is repeated for some reason            version (Win64)                enum INTERNALFRAMES = 1;            else version (Win32)                enum INTERNALFRAMES = 1;             skip += INTERNALFRAMES;        }        if (initialized)            m_trace = trace(tracebuf[], skip, context);    }     override int opApply( scope int delegate(ref const(char[])) dg ) const    {        return opApply( (ref size_t, ref const(char[]) buf)                        {                            return dg( buf );                        });    }      override int opApply( scope int delegate(ref size_t, ref const(char[])) dg ) const    {        int result;        foreach ( i, e; resolve(m_trace) )        {            if ( (result = dg( i, e )) != 0 )                break;        }        return result;    }      @trusted override string toString() const    {        string result;         foreach ( e; this )        {            result ~= e ~ "\n";        }        return result;    }     /**     * Receive a stack trace in the form of an address list. One form accepts     * an allocated buffer, the other form automatically allocates the buffer.     *     * Params:     *  skip = How many stack frames should be skipped.     *  context = The context that should be used. If null the current context is used.     *  buffer = The buffer to use for the trace. This should be at least 63 elements.     * Returns:     *  A list of addresses that can be passed to resolve at a later point in time.     */    static ulong[] trace(size_t skip = 0, CONTEXT* context = null)    {        return trace(new ulong[63], skip, context);    }     /// ditto    static ulong[] trace(ulong[] buffer, size_t skip = 0, CONTEXT* context = null) @nogc    {        EnterCriticalSection(&mutex);        scope(exit) LeaveCriticalSection(&mutex);         return traceNoSync(buffer, skip, context);    }     /**     * Resolve a stack trace.     * Params:     *  addresses = A list of addresses to resolve.     * Returns:     *  An array of strings with the results.     */    @trusted static char[][] resolve(const(ulong)[] addresses)    {        // FIXME: make @nogc to avoid having to disable resolution within finalizers        import core.memory : GC;        if (GC.inFinalizer)            return null;         EnterCriticalSection(&mutex);        scope(exit) LeaveCriticalSection(&mutex);         return resolveNoSync(addresses);    } private:    ulong[128] tracebuf;    ulong[] m_trace;      static ulong[] traceNoSync(ulong[] buffer, size_t skip, CONTEXT* context) @nogc    {        auto dbghelp  = DbgHelp.get();        if (dbghelp is null)            return []; // dbghelp.dll not available         if (buffer.length >= 63 && RtlCaptureStackBackTrace !is null &&            context is null)        {            version (Win64)            {                auto bufptr = cast(void**)buffer.ptr;            }            version (Win32)            {                size_t[63] bufstorage = void; // On windows xp the sum of "frames to skip" and "frames to capture" can't be greater then 63                auto bufptr = cast(void**)bufstorage.ptr;            }            auto backtraceLength = RtlCaptureStackBackTrace(cast(ULONG)skip, cast(ULONG)(63 - skip), bufptr, null);             // If we get a backtrace and it does not have the maximum length use it.            // Otherwise rely on tracing through StackWalk64 which is slower but works when no frame pointers are available.            if (backtraceLength > 1 && backtraceLength < 63 - skip)            {                debug(PRINTF) printf("Using result from RtlCaptureStackBackTrace\n");                version (Win32)                {                    foreach (i, ref e; buffer[0 .. backtraceLength])                    {                        e = bufstorage[i];                    }                }                return buffer[0..backtraceLength];            }        }         HANDLE       hThread  = GetCurrentThread();        HANDLE       hProcess = GetCurrentProcess();        CONTEXT      ctxt;         if (context is null)        {            ctxt.ContextFlags = CONTEXT_FULL;            RtlCaptureContext(&ctxt);        }        else        {            ctxt = *context;        }         //x86        STACKFRAME64 stackframe;        with (stackframe)        {            version (X86)            {                enum Flat = ADDRESS_MODE.AddrModeFlat;                AddrPC.Offset    = ctxt.Eip;                AddrPC.Mode      = Flat;                AddrFrame.Offset = ctxt.Ebp;                AddrFrame.Mode   = Flat;                AddrStack.Offset = ctxt.Esp;                AddrStack.Mode   = Flat;            }        else version (X86_64)            {                enum Flat = ADDRESS_MODE.AddrModeFlat;                AddrPC.Offset    = ctxt.Rip;                AddrPC.Mode      = Flat;                AddrFrame.Offset = ctxt.Rbp;                AddrFrame.Mode   = Flat;                AddrStack.Offset = ctxt.Rsp;                AddrStack.Mode   = Flat;            }        }         version (X86)         enum imageType = IMAGE_FILE_MACHINE_I386;        else version (X86_64) enum imageType = IMAGE_FILE_MACHINE_AMD64;        else                  static assert(0, "unimplemented");         size_t frameNum = 0;        size_t nframes = 0;         // do ... while so that we don't skip the first stackframe        do        {            if (frameNum >= skip)            {                buffer[nframes++] = stackframe.AddrPC.Offset;                if (nframes >= buffer.length)                    break;            }            frameNum++;        }        while (dbghelp.StackWalk64(imageType, hProcess, hThread, &stackframe,                                   &ctxt, null, null, null, null));        return buffer[0 .. nframes];    }     static char[][] resolveNoSync(const(ulong)[] addresses)    {        auto dbghelp  = DbgHelp.get();        if (dbghelp is null)            return []; // dbghelp.dll not available         HANDLE hProcess = GetCurrentProcess();         static struct BufSymbol        {        align(1):            IMAGEHLP_SYMBOLA64 _base;            TCHAR[1024] _buf = void;        }        BufSymbol bufSymbol=void;        IMAGEHLP_SYMBOLA64* symbol = &bufSymbol._base;        symbol.SizeOfStruct = IMAGEHLP_SYMBOLA64.sizeof;        symbol.MaxNameLength = bufSymbol._buf.length;         char[][] trace;        foreach (pc; addresses)        {            char[] res;            if (dbghelp.SymGetSymFromAddr64(hProcess, pc, null, symbol) &&                *symbol.Name.ptr)            {                DWORD disp;                IMAGEHLP_LINEA64 line=void;                line.SizeOfStruct = IMAGEHLP_LINEA64.sizeof;                 if (dbghelp.SymGetLineFromAddr64(hProcess, pc, &disp, &line))                    res = formatStackFrame(cast(void*)pc, symbol.Name.ptr,                                           line.FileName, line.LineNumber);                else                    res = formatStackFrame(cast(void*)pc, symbol.Name.ptr);            }            else                res = formatStackFrame(cast(void*)pc);            trace ~= res;        }        return trace;    }     static char[] formatStackFrame(void* pc)    {        import core.stdc.stdio : snprintf;        char[2+2*size_t.sizeof+1] buf=void;         immutable len = snprintf(buf.ptr, buf.length, "0x%p", pc);        cast(uint)len < buf.length || assert(0);        return buf[0 .. len].dup;    }     static char[] formatStackFrame(void* pc, char* symName)    {        char[2048] demangleBuf=void;         auto res = formatStackFrame(pc);        res ~= " in ";        const(char)[] tempSymName = symName[0 .. strlen(symName)];        res ~= demangle(tempSymName, demangleBuf);        return res;    }     static char[] formatStackFrame(void* pc, char* symName,                                   const scope char* fileName, uint lineNum)    {        import core.stdc.stdio : snprintf;        char[11] buf=void;         auto res = formatStackFrame(pc, symName);        res ~= " at ";        res ~= fileName[0 .. strlen(fileName)];        res ~= "(";        immutable len = snprintf(buf.ptr, buf.length, "%u", lineNum);        cast(uint)len < buf.length || assert(0);        res ~= buf[0 .. len];        res ~= ")";        return res;    }}  private string generateSearchPath(){    __gshared string[3] defaultPathList = ["_NT_SYMBOL_PATH",                                           "_NT_ALTERNATE_SYMBOL_PATH",                                           "SYSTEMROOT"];     string path;    char[2048] temp = void;    DWORD len;     foreach ( e; defaultPathList )    {        if ( (len = GetEnvironmentVariableA( e.ptr, temp.ptr, temp.length )) > 0 )        {            path ~= temp[0 .. len];            path ~= ";";        }    }    path ~= "\0";    return path;}  shared static this(){    auto dbghelp = DbgHelp.get();     if ( dbghelp is null )        return; // dbghelp.dll not available     auto kernel32Handle = LoadLibraryA( "kernel32.dll" );    if (kernel32Handle !is null)    {        RtlCaptureStackBackTrace = cast(RtlCaptureStackBackTraceFunc) GetProcAddress(kernel32Handle, "RtlCaptureStackBackTrace");        debug(PRINTF)        {            if (RtlCaptureStackBackTrace !is null)                printf("Found RtlCaptureStackBackTrace\n");        }    }     debug(PRINTF)    {        API_VERSION* dbghelpVersion = dbghelp.ImagehlpApiVersion();        printf("DbgHelp Version %d.%d.%d\n", dbghelpVersion.MajorVersion, dbghelpVersion.MinorVersion, dbghelpVersion.Revision);    }     HANDLE hProcess = GetCurrentProcess();     DWORD symOptions = dbghelp.SymGetOptions();    symOptions |= SYMOPT_LOAD_LINES;    symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;    symOptions |= SYMOPT_DEFERRED_LOAD;    symOptions  = dbghelp.SymSetOptions( symOptions );     debug(PRINTF) printf("Search paths: %s\n", generateSearchPath().ptr);     if (!dbghelp.SymInitialize(hProcess, generateSearchPath().ptr, TRUE))        return;     InitializeCriticalSection(&mutex);    initialized = true;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t