简体   繁体   English

如何使用MiniDumpWriteDump获得有意义的堆栈跟踪

[英]How do I get a meaningful stack-trace using MiniDumpWriteDump

I'm trying to programatically generate a stack trace. 我正在尝试以编程方式生成堆栈跟踪。 When my users are having a crash, in particular a random one, it's hard to talk them through the process of getting a dump so I can fix the problem. 当我的用户崩溃时,尤其是随机崩溃时,很难说服他们完成转储的过程,这样我就可以解决问题。 In the past once they would send me the trace I would cross reference the addresses in it to the Intermediate/foo.map file to figure out which function was the problem (is that the best way?) 在过去,一旦他们向我发送了跟踪信息,我就会将其中的地址交叉引用到Intermediate / foo.map文件中,以找出问题出在哪里(这是最好的方法吗?)

I built a library from various examples I found around the net, to output a minidump to make my job easier. 我从网上发现的各种示例中构建了一个库,以输出一个小型转储文件,使我的工作更轻松。 I staged a crash, but the stack trace I get from the minidump file is wildly different from a live stack trace I get from attaching windbg. 我上演了崩溃,但是从minidump文件中获得的堆栈跟踪与从附加windbg中获得的实时堆栈跟踪完全不同。 Examples of both are below: 两者的示例如下:

MiniDump.dmp: MiniDump.dmp:

KERNELBASE.dll!76a6c42d()
[Frames below may be incorrect and/or missing, no symbols loaded for KERNELBASE.dll]
KERNELBASE.dll!76a6c42d()
kernel32.dll!75bd14bd()
game.exe!00759035()
game.exe!00575ba3()

WinDbg.exe: WinDbg.exe:

0:000:x86> kv
ChildEBP RetAddr  Args to Child              
00186f44 00bc8ea9 19460268 0018a9b7 03f70a28 Minidump!crashme+0x2 (FPO: [0,0,0]) (CONV: cdecl) [c:\project\debug\minidump.cpp @ 68]
0018795c 00b9ef31 0018796c 03f56c00 6532716d Main!LoadPlugin+0x339 (FPO: [1,642,4]) (CONV: cdecl) [c:\project\main\pluginloader.cpp @ 129]
00188968 00b9667d 19460268 0018a9ac 00000000 Main!Command+0x1f1 (FPO: [2,1024,4]) (CONV: cdecl) [c:\project\main\commands.cpp @ 2617]
*** WARNING: Unable to verify checksum for C:\Game\game.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Game\game.exe
0018b1a8 005b5095 19460268 0018beac 00000000 Main!Hook::Detour+0x52d (FPO: [2,2570,0]) (CONV: thiscall) [c:\project\main\hook.cpp @ 275]
WARNING: Stack unwind information not available. Following frames may be wrong.
0018b1b4 00000000 19495200 19495200 00000006 game+0x1b5095

game.exe is not mine, and I don't have the source/symbols. game.exe不是我的,并且我没有源代码/符号。 The Main.dll is injected into game.exe and it provides front end functionality to load additional DLLs from within the game. Main.dll被注入game.exe中,并且它提供了前端功能以从游戏中加载其他DLL。 The debug code, and the staged crash is in Minidump.dll. 调试代码和分段崩溃位于Minidump.dll中。 After Main.dll loads Minidump it calls AfterLoad(), which sets the exception filter, and then triggers the crash. Main.dll加载Minidump后,它将调用AfterLoad(),后者将设置异常过滤器,然后触发崩溃。 The relevant minidump code is below: 相关的小型转储代码如下:

