|
|
// 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 <dbghelp.h>
#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(); }
|