文章目录

  • 1、简介
    • 1.1 MiniDumpWriteDump函数
    • 1.2 Visual Studio分析小型转储
    • 1.3 使用 Microsoft 公共符号服务器
    • 1.4 使用 WinDbg 调试小型转储
  • 2、代码实现
    • 2.1 例子一
    • 2.2 例子二
    • 2.3 例子三
    • 2.4 例子四
    • 2.5 其他例子
  • 3、调试工具
    • 3.1 windbg
    • 3.2 debugdiag
    • 3.3 Visual Studio
  • 结语

1、简介

并不是所有的bug都可以在发布前找到,也就是说并不是所有的抛出异常的bug都可以在发布前找到。幸运的是,微软在 Platform SDK 中包含了一个功能,可以帮助开发人员收集用户发现的异常信息。MiniDumpWriteDump函数将必要的故障转储信息写入文件,而不节省整个进程空间。此故障转储信息文件称为小型转储。这篇技术文章提供了有关如何编写和使用小型转储的信息。

1.1 MiniDumpWriteDump函数

minidump 函数为应用程序提供了一种方法来生成包含整个进程上下文的有用子集的故障转储文件;这称为小型转储文件。以下函数用于小型转储文件。将用户模式小型转储信息写入指定文件。

MiniDumpCallback
MiniDumpReadDumpStream
MiniDumpWriteDump
BOOL MiniDumpWriteDump([in] HANDLE                            hProcess,[in] DWORD                             ProcessId,[in] HANDLE                            hFile,[in] MINIDUMP_TYPE                     DumpType,[in] PMINIDUMP_EXCEPTION_INFORMATION   ExceptionParam,[in] PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,[in] PMINIDUMP_CALLBACK_INFORMATION    CallbackParam
);

如果可能的话,应该从一个单独的进程调用MiniDumpWriteDump ,而不是从被转储的目标进程中调用。当目标进程已经不稳定时尤其如此。例如,如果它刚刚崩溃。加载程序死锁是从目标进程中调用MiniDumpWriteDump的许多潜在副作用之一 。

MiniDumpWriteDump是 DBGHELP 库的一部分。这个库不是线程安全的,所以任何使用MiniDumpWriteDump的程序都应该在尝试调用MiniDumpWriteDump之前同步所有线程。请注意,MiniDumpWriteDump当前不适用于托管代码,仅适用于 Windows XP、Windows Vista、Windows 7。

MiniDumpWriteDump可能不会为调用线程生成有效的堆栈跟踪。要解决此问题,您必须在调用MiniDumpWriteDump之前捕获调用线程的状态并将其用作 ExceptionParam参数。一种方法是在 __try / __except块内强制异常,并使用 GetExceptionInformation提供的EXCEPTION_POINTERS信息 。或者,您可以从新的工作线程调用该函数并从转储中过滤此工作线程。

1.2 Visual Studio分析小型转储

  • 打开 Visual Studio。
  • 在文件菜单上,单击打开项目。
  • 将Files of type设置为Dump Files,导航到转储文件,选择它,然后单击Open。
  • 运行调试器。
  • 调试器将创建一个模拟进程。模拟过程将在导致崩溃的指令处停止。

1.3 使用 Microsoft 公共符号服务器

要获取驱动程序级或系统级崩溃的堆栈,可能需要将 Visual Studio 配置为指向 Microsoft 公共符号服务器。

  • 在“调试”菜单上,单击“选项” 。
  • 在选项对话框中,打开调试节点,然后单击符号。
  • 确保未选择仅在手动加载符号时搜索上述位置,除非您想在调试时手动加载符号。
  • 如果您在远程符号服务器上使用符号,则可以通过指定可以将符号复制到的本地目录来提高性能。为此,请输入从符号服务器到此目录的缓存符号路径。要连接到 Microsoft 公共符号服务器,您需要启用此设置。请注意,如果您在远程计算机上调试程序,则缓存目录是指远程计算机上的目录。
  • 单击确定。
  • 因为您使用的是 Microsoft 公共符号服务器,所以会出现一个最终用户许可协议对话框。单击“是”接受协议并将符号下载到本地缓存。

