在Windows/64位/混合模式下快速捕获堆栈跟踪

Tar*_*aro 8 windows mixed-mode memory-leaks stack-trace

像大多数人一样,你可能知道存在很多不同的机制来从堆栈跟踪开始,从windows api开始,并继续深入神奇的汇编世界 - 让我列举一些我已经研究过的链接.

总之,让我提一下,我想要有混合模式(托管和非托管)/ 64位+ AnyCPU应用程序的内存泄漏分析机制,并且从所有windows api的CaptureStackBackTrace最适合我的需求,但正如我所分析的 - 它不支持托管代码堆栈行走.但是该函数API最接近我所需要的(因为它还计算回跟踪散列 - 特定调用堆栈的唯一标识符).

我已经排除了查找内存泄漏的不同方法 - 我尝试过的大多数软件要么崩溃要么不能正常工作,要么产生不好的结果.

此外,我不想重新编译现有的软件并覆盖malloc/new其他机制 - 因为它是一项繁重的任务(我们拥有大量代码库和大量的dll).此外,我怀疑这不是我需要执行的一次性工作 - 问题以1 - 2年周期回归,具体取决于谁和编码,所以我更希望在应用程序本身内置内存泄漏检测(内存) api挂钩)而不是一遍又一遍地解决这个问题.

http://www.codeproject.com/Articles/11132/Walking-the-callstack

使用StackWalk64 Windows API函数,但不能与托管代码一起使用.此外64位支持还不完全清楚 - 我已经看到64位问题的一些解决方法 - 我怀疑当在同一线程内完成堆栈遍历时,此代码不能完全正常工作.

然后存在进程黑客:http: //processhacker.sourceforge.net/

其中也使用StackWalk64,但扩展了它的回调函数(第7和第8个参数)以支持混合模式堆栈行走.在使用7/8回调函数进行了大量复杂操作之后,我还成功地获得了支持混合模式支持的StackWalk64(将堆栈跟踪作为向量捕获 - 其中每个指针指向调用过去的汇编/ dll位置).但正如您可能猜到的那样 - StackWalk64的性能不足以满足我的需求 - 即使使用C#端的简单消息框,应用程序只需"挂起"一段时间直到它正确启动.

我没有看到CaptureStackBackTrace函数调用的这么大的延迟,所以我认为StackWalk64的性能不足以满足我的需求.

还存在基于COM的堆栈跟踪确定方法 - 如下所示:http: //www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter

http://blog.steveniemitz.com/building-a-mixed-mode-stack-walker-part-1/

但是我害怕 - 它需要COM,并且线程需要进行初始化,并且由于内存api挂钩我不应该触摸任何线程中的com状态,因为它可能导致更严重的问题(例如,不正确的公寓初始化,其他毛刺)

现在我已经达到了Windows API不能满足我自己需求的程度,我需要手动遍历调用堆栈.例如,可以找到这样的例子:

http://www.codeproject.com/Articles/11221/Easy-Detection-of-Memory-Leaks 请参阅函数FillStackInfo/32位,不支持托管代码.

有几个关于反转堆栈跟踪的提及 - 例如在以下链接上:

  1. http://blog.airesoft.co.uk/2009/02/grabbing-kernel-thread-contexts-the-process-explorer-way/
  2. http://cbloomrants.blogspot.fi/2009/01/01-30-09-stack-tracing-on-windows.html
  3. http://www.gamedev.net/topic/364861-stack-dump-on-win32-how-to-get-api-addresses/
  4. http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

特别是1,3,4个链接提供了一些有趣的夜间阅读.:-)

但即便如此,它们也是相当有趣的机制,其中任何一个都没有完全可用的演示示例.

我想其中一个例子是Wine的dbghelp实现(用于linux的Windows"模拟器"),它也显示了StackWalk64最终的工作原理,但我怀疑它与DWARF2文件格式的可执行文件有很大关系,因此它与当前的Windows PE不完全相同可执行文件格式.

有人可以指点我很好地实现堆栈行走,在64位架构上工作,支持混合模式(可以跟踪本机和托管内存分配),这完全是在寄存器/调用堆栈/代码分析中绑定的.(1,3,4的组合实施)

有人与Microsoft开发团队有任何良好的联系,他们可能会回答这个问题吗?

Tar*_*aro 3

