这篇文章主要讲述在 Linux 和 Windows 这 2 个平台上,如何用C++ 来捕获函数调用栈里的信息。

一、前言

程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段。

因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获取函数调用栈信息,为 debug 提供有效的信息。

这篇文章的理论知识很少,直接分享 2 段代码:在 Linux 和 Windows 这 2 个平台上,如何用C++ 来捕获函数调用栈里的信息。

二、Linux 平台

1. 注册异常信号的处理函数
需要处理哪些异常信号

#include
#include
#include  const std::map Signals = { {SIGINT, "SIGINT"},     {SIGABRT, "SIGABRT"},  {SIGFPE, "SIGFPE"},    {SIGILL, "SIGILL"},   {SIGSEGV, "SIGSEGV"} // 可以添加其他信号
};

注册信号处理函数

struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_sigaction = &sigHandler;
action.sa_flags = SA_SIGINFO;  for (const auto &sigPair : Signals) { if (sigaction(sigPair.first, &action, NULL) < 0) fprintf(stderr, "Error: sigaction failed! \n"); }

2. 捕获异常,获取函数调用栈信息

void sigHandler(int signum, siginfo_t *info, void *ctx)
{ const size_t dump_size = 50; void *array[dump_size]; int size = backtrace(array, dump_size); char **symbols = backtrace_symbols(array, size); std::ostringstream oss; for (int i = 0; i < size; ++i) { char *mangleName = 0; char *offsetBegin = 0; char *offsetEnd = 0; for (char *p = symbols[i]; *p; ++p) { if ('(' == *p) {    mangleName = p; }    else if ('+' == *p) { offsetBegin = p; } else if (')' == *p) { offsetEnd = p; break; } } if (mangleName && offsetBegin && offsetEnd && mangleName < offsetBegin) { *mangleName++ = '\0'; *offsetBegin++ = '\0'; *offsetEnd++ = '\0'; int status; char *realName = abi::__cxa_demangle(mangleName, 0, 0, &status); if (0 == status) oss << "\tstack dump [" << i << "]  " << symbols[i] << " : " << realName << "+"; else oss << "\tstack dump [" << i << "]  " << symbols[i] << mangleName << "+"; oss << offsetBegin << offsetEnd << std::endl; free(realName); } else { oss << "\tstack dump [" << i << "]  " << symbols[i] << std::endl; } } free(symbols); oss << std::endl; std::cout << oss.str(); // 打印函数调用栈信息
}

三、Windwos 平台

在 Windows 平台下的代码实现,参考了国外某个老兄的代码,如下:

1. 设置异常处理函数

#include
#include  SetUnhandledExceptionFilter(exceptionHandler);

2. 捕获异常,获取函数调用栈信息

void exceptionHandler(LPEXCEPTION_POINTERS info)
{ CONTEXT *context = info->ContextRecord; std::shared_ptr RaiiSysCleaner(nullptr, [&](void *) { SymCleanup(GetCurrentProcess()); }); const size_t dumpSize = 64; std::vector frameVector(dumpSize); DWORD machine_type = 0; STACKFRAME64 frame = {}; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat; #ifdef _M_IX86 frame.AddrPC.Offset = context->Eip; frame.AddrFrame.Offset = context->Ebp; frame.AddrStack.Offset = context->Esp; machine_type = IMAGE_FILE_MACHINE_I386;
#elif _M_X64 frame.AddrPC.Offset = context->Rip; frame.AddrFrame.Offset = context->Rbp; frame.AddrStack.Offset = context->Rsp; machine_type = IMAGE_FILE_MACHINE_AMD64;
#elif _M_IA64 frame.AddrPC.Offset = context->StIIP; frame.AddrFrame.Offset = context->IntSp; frame.AddrStack.Offset = context->IntSp; machine_type = IMAGE_FILE_MACHINE_IA64; frame.AddrBStore.Offset = context.RsBSP; frame.AddrBStore.Mode = AddrModeFlat;
#else frame.AddrPC.Offset = context->Eip; frame.AddrFrame.Offset = context->Ebp; frame.AddrStack.Offset = context->Esp; machine_type = IMAGE_FILE_MACHINE_I386;
#endif for (size_t index = 0; index < frameVector.size(); ++index) { if (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { frameVector[index] = frame.AddrPC.Offset; } else { break; } } std::string dump; const size_t kSize = frameVector.size(); for (size_t index = 0; index < kSize && frameVector[index]; ++index) { dump += getSymbolInfo(index, frameVector); dump += "\n"; } std::cout << dump;
}

主要是利用了 StackWalk64 这个函数,从地址转换为函数名称。

利用以上几个神器,基本上可以获取到程序崩溃时的函数调用栈信息,定位问题,有如神助!