1.4 使用 WinDbg 调试小型转储

您还可以使用 WinDbg(作为 Windows 调试工具的一部分的调试器)来调试小型转储。WinDbg 允许您在不使用 Visual Studio 的情况下进行调试。要下载 Windows 调试工具,请参阅Windows Hardware Developer Central上的Windows 调试工具。

  • 安装 Windows 调试工具后,必须在 WinDbg 中输入符号路径。
  • 在 WinDbg 中输入符号路径
  • 在文件菜单上,单击符号路径。
  • 在符号搜索路径窗口中,输入以下内容:
    “srv*c:\cache*https://msdl.microsoft.com/download/symbols;”

2、代码实现

PDB(Program Data Base),意即程序的基本数据,是VS编译链接时生成的文件。PDB文件主要存储了VS调试程序时所需要的基本信息,主要包括源文件名、变量名、函数名、FPO(帧指针)、对应的行号等等。因为存储的是调试信息,所以一般情况下PDB文件是在Debug模式下才会生成。在VS中可以进行设置,在Release版本中也可以生成PDB文件。
在Release下设置两处地方即可生成PDB文件:


将如下代码在程序初始化的时候执行,等待程序运行崩溃。
在打开EXE崩溃后,生成.dump文件,将其拷贝至包含exe和pdb文件同一目录,使用visual studio打开dump文件,即可进行调试。

2.1 例子一

#include <iostream>
#include <Windows.h>
#include <Dbghelp.h>
#include <strsafe.h>
#include <time.h>
#include <shellapi.h>
#include <shlobj.h>int GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{BOOL bMiniDumpSuccessful;WCHAR szPath[MAX_PATH];WCHAR szFileName[MAX_PATH];const WCHAR* szAppName = L"AppName";const WCHAR* szVersion = L"v1.0";DWORD dwBufferSize = MAX_PATH;HANDLE hDumpFile;SYSTEMTIME stLocalTime;MINIDUMP_EXCEPTION_INFORMATION ExpParam;GetLocalTime(&stLocalTime);GetTempPath(dwBufferSize, szPath);StringCchPrintf(szFileName, MAX_PATH, L"%s%s", szPath, szAppName);CreateDirectory(szFileName, NULL);StringCchPrintf(szFileName, MAX_PATH, L"%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",szPath, szAppName, szVersion,stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,GetCurrentProcessId(), GetCurrentThreadId());hDumpFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);ExpParam.ThreadId = GetCurrentThreadId();ExpParam.ExceptionPointers = pExceptionPointers;ExpParam.ClientPointers = TRUE;bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);return EXCEPTION_EXECUTE_HANDLER;
}void SomeFunction()
{__try{int *pBadPtr = NULL;*pBadPtr = 0;}__except (GenerateDump(GetExceptionInformation())){}
}

2.2 例子二

  • MiniDumper.h:
#ifndef MINIDUMPER_H
#define MINIDUMPER_H#include <windows.h>class CMiniDumper
{public:CMiniDumper(bool bPromptUserForMiniDump);~CMiniDumper(void);private:static LONG WINAPI unhandledExceptionHandler(struct _EXCEPTION_POINTERS *pExceptionInfo);void setMiniDumpFileName(void);bool getImpersonationToken(HANDLE* phToken);BOOL enablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);BOOL restorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);LONG writeMiniDump(_EXCEPTION_POINTERS *pExceptionInfo );_EXCEPTION_POINTERS *m_pExceptionInfo;TCHAR m_szMiniDumpPath[MAX_PATH];TCHAR m_szAppPath[MAX_PATH];TCHAR m_szAppBaseName[MAX_PATH];bool m_bPromptUserForMiniDump;static CMiniDumper* s_pMiniDumper;static LPCRITICAL_SECTION s_pCriticalSection;
};#endif // MINIDUMPER_H
  • MiniDumper.cpp:
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <strsafe.h>
//#include <tchar.h>
#include <dbghelp.h>
#include "miniDumper.h"#ifdef UNICODE#define _tcssprintf wsprintf#define tcsplitpath _wsplitpath
#else#define _tcssprintf sprintf#define tcsplitpath _splitpath
#endifconst int USER_DATA_BUFFER_SIZE = 4096;//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
CMiniDumper* CMiniDumper::s_pMiniDumper = NULL;
LPCRITICAL_SECTION CMiniDumper::s_pCriticalSection = NULL;// Based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,DWORD dwPid,HANDLE hFile,MINIDUMP_TYPE DumpType,CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);//-----------------------------------------------------------------------------
// Name: CMiniDumper()
// Desc: Constructor
//-----------------------------------------------------------------------------
CMiniDumper::CMiniDumper( bool bPromptUserForMiniDump )
{// Our CMiniDumper should act alone as a singleton.assert( !s_pMiniDumper );s_pMiniDumper = this;m_bPromptUserForMiniDump = bPromptUserForMiniDump;// The SetUnhandledExceptionFilter function enables an application to // supersede the top-level exception handler of each thread and process.// After calling this function, if an exception occurs in a process // that is not being debugged, and the exception makes it to the // unhandled exception filter, that filter will call the exception // filter function specified by the lpTopLevelExceptionFilter parameter.::SetUnhandledExceptionFilter( unhandledExceptionHandler );// Since DBGHELP.dll is not inherently thread-safe, making calls into it // from more than one thread simultaneously may yield undefined behavior. // This means that if your application has multiple threads, or is // called by multiple threads in a non-synchronized manner, you need to  // make sure that all calls into DBGHELP.dll are isolated via a global// critical section.s_pCriticalSection = new CRITICAL_SECTION;if( s_pCriticalSection )InitializeCriticalSection( s_pCriticalSection );
}//-----------------------------------------------------------------------------
// Name: ~CMiniDumper()
// Desc: Destructor
//-----------------------------------------------------------------------------
CMiniDumper::~CMiniDumper( void )
{if( s_pCriticalSection ){DeleteCriticalSection( s_pCriticalSection );delete s_pCriticalSection;}
}//-----------------------------------------------------------------------------
// Name: unhandledExceptionHandler()
// Desc: Call-back filter function for unhandled exceptions
//-----------------------------------------------------------------------------
LONG CMiniDumper::unhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo )
{if( !s_pMiniDumper )return EXCEPTION_CONTINUE_SEARCH;return s_pMiniDumper->writeMiniDump( pExceptionInfo );
}//-----------------------------------------------------------------------------
// Name: setMiniDumpFileName()
// Desc:
//-----------------------------------------------------------------------------
void CMiniDumper::setMiniDumpFileName( void )
{time_t currentTime;time( ¤tTime );wsprintf( m_szMiniDumpPath,L"%s%s.%ld.dmp",m_szAppPath,m_szAppBaseName,currentTime );
}//-----------------------------------------------------------------------------
// Name: getImpersonationToken()
// Desc: The method acts as a potential workaround for the fact that the
//       current thread may not have a token assigned to it, and if not, the
//       process token is received.
//-----------------------------------------------------------------------------
bool CMiniDumper::getImpersonationToken( HANDLE* phToken )
{*phToken = NULL;if( !OpenThreadToken( GetCurrentThread(),TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,TRUE,phToken) ){if( GetLastError() == ERROR_NO_TOKEN ){// No impersonation token for the current thread is available. // Let's go for the process token instead.if( !OpenProcessToken( GetCurrentProcess(),TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,phToken) )return false;}elsereturn false;}return true;
}//-----------------------------------------------------------------------------
// Name: enablePrivilege()
// Desc: Since a MiniDump contains a lot of meta-data about the OS and
//       application state at the time of the dump, it is a rather privileged
//       operation. This means we need to set the SeDebugPrivilege to be able
//       to call MiniDumpWriteDump.
//-----------------------------------------------------------------------------
BOOL CMiniDumper::enablePrivilege( LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
{BOOL bOk = FALSE;TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;bOk = LookupPrivilegeValue( 0, pszPriv, &tp.Privileges[0].Luid );if( bOk ){DWORD cbOld = sizeof(*ptpOld);bOk = AdjustTokenPrivileges( hToken, FALSE, &tp, cbOld, ptpOld, &cbOld );}return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
}//-----------------------------------------------------------------------------
// Name: restorePrivilege()
// Desc:
//-----------------------------------------------------------------------------
BOOL CMiniDumper::restorePrivilege( HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
{BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);return ( bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()) );
}//-----------------------------------------------------------------------------
// Name: writeMiniDump()
// Desc:
//-----------------------------------------------------------------------------
LONG CMiniDumper::writeMiniDump( _EXCEPTION_POINTERS *pExceptionInfo )
{LONG retval = EXCEPTION_CONTINUE_SEARCH;m_pExceptionInfo = pExceptionInfo;HANDLE hImpersonationToken = NULL;if( !getImpersonationToken( &hImpersonationToken ) )return FALSE;// You have to find the right dbghelp.dll. // Look next to the EXE first since the one in System32 might be old (Win2k)HMODULE hDll = NULL;TCHAR szDbgHelpPath[MAX_PATH];if( GetModuleFileName( NULL, m_szAppPath, _MAX_PATH ) ){TCHAR *pSlash = wcsrchr( m_szAppPath, '\\' );if( pSlash ){_tcscpy( m_szAppBaseName, pSlash + 1);*(pSlash+1) = 0;}wcscpy( szDbgHelpPath, m_szAppPath );wcscat( szDbgHelpPath, L"DBGHELP.DLL");hDll = ::LoadLibrary( szDbgHelpPath );}if( hDll == NULL ){// If we haven't found it yet - try one more time.hDll = ::LoadLibrary( L"DBGHELP.DLL");}LPCTSTR szResult = NULL;if( hDll ){// Get the address of the MiniDumpWriteDump function, which writes // user-mode mini-dump information to a specified file.MINIDUMPWRITEDUMP MiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );if( MiniDumpWriteDump != NULL ){TCHAR szScratch[USER_DATA_BUFFER_SIZE];setMiniDumpFileName();// Ask the user if he or she wants to save a mini-dump file...wsprintf( szScratch,L"There was an unexpected error:\n\nWould you "L"like to create a mini-dump file?\n\n%s ",m_szMiniDumpPath);// Create the mini-dump file...HANDLE hFile = ::CreateFile( m_szMiniDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );if( hFile != INVALID_HANDLE_VALUE ){_MINIDUMP_EXCEPTION_INFORMATION ExInfo;ExInfo.ThreadId          = ::GetCurrentThreadId();ExInfo.ExceptionPointers = pExceptionInfo;ExInfo.ClientPointers    = NULL;// We need the SeDebugPrivilege to be able to run MiniDumpWriteDumpTOKEN_PRIVILEGES tp;BOOL bPrivilegeEnabled = enablePrivilege( SE_DEBUG_NAME, hImpersonationToken, &tp );BOOL bOk;// DBGHELP.dll is not thread-safe, so we need to restrict access...EnterCriticalSection( s_pCriticalSection );{// Write out the mini-dump data to the file...bOk = MiniDumpWriteDump( GetCurrentProcess(),GetCurrentProcessId(),hFile,MiniDumpNormal,&ExInfo,NULL,NULL );}LeaveCriticalSection( s_pCriticalSection );// Restore the privileges when doneif( bPrivilegeEnabled )restorePrivilege( hImpersonationToken, &tp );if( bOk ){szResult = NULL;retval = EXCEPTION_EXECUTE_HANDLER;}else{wsprintf( szScratch,L"Failed to save the mini-dump file to '%s' (error %d)",m_szMiniDumpPath,GetLastError() );szResult = szScratch;}::CloseHandle( hFile );}else{wsprintf( szScratch,L"Failed to create the mini-dump file '%s' (error %d)",m_szMiniDumpPath,GetLastError() );szResult = szScratch;}}else{szResult = L"Call to GetProcAddress failed to find MiniDumpWriteDump. "L"The DBGHELP.DLL is possibly outdated." ;}}else{szResult = L"Call to LoadLibrary failed to find DBGHELP.DLL.";}if( szResult && m_bPromptUserForMiniDump )::MessageBox( NULL, szResult, NULL, MB_OK );TerminateProcess( GetCurrentProcess(), 0 );return retval;
}
  • test.cpp:
#include "MiniDumper.h"
CMiniDumper g_miniDumper( true );

2.3 例子三

  • MiniDump.h
#pragma once
#include <Windows.h>
#include <tchar.h>
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")#ifdef UNICODE
#define TSprintf    wsprintf
#else
#define TSprintf    sprintf
#endifclass MiniDump
{private:MiniDump();~MiniDump();public:// 程序崩溃时是否启动自动生成dump文件;// 只需要在main函数开始处调用该函数即可;static void EnableAutoDump(bool bEnable = true);private:static LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException);static void CreateDumpFile(LPCWSTR lpstrDumpFilePathName, EXCEPTION_POINTERS *pException);
};
  • MiniDump.cpp
#include "MiniDump.h"MiniDump::MiniDump()
{}MiniDump::~MiniDump()
{}void MiniDump::EnableAutoDump(bool bEnable)
{if (bEnable){SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) ApplicationCrashHandler);}
}LONG MiniDump::ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
{/*if (IsDebuggerPresent()){return EXCEPTION_CONTINUE_SEARCH;}*/TCHAR szDumpDir[MAX_PATH] = { 0 };TCHAR szDumpFile[MAX_PATH] = { 0 };TCHAR szMsg[MAX_PATH] = { 0 };SYSTEMTIME    stTime = { 0 };// 构建dump文件路径;GetLocalTime(&stTime);::GetCurrentDirectory(MAX_PATH, szDumpDir);TSprintf(szDumpFile, _T("%s\\%04d%02d%02d_%02d%02d%02d.dmp"), szDumpDir,stTime.wYear, stTime.wMonth, stTime.wDay,stTime.wHour, stTime.wMinute, stTime.wSecond);// 创建dump文件;CreateDumpFile(szDumpFile, pException);// 弹出一个错误对话框或者提示上传, 并退出程序;TSprintf(szMsg, _T("I'm so sorry, but the program crashed.\r\ndump file : %s"), szDumpFile);FatalAppExit(-1, szMsg);return EXCEPTION_EXECUTE_HANDLER;
}void MiniDump::CreateDumpFile(LPCWSTR strPath, EXCEPTION_POINTERS *pException)
{// 创建Dump文件;HANDLE hDumpFile = CreateFile(strPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);// Dump信息;MINIDUMP_EXCEPTION_INFORMATION dumpInfo;dumpInfo.ExceptionPointers = pException;dumpInfo.ThreadId = GetCurrentThreadId();dumpInfo.ClientPointers = TRUE;// 写入Dump文件内容;MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);CloseHandle(hDumpFile);
}