When I opened the MiniDump.dmp I pointed it to all of my symbol files (with the exception of game.exe, which I don't have) and that part seems like it's working. 当我打开MiniDump.dmp时,我将其指向了我所有的符号文件(game.exe除外,我没有),而该部分似乎正在运行。 I do point it to the game.exe binary since I have that. 我确实将它指向game.exe二进制文件,因为我有。 The stack trace I get out of it just really isn't helpful though. 我得到的堆栈跟踪信息实际上并没有帮助。 My ultimate goal is that the user can just load the DLL, cause the crash, and email the dump file to me. 我的最终目标是用户可以仅加载DLL,导致崩溃并通过电子邮件将转储文件发送给我。 Then I'll attach the symbol files and binaries and be able to diagnose the problem for them. 然后,我将附加符号文件和二进制文件,并能够为它们诊断问题。 Am I doing something wrong, or is it just not possible to get what I want. 我是在做错事,还是无法得到我想要的东西。

typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
    HANDLE hProcess, 
    DWORD ProcessId, 
    HANDLE hFile, 
    MINIDUMP_TYPE DumpType,
    CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
    CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

LONG WINAPI WriteDumpFilter(struct _EXCEPTION_POINTERS *pExceptionPointers)
{
    HANDLE hFile = NULL;
    HMODULE hDll = NULL;
    MINIDUMPWRITEDUMP pMiniDumpWriteDump = NULL;
    _MINIDUMP_EXCEPTION_INFORMATION ExceptionInformation = {0}; 

    //load MiniDumpWriteDump
    hDll = LoadLibrary(TEXT("DbgHelp.dll"));
    pMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump");

    //create output file
    hFile = CreateFile( _T( "C:\\temp\\MiniDump.dmp"), 
                            GENERIC_READ|GENERIC_WRITE, 0, NULL, 
                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); 

    //bail if we don't have a file
    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) 
    {
        //get exception information
        ExceptionInformation.ThreadId           = GetCurrentThreadId(); 
        ExceptionInformation.ExceptionPointers  = pExceptionPointers; 
        ExceptionInformation.ClientPointers     = TRUE; 

        //write the debug dump
        pMiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), 
                            hFile, MiniDumpWithFullMemory, &ExceptionInformation, 
                            NULL, NULL ); 


        //close the debug output file
        CloseHandle(hFile); 
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

VOID crashme() {int* foo = 0; *foo = 0;}

VOID AfterLoad(VOID)
{
    SetUnhandledExceptionFilter(WriteDumpFilter);
    crashme();
}

I tried to trim some of the fat out of all the details to simplify the problem, but I can be more explicit if needed. 我试图从所有细节中删除一些赘肉以简化问题,但是如果需要的话,我可以更明确一些。 I found the good write-up on CodeProject, and I tried finding more background information to read in order to help me understand the problem, but what I could find didn't help me understand they were just step-by-steps to get it running (which is already is). 我在CodeProject上找到了不错的文章,并且尝试查找更多背景信息以帮助我理解问题,但是我发现的内容并不能帮助我理解它们只是逐步解决问题的方法。运行(已经是)。 Anyone have any idea what I'm doing wrong, or maybe point me to relevant reading? 任何人都知道我在做错什么,或者可以指出相关的阅读内容吗?


After Sergei's suggestion I did .ecxr in windbg and got better output, but it still doesn't match the trace I get when I hook windbg straight up to the process and trigger the crash. 在Sergei的建议下,我在windbg中执行了.ecxr并获得了更好的输出,但是当我将windbg直接挂接到进程并触发崩溃时,它仍然与我得到的跟踪不匹配。 Here is the minidump trace; 这是最小转储跟踪;

  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0018e774 00759035 e06d7363 00000001 00000003 KERNELBASE!RaiseException+0x58
0018e7b4 00575ba3 00000000 00000000 00000001 game+0x359035
0018fc50 0057788a 009855ef 0018fdcb 00000001 game+0x175ba3
0018fc78 77b7e013 012d9230 002d91d0 002d9200 game+0x17788a
0018fc90 77ba9567 00290000 00000000 002d91d0 ntdll!RtlFreeHeap+0x7e
0018fd6c 0076ece2 0018ff78 007e1b7e ffffffff ntdll!LdrRemoveLoadAsDataTable+0x4e0
002bbc38 5c306174 61666544 00746c75 5d4c3055 game+0x36ece2
002bbc3c 61666544 00746c75 5d4c3055 8c000000 0x5c306174
002bbc40 00746c75 5d4c3055 8c000000 00000101 0x61666544
002bbc44 5d4c3055 8c000000 00000101 01000000 game+0x346c75
002bbc48 8c000000 00000101 01000000 00000000 0x5d4c3055
002bbc4c 00000000 01000000 00000000 0000006e 0x8c000000

and the trace from attaching the debugger to the process 以及将调试器附加到进程的跟踪

0:000:x86> kv
ChildEBP RetAddr  Args to Child              
00186f44 00bc8ea9 19460268 0018a9b7 03f70a28 Minidump!crashme+0x2 (FPO: [0,0,0]) (CONV: cdecl) [c:\project\debug\minidump.cpp @ 68]
0018795c 00b9ef31 0018796c 03f56c00 6532716d Main!LoadPlugin+0x339 (FPO: [1,642,4]) (CONV: cdecl) [c:\project\main\pluginloader.cpp @ 129]
00188968 00b9667d 19460268 0018a9ac 00000000 Main!Command+0x1f1 (FPO: [2,1024,4]) (CONV: cdecl) [c:\project\main\commands.cpp @ 2617]
*** WARNING: Unable to verify checksum for C:\Game\game.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Game\game.exe
0018b1a8 005b5095 19460268 0018beac 00000000 Main!Hook::Detour+0x52d (FPO: [2,2570,0]) (CONV: thiscall) [c:\project\main\hook.cpp @ 275]
WARNING: Stack unwind information not available. Following frames may be wrong.
0018b1b4 00000000 19495200 19495200 00000006 game+0x1b5095

