Walking the stack in C++ with MinGW32

Started by
0 comments, last by Jan Wassenberg 13 years, 11 months ago
Advertisement
Darragh
Author
308
July 31, 2007 05:14 AM
Hi everyone. At the moment I'm writing exception handling code that should show a message box on a crash to the user along with a backtrace of where the program has been up till the crash. I was originally intending on doing a stack trace manually by putting in appropriate code at the beginning and end of each function, but I figured this would be time consuming ( I have 70k lines of code in this project ) and also error prone so I looked for a better way of doing it. So, I found out about the Win32 StackWalk() function and I've been trying ( unsuccessfully ) to get it working. It is displaying a stack trace at the moment, but it's not the correct stack trace. I stuck in a simple line of code in my main() function that tried to manipulate a null pointer and got the following stack trace: 1 - 0x004086fc 2 - 0x004010ab 3 - 0x7c862cd3 UnhandledExceptionFilter 4 - 0x7c843622 FindAtomW Running the debugger and allowing it to capture the exception I get the following: 1 - 0x0043ede5 WinMain (Main.cpp:96) 2 - 0x00496e3a main (C:/Program Files/CodeBlocks/bin/../lib/gcc/mingw32/3.4.4/../../../../include/c++/3.4.4/bits/stl_algobase.h:150) Clearly something is going wrong.. I'm not even getting the right addresses or the right amount of calls. Here's the code I'm using to find the stack trace:


/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Errors::appendCallTrace()
Purpose:  Appends a list of the last 8 functions called to the given error string.
Params:   String to append the list of functions called to.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Errors::appendCallTrace( string & errorMessage )
{
    // Store the names of all the functions called here:

    list<string> functionNames;

    // Initialise the symbol table to get function names:

    SymInitialize
    (
        GetCurrentProcess(),        // Process to get symbol table for
        0,                          // Where to search for symbol files: dont care
        true                        // Get symbols for all dll's etc. attached to process
    );

    // Store the current stack frame here:

    STACKFRAME frame = { 0 };

    // Get processor information for the current thread:

    CONTEXT context; memset( & context , 0 , sizeof( CONTEXT ) );

    context.ContextFlags = CONTEXT_FULL;

    // Load the RTLCapture context function:

    HINSTANCE kernel32 = LoadLibrary("Kernel32.dll");

    typedef void ( * RtlCaptureContextFunc ) ( CONTEXT * ContextRecord );

    RtlCaptureContextFunc rtlCaptureContext = (RtlCaptureContextFunc) GetProcAddress( kernel32, "RtlCaptureContext" );

    // Capture the thread context

    rtlCaptureContext( & context );

    // Use this to fill the frame structure:

    frame.AddrPC.Offset         = context.Eip;      // Current location in program
    frame.AddrPC.Mode           = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
    frame.AddrStack.Offset      = context.Esp;      // Stack pointers current value
    frame.AddrStack.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing
    frame.AddrFrame.Offset      = context.Ebp;      // Value of register used to access local function variables.
    frame.AddrFrame.Mode        = AddrModeFlat;     // Address mode for this pointer: flat 32 bit addressing

    // Keep getting stack frames from windows till there are no more left:

    while
    (
        StackWalk
        (
            IMAGE_FILE_MACHINE_I386 ,       // MachineType - x86
            GetCurrentProcess()     ,	    // Process to get stack trace for
            GetCurrentThread()      ,	    // Thread to get stack trace for
            & frame                 ,	    // Where to store next stack frame
            & context               ,	    // Pointer to processor context record
            0                       ,	    // Routine to read process memory: use the default ReadProcessMemory
            SymFunctionTableAccess  ,	    // Routine to access the modules symbol table
            SymGetModuleBase        ,       // Routine to access the modules base address
            0                               // Something to do with 16-bit code. Not needed.
        )
    )
    {
            //------------------------------------------------------------------
            // Declare an image help symbol structure to hold symbol info and
            // name up to 256 chars This struct is of variable lenght though so
            // it must be declared as a raw byte buffer.
            //------------------------------------------------------------------

            static char symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 255 ];

            memset( symbolBuffer , 0 , sizeof(IMAGEHLP_SYMBOL) + 255 );

            // Cast it to a symbol struct:

            IMAGEHLP_SYMBOL * symbol = (IMAGEHLP_SYMBOL*) symbolBuffer;

            // Need to set the first two fields of this symbol before obtaining name info:

            symbol->SizeOfStruct    = sizeof(IMAGEHLP_SYMBOL) + 255;
            symbol->MaxNameLength   = 254;

            // The displacement from the beginning of the symbol is stored here: pretty useless

            unsigned displacement = 0;

            // Get the symbol information from the address of the instruction pointer register:

            if
            (
                SymGetSymFromAddr
                (
                    GetCurrentProcess()     ,   // Process to get symbol information for
                    frame.AddrPC.Offset     ,   // Address to get symbol for: instruction pointer register
                    (DWORD*) & displacement ,   // Displacement from the beginning of the symbol: whats this for ?
                    symbol                      // Where to save the symbol
                )
            )
            {
                // Add the name of the function to the function list:

                char buffer[2048]; sprintf( buffer , "0x%08x %s" ,  frame.AddrPC.Offset , symbol->Name );

                functionNames.push_back(buffer);
            }
            else
            {
                // Print an unknown location:

                // functionNames.push_back("unknown location");

                char buffer[64]; sprintf( buffer , "0x%08x" ,  frame.AddrPC.Offset );

                functionNames.push_back(buffer);
            }
    }

    // Cleanup the symbol table:

    SymCleanup( GetCurrentProcess() );


    //--------------------------------------------------------------------------
    // Print the names of all the functions called
    //--------------------------------------------------------------------------

    {
        errorMessage += "\nBacktrace of locations visited by program: \n";

        // Store the number of functions printed here:

        unsigned count = 1;

        // Run through the list of function names

        list<string>::iterator i = functionNames.begin();
        list<string>::iterator e = functionNames.end();

        while ( i != e && count < 17 )
        {
            // Get this function name:

            string & name = * i; i++;

            // Add it to the error message

            errorMessage += "\n";

            static char buffer[8]; itoa(count,buffer,10); errorMessage += buffer;

            errorMessage += " - ";

            errorMessage += name;

            // Increment the number of functions printed

            count++;
        }
    }

}


Can anyone spot anything I'm missing or anything that is wrong ? Thanks for your help.
Jan Wassenberg
July 31, 2007 05:29 PM
You're getting correct results - when an exception is raised, UnhandledExceptionFilter will most likely be on the current call stack.
To dump the call stack of the code that caused the exception, you need to use GetExceptionInformation())->ContextRecord as the context instead of RtlCaptureContext.
HTH+HAND
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
Share:

This topic is closed to new replies.

Advertisement