Windows 解析托管堆栈跟踪和本机堆栈跟踪-使用哪种API?
这是我上一个问题的继续——可以说是第二阶段 第一个问题是: 现在我已经解析了大量堆栈跟踪,现在想知道如何解析托管堆栈帧的符号信息 <本地C++侧比较简单—— 首先,指定从何处获取符号的进程:Windows 解析托管堆栈跟踪和本机堆栈跟踪-使用哪种API?,windows,stack-trace,mixed-mode,Windows,Stack Trace,Mixed Mode,这是我上一个问题的继续——可以说是第二阶段 第一个问题是: 现在我已经解析了大量堆栈跟踪,现在想知道如何解析托管堆栈帧的符号信息 另外还有一个问题,就是以后解析堆栈跟踪可能不起作用。您可以看到—开发人员可以使用Jit引擎/IL生成器动态生成代码,并对其进行处理—因此,在获得“void*”/指令地址后,您应该立即解析符号信息,而不是事后解析。但我暂时不讨论这个问题,假设开发人员不是一个太花哨的编码员,不会一直生成和处理新代码,并且不会无需调用FreeLibrary。(如果稍后我将钩住FreeLi
HANDLE g_hProcess = GetCurrentProcess();
您可以在运行时使用如下代码替换流程:
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_processId);
b = (g_hProcess != NULL );
if( !b )
errInfo.AppendFormat(_T("Process id '%08X' is not running anymore."), g_processId );
else
InitSymbolLoad();
extern HANDLE g_hProcess;
void StackFrame::Resolve()
{
struct {
union
{
SYMBOL_INFO symbol;
char buf[sizeof(SYMBOL_INFO) + 1024];
}u;
}ImageSymbol = { 0 };
HANDLE hProcess = g_hProcess;
DWORD64 offsetFromSymbol = 0;
ImageSymbol.u.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
ImageSymbol.u.symbol.Name[0] = 0;
ImageSymbol.u.symbol.MaxNameLen = sizeof(ImageSymbol) - sizeof(SYMBOL_INFO);
SYMBOL_INFO* pSymInfo = &ImageSymbol.u.symbol;
// Get file / line of source code.
IMAGEHLP_LINE64 lineStr = { 0 };
lineStr.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
function.clear();
if( SymGetLineFromAddr64(hProcess, (DWORD64)ip, (DWORD*)&offsetFromSymbol, &lineStr) )
{
function = lineStr.FileName;
function += "(";
function += std::to_string((_ULonglong) lineStr.LineNumber).c_str();
function += "): ";
}
// Successor of SymGetSymFromAddr64.
if( SymFromAddr(hProcess, (DWORD64)ip, &offsetFromSymbol, pSymInfo) )
function += ImageSymbol.u.symbol.Name;
}
并初始化符号加载:
void InitSymbolLoad()
{
SymInitialize(g_hProcess, NULL, TRUE);
DWORD dwFlags = SymGetOptions();
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_NO_IMAGE_SEARCH);
}
然后,解析本机符号,不知何故如下:
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_processId);
b = (g_hProcess != NULL );
if( !b )
errInfo.AppendFormat(_T("Process id '%08X' is not running anymore."), g_processId );
else
InitSymbolLoad();
extern HANDLE g_hProcess;
void StackFrame::Resolve()
{
struct {
union
{
SYMBOL_INFO symbol;
char buf[sizeof(SYMBOL_INFO) + 1024];
}u;
}ImageSymbol = { 0 };
HANDLE hProcess = g_hProcess;
DWORD64 offsetFromSymbol = 0;
ImageSymbol.u.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
ImageSymbol.u.symbol.Name[0] = 0;
ImageSymbol.u.symbol.MaxNameLen = sizeof(ImageSymbol) - sizeof(SYMBOL_INFO);
SYMBOL_INFO* pSymInfo = &ImageSymbol.u.symbol;
// Get file / line of source code.
IMAGEHLP_LINE64 lineStr = { 0 };
lineStr.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
function.clear();
if( SymGetLineFromAddr64(hProcess, (DWORD64)ip, (DWORD*)&offsetFromSymbol, &lineStr) )
{
function = lineStr.FileName;
function += "(";
function += std::to_string((_ULonglong) lineStr.LineNumber).c_str();
function += "): ";
}
// Successor of SymGetSymFromAddr64.
if( SymFromAddr(hProcess, (DWORD64)ip, &offsetFromSymbol, pSymInfo) )
function += ImageSymbol.u.symbol.Name;
}
这看起来很有效
但现在也管理堆栈帧
我找到了两个接口:
- (*)(包括示例代码)
- (4年内未触及代码)
- 混合模式stackwalk文章提供了一个很好的例子
- IXCLRDATAProcess/GetRuntimeNameByAddress
- 上面的两个链接也提到了
- 由process hacker使用(GPL许可证,C风格)
- (基于提交,此代码非常活跃)
- ICorProfiler/
因此,根据我的简要分析,我猜方法2是唯一值得使用的好的/活跃的API接口?您是否遇到过与这些api相关的任何问题。以下是Jan Kotas对此的回答:
From: Jan Kotas <jkotas@microsoft.com>
To: Tarmo Pikaro <tapika@yahoo.com>
Sent: Tuesday, January 12, 2016 5:09 AM
Subject: RE: Fast capture stack trace on windows 64 bit / mixed mode...
Your solution based on IXCLRDATAProcess sounds good to me.
PerfView (https://www.microsoft.com/en-us/download/details.aspx?id=28567) –
that does what you are trying to build as well as a lot of other stuff – is
using IXCLRDATA* as well. You may be interested in
https://github.com/Microsoft/clrmd . It is set of managed wrappers for
IXCLRDATA* that are easier to use than the COM interfaces.
发件人:Jan Kotas
收件人:Tarmo Pikaro
发送时间:2016年1月12日星期二上午5:09
主题:RE:windows 64位/混合模式下的快速捕获堆栈跟踪。。。
我觉得你基于IXCLRDATAProcess的解决方案不错。
性能视图(https://www.microsoft.com/en-us/download/details.aspx?id=28567) –
这就是你想要构建的东西,以及很多其他东西——是的
还可以使用IXCLRDATA*。你可能对我感兴趣
https://github.com/Microsoft/clrmd . 它是一组用于
IXCLRDATA*比COM接口更易于使用。
我简要试用过的内容—这需要Visual Studio 2015/C#6.0
而且这种技术是无法使用的。像.net StackTrace/StackFrame一样,我们正在立即解析调用堆栈和符号信息,之后我需要解析符号信息(在捕获堆栈跟踪之后)。备选方案1/IDebugClient/GetNameByOffset不可用于托管堆栈跟踪,它只能用于本机代码-对于本机调用堆栈,我已经有了上面的演示代码snipet。不确定IDebugClient是否提供了SymGetLineFromAddr64/SymFromAddr没有提供的功能-不确定。在浏览了大量的代码示例和接口之后,我了解到没有任何简单易用的API接口。为本地C++开发的代码和API仅使用本机C++,而开发用于托管代码的代码和API仅使用托管代码。p> 另外还有一个问题,就是以后解析堆栈跟踪可能不起作用。您可以看到—开发人员可以使用Jit引擎/IL生成器动态生成代码,并对其进行处理—因此,在获得“void*”/指令地址后,您应该立即解析符号信息,而不是事后解析。但我暂时不讨论这个问题,假设开发人员不是一个太花哨的编码员,不会一直生成和处理新代码,并且不会无需调用FreeLibrary。(如果稍后我将钩住FreeLibrary/Jit组件,可能我可以解决这个问题。) 解析函数名非常简单,通过IXCLRDataProcess使用一点魔法和运气-我能够得到函数名,但是-我想把它扩展得更深-精确到执行代码的源代码路径和源代码行,这变成了非常复杂的功能 最后,我偶然发现了执行这类操作的源代码——它是在这里完成的: GetLineByOffset是该文件中的函数名 我已经从源代码中分析、重新调整并制定了自己的解决方案,现在将其附在这里: 更新的代码可从以下位置找到: 但这里只是在某个时间点拍摄的相同代码的快照: 第M.h节:
#pragma once
#include <afx.h>
#pragma warning (disable: 4091) //dbghelp.h(1544): warning C4091: 'typedef ': ignored on left of '' when no variable is declared
#include <cor.h> //xclrdata.h requires this
#include "xclrdata.h" //IXCLRDataProcess
#include <atlbase.h> //CComPtr
#include <afxstr.h> //CString
#include <crosscomp.h> //TCONTEXT
#include <Dbgeng.h> //IDebugClient
#pragma warning (default: 4091)
class ResoveStackM
{
public:
ResoveStackM();
~ResoveStackM();
void Close(void);
bool InitSymbolResolver(HANDLE hProcess, CString& lastError);
bool GetMethodName(void* ip, CStringA& methodName);
bool GetManagedFileLineInfo(void* ip, CStringA& lineInfo);
HMODULE mscordacwks_dll;
CComPtr<IXCLRDataProcess> clrDataProcess;
CComPtr<ICLRDataTarget> target;
CComPtr<IDebugClient> debugClient;
CComQIPtr<IDebugControl> debugControl;
CComQIPtr<IDebugSymbols> debugSymbols;
CComQIPtr<IDebugSymbols3> debugSymbols3;
};
//
// Typically applications don't need more than one instance of this. If you do, use your own copies.
//
extern ResoveStackM g_managedStackResolver;
#pragma一次
#包括
#pragma warning(disable:4091)//dbghelp.h(1544):警告C4091:'typedef':当未声明变量时,忽略“”左侧的
#include//xclrdata.h需要此
#包括“xclrdata.h”//IXCLRDataProcess
#包括//CComPtr
#包括//CString
#include//t上下文
#包括//IDebugClient
#pragma警告(默认值:4091)
类ResoveStackM
{
公众:
ResoveStackM();
~ResoveStackM();
作废关闭(作废);
bool InitSymbolResolver(句柄hProcess、CString和lastError);
bool GetMethodName(void*ip、CStringA和methodName);
bool getmanagedFileInfo(void*ip、CStringA和lineInfo);
HMODULE mscordacwks_dll;
CComPtr clrDataProcess;
CComPtr目标;
CComPtr调试客户端;
CComQIPtr调试控制;
CComQIPtr调试符号;
CComQIPtr调试符号3;
};
//
//通常,应用程序不需要多个实例。如果有,请使用您自己的副本。
//
管理的外部资源