如何在系统崩溃时从C++中获取函数调用栈信息?相关推荐

  1. 栈windows linux,Linux+Windows: 程序崩溃时,在 C++ 代码中,如何获取函数调用栈信息...

    一.前言 程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段. 因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获 ...

  2. Windows,Android设备刷机(重装系统)时遇到USB读取不到设备信息(设备驱动异常)问题及解决办法

    Windows,Android设备刷机(重装系统)时遇到USB读取不到设备信息(设备驱动异常)问题及解决办法 前言 当刷Android系统时,我们要进入boot模式,**adb reboot boot ...

  3. 在Linux程序中输出函数调用栈

    程序发生异常时,将函数的调用栈打印出来,可以大大提高定位效率. Linux中提供了三个函数用来获取调用栈: /* 获取函数调用栈 */ int backtrace(void **buffer, int ...

  4. python查看系统进程_在Python中获取操作系统的进程信息

    本文主要介绍在 Python 中使用 psutil 获取系统的进程信息. 1 概述 psutil 是 Python 的一个进程和系统工具集模块,通过使用 psutil,我们可以在 Python 中获取 ...

  5. 微信中获取微信用户信息的2种方式

    微信中获取微信用户信息的2中方式 1, 在公众号底部菜单栏地址配置h5地址,如 http://test.dingdong.com/page1 然后前端在所有页面前拦截如login页面,查看是否有tok ...

  6. linux在shell中获取系统时间,linux在shell中获取时间

    获得当天的日期 date +%Y-%m-%d 输出: 2011-07-28 将当前日期赋值给DATE变量 DATE=$(date +%Y%m%d) 有时候我们需要使用今天之前或者往后的日期,这时可以使 ...

  7. linux程序崩溃时调用链,Linux 获取并分析程序崩溃时的调用堆栈

    下面是一个小例子,说明了程序出现段错误时,如何打印程序的堆栈信息. #include #include #include #include static void WidebrightSegvHand ...

  8. URL传参时 从URL中获取中文参数的方法

    利用url传参时如果url中的参数是中文时因为编码类型不同在页面中获取会出现乱码 使用此方法能获取url中的参数值 并解决乱码问题 调用时直接 GetUrlByParamName("参数名& ...

  9. JVM Runtime Data Area(运行时数据区中的堆/栈/方法区讲解)

    JVM的组成 JVM由4大部分组成:ClassLoader .Runtime Data Area .Execution Engine .Native Interface.我们这里重点讲得是Runtim ...

最新文章

  1. QIIME 2教程. 18序列双端合并read-joining(2020.11)
  2. 2018年GPS定位器会发生什么样变化?
  3. vb.net调用oracle存储过程,今天搞好了VB.NET调用Oracle存储过程返回游标的问题
  4. python从入门到精通书-Python从入门到精通
  5. Go 开发关键技术指南 | 为什么你要选择 GO?(内含超全知识大图)
  6. 微信 小程序组件 分享按钮
  7. 简介浏览器内核与JavaScript引擎
  8. 运维工程师必备之负载均衡集群及LVS详解
  9. 一种内核到用户空间的高效数据传输技术
  10. (Object detection)目标检测从入门到精通——第三部分非极大抑制
  11. MYSQL语句和多表查询
  12. AgileEAS.NET平台开发实例-药店系统-功能发布[模块注册][上]
  13. 数据库的redo undo 思考总结
  14. c语言转为or1200汇编语言,gogo全站-官网首页
  15. python打开autocad
  16. python为字体添加上下标
  17. Hadoop web端打开hdfs上的文件问题
  18. 蓝桥杯单片机国赛客观题_【单片机自学入门必+收藏】蓝桥杯Arduino单片机 | 02 趣玩TM1637四位数码管模块...
  19. ExoPlayer详解——入门(官方文档)
  20. BIOS设置和CMOS设置的区别与联系

热门文章

  1. 程序员请收好:10个非常实用的 VS Code 插件
  2. 双十一来这儿,华为昇腾的秘密都给你!
  3. 【每日一算法】什么是二分图?
  4. Vue之Element-ui和Vue-cli的使用
  5. python线程的注意点(线程之间执行是无序的、主线程会等待所有的子线程执行结束再结束(守护主线程)、线程之间共享全局变量、线程之间共享全局变量数据出现错误问题(线程等待(join)、互斥锁))
  6. Scrapy框架items数据建模、翻页请求、requests对象请求、meta参数的使用
  7. Failed at the node-sass@4.14.0 postinstall script. npm ERR! This is probably not a problem with npm
  8. 实验四 JSP数据库编程基础
  9. Traceback (most recent call last): File AttributeError: 'NoneType' object has no attribute 'group'
  10. 利用OpenCV实现基于深度学习的超分辨率处理