/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
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++;
}
}
}
Walking the stack in C++ with MinGW32
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:
Can anyone spot anything I'm missing or anything that is wrong ? Thanks for your help.
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
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
Share:
This topic is closed to new replies.
Advertisement
Advertisement