2.4 例子四

CrashRpt是一个免费的开源库,旨在拦截 C++ 程序中的异常、收集有关崩溃的技术信息并通过 Internet 向软件供应商发送错误报告。

http://crashrpt.sourceforge.net/docs/html/getting_started.html

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
// Include CrashRpt Header
#include "CrashRpt.h"FILE* g_hLog = NULL; // Global handle to the application log file// Define the callback function that will be called on crash
int CALLBACK CrashCallback(CR_CRASH_CALLBACK_INFO* pInfo)
{  // The application has crashed!// Close the log file here// to ensure CrashRpt is able to include it into error reportif(g_hLog!=NULL){fclose(g_hLog);g_hLog = NULL;// Clean up handle}// Return CR_CB_DODEFAULT to generate error reportreturn CR_CB_DODEFAULT;
}// The following function writes an entry to the log file
void log_write(LPCTSTR szFormat, ...)
{if (g_hLog == NULL) return; // Log file seems to be closedva_list args; va_start(args); _vftprintf_s(g_hLog, szFormat, args);fflush(g_hLog);
}// Thread procedure
DWORD WINAPI ThreadProc(LPVOID lpParam)
{// Install exception handlers for this threadcrInstallToCurrentThread2(0);log_write(_T("Entering the thread proc\n"));// Define the infinite loop where some processing will be done for(;;){// There is a hidden error somewhere inside of the loop...int* p = NULL;*p = 13; // This results in Access Violation}    log_write(_T("Leaving the thread proc\n"));// Unset exception handlers before exiting the threadcrUninstallFromCurrentThread();    return 0;
}int _tmain(int argc, _TCHAR* argv[])
{  // Define CrashRpt configuration parametersCR_INSTALL_INFO info;  memset(&info, 0, sizeof(CR_INSTALL_INFO));  info.cb = sizeof(CR_INSTALL_INFO);    info.pszAppName = _T("MyApp");  info.pszAppVersion = _T("1.0.0");  info.pszEmailSubject = _T("MyApp 1.0.0 Error Report");  info.pszEmailTo = _T("myapp_support@hotmail.com");    info.pszUrl = _T("http://myapp.com/tools/crashrpt.php");  info.uPriorities[CR_HTTP] = 3;  // First try send report over HTTP info.uPriorities[CR_SMTP] = 2;  // Second try send report over SMTP  info.uPriorities[CR_SMAPI] = 1; // Third try send report over Simple MAPI    // Install all available exception handlersinfo.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;// Restart the app on crash info.dwFlags |= CR_INST_APP_RESTART; info.dwFlags |= CR_INST_SEND_QUEUED_REPORTS; info.pszRestartCmdLine = _T("/restart");// Define the Privacy Policy URL info.pszPrivacyPolicyURL = _T("http://myapp.com/privacypolicy.html"); // Install crash reportingint nResult = crInstall(&info);    if(nResult!=0)  {    // Something goes wrong. Get error message.TCHAR szErrorMsg[512] = _T("");        crGetLastErrorMsg(szErrorMsg, 512);    _tprintf_s(_T("%s\n"), szErrorMsg);    return 1;} // Set crash callback functioncrSetCrashCallback(CrashCallback, NULL);// Add our log file to the error reportcrAddFile2(_T("log.txt"), NULL, _T("Log File"), CR_AF_MAKE_FILE_COPY);    // We want the screenshot of the entire desktop is to be added on crashcrAddScreenshot2(CR_AS_VIRTUAL_SCREEN, 0);   // Add a named property that means what graphics adapter is// installed on user's machinecrAddProperty(_T("VideoCard"), _T("nVidia GeForce 8600 GTS"));// The main code follows...// Open log fileerrno_t err = _tfopen_s(&g_hLog, _T("log.txt"), _T("wt"));if(err!=0 || g_hLog==NULL){_tprintf_s(_T("Error opening log.txt\n"));return 1; // Couldn't open log file}log_write(_T("Started successfully\n"));// Create the worker threadHANDLE hWorkingThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)NULL, 0, NULL);log_write(_T("Created working thread\n"));// There is a hidden error in the main() function// Call of _tprintf_s with NULL parameterTCHAR* szFormatString = NULL;_tprintf_s(szFormatString);// Wait until the worker thread is exitedWaitForSingleObject(hWorkingThread, INFINITE);log_write(_T("Working thread has exited\n"));// Close the log fileif(g_hLog!=NULL){fclose(g_hLog);g_hLog = NULL;// Clean up handle}// Uninitialize CrashRpt before exiting the main functioncrUninstall();// Exitreturn 0;
}

