我有一个简单的程序:
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 :(得分: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