I don't have the source for game.exe (I have it for the DLLs which is where the error is), but I decompiled game.exe and here is what is at game+0x359035. 我没有game.exe的源代码(我的错误源是DLL),但是我反编译了game.exe,这就是game + 0x359035的内容。

.text:00759001 ; =============== S U B R O U T I N E =======================================
.text:00759001
.text:00759001 ; Attributes: library function bp-based frame
.text:00759001
.text:00759001 ; __stdcall _CxxThrowException(x, x)
.text:00759001 __CxxThrowException@8 proc near         ; CODE XREF: .text:0040100Fp
.text:00759001                                         ; sub_401640+98p ...
.text:00759001
.text:00759001 dwExceptionCode = dword ptr -20h
.text:00759001 dwExceptionFlags= dword ptr -1Ch
.text:00759001 nNumberOfArguments= dword ptr -10h
.text:00759001 Arguments       = dword ptr -0Ch
.text:00759001 var_8           = dword ptr -8
.text:00759001 var_4           = dword ptr -4
.text:00759001 arg_0           = dword ptr  8
.text:00759001 arg_4           = dword ptr  0Ch
.text:00759001
.text:00759001                 push    ebp
.text:00759002                 mov     ebp, esp
.text:00759004                 sub     esp, 20h
.text:00759007                 mov     eax, [ebp+arg_0]
.text:0075900A                 push    esi
.text:0075900B                 push    edi
.text:0075900C                 push    8
.text:0075900E                 pop     ecx
.text:0075900F                 mov     esi, offset unk_853A3C
.text:00759014                 lea     edi, [ebp+dwExceptionCode]
.text:00759017                 rep movsd
.text:00759019                 mov     [ebp+var_8], eax
.text:0075901C                 mov     eax, [ebp+arg_4]
.text:0075901F                 mov     [ebp+var_4], eax
.text:00759022                 lea     eax, [ebp+Arguments]
.text:00759025                 push    eax             ; lpArguments
.text:00759026                 push    [ebp+nNumberOfArguments] ; nNumberOfArguments
.text:00759029                 push    [ebp+dwExceptionFlags] ; dwExceptionFlags
.text:0075902C                 push    [ebp+dwExceptionCode] ; dwExceptionCode
.text:0075902F                 call    ds:RaiseException
.text:00759035                 pop     edi
.text:00759036                 pop     esi
.text:00759037                 leave
.text:00759038                 retn    8
.text:00759038 __CxxThrowException@8 endp

My error that I'm triggering is in Minidump.dll, but this code at the top of the stack is in game.exe. 我触发的错误是在Minidump.dll中,但是堆栈顶部的这段代码在game.exe中。 There could be plenty going on inside the game.exe that I'm unaware of, could it maybe be hijacking the error that I'm triggering somehow? 我可能不知道game.exe内部发生了很多事情,这可能是劫持了我以某种方式触发的错误吗? IE, I trigger the error in the DLL, but something setup in the game.exe captures program flow before the exception filter that writes the minidump is called? IE,我触发了DLL中的错误,但是game.exe中的某些设置会在调用写入minidump的异常过滤器之前捕获程序流?

If that's the case, when I attach the debugger to the process, trigger the error and get the right output that points to the error being in my DLL, then that means game.exe isn't capturing the program flow before the debugger can do the trace. 如果是这样,当我将调试器附加到进程时,触发错误并获得指向错误的正确输出,该错误指向我的DLL中的错误,这意味着game.exe在调试器可以执行之前没有捕获程序流。痕迹。 How could I make my minidump code behave the same way... This is getting into territory I'm not terribly familiar with. 我如何使我的minidump代码表现出相同的行为...这正在进入我并不十分熟悉的领域。 Any ideas? 有任何想法吗?


I chased further back, and the function calling that one, has this line in it: 我往后追,调用该函数的函数包含以下内容:

.text:00575A8D                 mov     esi, offset aCrashDumpTooLa ; "Crash dump too large to send.\n"