2.5 其他例子

(1)除了 CrashRpt,chromium 里使用的 crashpad 也是一个非常好的选择。google 出品,质量有保障。
crashpad 的前身是 breakpad。
https://github.com/chromium/crashpad

(2)适用于.NET程序的 CrashReporter。
https://github.com/ravibpatel/CrashReporter.NET

(3)其它平台(Android,iOS等)也有类似的开源库。可以在 github 上搜索 crash report。

3、调试工具

3.1 windbg

Windows 调试程序 (WinDbg) 可用于调试内核模式和用户模式代码、分析故障转储以及在代码执行时检查 CPU 寄存器。

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools

从 SDK 获取用于 Windows (WinDbg) 的调试工具:Windows SDK。 使用Windows SDK 页上的下载链接,因为 Windows 调试工具在Visual Studio中不可用。

如果你只需要用于Windows的调试工具,而不是用于Windows的 (WDK) Windows 驱动程序工具包,则可以将调试工具安装为Windows软件开发工具包 (SDK) 的独立组件。

在 SDK 安装向导中选择“Windows 调试工具” ,并取消选择所有其他组件。

3.2 debugdiag

Debug Diagnostic Tool (DebugDiag)是微软提供的工具,可以用来追踪windows平台下的程序崩溃,卡死,内存泄漏等一些疑难问题的原因,按照问题类别配置收集后,反馈给公司技术人员。

