使用 SetUnhandledExceptionFilter() 创建访问冲突异常的小型转储

发布于 2024-10-14 20:03:26 字数 1718 浏览 4 评论 0原文

每当我的代码生成结构化异常时,我都会使用以下代码创建一个小型转储文件:

void CreateMiniDump( EXCEPTION_POINTERS* pep ) 
{
  // Open the file 
typedef BOOL (*PDUMPFN)( 
  HANDLE hProcess, 
  DWORD ProcessId, 
  HANDLE hFile, 
  MINIDUMP_TYPE DumpType, 
  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 
  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 
  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);


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

    HMODULE h = ::LoadLibrary(L"DbgHelp.dll");
    PDUMPFN pFn = (PDUMPFN)GetProcAddress(h, "MiniDumpWriteDump");

  if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) 
  {
    // Create the minidump 

    MINIDUMP_EXCEPTION_INFORMATION mdei; 

    mdei.ThreadId           = GetCurrentThreadId(); 
    mdei.ExceptionPointers  = pep; 
    mdei.ClientPointers     = TRUE; 

    MINIDUMP_TYPE mdt       = MiniDumpNormal; 

    BOOL rv = (*pFn)( GetCurrentProcess(), GetCurrentProcessId(), 
      hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0 ); 



    // Close the file 

    CloseHandle( hFile ); 

  }

}

LONG WINAPI MyUnhandledExceptionFilter(
 struct _EXCEPTION_POINTERS *ExceptionInfo
)
 {
     CreateMiniDump(ExceptionInfo);
     return EXCEPTION_EXECUTE_HANDLER;
 }

并且我正在从我的应用程序的主入口点执行 SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); (我不是不过为每个线程设置它)。之后为了测试此代码,我执行了以下操作来生成访问冲突: int* p = 0; *p = 0; 转储文件确实生成了。然后我使用windbg并打开转储文件并使用.ecxr命令获取异常记录。但是,那里没有任何信息(即我没有得到调用堆栈)。另外,如果我使用 !analyze -v 命令,那么它能够显示发生崩溃的行。有谁知道我缺少什么以及如何解决这个问题?

顺便说一句,我使用带有 /EHa(异步异常模型)标志的 VC7 编译器。

I am using the following code to create a minidump file whenever there is a structured exception generated from my code:

void CreateMiniDump( EXCEPTION_POINTERS* pep ) 
{
  // Open the file 
typedef BOOL (*PDUMPFN)( 
  HANDLE hProcess, 
  DWORD ProcessId, 
  HANDLE hFile, 
  MINIDUMP_TYPE DumpType, 
  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 
  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 
  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);


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

    HMODULE h = ::LoadLibrary(L"DbgHelp.dll");
    PDUMPFN pFn = (PDUMPFN)GetProcAddress(h, "MiniDumpWriteDump");

  if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) 
  {
    // Create the minidump 

    MINIDUMP_EXCEPTION_INFORMATION mdei; 

    mdei.ThreadId           = GetCurrentThreadId(); 
    mdei.ExceptionPointers  = pep; 
    mdei.ClientPointers     = TRUE; 

    MINIDUMP_TYPE mdt       = MiniDumpNormal; 

    BOOL rv = (*pFn)( GetCurrentProcess(), GetCurrentProcessId(), 
      hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0 ); 



    // Close the file 

    CloseHandle( hFile ); 

  }

}

LONG WINAPI MyUnhandledExceptionFilter(
 struct _EXCEPTION_POINTERS *ExceptionInfo
)
 {
     CreateMiniDump(ExceptionInfo);
     return EXCEPTION_EXECUTE_HANDLER;
 }

And I am doing SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); from the main entry point of my app ( I am not setting it for each thread though). After this to test this code I did the following to generate an access violation: int* p = 0; *p = 0; The dump file did get generated. Then I used windbg and opened the dump file and used .ecxr command to get the exception record. However, no information is coming there (i.e. I am not getting the call stack). Also if I use !analyze -v command then it is able to show the line where the crash occured. Does anybody know what I am missing and how to solve this?

BTW, I am using VC7 compiler with /EHa (asynchronuos exception model) flag.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小清晰的声音 2024-10-21 20:03:26

您创建小型转储的代码没问题,问题在于事后调试。调试器必须有权访问程序源代码、.pdb 文件(应该是构建此程序可执行文件时创建的 pdb 文件)和操作系统调试符号。只有拥有所有这些信息,调试器才能显示源代码和调用堆栈中的异常位置。

使用 Visual Studio 调试器的事后调试过程详细描述如下:http://www. codeproject.com/KB/debug/postmortemdebug_standalone1.aspx 对于 WinDbg,使用符号、源和图像文件路径为调试器提供相同的信息。

Your code creating minidump is OK, the problem is post-mortem debugging. Debugger must have access to the program source code, .pdb files (which should be pdb files created when this program executable was built), and OS debugging symbols. Only having all this information, debugger can show exception place in the source code and call stack.

Post-mortem debugging process using Visual Studio debugger is described in details here: http://www.codeproject.com/KB/debug/postmortemdebug_standalone1.aspx For WinDbg, use Symbol, Source and Image file path to provide the same information for debugger.

海之角 2024-10-21 20:03:26

但是,那里没有任何信息(即我没有获取调用堆栈)

.ecxr 不应该打印调用堆栈,它只是应该将上下文设置为存储的异常记录的上下文 -您需要执行类似 k 10 的操作来实际打印它。或者,由于您使用的是 WinDBG,请打开调用堆栈窗口。

(也许你已经在这样做了;从你的描述中我不确定 .ecxr 是如何失败的 - 它应该打印一些东西让你知道它是什么/不能做什么......)

另一件事要检查是你实际上得到了传入的异常指针 - 代码将生成一个没有它们的转储,但你会以这种方式得到一个时髦的调用堆栈。

您在对亚历克斯的回答的评论中提到 DLL 的一些干扰会用运行时库覆盖您的过滤器...这是一个常见问题。我很幸运地使用了Oleg Starodumov 描述的技术

还有一种可能的方法,而且它比前两种更容易实现。在我们注册了自己的过滤器之后,我们可以修补SetUnhandledExceptionFilter函数的开头,这样它就无法再注册过滤器了。

他为此提供了一些方便的示例代码,多年来对我很有帮助。

However, no information is coming there (i.e. I am not getting the call stack)

.ecxr isn't supposed to print a callstack, it's just supposed to set the context to that of the stored exception record - you'd want to do something like k 10 to actually print it. Or, since you're using WinDBG, open the callstack window.

(Maybe you're already doing this; I'm not sure from your description how .ecxr is failing - it should print something to let you know what it is / isn't able to do...)

Another thing to check is that you're actually getting exception pointers passed in - the code will generate a dump without them, but you'll get a funky callstack that way.

You mentioned in a comment on Alex's answer having some interference from a DLL overriding your filter with the runtime library's... This is a common problem. I've had good luck using the technique described by Oleg Starodumov:

There is one more approach possible, and it is much easier to implement than the previous two. After we have registered our own filter, we can patch the beginning of SetUnhandledExceptionFilter function so that it will not be able to register filters anymore.

He provides some handy example code for this purpose, which has served me well over the years.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文