How can you use CaptureStackBackTrace to capture the exception stack, not the calling stack?

前端 未结 3 793
终归单人心
终归单人心 2020-12-04 22:41

I marked up the following code:

#include \"stdafx.h\"
#include 
#include 
#include 
#include \"dbghelp.h\"
         


        
相关标签:
3条回答
  • 2020-12-04 23:24

    If you wanted to capture the stack backtrace of the point where the code threw an exception, you must capture the stack backtrace in the ctor of the exception object and store it within the exception object. Hence the part calling CaptureStackBackTrace() should be moved to the constructor of the exception object, which should also provide methods to fetch it either as a vector of addresses or as a vector of symbols. This is exactly how Throwable in Java and Exception in C# operate.

    Finally, please do not write:

    throw new exception;
    

    in C++, as you would in C# or Java. This is an excellent way to both produce memory leaks and to fail to catch the exceptions by type (as you are throwing pointers to these types). Rather use:

    throw exception();
    

    I'm aware that this is an old question but people (including myself) are still finding it.

    0 讨论(0)
  • On Windows, unhandled C++ exception automatically generates SEH exception. SEH __except block allows to attach a filter that accepts _EXCEPTION_POINTERS structure as a parameter, which contains the pointer to the processor's context record in the moment exception was thrown. Passing this pointer to StackWalk64 function gives the stack trace in the moment of exception. So, this problem can be solved by using SEH-style exception handling instead of C++ style.

    Example code:

    #include <stdlib.h>
    #include <locale.h>
    #include <stdio.h>
    #include <tchar.h>
    
    #include <process.h>
    #include <iostream>
    #include <Windows.h>
    #include "dbghelp.h"
    
    using namespace std;
    
    const int MaxNameLen = 256;
    
    #pragma comment(lib,"Dbghelp.lib")
    
    void printStack( CONTEXT* ctx ) //Prints stack trace based on context record
    {
        BOOL    result;
        HANDLE  process;
        HANDLE  thread;
        HMODULE hModule;
    
        STACKFRAME64        stack;
        ULONG               frame;    
        DWORD64             displacement;
    
        DWORD disp;
        IMAGEHLP_LINE64 *line;
    
        char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
        char name[MaxNameLen];
        char module[MaxNameLen];
        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
    
        memset( &stack, 0, sizeof( STACKFRAME64 ) );
    
        process                = GetCurrentProcess();
        thread                 = GetCurrentThread();
        displacement           = 0;
    #if !defined(_M_AMD64)
        stack.AddrPC.Offset    = (*ctx).Eip;
        stack.AddrPC.Mode      = AddrModeFlat;
        stack.AddrStack.Offset = (*ctx).Esp;
        stack.AddrStack.Mode   = AddrModeFlat;
        stack.AddrFrame.Offset = (*ctx).Ebp;
        stack.AddrFrame.Mode   = AddrModeFlat;
    #endif
    
        SymInitialize( process, NULL, TRUE ); //load symbols
    
        for( frame = 0; ; frame++ )
        {
            //get next call from stack
            result = StackWalk64
            (
    #if defined(_M_AMD64)
                IMAGE_FILE_MACHINE_AMD64
    #else
                IMAGE_FILE_MACHINE_I386
    #endif
                ,
                process,
                thread,
                &stack,
                ctx,
                NULL,
                SymFunctionTableAccess64,
                SymGetModuleBase64,
                NULL
            );
    
            if( !result ) break;        
    
            //get symbol name for address
            pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
            pSymbol->MaxNameLen = MAX_SYM_NAME;
            SymFromAddr(process, ( ULONG64 )stack.AddrPC.Offset, &displacement, pSymbol);
    
            line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
            line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);       
    
            //try to get line
            if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line))
            {
                printf("\tat %s in %s: line: %lu: address: 0x%0X\n", pSymbol->Name, line->FileName, line->LineNumber, pSymbol->Address);
            }
            else
            { 
                //failed to get line
                printf("\tat %s, address 0x%0X.\n", pSymbol->Name, pSymbol->Address);
                hModule = NULL;
                lstrcpyA(module,"");        
                GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 
                    (LPCTSTR)(stack.AddrPC.Offset), &hModule);
    
                //at least print module name
                if(hModule != NULL)GetModuleFileNameA(hModule,module,MaxNameLen);       
    
                printf ("in %s\n",module);
            }       
    
            free(line);
            line = NULL;
        }
    }
    
    //******************************************************************************
    
    void function2()
    {
        int a = 0;
        int b = 0;
        throw exception();
    }
    
    void function1()
    {
        int a = 0;
        function2();
    }
    
    void function0()
    {
        function1();
    }
    
    int seh_filter(_EXCEPTION_POINTERS* ex)
    {
        printf("*** Exception 0x%x occured ***\n\n",ex->ExceptionRecord->ExceptionCode);    
        printStack(ex->ContextRecord);
    
        return EXCEPTION_EXECUTE_HANDLER;
    }
    
    static void threadFunction(void *param)
    {    
    
        __try
        {
             function0();
        }
        __except(seh_filter(GetExceptionInformation()))
        {       
            printf("Exception \n");         
        }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {   
        _beginthread(threadFunction, 0, NULL);
        printf("Press any key to exit.\n");
        cin.get();
        return 0;
    }
    

    Example output (first two entries are noise, but the rest correctly reflects functions that caused exception):

    *** Exception 0xe06d7363 occured ***
    
            at RaiseException, address 0xFD3F9E20.
    in C:\Windows\system32\KERNELBASE.dll
            at CxxThrowException, address 0xDBB5A520.
    in C:\Windows\system32\MSVCR110D.dll
            at function2 in c:\work\projects\test\test.cpp: line: 146: address: 0x3F9C6C00
            at function1 in c:\work\projects\test\test.cpp: line: 153: address: 0x3F9C6CB0
            at function0 in c:\work\projects\test\test.cpp: line: 158: address: 0x3F9C6CE0
            at threadFunction in c:\work\projects\test\test.cpp: line: 174: address: 0x3F9C6D70
            at beginthread, address 0xDBA66C60.
    in C:\Windows\system32\MSVCR110D.dll
            at endthread, address 0xDBA66E90.
    in C:\Windows\system32\MSVCR110D.dll
            at BaseThreadInitThunk, address 0x773C6520.
    in C:\Windows\system32\kernel32.dll
            at RtlUserThreadStart, address 0x775FC520.
    in C:\Windows\SYSTEM32\ntdll.dll
    

    Another option is to create custom exception class that captures context in constructor and use it (or derived classes) to throw exceptions:

    class MyException{
    public:
        CONTEXT Context;
    
        MyException(){
            RtlCaptureContext(&Context);        
        }
    };
    
    void function2()
    {    
        throw MyException();    
    }
    
    //...   
    
    try
    {
         function0();
    }
    catch (MyException& e)
    {       
        printf("Exception \n");     
        printStack(&e.Context);                 
    }
    
    0 讨论(0)
  • 2020-12-04 23:43

    do you miss the call to below? SymInitialize(process, NULL, TRUE); SymSetOptions(SYMOPT_LOAD_LINES);

    0 讨论(0)
提交回复
热议问题