如何获取进程中运行的所有线程的堆栈跟踪?

时间:2012-02-28 11:10:40

标签: c++ winapi stack-trace

我正在尝试为多个线程运行的进程获取堆栈跟踪,我能够获得主线程的堆栈跟踪。但对于其他线程(属于同一进程),即使我使用了正确的threadIds,我也得到了所有线程的相同堆栈跟踪(与主线程相同)。我确信那些线程的跟踪不正确。

以下是代码,我不知道出了什么问题。如果您有任何想法,请告诉我。感谢..

我的pExPtrs为空,我在异常期间没有调用它。

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 ;
        }
    }
  }

1 个答案:

答案 0 :(得分:0)

线程快照的句柄与线程的句柄不同。在快照句柄上调用Suspend / ResumeThread是不正确的(如果不是它可能是危险的,如果你SuspendThread 这个线程怎么办?)。您需要使用线程ID的OpenThread来获取可与StackWalk64一起使用的句柄。

同样,假设GET_CURRENT_CONTEXT在当前线程上运行它将是不正确的。如果它适用于hnd,它再次无效,因为它不是线程句柄。