【问题标题】:Page Heap doesn't record useful stack infomation?Page Heap 不记录有用的堆栈信息?
【发布时间】:2013-10-02 10:07:58
【问题描述】:

我正在尝试使用 正常 页面堆(未满)测试崩溃场景(在隔离的测试应用中)。

我已经设置了标志

gflags /p /enable Test.exe

我正在用一个元素覆盖一个整数缓冲区

...
const size_t s = 100;
vector<int> v1(s, 0);
int* v1_base = &v1[0];
write_to_memory_int(v1_base, s+1);
...

确实,当向量 d'tor 中的块被释放时,我得到了休息。正确报告了中断的调用堆栈:

0:005> kp
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  
0785faa4 11229df2 verifier!VerifierStopMessage+0x1f8
0785fb08 1122a22a verifier!AVrfpDphReportCorruptedBlock+0x1c2
0785fb64 1122a742 verifier!AVrfpDphCheckNormalHeapBlock+0x11a
0785fb84 112290d3 verifier!AVrfpDphNormalHeapFree+0x22
0785fba8 77951564 verifier!AVrfDebugPageHeapFree+0xe3
0785fbf0 7790ac29 ntdll!RtlDebugFreeHeap+0x2f
0785fce4 778b34a2 ntdll!RtlpFreeHeap+0x5d
0785fd04 750c14dd ntdll!RtlFreeHeap+0x142
0785fd18 71fc4c39 kernel32!HeapFree+0x14
0785fd64 00404b0a msvcr80!free(void * pBlock = 0x0726f7b8)+0xcd [f:\dd\vctools\crt_bld\self_x86\crt\src\free.c @ 110]
0785fd90 00402ac7 Test!std::vector<int,std::allocator<int> >::_Tidy
...

但是,当我查看错误分配时,我只得到这个:

0:005> !heap -p -a 0x0726f7b8
    address 0726f7b8 found in
    _HEAP @ 30000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0726f790 0039 0000  [00]   0726f7b8    00190 - (busy)
        1122a6a7 verifier!AVrfpDphNormalHeapAllocate+0x000000d7
        11228f6e verifier!AVrfDebugPageHeapAllocate+0x0000030e
        77950d96 ntdll!RtlDebugAllocateHeap+0x00000030
        7790af0d ntdll!RtlpAllocateHeap+0x000000c4
        778b3cfe ntdll!RtlAllocateHeap+0x0000023a

也就是说,一个分配堆栈跟踪,但它停在RtlAllocateHeap,这显然完全没用。

查看内存中的堆栈跟踪:

dt _DPH_BLOCK_INFORMATION ....-0x20

=>

0:005> dds 0x03e556f4
03e556f4  00000000
03e556f8  00002050
03e556fc  00050000
03e55700  1122a6a7 verifier!AVrfpDphNormalHeapAllocate+0xd7
03e55704  11228f6e verifier!AVrfDebugPageHeapAllocate+0x30e
03e55708  77950d96 ntdll!RtlDebugAllocateHeap+0x30
03e5570c  7790af0d ntdll!RtlpAllocateHeap+0xc4
03e55710  778b3cfe ntdll!RtlAllocateHeap+0x23a
03e55714  00000000
03e55718  00003001
03e5571c  0004005e

似乎实际上没有更多记录。

如何修复页面堆以记录有用的堆栈跟踪?

请注意,Test 项目不是使用 FPO (/Oy) 编译的,我没想到 RtlAllocateHeap会受到 FPO 的影响?


更新:我通过手动进入分配检查了相关调用的 FPO 特性(见下文),看起来 VC80(VS2005) 运行时库的 mallocop new 都有启用了某种形式的 FPO ...所以这可能会弄乱页面堆的堆栈 DB 的堆栈跟踪。

