C++ 利用 windbg + dump + map + cod 文件分析 crash 原因

7 篇文章 0 订阅

写代码路上踩过的坑,特此记录,以免忘记
网上全部都是有 pdb 文件。Release 没有 pdb 文件。本文适用于没有 pdb 文件,如何定位

如何生成 dump 文件

作为一个程序员,必现的 bug 容易排查,最怕遇见非必现 bug,尤其是那种在自己环境上测多少次都没问题,到用户环境偶尔会 crash,无从下手,让人瞬间抓狂。并且像这种非必现 bug 一般很难采用日志的方式来定位。

这个时候就需要保留程序 crash 的环境,最好的方式就是生成 Dump 文件。如果要利用 C++ 代码生成,可以直接采用库函数
MiniDumpWriteDump

头文件

  • #include <dbghelp.h>
  • #pragma comment(lib, “dbghelp.lib”)      一定要连接这个库,否则无法使用
#include <windows.h>
#include <dbghelp.h>

#pragma comment(lib, "dbghelp.lib")

void exceptionHandler(PEXCEPTION_POINTERS excpInfo) {
  std::mutex g_handlerLock;
  std::unique_lock<std::mutex> lk(g_handlerLock);

  std::string dump_dir = "D:\\code\\crash.dmp";
  LONG ret = EXCEPTION_CONTINUE_SEARCH;

  HANDLE hFile =
      ::CreateFile(dump_dir.c_str(), GENERIC_WRITE, FILE_ATTRIBUTE_READONLY,
                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile != INVALID_HANDLE_VALUE) {
    MINIDUMP_EXCEPTION_INFORMATION exptInfo;
    exptInfo.ThreadId = ::GetCurrentThreadId();
    exptInfo.ExceptionPointers = excpInfo;

    BOOL bOK =
        ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(),
                            hFile, MiniDumpNormal, &exptInfo, NULL, NULL);
    if (bOK) {
      ret = EXCEPTION_EXECUTE_HANDLER;
    }
  }
}

LONG WINAPI unhandledException(PEXCEPTION_POINTERS excpInfo = nullptr) {
  if (excpInfo == nullptr) {
    __try 
    {
      RaiseException(EXCEPTION_BREAKPOINT, 0, 0, nullptr);
    } __except(exceptionHandler(GetExceptionInformation()),
                EXCEPTION_EXECUTE_HANDLER) {
    }
  } else {
    exceptionHandler(excpInfo);
  }

  return 0;
}

int main(int argc, char** argv) {
  SetUnhandledExceptionFilter(unhandledException);
}

通过上面的方法,就会生成 dump 文件,如果要生成指定信息的 dump 文件,只需修改 MiniDumpWriteDump() 对应的参数即可

如何生成 map 文件

map 文件中记录是程序的各种地址信息

VS生成
  1. 打开项目的“属性页”对话框。
  2. 链接器 —> 所有选项—>生成映射文件—>是在这里插入图片描述
  3. 就会在程序目录下生成 .map 文件。。如果找不到,直接搜索
cmake 命令

target_link_options(${EXE_NAME_UI} PRIVATE “/MAP:${CMAKE_CURRENT_BINARY_DIR}/test.map”)

其中,EXE_NAME_UI 是自己设置的 exe 的name ,后面可指定生成的文件夹

如果只有这两个文件的话,只能定位到 crash 的函数,而不能精确定位到 行数

如何生成 cod 文件

.cod 文件里面记录了源码已经对应的汇编代码,已经行号

VS 生成
  1. 打开项目的“属性页”对话框。
  2. 属性页 —> C/C++ —> 输出文件 —> 汇编程序输出 —> 选择 程序集、机器码和源代码在这里插入图片描述
  3. 就可以在默认路径下生成 .cod 文件
cmake 命令

set(MSVC_COVERAGE_COMPILE_FLAGS “/FAcs”)
set(CMAKE_CXX_FLAGS “${MSVC_COVERAGE_COMPILE_FLAGS} ${CMAKE_CXX_FLAGS}”)

这个目前没有找到可以指定输出路径的方法,知道的大佬可以告知一下

找 crash 地址

有了这三个文件,就可以定位到 crash 的地方了

写了一个很简单的例子。。空指针赋值问题,肯定会 crash

#include <iostream>
#include <windows.h>
#include <dbghelp.h>
#include <string>

#pragma comment(lib, "dbghelp.lib")

