当前位置:   article > 正文

Windows上使用dump文件调试_windows软件奔溃分析dump

windows软件奔溃分析dump

dump文件

dump文件记录当前程序运行某一时刻的信息,包括内存,线程,线程栈,变量等等,相当于调试程序时运行到某个断点上,把程序运行的信息记录下来。可以通过Windbg打开dump,查看程序运行的变量等,来调试程序。

Liunx上也有类似的技术,Coredump,具体可以参考:coredump详解_coredump文件分析_贺二公子的博客-CSDN博客

dump 文件分类

dump可以分为 minidump 和 Full dump

minidump通常只包含了一些关键信息,一般比较小,通常只要几MB,

Full dump包含了程序运行时的所有信息,包括程序的所有内存,一般有几十MB到几GB。

minidump虽然只包含了部分信息,但这些信息大部分情况足够用于调试,所以通常都是使用minidump调试

生成dump文件

通过任务管理器导出

在进程上右击->创建内存转储文件,这样创建的是Full dump

通过Process Explorer导出

Process Explorer - Sysinternals | Microsoft Learn

选择对应的进程->Process->Create Dump,然后选择要创建minidump 还是 Full Dump

使用MiniDumpWriteDump函数序生成 

  1. #include <iostream>
  2. #include <Windows.h>
  3. #include <Dbghelp.h>
  4. #include <thread>
  5. #pragma comment(lib, "Dbghelp.lib")
  6. void createMinidump()
  7. {
  8. wchar_t DumpPath[MAX_PATH] = {0};
  9. SYSTEMTIME SystemTime;
  10. GetLocalTime(&SystemTime);
  11. WCHAR szExeFileName[256] = {0};
  12. GetModuleFileNameW(nullptr, szExeFileName, 99);
  13. wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth,
  14. SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
  15. HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
  16. if (file != INVALID_HANDLE_VALUE)
  17. {
  18. DWORD Flags = MiniDumpWithHandleData |
  19. MiniDumpWithUnloadedModules |
  20. MiniDumpScanMemory|
  21. MiniDumpWithIndirectlyReferencedMemory |
  22. MiniDumpWithProcessThreadData |
  23. MiniDumpWithThreadInfo;
  24. if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD) GetCurrentProcessId(), file,
  25. (MINIDUMP_TYPE) (Flags),
  26. nullptr, nullptr, nullptr) != 0)
  27. {
  28. std::cout << "Create Minidump successful!! file:";
  29. std::wcout << DumpPath << std::endl;
  30. }
  31. else
  32. {
  33. std::cout << "Create Minidump failed!!" << std::endl;
  34. }
  35. }
  36. CloseHandle(file);
  37. }
  38. void createFullDump()
  39. {
  40. wchar_t DumpPath[MAX_PATH] = {0};
  41. SYSTEMTIME SystemTime;
  42. GetLocalTime(&SystemTime);
  43. WCHAR szExeFileName[256] = {0};
  44. GetModuleFileNameW(nullptr, szExeFileName, 99);
  45. wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d_full.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth,
  46. SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
  47. HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
  48. if (file != INVALID_HANDLE_VALUE)
  49. {
  50. const DWORD Flags = MiniDumpWithFullMemory |
  51. MiniDumpWithFullMemoryInfo |
  52. MiniDumpWithHandleData |
  53. MiniDumpWithUnloadedModules |
  54. MiniDumpWithProcessThreadData |
  55. MiniDumpWithThreadInfo;
  56. if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD) GetCurrentProcessId(), file,
  57. (MINIDUMP_TYPE) (Flags),
  58. nullptr, nullptr, nullptr) != 0)
  59. {
  60. std::cout << "Create Full dump successful!! file:";
  61. std::wcout << DumpPath << std::endl;
  62. }
  63. else
  64. {
  65. std::cout << "Create Full dump failed!!" << std::endl;
  66. }
  67. }
  68. CloseHandle(file);
  69. }

程序崩溃时自动导出Dump

我们希望程序运行崩溃时可以自动导出dump,这样可以通过分析dump文件找到崩溃原因。

程序崩溃很多情况都是由异常引起的,Windows提供了SetUnhandledExceptionFilter函数用来设置一个函数指针,用于处理未处理的异常,可以在这个函数中导出Dump文件。

步骤:

1. 准备一个处理异常的函数,并在其中导出dump。异常处理函数有一个参数,这个参数记录了当前异常信息,这个异常信息可以一起随dump文件导出,方便后续查找Bug

  1. LONG WINAPI DumpException(EXCEPTION_POINTERS* info)
  2. {
  3. std::cout << "DumpException, Thread ID:"<< std::this_thread::get_id() << std::endl;
  4. std::cout << "Exception: 0x" << std::hex << info->ExceptionRecord->ExceptionCode << std::endl;
  5. wchar_t DumpPath[MAX_PATH] = { 0 };
  6. SYSTEMTIME SystemTime;
  7. GetLocalTime(&SystemTime);
  8. WCHAR szExeFileName[100] = { 0 };
  9. GetModuleFileNameW(nullptr, szExeFileName, 99);
  10. wsprintfW(DumpPath, L"%s_%d-%d-%d_%d-%d-%d_crash.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
  11. HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  12. if (file != INVALID_HANDLE_VALUE)
  13. {
  14. MINIDUMP_EXCEPTION_INFORMATION mdei;
  15. mdei.ThreadId = (DWORD)GetCurrentThreadId();
  16. mdei.ExceptionPointers = info;
  17. mdei.ClientPointers = 0;
  18. DWORD Flags = MiniDumpWithHandleData |
  19. MiniDumpWithUnloadedModules |
  20. MiniDumpScanMemory|
  21. MiniDumpWithIndirectlyReferencedMemory |
  22. MiniDumpWithProcessThreadData |
  23. MiniDumpWithThreadInfo;
  24. // Flags = MiniDumpWithFullMemory |
  25. // MiniDumpWithFullMemoryInfo |
  26. // MiniDumpWithHandleData |
  27. // MiniDumpWithUnloadedModules |
  28. // MiniDumpWithThreadInfo;
  29. if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD)GetCurrentProcessId(), file,
  30. (MINIDUMP_TYPE)(Flags),
  31. &mdei, nullptr, nullptr) != 0)
  32. {
  33. std::cout << "Create Crash dump successful!! file:";
  34. std::wcout << DumpPath << std::endl;
  35. CloseHandle(file);
  36. return EXCEPTION_EXECUTE_HANDLER;
  37. }
  38. }
  39. std::cout << "Create Crash dump failed!!" << std::endl;
  40. CloseHandle(file);
  41. return EXCEPTION_CONTINUE_SEARCH;
  42. }

2. 在程序启动时设置异常处理函数

  1. int main()
  2. {
  3. std::cout << "Main Thread ID:" << std::this_thread::get_id() << std::endl;
  4. LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter = nullptr;
  5. oldExceptionFilter = SetUnhandledExceptionFilter(&DumpException);
  6. // ... ...
  7. // ... ...
  8. }
PS

1. 这里是通过异常捕获生成dump,如果是调用abort,exit,TerminateProcess, TerminateThread函数,这些函数会立即结束进程,所以不会生成dump。

2. SetUnhandledExceptionFilter是全局的,只需设置一次,设置后对所有线程有效。SetUnhandledExceptionFilter有些异常捕获不到。

3. 可以使用第三方库捕获崩溃事件,例如:crashrpt,google breakpad,qBreakpad,Crashpad

UnhandledExceptionFilter未处理的异常

Windows中所有的函数都是从BaseThreadStart函数开始运行

  1. VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
  2. __try {
  3. ExitThread((pfnStartAddr)(pvParam));
  4. }
  5. __except (UnhandledExceptionFilter(GetExceptionInformation())) {
  6. ExitProcess(GetExceptionCode());
  7. }
  8. // NOTE: We never get here
  9. }

这里的函数UnhandledExceptionFilter用来处理线程中捕获的未处理的异常,调用SetUnhandledExceptionFilter就是用来设置这个函数。

这里的__try{}__except{} 是 Windows系统的结构化异常处理(SEH),具体参考 《Windows核心编程第五版》——第24章

使用VS调试Dump文件

调试Dump文件,dump文件以外,还需要pdb符号文件,pdb符号文件是编译时和exe程序同时生成的,默认情况下Debug版本会生成符号文件,Release文件不生成符号文件,Release模式下需要手动打开生成符号文件。

打开Dump文件

文件->打开->文件,选择dump文件

设置符号文件

符号文件(pdb)必须保证时和exe同时生成的,且不能改文件名,否则会加载失败

有两种方法

1. 把符号文件和dump文件放在同一个目录下,VS在加载dump时会读取dump目录下的符号文件。

2. 通过 【工具->选项->调试->符号】设置符号文件路径。

调试 

点击右上角的 【使用 混合 进行调试】,则可以查看dump文件的内容,如果有异常会显示对应异常位置。整体效果和调试模式下运行出现异常是一样的。

 

使用Windbg调试Dump文件

Windbg 下载 Install WinDbg - Windows drivers | Microsoft Learn

Windbg打开dump后会提示是否有异常

调试步骤:

1. 设置符号文件 【文件->settings->debuging settings】

2. 输入 .ecxr 命令

3. 输入 kn 命令

查看exe编译时间

lm vm test_win*

通过这个时间可以去查找exe对应的pdb文件 

完整代码例子:小康6650/StudyProject - Gitee.com

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/179276?site
推荐阅读
相关标签
  

闽ICP备14008679号