这是我们的代码版本。原文来自 MSJ 的一篇文章,但经过大量修改。
bool MSJExceptionHandler::ImagehlpStackWalk( PCONTEXT pCtx )
{
bool result = false;
// PCONTEXT pContext = pEP->ContextRecord;
if( m_Levels == 0 ) {
dbbTrace::OutputWithoutTimestamp( "\nCall stack:\n" );
dbbTrace::OutputWithoutTimestamp( "Address Frame\n" );
}
// Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
STACKFRAME64 sf;
bool first = true;
FIXED_ARRAY( buf, char, 300 );
size_t buflen = sizeof(buf);
// Indexes into MSJExceptionHandler data when m_Levels > 0
int i_line = 0; // Into char * m_Lines[]
int i_buf = 0; // into char m_Buffer[]
memset( &sf, 0, sizeof(sf) );
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
DWORD64 pFrame = 0;
DWORD64 pPrevFrame = 0;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Mode = AddrModeFlat;
#ifdef _M_IX86 // Intel Only!
sf.AddrPC.Offset = pCtx->Eip;
sf.AddrStack.Offset = pCtx->Esp;
sf.AddrFrame.Offset = pCtx->Ebp;
#endif
#ifdef _M_X64 // Intel Only!
sf.AddrPC.Offset = pCtx->Rip;
sf.AddrStack.Offset = pCtx->Rsp;
sf.AddrFrame.Offset = pCtx->Rbp;
#endif
#ifdef _M_IA64
// Itanium
DWORD64 pFrame, pPrevFrame;
#pragma message("fix me")
sf.AddrPC.Offset = 0;
sf.AddrStack.Offset = 0;
sf.AddrFrame.Offset = 0;
#endif
#ifdef _M_IX86
DWORD MachineType = IMAGE_FILE_MACHINE_I386;
#endif
#ifdef _M_X64
DWORD MachineType = IMAGE_FILE_MACHINE_AMD64;
#endif
#ifdef _M_IA64
DWORD MachineType = IMAGE_FILE_MACHINE_IA64;
#endif
while ( 1 )
{
SetLastError( 0 );
if ( ! StackWalk64( MachineType,
m_hProcess,
GetCurrentThread(),
&sf,
pCtx,
0,
SymFunctionTableAccess64,
SymGetModuleBase64,
0 ) )
break;
pFrame = sf.AddrFrame.Offset;
if ( 0 == pFrame ) // Basic sanity check to make sure
break; // the frame is OK. Bail if not.
// dbbTrace::OutputWithoutTimestamp( "%08X %08X ", sf.AddrPC.Offset, pFrame );
if( ! first ) {
if( pFrame <= pPrevFrame ) {
// Sanity check
break;
}
if( (pFrame - pPrevFrame) > 10000000 ) {
// Sanity check
break;
}
}
if( (pFrame % sizeof(void *)) != 0 ) {
// Sanity check
break;
}
pPrevFrame = pFrame;
first = false;
// IMAGEHLP is wacky, and requires you to pass in a pointer to an
// IMAGEHLP_SYMBOL structure. The problem is that this structure is
// variable length. That is, you determine how big the structure is
// at runtime. This means that you can't use sizeof(struct).
// So...make a buffer that's big enough, and make a pointer
// to the buffer. We also need to initialize not one, but TWO
// members of the structure before it can be used.
enum { emMaxNameLength = 512 };
// Use union to ensure proper alignment
union {
SYMBOL_INFO symb;
BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + emMaxNameLength ];
} u;
PSYMBOL_INFO pSymbol = & u.symb;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = emMaxNameLength;
PDWORD64 symDisplacement = 0; // Displacement of the input address,
// relative to the start of the symbol
DWORD lineDisplacement = 0;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(line);
line.LineNumber = 0;
BOOL bLine = FALSE;
bLine = SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
& lineDisplacement, & line );
if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset,
symDisplacement, pSymbol) )
{
if( bLine ) {
_snprintf( buf, buflen,
ADDR_FORMAT " " ADDR_FORMAT " %s() line %d\n",
sf.AddrPC.Offset, pFrame,
pSymbol->Name, line.LineNumber );
} else {
_snprintf( buf, buflen,
ADDR_FORMAT " " ADDR_FORMAT " %s() + %X\n",
sf.AddrPC.Offset, pFrame,
pSymbol->Name, symDisplacement );
}
}
else // No symbol found. Print out the logical address instead.
{
DWORD err = GetLastError();
FIXED_ARRAY( szModule , TCHAR, MAX_PATH );
szModule[0] = '\0';
DWORD section = 0, offset = 0;
GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
szModule, sizeof(szModule), section, offset );
_snprintf( buf, buflen,
ADDR_FORMAT " " ADDR_FORMAT " %04X:%08X %s (err = %d)\n",
sf.AddrPC.Offset, pFrame,
section, offset, szModule, err );
result = true;
}
if( m_Levels == 0 ) {
dbbTrace::OutputString( buf, false );
} else {
// Save line
size_t l = strlen(buf);
if( i_line >= m_Levels || i_buf + l >= m_Bytes ) {
// We have saved all of the stack we can save
break;
}
buf[ l - 1 ] = '\0'; // Remove trailing '\n'
char * s = & m_Buffer[ i_buf ];
m_Lines[ i_line++ ] = s;
strncpy( s, buf, l );
i_buf += l;
}
} // while
return result;
} // MSJExceptionHandler::ImagehlpStackWalk()