StackWalk64() 返回单帧

Posted

技术标签:

【中文标题】StackWalk64() 返回单帧【英文标题】:StackWalk64() returns a single frame 【发布时间】:2011-12-04 21:54:28 【问题描述】:

当试图获取某个进程的线程的调用堆栈时,我总是得到一个相同的帧,尽管它肯定有更多(至少 5 个帧)。

StackWalk64() 在第一次调用时总是成功 - 返回一个帧:

AddrPC.Offset = 18446744072850558156

但是,在第二次调用时,它立即失败,错误 id 为 998-ERROR_NOACCESS(可能这个错误不是因为这个调用,正如 MSDN 所说)。

此外,尝试使用 SymFromAddr() 将此地址解析为其符号名称失败,并出现错误 126-ERROR_MOD_NOT_FOUND(在成功调用 SymInitialize(m_processHandler,NULL,TRUE) 之后)。

代码如下:

#ifdef _M_IX86
  //
  // Disable global optimization and ignore /GS waning caused by
  // inline assembly.
  //
  #pragma optimize( "g", off )
  #pragma warning( push )
  #pragma warning( disable : 4748 )
#endif

bool EchoProfiler::getThreadStackTrace(__in HANDLE h_thread, __out vector<DWORD64> &framesVec)

CONTEXT threadContext;
if (GetThreadContext(h_thread, &threadContext) == 0)

    cout << "Error: GetThreadContext() failed with error ID " << GetLastError() << endl;
    return false;


//initialize stack frame
DWORD MachineType;
STACKFRAME64 StackFrame;
ZeroMemory( &StackFrame, sizeof( STACKFRAME64 ) );

MachineType                 = IMAGE_FILE_MACHINE_I386;
StackFrame.AddrPC.Offset    = threadContext.Eip;
StackFrame.AddrPC.Mode      = AddrModeFlat;
StackFrame.AddrFrame.Offset = threadContext.Ebp;
StackFrame.AddrFrame.Mode   = AddrModeFlat;
StackFrame.AddrStack.Offset = threadContext.Esp;
StackFrame.AddrStack.Mode   = AddrModeFlat;

PVOID contextRec = (MachineType == IMAGE_FILE_MACHINE_I386) ? NULL : &threadContext;
int i=0;
// enumerate all the frames in the stack
for (i=1 ; ; i++)

    if (StackWalk64( MachineType, targetProcessHandler, h_thread, &StackFrame,
        contextRec, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ) == false)
    
        // in case it failed or we have finished walking the stack.
        cout << "Error: StackWalk64() failed with error ID " << GetLastError() << endl;
        i--;
        break;
        // return false;
    

    if ( StackFrame.AddrPC.Offset != 0 )
    
        // Valid frame.
        cout << "Frame #" << i << " address - " << StackFrame.AddrPC.Offset << endl;
        framesVec.push_back(StackFrame.AddrPC.Offset);
    
    else
    
        // Base reached.
        break;
    


//cout << "StackWalk64 found " << i << " stack frames:" << endl;
//i = 1;
//for (FramesConstItr itr=framesVec.begin() ; itr != framesVec.end() ; itr++ , i++)
//  cout << i << " - " << *itr << endl;

return true;


#ifdef _M_IX86
  #pragma warning( pop )
  #pragma optimize( "g", on )
#endif

会是什么?

【问题讨论】:

什么是targetProcessHandler,它是如何初始化的? 使用HANDLE targetProcessHandler = OpenProcess(PROCESS_ALL_ACCESS,false,pId)初始化 【参考方案1】:

解决方案:

我错过了必须正确初始化上下文结构的部分。 添加以下内容解决了我的问题:

memset(&threadContext, 0, sizeof(CONTEXT));
threadContext.ContextFlags = CONTEXT_FULL;

谢谢

【讨论】:

你为什么要在这里禁用优化,Hagay ?看起来你这样做是绝对正确的,但是为什么,它是必需的?如果我没有在我的应用程序中禁用优化,我会得到不正确的堆栈。我在堆栈打印机应用程序的输出中看到更少的函数地址。你知道可能是什么原因吗?【参考方案2】:

对于将来遇到此问题的任何人,当我从与当前进程不同的进程获取堆栈信息时,我也在我们自己的本地代码库中遇到了这个问题。原因是我们在使用 OpenProcess() 获取进程句柄时缺少 PROCESS_VM_READ。

【讨论】:

以上是关于StackWalk64() 返回单帧的主要内容,如果未能解决你的问题,请参考以下文章

StackWalk64

Stackwalk 在调试中获取函数名、行号和文件名

C++/Windows 多线程同步/数据共享

AS3:单帧时间轴重复功能

Dicom多帧影像的单帧提取功能的实现

为啥 base64.b64encode() 返回一个字节对象?