下载后按缺省提示安装即可:
https://www.microsoft.com/en-US/download/details.aspx?id=58210
https://www.microsoft.com/en-us/download/details.aspx?id=49924

3.3 Visual Studio

转储文件是一个快照,其显示某个时间点正在为应用执行的进程和已为应用加载的模块。 带堆信息的转储还包括该时间点的应用内存的快照。

在 Visual Studio 中打开带堆的转储文件类似于在调试会话中在断点处停止。 尽管你无法继续执行,但在转储时可以检查应用的堆栈、线程和变量值。

转储最常用于调试开发人员无权访问的计算机中的问题。 当你无法在自己的计算机上重现崩溃或无响应的程序时,可以使用来自客户计算机的转储文件。 测试人员还会创建转储以保存崩溃或无响应程序数据,从而用于更多测试。

Visual Studio 调试器可为托管或本机代码保存转储文件。 它可以调试由 Visual Studio 或其他以小型转储格式保存文件的应用创建的转储文件。


利用VS可以很方便的分析dump文件,如果有生成dump文件时对应的.pdb文件,就可以直接定位到出错的代码行。

PDB (Program Data Base) 即程序的基本数据,是 VS 编译链接时生成的文件,每个程序集(EXE 或 DLL)都有一个与之对应的 PDB 文件。DPB 文件主要存储了 VS 调试程序时所需要的基本信息,主要包括源文件名、变量名、函数名、对应的行号等等。因为存储的是调试信息,所以一般情况下 PDB 文件是在 Debug 模式下才会生成。有了这个文件,我们才能对程序进行 断点调试 ,才能一步步执行程序。

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

