作者:mizrke | 来源:互联网 | 2023-10-11 08:28
在Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问题的bug,最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,crash的现场很难保留和重现。目前有一些方法可以解决:
崩溃地址 + MAP文件;MAP文件;
SetUnhandledExceptionFilter + Minidump。
本文重点解决Minidump方式。
1、Minidump概念
minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在系统属性 -> 高级 -> 启动和故障恢复 -> 设置 -> 写入调试信息中选择“小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump。
我们要生成的是用户态的minidump,文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性,dump文件是压缩过的。
2、生成minidump文件
通过drwtsn32、NTSD、CDB等调试工具生成Dump文件, drwtsn32存在的缺点虽然NTSD、CDB可以完全解决,但并不是所有的操作系统中都安装了NTSD、CDB等调试工具。根据MiniDumpWriteDump接口,完全可以程序自动生成Dump文件。
3、 自动生成Minidump文件
当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter()函数,异常交给函数处理。MSDN中描述为:
Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.
因而,在程序开始处增加SetUnhandledExceptionFilter()函数,并在函数中利用适当的方法生成Dump文件,即可实现需要的功能。minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在系统属性 -> 高级 -> 启动和故障恢复 -> 设置 -> 写入调试信息中选择“小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump。
我们要生成的是用户态的minidump,文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性,dump文件是压缩过的。
2、生成minidump文件
通过drwtsn32、NTSD、CDB等调试工具生成Dump文件, drwtsn32存在的缺点虽然NTSD、CDB可以完全解决,但并不是所有的操作系统中都安装了NTSD、CDB等调试工具。根据MiniDumpWriteDump接口,完全可以程序自动生成Dump文件。
3、 自动生成Minidump文件
当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter()函数,异常交给函数处理。MSDN中描述为:
Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.
因而,在程序开始处增加SetUnhandledExceptionFilter()函数,并在函数中利用适当的方法生成Dump文件,即可实现需要的功能。
新建工程
下一步
下一步
Minidmp.h
#pragma once#include
#include
#include inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName);inline BOOL CALLBACK MiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput);//创建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName);LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);BOOL PreventSetUnhandledExceptionFilter();LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException);//运行异常处理
void RunCrashHandler();
Minidmp.cpp
#include "stdafx.h"#include "Minidmp.h"#pragma comment(lib, "dbghelp.lib")inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{if(pModuleName == 0){return FALSE;}WCHAR szFileName[_MAX_FNAME] = L"";_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);if(wcsicmp(szFileName, L"ntdll") == 0)return TRUE;return FALSE;
}inline BOOL CALLBACK MiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput)
{if(pInput == 0 || pOutput == 0)return FALSE;switch(pInput->CallbackType){case ModuleCallback: if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg) if(!IsDataSectionNeeded(pInput->Module.FullPath)) pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg); case IncludeModuleCallback:case IncludeThreadCallback:case ThreadCallback:case ThreadExCallback:return TRUE;default:;}return FALSE;
}//创建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
{HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)){MINIDUMP_EXCEPTION_INFORMATION mdei;mdei.ThreadId = GetCurrentThreadId();mdei.ExceptionPointers = pep;mdei.ClientPointers = FALSE;MINIDUMP_CALLBACK_INFORMATION mci;mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;mci.CallbackParam = 0;MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);CloseHandle(hFile); }
}LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{return NULL;
}BOOL PreventSetUnhandledExceptionFilter()
{HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));if (hKernel32 == NULL)return FALSE;void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");if(pOrgEntry == NULL)return FALSE;unsigned char newJump[ 100 ];DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp farvoid *pNewFunc = &MyDummySetUnhandledExceptionFilter;DWORD dwNewEntryAddr = (DWORD) pNewFunc;DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;newJump[ 0 ] = 0xE9; // JMP absolutememcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));SIZE_T bytesWritten;BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);return bRet;
}LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{TCHAR szMbsFile[MAX_PATH] = { 0 };::GetModuleFileName(NULL, szMbsFile, MAX_PATH);TCHAR* pFind = _tcsrchr(szMbsFile, '\\');if(pFind){*(pFind+1) = 0;_tcscat(szMbsFile, _T("CreateMiniDump.dmp"));CreateMiniDump(pException,szMbsFile);}// TODO: MiniDumpWriteDumpFatalAppExit(-1, _T("Fatal Error"));return EXCEPTION_CONTINUE_SEARCH;
}//运行异常处理
void RunCrashHandler()
{SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);PreventSetUnhandledExceptionFilter();
}
CrashTest.h
#pragma once
#include "Minidmp.h"class CCrashTest
{
public:CCrashTest(void);~CCrashTest(void);public:void Test();private:void Crash();
};
CrashTest.cpp
#include "StdAfx.h"
#include "CrashTest.h"CCrashTest::CCrashTest(void)
{
}CCrashTest::~CCrashTest(void)
{
}void CCrashTest::Test()
{Crash();
}void CCrashTest::Crash()
{// 除零,人为的使程序崩溃//int i = 13;int j = 0;//int m = i / j;strcpy(NULL,"adfadfg");
}
CrashMinidumpTest.cpp
// CrashMinidumpTest.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include "CrashTest.h"int _tmain(int argc, _TCHAR* argv[])
{//设置异常处理回调函数RunCrashHandler();CCrashTest test;test.Test();getchar();return 0;
}
配置工程目录
下一步
下一步
下一步
下一步