以编程方式生成的Windbg转储无法进行调试

时间:2011-08-25 09:37:57

标签: 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.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调试转储文件时,我做错了吗?

2 个答案:

答案 0 :(得分:2)

在异常发生后附加调试器时,调试器不会看到异常事件。它创建一个具有断点的线程,因此该线程上的堆栈看起来像这样:

0:001> kc
Call Site
ntdll!DbgBreakPoint
ntdll!DbgUiRemoteBreakin+0x38
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d

如果您手动将当前线程设置为线程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

当您在调试器下启动程序时,会发生两件事情,首先,只有一个线程,第二个调试器知道异常,所以它会打印出这样的内容:

This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.

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

答案 1 :(得分:1)

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

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