【C/C++】软件异常状态快速定位技巧

前言

在软件开发维护过程中,开发人员难免会遇到软件发版或者上线后出现异常,且通过日志无法分析出造成异常具体原因;也许是程序出现死锁导致运行阻塞,也许是程序出现内存出现异常导致软件崩溃,也许这个问题是偶现难以找到快速复现手段。当你无法通过Debug调试时,你需要掌握抓取程序运行过程中和程序奔溃时的程序运行快照技巧,便于开发人员定位问题。

本文主要介绍在Window和Linux平台上定位软件异常状态的技巧。

WinDbg工具下载地址:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools

Window PC软件崩溃位置定位

在程序中嵌入dump文件生成对象,用于捕捉崩溃时刻软件运行状态。

dump.h

#pragma once
#include 
namespace dump
{
	class Dump
	{
	public:
		Dump();
		~Dump();

	private:
		static long __stdcall UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo);
	};
}

dump.cpp

#include 
#include    
#include 
#include 
#include 
#include 
#include "dump.h"
#pragma comment(lib,  "dbghelp.lib")

namespace dump
{
	Dump::Dump()
	{
		/* 设置程序崩溃前的回调函数 */
		SetUnhandledExceptionFilter(UnhandleExceptionFilter);
	}
	Dump::~Dump() {}

	long __stdcall Dump::UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo)
	{

		auto tp = std::chrono::system_clock::now(); 
		std::time_t curTime = std::chrono::system_clock::to_time_t(tp);  
		std::stringstream fileName;
		fileName  << curTime << ".dmp"; // 崩溃生成的dump文件名为[时间戳].dmp 
		
		/* 创建dump文件 */
		HANDLE hFile = CreateFile(fileName.str().c_str(), 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 = ExceptionInfo; // 指向EXCEPTION_POINTERS结构的指针,该 结构指定异常的计算机独立描述和异常时的处理器上下文。
			exInfo.ClientPointers = FALSE; // 如果在调试过程中则设置为TRUE,否则是FALSE
			
			/* 写Dump文件 */
			BOOL  ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &exInfo, NULL, NULL);
			CloseHandle(hFile);
			if (!ret)
			{
				/* 写dump文件出错处理,异常交给windows处理 */
				system("pause");
				return EXCEPTION_CONTINUE_SEARCH;
			}
			else
			{    /* 表示该异常已经被处理 */
				system("pause");
				return EXCEPTION_EXECUTE_HANDLER;
			}
		}
		else
		{
			/* 写dump文件出错处理,异常交给windows处理 */
			system("pause");
			return EXCEPTION_CONTINUE_SEARCH;
		}
	}
}



使用示例

main.cpp

#include "dump.h"

void run_test()
{
	int *a = nullptr;
	*a = 3;  // 崩溃点
}

int main()
{
	auto dmp = dump::Dump();
	run_test();
	return 0;
}

如果开发人员想要精准定位崩溃上下文,则可以开启以下配置:

以 VS2017为例,工程属性中【调试信息格式】选择为【程序数据库】,【生成调试信息】选择为【生成调试信息】。

【C/C++】软件异常状态快速定位技巧_第1张图片

【C/C++】软件异常状态快速定位技巧_第2张图片

程序运行后生成dmp文件

【C/C++】软件异常状态快速定位技巧_第3张图片

使用WinDbg工具加载dmp文件,使用【!analyze -v】命令查看崩溃信息

如下图所示在STATCK_TEXT堆栈信息上可以看到奔溃点为run_test函数

【C/C++】软件异常状态快速定位技巧_第4张图片

如果在工程属性上增加调试信息,如下图所示在FAULTING_SOURCE_CODE故障源码信息上可以看到具体奔溃点的上下文代码

【C/C++】软件异常状态快速定位技巧_第5张图片

Linux 程序崩溃位置定位

在程序中嵌入dump文件生成对象,用于捕捉崩溃时刻软件运行状态。

dump.h

#pragma once

int set_dump();

dump.cpp

#include
#include 
#include 
#include "dump.h"

#define CORE_SIZE   (1024 * 1024 * 500)

int set_dump()
{
	/* 设置进程资源限制 */
	struct rlimit rlmt;
	rlmt.rlim_cur = (rlim_t)CORE_SIZE;
	rlmt.rlim_max  = (rlim_t)CORE_SIZE;
	return setrlimit(RLIMIT_CORE, &rlmt);	
}

使用示例

main.cpp

#include 
#include "dump.h"

void run_test()
{
	int *a = nullptr;
	*a = 3;  // 崩溃点
}

int main()
{
	set_dump();
	run_test();
	return 0;
}

编译选项需要加-g

程序运行后生成core文件

使用gdb工具调试,输入命令【gdb test2 core】,可以查看程序堆栈信息和崩溃语句

【C/C++】软件异常状态快速定位技巧_第6张图片

 

 

 

你可能感兴趣的:(C++/C,工具)