调试技巧 —— 如何利用windbg + dump + map分析程序异常

本文提供了一套使用WinDbg和VS2022自动生成DUMP文件的方法,帮助开发者快速定位和解决程序异常崩溃问题。通过在工程中添加特定的源代码,实现自动捕获内存崩溃时的详细信息,进而进行深入分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~

所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VS2022,

下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:

MTCrashDump.h

#include <windows.h>
#include <tlhelp32.h>
#include "dbghelp.h"

#pragma optimize("y", off)		//generate stack frame pointers for all functions - same as /Oy- in the project
#pragma warning(disable: 4200)	//nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable: 4100)	//unreferenced formal parameter

// 异常崩溃消息 by MT
#define WM_MSG_RESTART		(WM_USER + 1000)

extern void setMainWindow(HWND hWnd);
extern void initDump();

MTCrashDump.cpp

#include "pch.h"
#include "MTCrashDump.h"
#include <Shlwapi.h>
 
#pragma comment(lib,"shlwapi.lib")
#pragma comment(lib, "Dbghelp.lib")

HWND	g_hWnd = NULL;

// 主界面异常崩溃消息 by MT
void setMainWindow(HWND hWnd)
{
	g_hWnd = hWnd;
}

CString GetExePath()
{
	CString strPath;
	// 获取路径
	TCHAR pszFilePath[MAX_PATH] = { 0x00 };
	memset(pszFilePath, 0 , sizeof(pszFilePath) * sizeof(char));
	GetModuleFileName(NULL, pszFilePath, MAX_PATH);
	
	// 去除末尾的"\xxx.exe"
	TCHAR *pPos = NULL;
	pPos = _tcsrchr(pszFilePath, '\\');
	*pPos = NULL;
	
	// 给末尾添加上"\"
	if (pszFilePath[_tcslen(pszFilePath)-1] != '\\')
		_tcscat(pszFilePath, _T("\\"));
	
	strPath.Format(_T("%s"), pszFilePath);
	return strPath;
}



static BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
	if (pModuleName == 0)
	{
		return FALSE;
	}

	WCHAR szFileName[_MAX_FNAME] = L"";
	//_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
	_wsplitpath_s(pModuleName, NULL, 0, NULL, 0, szFileName, _MAX_FNAME, NULL, 0);

	//if (wcsicmp(szFileName, L"ntdll") == 0)
	if (_wcsicmp(szFileName, L"ntdll") == 0)
		return TRUE;

	return FALSE;
}

static 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;
}

static void CreateMiniDump(PEXCEPTION_POINTERS pep)
{
	CString		strDir, strPath;
	CTime		tm = CTime::GetCurrentTime();
	HANDLE		hFile = INVALID_HANDLE_VALUE;

	strDir.Format(_T("%s\\log"), GetExePath());
	if (!PathFileExists(strDir))
		CreateDirectory(strDir, NULL);
	strPath.Format(_T("%s\\log\\%04d-%02d-%02d %02d%02d%02d.dmp"), GetExePath(), tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute(), tm.GetSecond());

	hFile = CreateFile(strPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 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 = NULL;

		//MINIDUMP_CALLBACK_INFORMATION mci;
		//mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
		//mci.CallbackParam = 0;

		//::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, (pep != 0) ? &mdei : 0, NULL, &mci);
		::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, (pep != 0) ? &mdei : 0, NULL, NULL);

		FlushFileBuffers(hFile);
		CloseHandle(hFile);
	}
}



/*
	异常处理模块
*/
LONG WINAPI CrashReportEx(LPEXCEPTION_POINTERS ExceptionInfo)
{
	TCHAR	szFileName[MAX_PATH] = { 0x00 };
	UINT	nRet = 0;

	// 创建DUMP文件
	CreateMiniDump(ExceptionInfo);

	if (g_hWnd)
		SendMessage(g_hWnd, WM_MSG_RESTART, NULL, NULL);

	return EXCEPTION_EXECUTE_HANDLER;
}

void initDump()
{
	SetUnhandledExceptionFilter(CrashReportEx);
}


具体参考方法如下:

1、在CXXDlg::OnInitDialog()中添加这样一段:

#include "Dump/MTCrashDump.h"

// 主界面异常崩溃消息 by MT
LRESULT CDumpMsgDlg::OnMsgRestart(WPARAM wParam, LPARAM lParam)
{
	TCHAR				szPath[MAX_PATH] = { 0x00 };
	TCHAR				szDir[MAX_PATH] = { 0x00 };
	TCHAR				szCmdLine[MAX_PATH] = { 0x00 };
	CString				strExe = GetExePath();
	STARTUPINFO			si;
	PROCESS_INFORMATION pi;
	BOOL				bResult = FALSE;

	// 在这个回调函数做主界面初始化操作的,释放内存啥的
	// ...

	// 设置进程信息
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	GetModuleFileName(NULL, szPath, MAX_PATH);
	wsprintf(szDir, _T("%s"), strExe);

	// 启动参数 111.exe -a -b
	wsprintf(szCmdLine, _T(" -a -b"));

	// 启动进程
	bResult = CreateProcess(szPath, szCmdLine, NULL, NULL, FALSE, NULL, NULL, szDir, &si, &pi);
	if (!bResult)
	{
		//AfxMessageBox(_T("启动失败!"));
		return 0;
	}

	//AfxMessageBox(_T("启动成功!"));
	return 1L;
}

BOOL CDumpMsgDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// 主界面异常崩溃消息 by MT
	setMainWindow(this->GetSafeHwnd());

	// 注册异常回调 by MT
	initDump();

    // 初始化成员量的指针,为后续模拟崩溃操作
	m_pData = new UCHAR[10];
	for (int i = 0; i < 10; i++)
		m_pData[i] = i;

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 模拟主界面异常崩溃 by MT
// 工程属性,调试,生成调试信息 (/DEBUG)
void CDumpMsgDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	CString		strMsg;
	memset(m_pData, 0x00, 4096);
	strMsg.Format(_T("Val = %02x - %02X"), m_pData[11], m_pData[1024]);
	MessageBox(strMsg);
}

图(1): 

图(2):  

图(3): 

图(4): 

图(5): 

图(6): 

图(7):  

图(8): 

图(9):  

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪宁宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值