// Ruler // 1 2 3 4 5 6 7 8 //345678901234567890123456789012345678901234567890123456789012345678901234567890 /********************************************************************/ /* */ /* The standard layout. */ /* */ /* The standard layout for 'cpp' files in this code is as */ /* follows: */ /* */ /* 1. Include files. */ /* 2. Constants local to the class. */ /* 3. Data structures local to the class. */ /* 4. Data initializations. */ /* 5. Static functions. */ /* 6. Class functions. */ /* */ /* The constructor is typically the first function, class */ /* member functions appear in alphabetical order with the */ /* destructor appearing at the end of the file. Any section */ /* or function this is not required is simply omitted. */ /* */ /********************************************************************/ #include "LibraryPCH.hpp" #ifndef DISABLE_DEBUG_HELP #include #endif #include "CallStack.hpp" /********************************************************************/ /* */ /* Compiler options. */ /* */ /* Ensure that the last function call(s) before 'StackWalk' */ /* are not FPO-optimized. */ /* */ /********************************************************************/ #pragma optimize("y", off) /********************************************************************/ /* */ /* Constants local to the class. */ /* */ /* The constants supplied here control the debug buffer size. */ /* */ /********************************************************************/ CONST SBIT32 MaxBufferSize = 512; CONST SBIT32 SymbolNameLength = 512; /********************************************************************/ /* */ /* Static member initialization. */ /* */ /* Static member initialization sets the initial value for all */ /* static members. */ /* */ /********************************************************************/ BOOLEAN CALL_STACK::Active = False; SBIT32 CALL_STACK::Activations = 0; HANDLE CALL_STACK::Process = NULL; SPINLOCK CALL_STACK::Spinlock = NULL; /********************************************************************/ /* */ /* Class constructor. */ /* */ /* Create a call stack class and initialize it. This call is */ /* not thread safe and should only be made in a single thread */ /* environment. */ /* */ /********************************************************************/ CALL_STACK::CALL_STACK( VOID ) { // // Claim a lock to prevent multiple threads // from using the symbol lookup mechanism. // Spinlock.ClaimLock(); #ifndef DISABLE_DEBUG_HELP // // We will activate the symbols if they are // not already available. // if ( ! Active ) { // // Setup the process handle, load image help // and then load any available symbols. // Process = GetCurrentProcess(); // // Setup the image help library. // if ( ! (Active = ((BOOLEAN) SymInitialize( Process,NULL,TRUE ))) ) { // // We only issue the warning message once // when we fail to load the symbols. // if ( Activations == 0 ) { // // Format the error message and output it // to the debug stream. // DebugPrint ( "Missing or mismatched symbols files: %x\n", HRESULT_FROM_WIN32( GetLastError() ) ); } } } // // We keep track of the number of activations // so we can delete the symbols at the // required point. // Activations ++; #endif // // Release the lock. // Spinlock.ReleaseLock(); // // Update the available symbols. // UpdateSymbols(); } /********************************************************************/ /* */ /* Extract the current call stack. */ /* */ /* Extract the current call stack and return it to the caller */ /* so it can be used later. */ /* */ /********************************************************************/ SBIT32 CALL_STACK::GetCallStack ( VOID *Frames[], SBIT32 MaxFrames, SBIT32 SkipFrames ) { REGISTER SBIT32 Count = 0; #ifndef DISABLE_DEBUG_HELP // // We can only examine the symbol information if // we were able to load image help. // if ( Active ) { REGISTER CONTEXT Context; REGISTER HANDLE Thread; REGISTER SBIT32 MachineType; REGISTER STACKFRAME StackFrame; // // Zero all the data structures to make // sure they are clean. // ZeroMemory( & Context,sizeof(CONTEXT) ); ZeroMemory( & StackFrame,sizeof(STACKFRAME) ); // // Setup the necessary flags and extract // the thread context. // Context.ContextFlags = CONTEXT_FULL; MachineType = IMAGE_FILE_MACHINE_I386; Thread = GetCurrentThread(); GetThreadContext( Thread,& Context ); // // Extract the details of the current // stack frame. // _asm { mov StackFrame.AddrStack.Offset, esp mov StackFrame.AddrFrame.Offset, ebp mov StackFrame.AddrPC.Offset, offset DummyLabel DummyLabel: } StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrFrame.Mode = AddrModeFlat; // // Claim a lock to prevent multiple threads // from using the symbol lookup mechanism. // Spinlock.ClaimLock(); // // Walk the stack frames extracting the // details from each frame examined. // while ( Count < MaxFrames ) { // // Walk the each stack frame. // if ( StackWalk ( MachineType, Process, Thread, & StackFrame, & Context, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ) ) { // // Examine and process the current // stack frame. // if ( SkipFrames <= 0 ) { // // Collect the current function // address and store it. // Frames[ (Count ++) ] = ((VOID*) StackFrame.AddrPC.Offset); } else { SkipFrames --; } } else { break; } } // // Release the lock. // Spinlock.ReleaseLock(); } #endif return Count; } /********************************************************************/ /* */ /* Format a call stack. */ /* */ /* We format an entire call stack into a single string ready */ /* for output. */ /* */ /********************************************************************/ VOID CALL_STACK::FormatCallStack ( CHAR *Buffer, VOID *Frames[], SBIT32 MaxBuffer, SBIT32 MaxFrames ) { #ifndef DISABLE_DEBUG_HELP // // We can only examine the symbol information if // we were able to load image help. // if ( Active ) { REGISTER SBIT32 Count; // // Delete any existing string. // strcpy( Buffer,"" ); // // Format each frame and then update the // main buffer. // for ( Count=0;Count < MaxFrames;Count ++ ) { AUTO CHAR NewSymbol[ MaxBufferSize ]; REGISTER SBIT32 Size; // // Format the symbol. // FormatSymbol( Frames[ Count ],NewSymbol,MaxBufferSize ); // // Make sure there is enough space in the // output buffer. // if ( ((Size = strlen( NewSymbol )) + 1) < MaxBuffer) { // // Copy the symbol into the buffer. // strcpy( Buffer,NewSymbol ); Buffer += Size; strcpy( Buffer ++,"\n" ); MaxBuffer -= (Size + 1); } else { break; } } } else { strcpy( Buffer,"" ); } #else strcpy( Buffer,"" ); #endif } #ifndef DISABLE_DEBUG_HELP /********************************************************************/ /* */ /* Format a single symbol. */ /* */ /* We format a single simple converting it from an address to */ /* a text string. */ /* */ /********************************************************************/ BOOLEAN CALL_STACK::FormatSymbol ( VOID *Address, CHAR *Buffer, SBIT32 MaxBuffer ) { AUTO CHAR SymbolBuffer[ (sizeof(IMAGEHLP_SYMBOL) + SymbolNameLength) ]; AUTO IMAGEHLP_MODULE Module = { 0 }; REGISTER BOOLEAN Result = True; REGISTER PIMAGEHLP_SYMBOL Symbol = ((PIMAGEHLP_SYMBOL) SymbolBuffer); // // Setup values ready for main symbol // extraction function body. // Module.SizeOfStruct = sizeof(IMAGEHLP_MODULE); ZeroMemory( Symbol,(sizeof(IMAGEHLP_SYMBOL) + SymbolNameLength) ); Symbol -> SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); Symbol -> MaxNameLength = SymbolNameLength; // // Claim a lock to prevent multiple threads // from using the symbol lookup mechanism. // Spinlock.ClaimLock(); // // Extract the module information for the // symbol and format it. // if ( SymGetModuleInfo( Process,((DWORD) Address),& Module ) ) { REGISTER SBIT32 Size; // // Make sure there is enough space in the // output buffer. // if ( ((Size = strlen( Module.ModuleName )) + 1) < MaxBuffer) { // // Copy the module name into the buffer. // strcpy( Buffer,Module.ModuleName ); Buffer += Size; strcpy( Buffer ++,"!" ); MaxBuffer -= (Size + 1); } } else { REGISTER SBIT32 Size; // // Make sure there is enough space in the // output buffer. // if ( (Size = strlen( "None!" )) < MaxBuffer) { // // Copy the module name into the buffer. // strcpy( Buffer,"None!" ); Buffer += Size; MaxBuffer -= Size; } // // We failed to extract the module name. // Result = False; } // // We will not even bother to try to decode // the symbol if we can't decode the module. // if ( Result ) { AUTO CHAR SymbolName[ SymbolNameLength ]; AUTO DWORD Offset = 0; // // Try to convert the symbol from an // address to a name. // if ( SymGetSymFromAddr ( Process, ((DWORD) Address), & Offset, Symbol ) ) { REGISTER SBIT32 Size; // // Try to undecorate the name. If // this fails just use the decorated // name is it is better than nothing. // if ( ! SymUnDName( Symbol,SymbolName,sizeof(SymbolName) ) ) { lstrcpynA( SymbolName,& Symbol->Name[1],sizeof(SymbolName) ); } // // Make sure there is enough space in the // output buffer. // if ( (Size = strlen( SymbolName )) < MaxBuffer) { // // Copy the symbol name into the buffer. // strcpy( Buffer,SymbolName ); Buffer += Size; MaxBuffer -= Size; } // // Format the offset if is is non-zero. // if ( Offset != 0 ) { // // Format the symbol offset. // sprintf( SymbolName,"+0x%x",Offset ); // // Make sure there is enough space in the // output buffer. // if ( (Size = strlen( SymbolName )) < MaxBuffer) { // // Copy the symbol name into the buffer. // strcpy( Buffer,SymbolName ); Buffer += Size; MaxBuffer -= Size; } } } else { REGISTER SBIT32 Size; // // Format the symbol address. // sprintf( SymbolName,"0x%p",Address ); // // Make sure there is enough space in the // output buffer. // if ( (Size = strlen( SymbolName )) < MaxBuffer) { // // Copy the symbol name into the buffer. // strcpy( Buffer,SymbolName ); Buffer += Size; MaxBuffer -= Size; } // // We failed to extract the symbol name. // Result = False; } } else { AUTO CHAR SymbolName[ SymbolNameLength ]; REGISTER SBIT32 Size; // // Format the symbol address. // sprintf( SymbolName,"0x%p",Address ); // // Make sure there is enough space in the // output buffer. // if ( (Size = strlen( SymbolName )) < MaxBuffer) { // // Copy the symbol name into the buffer. // strcpy( Buffer,SymbolName ); Buffer += Size; MaxBuffer -= Size; } } // // Release the lock. // Spinlock.ReleaseLock(); return Result; } /********************************************************************/ /* */ /* Load symbols callback. */ /* */ /* When we load the symbols we get a callback for every module */ /* that is currently loaded into the application. */ /* */ /********************************************************************/ BOOL STDCALL CALL_STACK::UpdateSymbolCallback ( PSTR Module, ULONG_PTR BaseOfDLL, ULONG SizeOfDLL, VOID *Context ) { if ( SymGetModuleBase( Process,BaseOfDLL ) == 0 ) { SymLoadModule( Process,NULL,Module,NULL,BaseOfDLL,SizeOfDLL ); } return TRUE; } #endif /********************************************************************/ /* */ /* Load the symbols. */ /* */ /* Load the symbols for the current process so we can translate */ /* code addresses into names. */ /* */ /********************************************************************/ BOOLEAN CALL_STACK::UpdateSymbols( VOID ) { REGISTER BOOLEAN Result = True; #ifndef DISABLE_DEBUG_HELP // // We can only examine the symbol information if // we were able to load image help. // if ( Active ) { // // Claim a lock to prevent multiple threads // from using the symbol lookup mechanism. // Spinlock.ClaimLock(); // // Enumaerate all of the loaded modules and // cascade load all of the symbols. // if ( ! EnumerateLoadedModules( Process,UpdateSymbolCallback,NULL ) ) { // // Format the error message and output it // to the debug window. // DebugPrint ( "EnumerateLoadedModules returned: %x\n", HRESULT_FROM_WIN32( GetLastError() ) ); Result = False; } // // Release the lock. // Spinlock.ReleaseLock(); } #endif return Result; } /********************************************************************/ /* */ /* Class destructor. */ /* */ /* Destory the call stack. This call is not thread safe and */ /* should only be made in a single thread environment. */ /* */ /********************************************************************/ CALL_STACK::~CALL_STACK( VOID ) { // // Claim a lock to prevent multiple threads // from using the symbol lookup mechanism. // Spinlock.ClaimLock(); #ifndef DISABLE_DEBUG_HELP // // Cleanup the symbol library. // if ( ((-- Activations) == 0) && (Active) ) { Active = False; // // I don't understand why this does not work at // the moment so I will fix it later. // // SymCleanup( Process ); // // Just to be neat lets zero everything. // Process = NULL; } #endif // // Release the lock. // Spinlock.ReleaseLock(); }