以编程方式生成的Windbg转储可以';无法调试

以编程方式生成的Windbg转储可以';无法调试,windbg,dump,crash-dumps,Windbg,Dump,Crash Dumps,我有一个简单的程序: int ExecuteCommand(wchar_t* commandLine) { STARTUPINFO si; PROCESS_INFORMATION pi; BOOL bRet; DWORD lpExitCode; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wSh

我有一个简单的程序:

int ExecuteCommand(wchar_t* commandLine)
{

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL bRet;
    DWORD lpExitCode;

    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    bRet = CreateProcess(
        NULL, // pointer to name of executable module
        commandLine, // pointer to command line string
        NULL, // process security attributes
        NULL, // thread security attributes
        FALSE, // handle inheritance flag
        NORMAL_PRIORITY_CLASS, // creation flags
        NULL, // pointer to new environment block
        NULL, // pointer to current directory name
        &si, // pointer to STARTUPINFO
        &pi // pointer to PROCESS_INFORMATION
        );

    if(bRet) WaitForSingleObject(pi.hProcess, INFINITE); // wait for process to finish

    GetExitCodeProcess(pi.hProcess, &lpExitCode);

    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    return lpExitCode;
}

void CreateCoreDump()
{
    wchar_t buffer[256];
    wsprintf(buffer, _T("windbg -p %d -c \".dump /mfh /u C:\\Tmp\\crashdump.dmp\""), GetCurrentProcessId());

    ExecuteCommand(buffer);
}

DWORD ExceptionFilter()
{
    CreateCoreDump();

    return EXCEPTION_CONTINUE_SEARCH;
}


int _tmain(int argc, _TCHAR* argv[])
{
    __try
    {
        int* p = NULL;
        *p = 100;
    }
    __except(ExceptionFilter())
    {
    }
    return 0;
}
当出现异常时,它将使用函数CreateCoreDump生成核心转储。虽然可以成功生成转储文件,但它似乎毫无用处:
如果我使用windbg打开此转储文件,则调用堆栈中没有任何内容

但是,如果我直接在windbg中调试此应用程序,并在调用CreateCoreDump的行上设置断点,然后运行windbg命令:

.dump /mfh C:\Tmp\mydump.dmp
用WinDbg打开这个转储文件,我可以看到完整的调用堆栈


我是否在生成转储文件或使用windbg调试转储文件时出错

在异常发生后附加调试器时,调试器不会看到异常事件。它创建了一个具有断点的线程,因此该线程上的堆栈如下所示:

0:001> kc
Call Site
ntdll!DbgBreakPoint
ntdll!DbgUiRemoteBreakin+0x38
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
如果手动将当前线程设置为线程0(使用
~0s
),您将看到堆栈

0:001> ~0s
ntdll!ZwWaitForSingleObject+0xa:
00000000`76e5135a c3              ret
0:000> kc
Call Site
ntdll!ZwWaitForSingleObject
KERNELBASE!WaitForSingleObjectEx
tmp!ExceptionFilter
tmp!main$filt$0
ntdll!__C_specific_handler
ntdll!RtlpExecuteHandlerForException
ntdll!RtlDispatchException
ntdll!KiUserExceptionDispatch
tmp!main
tmp!__mainCRTStartup
kernel32!BaseThreadInitThunk
ntdll!RtlUserThreadStart
当您在调试器下启动程序时,会发生两件事,第一,只有一个线程,第二,调试器知道异常,因此它将打印如下内容:

0:001> kc
Call Site
ntdll!DbgBreakPoint
ntdll!DbgUiRemoteBreakin+0x38
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.

它告诉您需要使用
.ecxr
命令来访问感兴趣的线程。在这种情况下,您不需要这样做,因为当前调试器线程已经是您想要的线程。

您必须将异常记录添加到转储。例如,我更改了示例以检索过滤器中的异常信息,并在生成转储时在命令行上传递它

void CreateCoreDump(LPEXCEPTION_POINTERS p)
{
    wchar_t buffer[256];
    // I used the command line debugger, cdb, and added a "qd" command for it to exit after dumping.
    wsprintf(buffer, _T("cdb.exe -p %d -c \".dump /mfh /u /xt 0x%x /xp 0x%p C:\\Tmp\\crashdump.dmp\";qd"), GetCurrentProcessId(), GetCurrentThreadId(), p);
    ExecuteCommand(buffer);
}

DWORD ExceptionFilter(LPEXCEPTION_POINTERS p)
{
    CreateCoreDump(p);
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    __try
    {
        int* p = NULL;
        *p = 100;
    }
    __except(ExceptionFilter(GetExceptionInformation()))
    {
    }
    return 0;
}
然后,当您在windgb中打开转储时,调试器知道异常事件。您可以使用
.ecxr
在异常点设置当前线程和堆栈

0:000> .ecxr
eax=00000000 ebx=00000000 ecx=6ec4471c edx=00000000 esi=00000001 edi=010c337c
eip=010c108b esp=0038f5e8 ebp=0038f818 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
test!wmain+0x14:
010c108b c70064000000    mov     dword ptr [eax],64h  ds:002b:00000000=????????
0:000> kc
test!wmain
test!__tmainCRTStartup
kernel32!BaseThreadInitThunk
ntdll!__RtlUserThreadStart
ntdll!_RtlUserThreadStart

谢谢你,约翰,很管用。您提到“调试器没有看到异常事件”,这是因为转储文件是由代码创建的,对吗?当调试器连接到进程时,它正在侦听调试事件(请参阅)。当异常发生时附加调试器时,它将看到异常调试事件,并知道进程中存在异常。事件通知发生在异常处理之前(第一次),如果在链中找不到异常处理程序,则可能发生第二次。附加调试器时,它发生在第一次机会通知之后。您可以使用
-g
-e事件
让调试器看到第二次机会异常(如果其他人未捕获该异常)。旗帜。
-e
告诉调试器在附加给定事件时向其发送信号,因此需要创建可继承的通知事件,将其句柄(作为十进制数)作为
-e
参数的值发送,并在创建子进程后等待事件。