9-1-2015 - 我找到了被进程黑客调用的原始函数,那个函数是

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscordacwks.dll OutOfProcessFunctionTableCallback

它的源代码 - 在这里: https: //github.com/dotnet/coreclr/blob/master/src/debug/daccess/fntableaccess.cpp

从那里,我拥有了该源代码中大部分更改的所有者 - Jan Kotas (jkotas@microsoft.com),并就这个问题与他联系。

From: Jan Kotas <jkotas@microsoft.com>
To: Tarmo Pikaro <tapika@yahoo.com> 
Sent: Friday, January 8, 2016 3:27 PM
Subject: RE: Fast capture stack trace on windows 64 bit / mixed mode...

...

The mscordacwks.dll is called mscordaccore.dll in CoreCLR / github repro. The VS project 
files are auto-generated for it during the build 
(\coreclr\bin\obj\Windows_NT.x64.Debug\src\dlls\mscordac\mscordaccore.vcxproj).
You should be able to build and debug CoreCLR to understand how it works.
...

From: Jan Kotas <jkotas@microsoft.com>
To: Tarmo Pikaro <tapika@yahoo.com> 
Sent: Saturday, January 9, 2016 2:02 AM
Subject: RE: Fast capture stack trace on windows 64 bit / mixed mode...

> I've tried to replace 
> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscordacwks.dll dll loading 
> with C:\Prototyping\dotNet\coreclr-master\bin\obj\Windows_NT.x64.Debug\src\dlls\mscordac\Debug\mscordaccore.dll
> loading (just compiled), but if previously I could get mixed mode stack trace correctly:
> ...

mscordacwks.dll is tightly coupled with the runtime. You cannot mix and match them between runtimes.
What I meant is that you can use CoreCLR to understand how this works.
Run Code Online (Sandbox Code Playgroud)

但后来他推荐了这个对我有用的解决方案:

int CaptureStackBackTrace3(int FramesToSkip, int nFrames, PVOID* BackTrace, PDWORD pBackTraceHash)
{
    CONTEXT ContextRecord;
    RtlCaptureContext(&ContextRecord);

    UINT iFrame;
    for (iFrame = 0; iFrame < nFrames; iFrame++)
    {
        DWORD64 ImageBase;
        PRUNTIME_FUNCTION pFunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip, &ImageBase, NULL);

        if (pFunctionEntry == NULL)
            break;

        PVOID HandlerData;
        DWORD64 EstablisherFrame;
        RtlVirtualUnwind(UNW_FLAG_NHANDLER,
            ImageBase,
            ContextRecord.Rip,
            pFunctionEntry,
            &ContextRecord,
            &HandlerData,
            &EstablisherFrame,
            NULL);

        BackTrace[iFrame] = (PVOID)ContextRecord.Rip;
    }

    return iFrame;
}
Run Code Online (Sandbox Code Playgroud)

此代码片段仍然缺少回溯哈希计算,但这是可以在之后添加的内容。

还需要注意的是,在调试此代码片段时,您应该使用本机调试,而不是混合模式(C# 项目默认使用混合模式),因为它会以某种方式干扰调试器中的堆栈跟踪。(弄清楚这种扭曲是如何以及为何发生的)

仍然缺少一个难题 - 如何使符号解析完全抵抗 FreeLibrary / Jit 代码处理,但这仍然是我需要弄清楚的事情。

请注意,RtlVirtualUnwind 很可能仅适用于 64 位架构,不适用于 ARM 或 32 位架构。

更有趣的是,存在函数 RtlCaptureStackBackTrace ,它在某种程度上类似于 Windows api 函数 CaptureStackBackTrace - 但它们在某种程度上有所不同 - 至少在命名上。另外,如果您检查 RtlCaptureStackBackTrace - 它最终调用 RtlVirtualUnwind - 您可以从 Windows Research Kernel 源代码中检查它

RtlCaptureStackBackTrace
>
RtlWalkFrameChain
>
RtlpWalkFrameChain
>
RtlVirtualUnwind
Run Code Online (Sandbox Code Playgroud)

但我测试过的 RtlCaptureStackBackTrace 无法正常工作。与上面的 RtlVirtualUnwind 函数不同。

这有点神奇。:-)

我将继续此调查问卷的第二阶段问题 - 在这里:

解析托管和本机堆栈跟踪 - 使用哪个 API?