void exceptionHandler(PEXCEPTION_POINTERS excpInfo) {

	std::string dump_dir = "F:\\code\\crash.dmp";
	LONG ret = EXCEPTION_CONTINUE_SEARCH;

	HANDLE hFile = CreateFileA(dump_dir.c_str(), GENERIC_WRITE, FILE_ATTRIBUTE_READONLY,
			NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) {
		MINIDUMP_EXCEPTION_INFORMATION exptInfo;
		exptInfo.ThreadId = ::GetCurrentThreadId();
		exptInfo.ExceptionPointers = excpInfo;

		BOOL bOK =
			::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(),
				hFile, MiniDumpNormal, &exptInfo, NULL, NULL);
	}
}

LONG WINAPI unhandledException(PEXCEPTION_POINTERS excpInfo = nullptr) {
	if (excpInfo == nullptr) {
		__try
		{
			RaiseException(EXCEPTION_BREAKPOINT, 0, 0, nullptr);
		}
		__except (exceptionHandler(GetExceptionInformation()),
			EXCEPTION_EXECUTE_HANDLER) {
		}
	}
	else {
		exceptionHandler(excpInfo);
	}

	return 0;
}

void errorFun()
{
	int* p = nullptr;
	*p = 1;
}

void testfun1()
{
}

void testfun2()
{
}
void testfun3()
{
}

int main()
{
	SetUnhandledExceptionFilter(unhandledException);

	testfun1();
	testfun2();
	testfun3();
	errorFun();
	return 0;
}

编译运行。。dump 文件的生成一定要在外面独立运行 exe,不要在 VS 里面执行,因为 VS 会在 crash 的地方中断

step 1

用 windbg 工具打开 dump 文件。。我用的是 windbg preview

在这里插入图片描述
点击红框即可,windbg 会自动帮你输入命令进行分析
在这里插入图片描述

crash 的地址以及出现了。就会得到 0x000012ed。。记住这个值

step2

打开 .map 文件
会是一堆这样的信息。。具体含义我就不介绍了,可以玩网上搜,有介绍。。往下拉
在这里插入图片描述
这四个东西是我们需要相关注的。。

Address 基地址
publics by value 就是函数名
Rva+Base 经过计算后的地址
Lib:Object 文件名
在这里插入图片描述

这时候就需要拿第一步得到的地址 0x000012ed 和 Rva+Base 进行比较。。判断位于哪两个地址之间
在这里插入图片描述
就会发现 crash 的地方是在 main 函数。。
这时候你肯定会疑惑,明明 crash 的地方是在 errorfun 函数里面。但是不是在 main 里面调用的 errorfun()

说实话。。这块按理来说 map 文件中会存储调用函数的地址的,就可以很准确的找到是 errorfun 函数的问题。我做的项目就有,不知道这个测试例子为啥没有。。

但这并不影响,接着往下看

我们第二步已经精确定位到函数了。接下来就是行数了

step3

再回到 windbg 工具中
在这里插入图片描述
拿到这个地址,然后去反汇编里面搜索
在这里插入图片描述
复制后面的汇编语句。打开 cod 文件,进行搜索

在这里插入图片描述
阿西吧!!!!!
大功告成。。找到了具体的位置。。

有人说,,为什么不直接在 cod 中搜索,要第二步干什么。。。
如果你是在做一个大项目的话,汇编语句肯定会有重复的,所以说一定要先定位到具体的函数,再去找具体的行数

如果你有 pdb 文件的话,就会轻松很多很多

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Windbg是Windows平台上的一款强大的调试工具,可以用于分析dump文件。当一个程序崩溃或异常退出时,系统会生成一个dump文件,其中包含了程序在崩溃前的内存状态、寄存器的值以及调用栈等信息。通过分析dump文件,可以帮助我们确定程序崩溃的原因。 使用Windbg分析dump文件的步骤如下: 首先,打开Windbg并选择“File”菜单中的“Open Crash Dump”,然后选择要分析dump文件。打开dump文件后,Windbg会加载其中的调试信息,包括程序、模块、符号等。 在Windbg的命令窗口中,可以输入一系列的命令来分析dump文件。其中一些常用的命令如下: 1. "!analyze -v":分析dump文件并提供详细的分析报告,报告中包含了崩溃的原因和相关的线程堆栈信息。 2. "kb":显示当前线程的调用栈,可以根据调用栈信息来查找崩溃的位置。 3. "lm":显示加载的模块信息,可以查看程序中加载的模块和其对应的版本号。 4. ".exr -1":显示当前异常的记录,包括异常的类型和相关的寄存器的值。 5. ".reload /f":强制重新加载符号文件,以确保符号信息的准确性。 通过分析命令的执行结果,我们可以逐步追踪问题并找到程序崩溃的原因。在分析过程中,还可以使用其他的命令来查看内存的内容、寄存器的值以及线程的信息等。 总的来说,Windbg是一款功能强大的调试工具,通过分析dump文件可以帮助我们深入了解程序崩溃的原因,从而进行相应的调试和修复。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值