0:004> kv
ChildEBP RetAddr  Args to Child              
077efa7c 77c8af0d 05290000 01001002 00000190 ntdll!RtlDebugAllocateHeap+0x16 (FPO: [Non-Fpo])
077efb60 77c33cfe 00000190 00000000 00000000 ntdll!RtlpAllocateHeap+0xc4 (FPO: [Non-Fpo])
077efbe4 72344d83 05290000 01001002 00000190 ntdll!RtlAllocateHeap+0x23a (FPO: [Non-Fpo])
077efc04 62f595ee 00000190 00000000 00000000 MSVCR80!malloc+0x7a (FPO: [1,0,0]) (CONV: cdecl) 
077efc1c 00406a44 00000190 ebecf74f 00000001 MFC80U!operator new+0x2f (FPO: [Uses EBP] [1,0,0]) (CONV: cdecl)
077efc48 00405479 00000064 00000000 3fffffff Test!std::_Allocate<ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > > >+0x84 (FPO: [Non-Fpo]) (CONV: cdecl)
077efcb8 004049f4 00000064 ebecf68f 00000000 Test!std::vector<unsigned int,std::allocator<unsigned int> >::_Buy+0x69 (FPO: [Non-Fpo]) (CONV: thiscall)
077efd88 00402a4f 00000064 077efdc0 ebecf44b Test!std::vector<int,std::allocator<int> >::_Construct_n+0x44 (FPO: [Non-Fpo]) (CONV: thiscall)
077eff4c 72342848 00000000 ebec8474 00000000 Test!crashFN+0x35f (FPO: [Non-Fpo]) (CONV: cdecl) 
077eff84 723428c8 75da33aa 072ab3d8 077effd4 MSVCR80!_callthreadstart+0x1b (FPO: [Non-Fpo]) (CONV: cdecl)
077eff88 75da33aa 072ab3d8 077effd4 77c39f72 MSVCR80!_threadstart+0x5a (FPO: [1,0,0]) (CONV: stdcall)
077eff94 77c39f72 072ab3d8 70fca8b2 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
077effd4 77c39f45 7234286e 072ab3d8 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
077effec 00000000 7234286e 072ab3d8 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

【问题讨论】:

  • 也许 C++ 运行时和/或 CRT 被编译为 FPO。如果用dds 0x03e556f4 L80 增加dds 的范围怎么办?你看到什么有趣的退货地址了吗?
  • @Marc:不,除了第一个 stac 之外似乎没有任何有趣的东西(只是更多,不相关的堆栈)
  • 您发布的内存中的堆栈跟踪的奇怪之处在于,这些返回地址立即相互跟随。通常你会在它们之间看到一个保存的 EBP。您最初是如何找到值 0x03e556f4 的?
  • @Marc - 这是页面堆堆栈数据库记录的堆栈跟踪
  • 要排除 FPO,如果在 ntdll!RtlpAllocateHeap 和问题 kv 上设置断点会显示 FPO 数据(如果有)。

标签: debugging visual-c++ windbg gflags pageheap


【解决方案1】:

感谢@Marc Sherman 在 cmets 中指出我应该检查真正的分配堆栈跟踪。

正如已经在问题中编辑的那样,VC80(VS2005) 是这里的问题,因为它的 CRT 启用了 FPO,如堆栈跟踪所示:

MSVCR80!malloc+0x7a (FPO: [1,0,0]) (CONV: cdecl) 
MFC80U!operator new+0x2f (FPO: [Uses EBP] [1,0,0]) (CONV: cdecl)

现在,有了要搜索的锚点,我们发现以下内容:

Why does every heap trace in UMDH get stuck at “malloc”?

添加几个引号:

特别是,看起来默认的 malloc 实现 在 Visual C++ 2005 上的静态链接 CRT 不仅不使用框架 指针,但它会将 ebp 作为临时寄存器丢弃 ...

这一切意味着什么?好吧,任何使用 malloc 构建的东西 Visual C++ 2005 不能用 UMDH 或其他任何东西来诊断 依赖于基于 ebp 的堆栈跟踪,至少不依赖于 x86 构建。

cmets 里也有回复,有很好的信息:

马克·罗伯茨 [MSFT] 说:2008 年 2 月 25 日下午 3:03

你好,

为 8.0 CRT 启用 FPO 并非有意为之。视觉工作室 2008 CRT (9.0) 没有启用 FPO,UMDH 应该可以运行 正常。

对于 8.0,UMDH 的替代方法是使用 LeakDiag。泄漏诊断 实际上会检测内存分配器以获取堆栈跟踪。 这使得它比 UMDH 更通用,因为它可以连接多个 不同粒度的不同分配器类型(范围从 c 运行时到原始虚拟内存分配)。

默认情况下,LeakDiag 只是遍历堆栈基指针,但它可以 修改为使用 Dbghlp StackWalkAPI 来解析 FPO 数据。这 将产生完整的堆栈,尽管性能损失更高。在 另一方面,您可以将堆栈行走行为自定义为仅去 到一定的深度等,以尽量减少性能损失。

请在此处查找 LeakDiag: ftp://ftp.microsoft.com/PSS/Tools/Developer%20Support%20Tools/LeakDiag/leakdiag125.msi

【讨论】:

  • MSVC 库中的 FPO 多年来一直是一个反复出现的问题,可以选择绕过,运行通常没有 FPO 的调试版本(但不确定您的版本)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 2019-07-11
  • 2015-02-16
  • 2011-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多