C++ 捕获程序异常奔溃minidump相关推荐

  1. 捕获Windows C/C++程序异常奔溃工具CrashRpt

    背景 在Windows环境下做C/C++程序开发有段时间了,经常遇到程序奔溃时缺无法捕获,经同事推荐,知道了CrashRpt这个捕获C/C++程序异常的工具,让我省去了通过增加打印调试去查询程序异常的 ...

  2. 安卓软件错误log_Android编程实现捕获程序异常退出时的错误log信息功能详解

    本文实例讲述了Android编程实现捕获程序异常退出时的错误log信息功能.分享给大家供大家参考,具体如下: 很多时候我们程序无缘无故的就挂掉了,让我们一头雾水,如果刚好我们在调试,那我们可以通过错误 ...

  3. 「视频版」当线程池溢出之后,程序会奔溃吗?面试突击 007 期

    哈喽,大家好,我是老王,欢迎来到第 7 期的 Java 面试突击. 本文的面试题是,当线程池的任务溢出之后,程序会奔溃吗? 这个问题问的是关于线程池的任务数超过线程池的承载能力之后,会出现什么情况? ...

  4. 程序Crash(奔溃)记录和分析工具——Crashlytics

    摘自:http://www.infoq.com/cn/articles/crashlytics-crash-statistics-tools 简介 Crashlytic 成立于2011年,是专门为移动 ...

  5. Android之网络请求通过协程+okhttp的没有做网络异常处理导致程序奔溃问题

    1 问题 app里面的网络请求是通过协程+okhttp来实现的,但是没有做网络异常处理(域名无法解析.502错误等等一系列),导致程序奔溃 2 尝试 因为app基本上做好了,外面有大几十个地方调用,然 ...

  6. Android ExceptionThrowable 常见异常和解决方法 奔溃日志上报 monkey异常修改

    java将所有的错误封装为一个对象,其根本父类为Throwable, Throwable有两个子类:Error和Exception. 注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理. ...

  7. winform程序捕获全局异常,对错误信息写入日志并弹窗

    使用场景:在winform程序中如果没对方法进行try catch操作,若方法内出错,则整个程序报错并退出,如下图 如果程序已在客户手中,若没对错误的详细信息进行拍照,我们则不知道错误原因是什么.我们 ...

  8. 软件Release版本异常捕获程序(BugReport)

    软件Release版本异常捕获程序(BugReport) http://wangzai9931.blog.51cto.com/168627/164084 2009-06-08 23:09:43 标签: ...

  9. Python程序异常处理:try、except、else、finally,捕获指定异常类型、捕获多个异常类型、捕获所有异常类型、捕获异常信息、异常的传递、raise抛出自定义异常

    输入与预期不匹配,触发异常,程序退出: 一.异常处理:使用try.except进行错误处理 为了保证程序运行的稳定性,错误应该被程序捕捉并合理控制 Python使用保留字try和except进行异常处 ...

  10. C# 程序异常关闭时的捕获

    C# 程序异常关闭时的捕获 参考文章: (1)C# 程序异常关闭时的捕获 (2)https://www.cnblogs.com/hsiang/p/9388186.html (3)https://www ...