So, I think game.exe is hijacking the exception to do it's own dump before my code tries to get the dump. 因此,我认为game.exe在我的代码尝试获取转储之前,正在劫持异常以执行其自身的转储。 And then my dumps trace is just a trace of game.exe's dump process... 然后我的转储跟踪只是game.exe的转储过程的跟踪...


Answer 回答

I've got it figured out. 我知道了。 I'm not sure how to answer my own post, so here is the deal. 我不确定如何回答自己的帖子,所以这很重要。

.text:0057494A                 push    offset aDbghelp_dll ; "DbgHelp.dll"
.text:0057494F                 call    ds:LoadLibraryA
.text:00574955                 test    eax, eax
.text:00574957                 jz      short loc_5749C8
.text:00574959                 push    offset aMinidumpwrited ; "MiniDumpWriteDump"
.text:0057495E                 push    eax             ; hModule
.text:0057495F                 call    ds:GetProcAddress
.text:00574965                 mov     edi, eax
.text:00574967                 test    edi, edi
.text:00574969                 jz      short loc_5749C8
.text:0057496B                 mov     edx, lpFileName
.text:00574971                 push    0               ; hTemplateFile
.text:00574973                 push    80h             ; dwFlagsAndAttributes
.text:00574978                 push    2               ; dwCreationDisposition
.text:0057497A                 push    0               ; lpSecurityAttributes
.text:0057497C                 push    0               ; dwShareMode
.text:0057497E                 push    40000000h       ; dwDesiredAccess
.text:00574983                 push    edx             ; lpFileName
.text:00574984                 call    ds:CreateFileA
.text:0057498A                 mov     esi, eax
.text:0057498C                 cmp     esi, 0FFFFFFFFh
.text:0057498F                 jz      short loc_5749C8
.text:00574991                 call    ds:GetCurrentThreadId
.text:00574997                 push    0
.text:00574999                 push    0
.text:0057499B                 mov     [ebp+var_1C], eax
.text:0057499E                 lea     eax, [ebp+var_1C]
.text:005749A1                 push    eax
.text:005749A2                 push    0
.text:005749A4                 push    esi
.text:005749A5                 mov     [ebp+var_18], ebx
.text:005749A8                 mov     [ebp+var_14], 1
.text:005749AF                 call    ds:__imp_GetCurrentProcessId
.text:005749B5                 push    eax
.text:005749B6                 call    ds:GetCurrentProcess
.text:005749BC                 push    eax
.text:005749BD                 call    edi
.text:005749BF                 push    esi             ; hObject
.text:005749C0                 call    ds:CloseHandle
.text:005749C6                 jmp     short loc_574A02

Thats from game.exe. 多数民众赞成从game.exe。 It turns out game.exe does it's own minidump. 事实证明game.exe确实是自己的小型转储。 My minidump was triggering after theirs so what I was seeing in my stack trace was a trace of their dump process. 我的小型转储是在他们的转储之后触发的,因此我在堆栈跟踪中看到的是他们转储过程的跟踪。 I found a dmp file in the game''s installation directory and once I loaded my symbols into it, it showed the correct output I was after. 我在游戏的安装目录中找到一个dmp文件,将符号加载到其中后,它会显示正确的输出。

You are doing just fine. 你还好吧 When you open the minidump you generated, after you load the symbols, do 当您打开生成的小型转储时,请在加载符号后执行

.ecxr

first to set context to what you saved in ExceptionInformation parameter to MiniDumpWriteDump() . 首先将上下文设置为您在ExceptionInformation参数中保存到MiniDumpWriteDump() Then you will have a legit stack trace. 然后,您将获得合法的堆栈跟踪。

We use a similar dump generation mechanism at the place where I work. 在我工作的地方,我们使用了类似的转储生成机制。

there are some future gotchas though. 虽然有一些未来的陷阱。 You want to check whether your dump catch mechanism is triggered on things like an abort() call. 您想检查是否在诸如abort()调用之类的事件上触发了转储捕获机制。

For that, check out _set_invalid_parameter_handler() and signal(SIGABRT, ...) . 为此,请检查_set_invalid_parameter_handler()signal(SIGABRT, ...)

I figured it out. 我想到了。 Basically game.exe had its own MiniDumpWriteDump code that was triggering before my code. 基本上game.exe有自己的MiniDumpWriteDump代码,该代码在我的代码之前触发。 So the stack trace I was getting wasn't a trace of the error, it was a trace of game.exe doing its own MiniDump. 因此,我得到的堆栈跟踪信息不是错误的跟踪信息,而是game.exe在执行自己的MiniDump时的跟踪信息。 I put more details up in the original post. 我在原始帖子中提出了更多细节。

Thanks! 谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM