I am trying to get stack trace for a process running with multiple threads, I am able to get the stack trace for main thread. But for other threads(belongs to same process), even though i used proper threadIds, I am getting the same stacktrace for all threads(same as main thread). I am sure those are not correct traces for that threads.
following is the code, I have no idea what went wrong. If you have any idea please let me know. Thanks..
My pExPtrs is null, i am not calling this during exception.
void DoStackTraces ( LPTSTR szString,DWORD dwSize, EXCEPTION_POINTERS *pExPtrs)
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, static_cast<DWORD>(getpid()));
if (h != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te)) {
do {
if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
sizeof(te.th32OwnerProcessID)) {
if(te.th32OwnerProcessID == static_cast<DWORD>(getpid())) {
std::cout << "Process 0x%04x | Thread 0x%04x\n"
<< te.th32OwnerProcessID << " | " << te.th32ThreadID
<< " Current ProcessID : " << getpid()
<< " dwSize : " << dwSize
<< " pExPtrs : " << pExPtrs
<< std::endl;
HANDLE hnd = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, te.th32ThreadID);
SuspendThread(hnd);
DoStackTraceThread(hnd,szString,dwSize,0);
ResumeThread(hnd);
std::cout << szString << std::endl;
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h, &te));
}
CloseHandle(h);
}
//HANDLE hThread = GetCurrentThread();
//DoStackTraceThread (hThread, szString,dwSize,pExPtrs);
}
void DoStackTraceThread ( HANDLE hThread, LPTSTR szString ,
DWORD dwSize , EXCEPTION_POINTERS *pExPtrs)
{
if (g_bCsysDontGetProcessCritSec){return;}
sAutoLock al(g_stackTraceMux); // The code probably isn't thread safe.
if (g_cSym.isInstalled() == false) return;
HANDLE hProcess = GetCurrentProcess ( ) ;
// If the symbol engine is not initialized, do it now.
if ( FALSE == g_bSymIsInit )
{
DWORD dwOpts = APFSymGetOptions ( ) ;
// Turn on load lines.
APFSymSetOptions ( dwOpts |
SYMOPT_LOAD_LINES ) ;
if ( FALSE == g_cSym.SymInitialize ( hProcess ,
NULL ,
TRUE ) )
{
std::cerr << "APF ERROR: DiagAssert : Unable to initialize the "
"symbol engine!!!" << std::endl;
}
else
{
g_bSymIsInit = TRUE ;
}
}
// The symbol engine is initialized so do the stack walk.
// The array of addresses.
ADDRVECTOR vAddrs ;
// The thread information.
CONTEXT stCtx ;
CONTEXT *pstCtx ;
GET_CURRENT_CONTEXT(stCtx, CONTEXT_FULL);
{
STACKFRAME64 stFrame ;
DWORD dwMachine ;
ZeroMemory ( &stFrame , sizeof ( STACKFRAME64 ) ) ;
stFrame.AddrPC.Mode = AddrModeFlat ;
if (pExPtrs)
{
pstCtx=pExPtrs->ContextRecord;
}
else {
pstCtx=&stCtx;
}
dwMachine = IMAGE_FILE_MACHINE_I386 ;
if (pExPtrs){
stFrame.AddrPC.Offset = pstCtx->Eip ;
stFrame.AddrStack.Offset = pstCtx->Esp ;
stFrame.AddrFrame.Offset = pstCtx->Ebp ;
}
else {
stFrame.AddrPC.Offset = stCtx.Eip ;
stFrame.AddrStack.Offset = stCtx.Esp ;
stFrame.AddrFrame.Offset = stCtx.Ebp ;
}
stFrame.AddrStack.Mode = AddrModeFlat ;
stFrame.AddrFrame.Mode = AddrModeFlat ;
// Loop for the first 512 stack elements.
for ( DWORD i = 0 ; i < 512 ; i++ )
{
if ( FALSE == StackWalkProc ( dwMachine ,
hProcess ,
hThread ,
&stFrame ,
pstCtx ,
NULL ,
(PFUNCTION_TABLE_ACCESS_ROUTINE64)
APFSymFunctionTableAccess ,
GetModBase ,
NULL ) )
{
break ;
}
// Also check that the address is not zero. Sometimes
// StackWalk returns TRUE with a frame of zero.
if ( 0 != stFrame.AddrPC.Offset )
{
vAddrs.push_back ( stFrame.AddrPC.Offset ) ;
}
}
// Now start converting the addresses.
DWORD64 dwSizeLeft = dwSize ;
DWORD64 dwSymSize ;
TCHAR szSym [ MAX_PATH * 2 ] ;
LPTSTR szCurrPos = szString ;
ADDRVECTOR::iterator loop ;
for ( loop = vAddrs.begin ( ) ;
loop != vAddrs.end ( ) ;
loop++ )
{
dwSymSize = DoConvertAddress ( *loop , szSym ) ;
if ( dwSizeLeft <= dwSymSize )
{
break ;
}
_tcscpy ( szCurrPos , szSym ) ;
szCurrPos += dwSymSize ;
dwSizeLeft -= dwSymSize ;
}
}
}
The handle to the thread snapshot is not the same thing as the handle to the thread. Calling Suspend/ResumeThread on the snapshot handle is incorrect (and if it weren’t it could be dangerous, what if you SuspendThread this thread?). You need to OpenThread with the thread id to get a handle that can be used with StackWalk64.
Likewise, assuming GET_CURRENT_CONTEXT operates on the current thread it will not be correct. If it works on hnd, it again won’t work because again, that’s not the thread handle.