最新文章

  1. 农银:银行业中台系统的建设思路
  2. css expressionr,CSS自定义属性expression_r
  3. cuda 图片拆分_急需,PDF怎么拆分啊?
  4. 【十二】Jmeter:“CSV 数据文件设置”参数化请求出现中文乱码
  5. 使用doxygen查看文件包含关系图
  6. jquery 弹出窗口_jQuery弹出窗口和工具提示窗口动画效果
  7. hge引擎写的一个扫雷小游戏
  8. Hadoop安装详细教程 单机版
  9. (转载)Tuxedo中间件简介
  10. “幸运盒子”可悲的命运
  11. cam350 不能打开光绘文件_怎么使用CAM350打开gerber文件
  12. ROS运行管理之launch文件
  13. bootstrap树节点如何设置默认不展开_GraPhlAn:最美进化树或层级分类树学习笔记
  14. 微信之分享到朋友圈并记录分享次数
  15. 什么是瑞士加密谷Crypto Valley、CV Labs
  16. 计算机机房装修效果图,机房布线效果图
  17. LINUX软中断-softirq
  18. flash与PHP的交互技巧
  19. mysql ceil_MySQL笔记之数学函数详解_MySQL - ceil
  20. 回顾南仁东先生-时代楷模(FAST之父)//2021-2-6

热门文章

  1. linux qgis编译安装,QGIS安装
  2. BP神经网络(Python代码实现)基于pytorch
  3. turtle库的精讲
  4. 通过一个具体的例子,讲解 SAP Cloud Platform Integration(CPI) 的使用方法
  5. linux spyder 安装,Python开发环境Spyder安装方法(示例代码)
  6. Mat拜耳数据邻域转换算法返回Mat对象,16bit三通道
  7. k8s1.18-kubeadm安装手册
  8. 计算机专业职业生涯规划书,计算机专业职业生涯规划书2000字.doc
  9. 计算机网络职业生涯规划1000字,计算机职业生涯规划1000字
  10. c语言倒序输出单词_英语句子按单词倒序输出C语言版