• 【C++】定位程序崩溃(Mingw)


    站在巨人的肩膀上

    Dr. Mingw

    概述

    • 剖析 Dr. Mingw 的源代码,抽取最核心的代码,封装成一个动态库,名为 MiniDrMingw.dll。
    • MiniDrMingw.dll 是面向过程的,容易看懂,没有什么跳转。
    • 可以加入各种个性化需求(如在崩溃时,记录崩溃时的 CPU 占用率、内存占用情况等)。
    • 大概使用方式就是:
      1、应用程序加上编译参数,生成 .map 文件,里面记录着程序的函数地址等。
      # gcc 编译选项,生成 .map 文件
      QMAKE_LFLAGS += -Wl,-Map=$$PWD'/bin/'$$TARGET'.map'
      
      2、应用程序在 main 函数里面,使用下面语句进行初始化:
      ExcHndlInit((a.applicationDirPath() +"/" + a.applicationName() + ".RPT").toLocal8Bit().data());
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 在崩溃时,如何定位出崩溃的函数,后面在讲 Demo 的时候再说清楚。

    MiniDrMingw.dll 的主要函数:SetUnhandledExceptionFilter-设置异常捕获函数。

    • 当异常没有处理的时候,系统就会调用 SetUnhandledExceptionFilter 所设置异常处理函数.
    • 当发生异常时,比如内存访问违例时,CPU 硬件会发现此问题,并产生一个异常(你可以把它理解为中断),然后 CPU 会把代码流程切换到异常处理服务例程。操作系统异常处理服务例程会查看当前进程是否处于调试状态。如果是,则通知调试器发生了异常,如果不是则操作系统会查看当前线程是否安装了的异常帧链(FS[0]),如果安装了 SEH(try… catch…),则调用 SEH,并根据返回结果决定是否全局展开或局部展开。如果异常链中所有的 SEH 都没有处理此异常,而且此进程还处于调试状态,则操作系统会再次通知调试器发生异常(二次异常)。如果还没人处理,则调用操作系统的默认异常处理代码 UnhandledExceptionHandler,不过操作系统允许你 Hook 这个函数,就是通过 SetUnhandledExceptionFilter 函数来设置。大部分异常通过此种方法都能捕获,不过栈溢出、覆盖的有可能捕获不到。
    • 该函数说明如下:
      LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( _In_LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
      - 参数:lpTopLevelExceptionFilter,函数指针。当异常发生时,且程序不处于调试模式(在 vs 或者别的调试器里运行)则首先调用该函数。
      - 返回值:返回以前设置的回调函数。
      
      • 1
      • 2
      • 3

    MiniDrMingw.dll 的函数调用关系

    void ExcHndlInit(const char *pCrashFileName)
    	↓
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter)
    	↓
    MyUnhandledExceptionFilter
    	↓
    GenerateExceptionReport
    	↓
    dumpException(hProcess, pExceptionRecord)、dumpStack(hProcess, GetCurrentThread(), pContext)、dumpModules(hProcess);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 详细说明可以看下面源代码的注释。

    MiniDrMingw.dll 的源代码

    • 虽然用 QtCreator 开发,但是源代码基本没有用到 Qt 的东西。
    • 以动态库的形式。
    • 分为以前几个文件:
      • main.cpp:导出函数、主要函数。
      • dllexport.h:导出接口。
      • log.h:写文件。
      • formatoutput.h:将崩溃信息格式为可读信息。
      • util.h:一些辅助类的函数。
    • 工程文件 DrMingwDemo.pro 源代码如下:
      // MiniDrMingw.pro
      QT += core
      
      TARGET = MiniDrMingw
      TEMPLATE = lib
      DESTDIR = bin
      
      CONFIG += skip_target_version_ext
      
      DEFINES += __SHARE_EXPORT
      
      win32{
      QMAKE_LFLAGS += -Wl,--add-stdcall-alias
      }
      
      LIBS += -lpsapi -lversion -ldbghelp
      
      HEADERS += \
          dllexport.h \
          log.h \
          formatoutput.h \
          util.h
      
      unix {
          target.path = /usr/lib
          INSTALLS += target
      }
      
      SOURCES += \
          main.cpp
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
    • main.cpp 源代码如下:
      // main.cpp
      #include "dllexport.h"
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include "log.h"
      #include "formatoutput.h"
      
      static std::string g_sCrashFileName;
      static BOOL g_bHandlerSet = FALSE;
      static LPTOP_LEVEL_EXCEPTION_FILTER g_prevUnhandledExceptionFilter = nullptr;
      
      DWORD SetSymOptions(BOOL fDebug)
      {
          DWORD dwSymOptions = SymGetOptions();
      
          // We have more control calling UnDecorateSymbolName directly Also, we
          // don't want DbgHelp trying to undemangle MinGW symbols (e.g., from DLL
          // exports) behind our back (as it will just strip the leading underscore.)
          if (0) {
              dwSymOptions |= SYMOPT_UNDNAME;
          } else {
              dwSymOptions &= ~SYMOPT_UNDNAME;
          }
      
          dwSymOptions |= SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST;
      
          if (TRUE) {
              dwSymOptions |= SYMOPT_DEFERRED_LOADS;
          }
      
          if (fDebug) {
              dwSymOptions |= SYMOPT_DEBUG;
          }
      
      #ifdef _WIN64
          dwSymOptions |= SYMOPT_INCLUDE_32BIT_MODULES;
      #endif
      
          return SymSetOptions(dwSymOptions);
      }
      
      BOOL InitializeSym(HANDLE hProcess, BOOL fInvadeProcess)
      {
          // Provide default symbol search path
          // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680689.aspx
          // http://msdn.microsoft.com/en-gb/library/windows/hardware/ff558829.aspx
          std::string sSymSearchPathBuf;
          const char *szSymSearchPath = nullptr;
          if (getenv("_NT_SYMBOL_PATH") == nullptr && getenv("_NT_ALT_SYMBOL_PATH") == nullptr) {
              char szLocalAppData[MAX_PATH];
              HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, szLocalAppData);
              assert(SUCCEEDED(hr));
              if (SUCCEEDED(hr)) {
                  // Cache symbols in %APPDATA%\drmingw
                  sSymSearchPathBuf += "srv*";
                  sSymSearchPathBuf += szLocalAppData;
                  sSymSearchPathBuf += "\\drmingw*http://msdl.microsoft.com/download/symbols";
                  szSymSearchPath = sSymSearchPathBuf.c_str();
              } else {
                  // No cache
                  szSymSearchPath = "srv*http://msdl.microsoft.com/download/symbols";
              }
          }
      
          return SymInitialize(hProcess, szSymSearchPath, fInvadeProcess);
      }
      
      void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
      {
          // pExceptionInfo 的结构体信息扩展开来如下:
          // typedef struct _EXCEPTION_POINTERS {
          //  DWORD ExceptionCode;
          //  DWORD ExceptionFlags;
          //  struct _EXCEPTION_RECORD *ExceptionRecord;
          //  PVOID ExceptionAddress;
          //  DWORD NumberParameters;
          //  ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
          //
          //  DWORD ContextFlags;
          //  DWORD Dr0;
          //  DWORD Dr1;
          //  DWORD Dr2;
          //  DWORD Dr3;
          //  DWORD Dr6;
          //  DWORD Dr7;
          //  DWORD ControlWord;
          //  DWORD StatusWord;
          //  DWORD TagWord;
          //  DWORD ErrorOffset;
          //  DWORD ErrorSelector;
          //  DWORD DataOffset;
          //  DWORD DataSelector;
          //  BYTE RegisterArea[SIZE_OF_80387_REGISTERS];
          //  DWORD Cr0NpxState;
          //  DWORD SegGs;
          //  DWORD SegFs;
          //  DWORD SegEs;
          //  DWORD SegDs;
          //  DWORD Edi;
          //  DWORD Esi;
          //  DWORD Ebx;
          //  DWORD Edx;
          //  DWORD Ecx;
          //  DWORD Eax;
          //  DWORD Ebp;
          //  DWORD Eip;
          //  DWORD SegCs;
          //  DWORD EFlags;
          //  DWORD Esp;
          //  DWORD SegSs;
          //  BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
          //} EXCEPTION_POINTERS,*PEXCEPTION_POINTERS;
      
          // Start out with a banner
          writeLog("-------------------\n\n");
      
          // 打印系统时间
          SYSTEMTIME SystemTime;
          GetLocalTime(&SystemTime);
          char szDateStr[128];
          LCID Locale = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
          GetDateFormatA(Locale, 0, &SystemTime, "dddd',' MMMM d',' yyyy", szDateStr, _countof(szDateStr));
          char szTimeStr[128];
          GetTimeFormatA(Locale, 0, &SystemTime, "HH':'mm':'ss", szTimeStr, _countof(szTimeStr));
          writeLog("Error occurred on %s at %s.\n\n", szDateStr, szTimeStr);
      
          // 获取当前进程的一个句柄
          HANDLE hProcess = GetCurrentProcess();
      
          SetSymOptions(FALSE);
      
          PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
          if(InitializeSym(hProcess, TRUE))
          {
              // 打印崩溃地址信息
              dumpException(hProcess, pExceptionRecord);
      
              PCONTEXT pContext = pExceptionInfo->ContextRecord;
              assert(pContext);
      
              // XXX: In 64-bits WINE we can get context record that don't match the exception record somehow
      #ifdef _WIN64
              PVOID ip = (PVOID)pContext->Rip;
      #else
              PVOID ip = (PVOID)pContext->Eip;
      #endif
              if(pExceptionRecord->ExceptionAddress != ip)
              {
                  writeLog("warning: inconsistent exception context record\n");
              }
      
              // 函数调用栈信息
              dumpStack(hProcess, GetCurrentThread(), pContext);
      
              if (!SymCleanup(hProcess)) {
                  assert(0);
              }
          }
      
          // 打印调用模块的信息
          dumpModules(hProcess);
          writeLog("\n");
      }
      
      // 当出现未被处理的异常时,会先调用该函数
      static LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
      {
          static LONG nLockNum = 0;
          // 实现数的原子性加减
          if(1 == InterlockedIncrement(&nLockNum))
          {
              // SetErrorMode() 函数控制 Windows 是否处理指定类型的严重错误或使调用应用程序来处理它们。
              // SEM_FAILCRITICALERRORS: 系统不显示关键错误处理消息框。相反,系统发送错误给调用进程。
              // SEM_NOGPFAULTERRORBOX: 系统不显示Windows错误报告对话框。
              // SEM_NOALIGNMENTFAULTEXCEPT: 系统会自动修复故障。此功能只支持部分处理器架构。
              // SEM_NOOPENFILEERRORBOX:当无法找到文件时不弹出错误对话框。相反,错误返回给调用进程。
              UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
      
              // 创建崩溃时写到磁盘的 .RPT 文件
              if(nullptr == g_handleCrashFile)
              {
                  g_handleCrashFile = CreateFileA(g_sCrashFileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);
              }
      
              if(nullptr != g_handleCrashFile)
              {
                  // 在一个文件中设置新的读取位置
                  SetFilePointer(g_handleCrashFile, 0, 0, FILE_END);
      
                  // 主要通过该函数来生成异常报告信息,其中 pExceptionInfo 是异常信息结构体指针。
                  GenerateExceptionReport(pExceptionInfo);
      
                  // 刷新输出缓存区(也就是为了实时写磁盘)
                  FlushFileBuffers(g_handleCrashFile);
              }
      
              // 把错误模式设置回去
              SetErrorMode(oldErrorMode);
          }
      
          // 实现数的原子性加减
          InterlockedDecrement(&nLockNum);
      
          // 以前也设置过异常捕获函数,那么处理完我们的异常信息之后,我们再调用它。
          if(g_prevUnhandledExceptionFilter)
              return g_prevUnhandledExceptionFilter(pExceptionInfo);
          else
              return EXCEPTION_CONTINUE_SEARCH;
      }
      
      void ExcHndlInit(const char *pCrashFileName)
      {
          if(nullptr != pCrashFileName)
              g_sCrashFileName = pCrashFileName;
          else
              g_sCrashFileName = "./crash.RPT";
      
          if(FALSE == g_bHandlerSet)
          {
              // 设置自己的异常捕获函数,返回以前设置的异常捕获函数
              g_prevUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
              g_bHandlerSet = TRUE;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
      • 136
      • 137
      • 138
      • 139
      • 140
      • 141
      • 142
      • 143
      • 144
      • 145
      • 146
      • 147
      • 148
      • 149
      • 150
      • 151
      • 152
      • 153
      • 154
      • 155
      • 156
      • 157
      • 158
      • 159
      • 160
      • 161
      • 162
      • 163
      • 164
      • 165
      • 166
      • 167
      • 168
      • 169
      • 170
      • 171
      • 172
      • 173
      • 174
      • 175
      • 176
      • 177
      • 178
      • 179
      • 180
      • 181
      • 182
      • 183
      • 184
      • 185
      • 186
      • 187
      • 188
      • 189
      • 190
      • 191
      • 192
      • 193
      • 194
      • 195
      • 196
      • 197
      • 198
      • 199
      • 200
      • 201
      • 202
      • 203
      • 204
      • 205
      • 206
      • 207
      • 208
      • 209
      • 210
      • 211
      • 212
      • 213
      • 214
      • 215
      • 216
      • 217
      • 218
      • 219
      • 220
      • 221
      • 222
      • 223
      • 224
      • 225
      • 226
      • 227
      • 228
      • 229
    • formatoutput.h 源代码如下:
      // formatoutput.h
      #ifndef FORMATOUTPUT_H
      #define FORMATOUTPUT_H
      
      #include "log.h"
      #include "util.h"
      
      BOOL dumpSourceCode(LPCSTR lpFileName, DWORD dwLineNumber)
      {
          FILE *fp;
          unsigned i;
          DWORD dwContext = 2;
      
          if ((fp = fopen(lpFileName, "r")) == NULL)
              return FALSE;
      
          i = 0;
          while (!feof(fp) && ++i <= dwLineNumber + dwContext) {
              int c;
      
              if ((int)i >= (int)dwLineNumber - (int)dwContext) {
                  writeLog(i == dwLineNumber ? ">%5i: " : "%6i: ", i);
                  while (!feof(fp) && (c = fgetc(fp)) != '\n')
                      if (isprint(c))
                          writeLog("%c", c);
                  writeLog("\n");
              } else {
                  while (!feof(fp) && fgetc(fp) != '\n')
                      ;
              }
          }
      
          fclose(fp);
          return TRUE;
      }
      
      void dumpContext(
      #ifdef _WIN64
          const WOW64_CONTEXT *pContext
      #else
          const CONTEXT *pContext
      #endif
      )
      {
          // Show the registers
          writeLog("Registers:\n");
          if (pContext->ContextFlags & CONTEXT_INTEGER) {
              writeLog("eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n", pContext->Eax,
                      pContext->Ebx, pContext->Ecx, pContext->Edx, pContext->Esi, pContext->Edi);
          }
          if (pContext->ContextFlags & CONTEXT_CONTROL) {
              writeLog("eip=%08lx esp=%08lx ebp=%08lx iopl=%1lx %s %s %s %s %s %s %s %s %s %s\n",
                      pContext->Eip, pContext->Esp, pContext->Ebp,
                      (pContext->EFlags >> 12) & 3,                  //  IOPL level value
                      pContext->EFlags & 0x00100000 ? "vip" : "   ", //  VIP (virtual interrupt pending)
                      pContext->EFlags & 0x00080000 ? "vif" : "   ", //  VIF (virtual interrupt flag)
                      pContext->EFlags & 0x00000800 ? "ov" : "nv",   //  VIF (virtual interrupt flag)
                      pContext->EFlags & 0x00000400 ? "dn" : "up",   //  OF (overflow flag)
                      pContext->EFlags & 0x00000200 ? "ei" : "di",   //  IF (interrupt enable flag)
                      pContext->EFlags & 0x00000080 ? "ng" : "pl",   //  SF (sign flag)
                      pContext->EFlags & 0x00000040 ? "zr" : "nz",   //  ZF (zero flag)
                      pContext->EFlags & 0x00000010 ? "ac" : "na",   //  AF (aux carry flag)
                      pContext->EFlags & 0x00000004 ? "po" : "pe",   //  PF (parity flag)
                      pContext->EFlags & 0x00000001 ? "cy" : "nc"    //  CF (carry flag)
              );
          }
          if (pContext->ContextFlags & CONTEXT_SEGMENTS) {
              writeLog("cs=%04lx  ss=%04lx  ds=%04lx  es=%04lx  fs=%04lx  gs=%04lx", pContext->SegCs,
                      pContext->SegSs, pContext->SegDs, pContext->SegEs, pContext->SegFs,
                      pContext->SegGs);
              if (pContext->ContextFlags & CONTEXT_CONTROL) {
                  writeLog("             efl=%08lx", pContext->EFlags);
              }
          } else {
              if (pContext->ContextFlags & CONTEXT_CONTROL) {
                  writeLog("                                                                       "
                          "efl=%08lx",
                          pContext->EFlags);
              }
          }
          writeLog("\n\n");
      }
      
      void dumpStack(HANDLE hProcess, HANDLE hThread, const CONTEXT *pContext)
      {
          DWORD MachineType;
      
          assert(pContext);
      
          STACKFRAME64 StackFrame;
          ZeroMemory(&StackFrame, sizeof StackFrame);
      
          BOOL bWow64 = FALSE;
          if (0) {       // if (HAVE_WIN64)
              IsWow64Process(hProcess, &bWow64);
          }
          if (bWow64) {
              const WOW64_CONTEXT *pWow64Context = reinterpret_cast(pContext);
              // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
              assert((pWow64Context->ContextFlags & WOW64_CONTEXT_FULL) == WOW64_CONTEXT_FULL);
      #ifdef _WIN64
              dumpContext(pWow64Context);
      #endif
              MachineType = IMAGE_FILE_MACHINE_I386;
              StackFrame.AddrPC.Offset = pWow64Context->Eip;
              StackFrame.AddrStack.Offset = pWow64Context->Esp;
              StackFrame.AddrFrame.Offset = pWow64Context->Ebp;
          } else {
              // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
              assert((pContext->ContextFlags & CONTEXT_FULL) == CONTEXT_FULL);
      #ifndef _WIN64
              MachineType = IMAGE_FILE_MACHINE_I386;
              dumpContext(pContext);                      // 打印当时寄存器的信息
              StackFrame.AddrPC.Offset = pContext->Eip;
              StackFrame.AddrStack.Offset = pContext->Esp;
              StackFrame.AddrFrame.Offset = pContext->Ebp;
      #else
              MachineType = IMAGE_FILE_MACHINE_AMD64;
              StackFrame.AddrPC.Offset = pContext->Rip;
              StackFrame.AddrStack.Offset = pContext->Rsp;
              StackFrame.AddrFrame.Offset = pContext->Rbp;
      #endif
          }
          StackFrame.AddrPC.Mode = AddrModeFlat;
          StackFrame.AddrStack.Mode = AddrModeFlat;
          StackFrame.AddrFrame.Mode = AddrModeFlat;
      
          /*
           * StackWalk64 modifies Context, so pass a copy.
           */
          CONTEXT Context = *pContext;
      
          if (MachineType == IMAGE_FILE_MACHINE_I386) {
              writeLog("AddrPC   Params\n");
          } else {
              writeLog("AddrPC           Params\n");
          }
      
          BOOL bInsideWine = []() -> BOOL
          {
                  HMODULE hNtDll = GetModuleHandleA("ntdll");
                  if (!hNtDll) {
                      return FALSE;
                  }
                  return GetProcAddress(hNtDll, "wine_get_version") != NULL;
          }();
      
          DWORD64 PrevFrameStackOffset = StackFrame.AddrStack.Offset - 1;
          int nudge = 0;
      
          while (TRUE) {
              constexpr int MAX_SYM_NAME_SIZE = 512;
              char szSymName[MAX_SYM_NAME_SIZE] = "";
              char szFileName[MAX_PATH] = "";
              DWORD dwLineNumber = 0;
      
              if (!StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context,
                               NULL, // ReadMemoryRoutine
                               SymFunctionTableAccess64, SymGetModuleBase64,
                               NULL // TranslateAddress
                               ))
                  break;
      
              if (MachineType == IMAGE_FILE_MACHINE_I386) {
                  writeLog("%08lX %08lX %08lX %08lX", (DWORD)StackFrame.AddrPC.Offset,
                          (DWORD)StackFrame.Params[0], (DWORD)StackFrame.Params[1],
                          (DWORD)StackFrame.Params[2]);
              } else {
                  writeLog("%016I64X %016I64X %016I64X %016I64X", StackFrame.AddrPC.Offset,
                          StackFrame.Params[0], StackFrame.Params[1], StackFrame.Params[2]);
              }
      
              BOOL bSymbol = TRUE;
              BOOL bLine = FALSE;
              DWORD dwOffsetFromSymbol = 0;
      
              DWORD64 AddrPC = StackFrame.AddrPC.Offset;
              HMODULE hModule = (HMODULE)(INT_PTR)SymGetModuleBase64(hProcess, AddrPC);
              char szModule[MAX_PATH];
              if (hModule && GetModuleFileNameExA(hProcess, hModule, szModule, MAX_PATH)) {
                  writeLog("  %s", getBaseName(szModule));
      
                  bSymbol = GetSymFromAddr(hProcess, AddrPC + nudge, szSymName, MAX_SYM_NAME_SIZE,
                                           &dwOffsetFromSymbol);
                  if (bSymbol) {
                      writeLog("!%s+0x%lx", szSymName, dwOffsetFromSymbol - nudge);
      
                      bLine =
                          GetLineFromAddr(hProcess, AddrPC + nudge, szFileName, MAX_PATH, &dwLineNumber);
                      if (bLine) {
                          writeLog("  [%s @ %ld]", szFileName, dwLineNumber);
                      }
                  } else {
                      writeLog("!0x%I64x", AddrPC - (DWORD64)(INT_PTR)hModule);
                  }
              }
      
              writeLog("\n");
      
              if (bLine) {
                  dumpSourceCode(szFileName, dwLineNumber);
              }
      
              // Basic sanity check to make sure  the frame is OK.  Bail if not.
              if (StackFrame.AddrStack.Offset <= PrevFrameStackOffset ||
                  StackFrame.AddrPC.Offset == 0xBAADF00D) {
                  break;
              }
              PrevFrameStackOffset = StackFrame.AddrStack.Offset;
      
              // Wine's StackWalk64 implementation on certain yield never ending
              // stack backtraces unless one bails out when AddrFrame is zero.
              if (bInsideWine && StackFrame.AddrFrame.Offset == 0) {
                  break;
              }
      
              /*
               * When we walk into the callers, StackFrame.AddrPC.Offset will not
               * contain the calling function's address, but rather the return
               * address.  This could be the next statement, or sometimes (for
               * no-return functions) a completely different function, so nudge the
               * address by one byte to ensure we get the information about the
               * calling statement itself.
               */
              nudge = -1;
          }
      
          writeLog("\n");
      }
      
      void dumpException(HANDLE hProcess, PEXCEPTION_RECORD pExceptionRecord)
      {
          NTSTATUS ExceptionCode = pExceptionRecord->ExceptionCode;
      
          char szModule[MAX_PATH];
          LPCSTR lpcszProcess;
          HMODULE hModule;
      
          if (GetModuleFileNameExA(hProcess, NULL, szModule, MAX_PATH))
          {
              lpcszProcess = getBaseName(szModule);
          }
          else
          {
              lpcszProcess = "Application";
          }
      
          // First print information about the type of fault
          writeLog("%s caused", lpcszProcess);
      
          LPCSTR lpcszException = getExceptionString(ExceptionCode);
          if (lpcszException) {
              LPCSTR lpszArticle;
              switch (lpcszException[0]) {
              case 'A':
              case 'E':
              case 'I':
              case 'O':
              case 'U':
                  lpszArticle = "an";
                  break;
              default:
                  lpszArticle = "a";
                  break;
              }
      
              writeLog(" %s %s", lpszArticle, lpcszException);
          } else {
              writeLog(" an Unknown [0x%lX] Exception", ExceptionCode);
          }
      
          // Now print information about where the fault occurred
          writeLog(" at location %p", pExceptionRecord->ExceptionAddress);
          if((hModule = (HMODULE)(INT_PTR)
                   SymGetModuleBase64(hProcess, (DWORD64)(INT_PTR)pExceptionRecord->ExceptionAddress)) &&
              GetModuleFileNameExA(hProcess, hModule, szModule, sizeof szModule))
          {
              writeLog(" in module %s", getBaseName(szModule));
          }
      
          // If the exception was an access violation, print out some additional information, to the error
          // log and the debugger.
          // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082%28v=vs.85%29.aspx
          if ((ExceptionCode == EXCEPTION_ACCESS_VIOLATION || ExceptionCode == EXCEPTION_IN_PAGE_ERROR) &&
              pExceptionRecord->NumberParameters >= 2)
          {
              LPCSTR lpszVerb;
              switch (pExceptionRecord->ExceptionInformation[0]) {
              case 0:
                  lpszVerb = "Reading from";
                  break;
              case 1:
                  lpszVerb = "Writing to";
                  break;
              case 8:
                  lpszVerb = "DEP violation at";
                  break;
              default:
                  lpszVerb = "Accessing";
                  break;
              }
      
              writeLog(" %s location %p", lpszVerb, (PVOID)pExceptionRecord->ExceptionInformation[1]);
          }
      
          writeLog(".\n\n");
      }
      
      void dumpModules(HANDLE hProcess)
      {
          HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hProcess));
          if (hModuleSnap == INVALID_HANDLE_VALUE) {
              return;
          }
      
          DWORD MachineType;
      #ifdef _WIN64
          BOOL bWow64 = FALSE;
          IsWow64Process(hProcess, &bWow64);
          if (bWow64) {
              MachineType = IMAGE_FILE_MACHINE_I386;
          } else {
      #else
          {
      #endif
      #ifndef _WIN64
              MachineType = IMAGE_FILE_MACHINE_I386;
      #else
              MachineType = IMAGE_FILE_MACHINE_AMD64;
      #endif
          }
      
          MODULEENTRY32 me32;
          me32.dwSize = sizeof me32;
          if (Module32First(hModuleSnap, &me32)) {
              do {
                  if (MachineType == IMAGE_FILE_MACHINE_I386) {
                      writeLog(
                          "%08lX-%08lX ",
                          (DWORD)(DWORD64)me32.modBaseAddr,
                          (DWORD)(DWORD64)me32.modBaseAddr + me32.modBaseSize
                      );
                  } else {
                      writeLog(
                          "%016I64X-%016I64X ",
                          (DWORD64)me32.modBaseAddr,
                          (DWORD64)me32.modBaseAddr + me32.modBaseSize
                      );
                  }
      
                  char *ptr = nullptr;
                  long len = WideCharToMultiByte(CP_ACP, 0, me32.szExePath, -1, NULL, 0, NULL, NULL);
                  WideCharToMultiByte(CP_ACP, 0, me32.szExePath, -1, ptr, len + 1, NULL, NULL);
      
                  const char *szBaseName = getBaseName(ptr);
      
                  DWORD dwVInfo[4];
                  if (getModuleVersionInfo(ptr, dwVInfo)) {
                      writeLog("%-12s\t%lu.%lu.%lu.%lu\n", szBaseName, dwVInfo[0], dwVInfo[1], dwVInfo[2],
                              dwVInfo[3]);
                  } else {
                      writeLog("%s\n", szBaseName);
                  }
              } while (Module32Next(hModuleSnap, &me32));
              writeLog("\n");
          }
      
          CloseHandle(hModuleSnap);
      }
      
      #endif // FORMATOUTPUT_H
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
      • 136
      • 137
      • 138
      • 139
      • 140
      • 141
      • 142
      • 143
      • 144
      • 145
      • 146
      • 147
      • 148
      • 149
      • 150
      • 151
      • 152
      • 153
      • 154
      • 155
      • 156
      • 157
      • 158
      • 159
      • 160
      • 161
      • 162
      • 163
      • 164
      • 165
      • 166
      • 167
      • 168
      • 169
      • 170
      • 171
      • 172
      • 173
      • 174
      • 175
      • 176
      • 177
      • 178
      • 179
      • 180
      • 181
      • 182
      • 183
      • 184
      • 185
      • 186
      • 187
      • 188
      • 189
      • 190
      • 191
      • 192
      • 193
      • 194
      • 195
      • 196
      • 197
      • 198
      • 199
      • 200
      • 201
      • 202
      • 203
      • 204
      • 205
      • 206
      • 207
      • 208
      • 209
      • 210
      • 211
      • 212
      • 213
      • 214
      • 215
      • 216
      • 217
      • 218
      • 219
      • 220
      • 221
      • 222
      • 223
      • 224
      • 225
      • 226
      • 227
      • 228
      • 229
      • 230
      • 231
      • 232
      • 233
      • 234
      • 235
      • 236
      • 237
      • 238
      • 239
      • 240
      • 241
      • 242
      • 243
      • 244
      • 245
      • 246
      • 247
      • 248
      • 249
      • 250
      • 251
      • 252
      • 253
      • 254
      • 255
      • 256
      • 257
      • 258
      • 259
      • 260
      • 261
      • 262
      • 263
      • 264
      • 265
      • 266
      • 267
      • 268
      • 269
      • 270
      • 271
      • 272
      • 273
      • 274
      • 275
      • 276
      • 277
      • 278
      • 279
      • 280
      • 281
      • 282
      • 283
      • 284
      • 285
      • 286
      • 287
      • 288
      • 289
      • 290
      • 291
      • 292
      • 293
      • 294
      • 295
      • 296
      • 297
      • 298
      • 299
      • 300
      • 301
      • 302
      • 303
      • 304
      • 305
      • 306
      • 307
      • 308
      • 309
      • 310
      • 311
      • 312
      • 313
      • 314
      • 315
      • 316
      • 317
      • 318
      • 319
      • 320
      • 321
      • 322
      • 323
      • 324
      • 325
      • 326
      • 327
      • 328
      • 329
      • 330
      • 331
      • 332
      • 333
      • 334
      • 335
      • 336
      • 337
      • 338
      • 339
      • 340
      • 341
      • 342
      • 343
      • 344
      • 345
      • 346
      • 347
      • 348
      • 349
      • 350
      • 351
      • 352
      • 353
      • 354
      • 355
      • 356
      • 357
      • 358
      • 359
      • 360
      • 361
      • 362
      • 363
      • 364
      • 365
      • 366
      • 367
      • 368
      • 369
      • 370
      • 371
    • util.h 源代码如下:
      // util.h
      #ifndef UTIL_H
      #define UTIL_H
      
      #include 
      #include 
      
      const char *getSeparator(const char *szFilename)
      {
          const char *p, *q;
          p = NULL;
          q = szFilename;
          char c;
          do {
              c = *q++;
              if (c == '\\' || c == '/' || c == ':')
              {
                  p = q;
              }
          } while (c);
          return p;
      }
      
      const char *getBaseName(const char *szFilename)
      {
          const char *pSeparator = getSeparator(szFilename);
          if (!pSeparator)
          {
              return szFilename;
          }
          return pSeparator;
      }
      
      BOOL GetSymFromAddr(HANDLE hProcess,
                     DWORD64 dwAddress,
                     LPSTR lpSymName,
                     DWORD nSize,
                     LPDWORD lpdwDisplacement)
      {
          PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)malloc(sizeof(SYMBOL_INFO) + nSize * sizeof(char));
      
          DWORD64 dwDisplacement =
              0; // Displacement of the input address, relative to the start of the symbol
          BOOL bRet;
      
          pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
          pSymbol->MaxNameLen = nSize;
      
          DWORD dwOptions = SymGetOptions();
      
          bRet = SymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol);
      
          if (bRet) {
              // Demangle if not done already
              if ((dwOptions & SYMOPT_UNDNAME) ||
                  UnDecorateSymbolName(pSymbol->Name, lpSymName, nSize, UNDNAME_NAME_ONLY) == 0) {
                  strncpy(lpSymName, pSymbol->Name, nSize);
              }
              if (lpdwDisplacement) {
                  *lpdwDisplacement = dwDisplacement;
              }
          }
      
          free(pSymbol);
      
          return bRet;
      }
      
      BOOL GetLineFromAddr(HANDLE hProcess,
                      DWORD64 dwAddress,
                      LPSTR lpFileName,
                      DWORD nSize,
                      LPDWORD lpLineNumber)
      {
          IMAGEHLP_LINE64 Line;
          DWORD dwDisplacement =
              0; // Displacement of the input address, relative to the start of the symbol
      
          // Do the source and line lookup.
          memset(&Line, 0, sizeof Line);
          Line.SizeOfStruct = sizeof Line;
      
          if (!SymGetLineFromAddr64(hProcess, dwAddress, &dwDisplacement, &Line))
              return FALSE;
      
          assert(lpFileName && lpLineNumber);
      
          strncpy(lpFileName, Line.FileName, nSize);
          *lpLineNumber = Line.LineNumber;
      
          return TRUE;
      }
      
      BOOL getModuleVersionInfo(LPCSTR szModule, DWORD *dwVInfo)
      {
          DWORD dummy, size;
          BOOL success = FALSE;
      
          size = GetFileVersionInfoSizeA(szModule, &dummy);
          if (size > 0) {
              LPVOID pVer = malloc(size);
              ZeroMemory(pVer, size);
              if (GetFileVersionInfoA(szModule, 0, size, pVer)) {
                  VS_FIXEDFILEINFO *ffi;
                  if (VerQueryValueA(pVer, "\\", (LPVOID *)&ffi, (UINT *)&dummy)) {
                      dwVInfo[0] = ffi->dwFileVersionMS >> 16;
                      dwVInfo[1] = ffi->dwFileVersionMS & 0xFFFF;
                      dwVInfo[2] = ffi->dwFileVersionLS >> 16;
                      dwVInfo[3] = ffi->dwFileVersionLS & 0xFFFF;
                      success = TRUE;
                  }
              }
              free(pVer);
          }
          return success;
      }
      
      #ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION
      #define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS)0xC000041DL)
      #endif
      #ifndef STATUS_CPP_EH_EXCEPTION
      #define STATUS_CPP_EH_EXCEPTION ((NTSTATUS)0xE06D7363L)
      #endif
      #ifndef STATUS_CLR_EXCEPTION
      #define STATUS_CLR_EXCEPTION ((NTSTATUS)0xE0434f4DL)
      #endif
      #ifndef STATUS_WX86_BREAKPOINT
      #define STATUS_WX86_BREAKPOINT ((NTSTATUS)0x4000001FL)
      #endif
      #ifndef STATUS_POSSIBLE_DEADLOCK
      #define STATUS_POSSIBLE_DEADLOCK ((DWORD)0xC0000194L)
      #endif
      
      LPCSTR getExceptionString(NTSTATUS ExceptionCode)
      {
          switch (ExceptionCode) {
          case EXCEPTION_ACCESS_VIOLATION: // 0xC0000005
              return "Access Violation";
          case EXCEPTION_IN_PAGE_ERROR: // 0xC0000006
              return "In Page Error";
          case EXCEPTION_INVALID_HANDLE: // 0xC0000008
              return "Invalid Handle";
          case EXCEPTION_ILLEGAL_INSTRUCTION: // 0xC000001D
              return "Illegal Instruction";
          case EXCEPTION_NONCONTINUABLE_EXCEPTION: // 0xC0000025
              return "Cannot Continue";
          case EXCEPTION_INVALID_DISPOSITION: // 0xC0000026
              return "Invalid Disposition";
          case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: // 0xC000008C
              return "Array bounds exceeded";
          case EXCEPTION_FLT_DENORMAL_OPERAND: // 0xC000008D
              return "Floating-point denormal operand";
          case EXCEPTION_FLT_DIVIDE_BY_ZERO: // 0xC000008E
              return "Floating-point division by zero";
          case EXCEPTION_FLT_INEXACT_RESULT: // 0xC000008F
              return "Floating-point inexact result";
          case EXCEPTION_FLT_INVALID_OPERATION: // 0xC0000090
              return "Floating-point invalid operation";
          case EXCEPTION_FLT_OVERFLOW: // 0xC0000091
              return "Floating-point overflow";
          case EXCEPTION_FLT_STACK_CHECK: // 0xC0000092
              return "Floating-point stack check";
          case EXCEPTION_FLT_UNDERFLOW: // 0xC0000093
              return "Floating-point underflow";
          case EXCEPTION_INT_DIVIDE_BY_ZERO: // 0xC0000094
              return "Integer division by zero";
          case EXCEPTION_INT_OVERFLOW: // 0xC0000095
              return "Integer overflow";
          case EXCEPTION_PRIV_INSTRUCTION: // 0xC0000096
              return "Privileged instruction";
          case EXCEPTION_STACK_OVERFLOW: // 0xC00000FD
              return "Stack Overflow";
          case EXCEPTION_POSSIBLE_DEADLOCK: // 0xC0000194
              return "Possible deadlock condition";
          case STATUS_FATAL_USER_CALLBACK_EXCEPTION: // 0xC000041D
              return "Fatal User Callback Exception";
          case STATUS_ASSERTION_FAILURE: // 0xC0000420
              return "Assertion failure";
      
          case STATUS_CLR_EXCEPTION: // 0xE0434f4D
              return "CLR exception";
          case STATUS_CPP_EH_EXCEPTION: // 0xE06D7363
              return "C++ exception handling exception";
      
          case EXCEPTION_GUARD_PAGE: // 0x80000001
              return "Guard Page Exception";
          case EXCEPTION_DATATYPE_MISALIGNMENT: // 0x80000002
              return "Alignment Fault";
          case EXCEPTION_BREAKPOINT: // 0x80000003
              return "Breakpoint";
          case EXCEPTION_SINGLE_STEP: // 0x80000004
              return "Single Step";
      
          case STATUS_WX86_BREAKPOINT: // 0x4000001F
              return "Breakpoint";
          case DBG_TERMINATE_THREAD: // 0x40010003
              return "Terminate Thread";
          case DBG_TERMINATE_PROCESS: // 0x40010004
              return "Terminate Process";
          case DBG_CONTROL_C: // 0x40010005
              return "Control+C";
          case DBG_CONTROL_BREAK: // 0x40010008
              return "Control+Break";
          case 0x406D1388:
              return "Thread Name Exception";
      
          case RPC_S_UNKNOWN_IF:
              return "Unknown Interface";
          case RPC_S_SERVER_UNAVAILABLE:
              return "Server Unavailable";
      
          default:
              return NULL;
          }
      }
      
      #endif // UTIL_H
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
      • 136
      • 137
      • 138
      • 139
      • 140
      • 141
      • 142
      • 143
      • 144
      • 145
      • 146
      • 147
      • 148
      • 149
      • 150
      • 151
      • 152
      • 153
      • 154
      • 155
      • 156
      • 157
      • 158
      • 159
      • 160
      • 161
      • 162
      • 163
      • 164
      • 165
      • 166
      • 167
      • 168
      • 169
      • 170
      • 171
      • 172
      • 173
      • 174
      • 175
      • 176
      • 177
      • 178
      • 179
      • 180
      • 181
      • 182
      • 183
      • 184
      • 185
      • 186
      • 187
      • 188
      • 189
      • 190
      • 191
      • 192
      • 193
      • 194
      • 195
      • 196
      • 197
      • 198
      • 199
      • 200
      • 201
      • 202
      • 203
      • 204
      • 205
      • 206
      • 207
      • 208
      • 209
      • 210
      • 211
      • 212
      • 213
      • 214
      • 215
      • 216
      • 217
    • log.h 源代码如下:
      // log.h
      #ifndef LOG_H
      #define LOG_H
      
      #include 
      
      static HANDLE g_handleCrashFile = nullptr;
      
      int writeLog(const char *format, ...)
      {
          char szBuffer[1024];
          int retValue;
          va_list ap;
      
          va_start(ap, format);
          retValue = _vsnprintf(szBuffer, sizeof szBuffer, format, ap);
          va_end(ap);
      
          DWORD cbWritten;
          const char *szText = szBuffer;
          while (*szText != '\0') {
              const char *p = szText;
              while (*p != '\0' && *p != '\n') {
                  ++p;
              }
              WriteFile(g_handleCrashFile, szText, p - szText, &cbWritten, 0);
              if (*p == '\n') {
                  WriteFile(g_handleCrashFile, "\r\n", 2, &cbWritten, 0);
                  ++p;
              }
              szText = p;
          };
      
          return retValue;
      }
      
      #endif // LOG_H
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
    • dllexport.h 源代码如下:
      // dllexport.h
      #ifndef HEADERDEF_H
      #define HEADERDEF_H
      
      #ifdef __cplusplus
      #define D_EXTERN_C extern "C"
      #else
      #define D_EXTERN_C
      #endif
      
      #ifdef __SHARE_EXPORT
      #define D_SHARE_EXPORT D_DECL_EXPORT
      #else
      #define D_SHARE_EXPORT D_DECL_IMPORT
      #endif
      
      #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__)
      #define D_CALLTYPE __stdcall
      #define D_DECL_EXPORT __declspec(dllexport)
      #define D_DECL_IMPORT
      #else
      #define D_CALLTYPE
      #define D_DECL_EXPORT __attribute__((visibility("default")))
      #define D_DECL_IMPORT __attribute__((visibility("default")))
      #endif
      
      D_EXTERN_C D_SHARE_EXPORT void D_CALLTYPE ExcHndlInit(const char *pCrashFileName);
      
      #endif // HEADERDEF_H
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29

    测试工具 DrMingwDemo

    • 工程文件 DrMingwDemo.pro 源代码如下:
      QT += core
      QT -= gui
      
      CONFIG += c++11
      
      TARGET = DrMingwDemo
      CONFIG += console
      CONFIG -= app_bundle
      
      TEMPLATE = app
      
      DESTDIR = $$PWD/bin
      
      LIBS += -L$$PWD/lib -lMiniDrMingw
      
      INCLUDEPATH += $$PWD/lib/include
      
      # gcc 编译选项,生成 .map 文件
      QMAKE_LFLAGS += -Wl,-Map=$$PWD'/bin/'$$TARGET'.map'
      
      SOURCES += main.cpp
      
      QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
      QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
      
      HEADERS +=
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
    • main.cpp 源代码如下:
      #include 
      #include 
      
      extern "C" void ExcHndlInit(const char *pCrashFileName);
      
      class TestClass : public QThread
      {
      public:
          void run() override
          {
              int* a = (int*)(NULL);
              *a = 1;
          }
      };
      
      int main(int argc, char *argv[])
      {
          QCoreApplication a(argc, argv);
      
          ExcHndlInit((a.applicationDirPath() +"/" + a.applicationName() + ".RPT").toLocal8Bit().data());
      
          // 制造崩溃
          TestClass instance;
          instance.start();
      
          return a.exec();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27

    崩溃分析

    • DrMingwDemo.exe 编译后,会在工程的 bin 目录下生成 DrMingwDemo.map。
    • 双击运行 DrMingwDemo.exe,然后闪退,此时在程序的运行目录,产生了 DrMingwDemo.RPT。
    • 打开 DrMingwDemo.RPT 文件,内容大概如下:
      -------------------
      
      Error occurred on Friday, September 9, 2022 at 20:54:00.
      
      DrMingwDemo.exe caused an Access Violation at location 004028A0 in module DrMingwDemo.exe Writing to location 00000000.
      
      Registers:
      eax=004042f4 ebx=0065fe78 ecx=0065fe78 edx=00000001 esi=0108b4a0 edi=0108b468
      eip=004028a0 esp=02c1fefc ebp=02c1ff28 iopl=0         nv up ei pl nz na po nc
      cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
      
      AddrPC   Params
      004028A0 7606689C 76066CFF 0065FE78  DrMingwDemo.exe!0x28a0
      76066CFF 02C1FF80 769DFA29 0108C610  msvcrt.dll!_beginthreadex+0xcf
      76066DC1 0108C610 769DFA10 02C1FFDC  msvcrt.dll!_endthreadex+0x91
      769DFA29 0108C610 4F76EDC1 00000000  KERNEL32.DLL!BaseThreadInitThunk+0x19
      771A7A9E FFFFFFFF 771C8BAC 00000000  ntdll.dll!RtlGetAppContainerNamedObjectPath+0x11e
      771A7A6E 76066D60 0108C610 00000000  ntdll.dll!RtlGetAppContainerNamedObjectPath+0xee
      
      00400000-00451000 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
    • 由上面可以可以看出是 004028A0 这个地址出现了崩溃。此时,我们打开 DrMingwDemo.map 文件,找到离该地址最近的函数(一般函数地址会小于等于崩溃地址)。
    • DrMingwDemo.map 的部分内容如下:
      ...
       *(SORT(.text$*))
       *fill*         0x00402818        0x8 
       .text$_ZN10QByteArrayD1Ev
                      0x00402820       0x40 release/main.o
                      0x00402820                QByteArray::~QByteArray()
       .text$_ZN7QStringD1Ev
                      0x00402860       0x40 release/main.o
                      0x00402860                QString::~QString()
       .text$_ZN9TestClass3runEv
                      0x004028a0       0x10 release/main.o
                      0x004028a0                TestClass::run()
       .text$_ZN9TestClassD0Ev
                      0x004028b0       0x20 release/main.o
                      0x004028b0                TestClass::~TestClass()
       .text$_ZN9TestClassD1Ev
                      0x004028d0       0x10 release/main.o
                      0x004028d0                TestClass::~TestClass()
       .text$_ZplRK7QStringPKc
                      0x004028e0       0xc0 release/main.o
                      0x004028e0                operator+(QString const&, char const*)
      ...
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    • 因此,可以看出是 TestClass::run() 函数发生了崩溃。
    • 注意,该方法只能定位出哪个函数出现了崩溃,却难以定位出是哪行导致的崩溃,就算在项目工程中加入了 Debug 信息也如此。

    广告时间:我的 GitHub (希望给个star)

    MiniDrMingw

  • 相关阅读:
    中国石油大学(北京)-《汽车保险与理赔》作业
    ApplicationContextAware、ApplicationContext
    springboot+高校教室排课系统 毕业设计-附源码221556
    借助Spire.Doc for Java控件,将 ODT 转换为 PDF。
    英文转换-在线英文批量转换器免费
    淘宝/天猫获得淘宝商品评论 API 返回值说明
    英国国家卫生服务遭受攻击,系统出现大面积故障
    注册大量短视频矩阵账号很简单,这个方法教会你,还有这个批量剪辑神器帮你完成矩阵分发
    调优C / C ++编译器以在多核应用程序中获得最佳并行性能:第一部分
    Flutter循序渐进==>与基金mysql数据库交互
  • 原文地址:https://blog.csdn.net/github_38647413/article/details/126789210