C++封装一个易用的打印backtrace信息的函数

C++封装一个易用的打印backtrace信息的函数C 封装一个易用的打印 backtrace 信息的函数 1 前言 2 几个需要用到函数 2 1 backtrace 函数 2 2 backtrace symbols 函数 2 3 cxa demangle 函数 3 测试各个函数的使用 3 1 使用 backtrace 获取到的堆栈信息 3 2 使用 backtrace symbols 转换后的堆栈信息 3 3 使用 cxa demangle 获取原始的函数名 4 完整源代码展示 1 前言当我们平时在调试程序时 在遇见某些错误时 往往需要打印出当前错误点的函数调用堆栈信息

1、前言

当我们平时在调试程序时,在遇见某些错误时,往往需要打印出当前错误点的函数调用堆栈信息(通常是在调用assert函数之前调用),这样可以更快地找出程序的问题。接下来重点讲一下如何设计一个易用的打印backtrace的函数

2、几个需要用到函数

2.1、backtrace函数

函数原型如下

#include  
     int backtrace(void **buffer, int size); 
  • 功能描述:backtrace函数会将当前程序的调用堆栈信息写入buffer所指向的数组。buffer中的每一项都是void *类型的,是对应堆栈帧的返回地址。而size参数指定可以存储在缓冲区中的最大地址数,如果回溯大于size,则返回最近size个调用堆栈信息,为了获得完整的回溯,我们必须确保缓冲区和大小足够大。
    (需要注意的是返回信息是倒序的,最近的函数调用在最前面,最远的调用在返回信息的最后面)

  • 返回值:返回获取到的调用堆栈信息的数量,该值不大于size。如果返回值小于size,则表示获取到了存储调用堆栈信息;如果它等于size,那么它可能已经被截断了,在这种情况下,最早的堆栈帧的地址可能不会被返回了

2.2、backtrace_symbols函数

函数原型如下

#include  
     char **backtrace_symbols(void *const *buffer, int size); 
  • 功能描述:第一个参数是backtrace返回信息bufferbacktrace_symbols的功能将地址信息转换为一个字符串数组,用于描述堆栈信息。size参数指定缓冲区中地址的数量。
  • 返回值:backtrace_symbols的返回值是一个指向字符串数组的指针,它的大小通buffer相同,每个字符串包含了一个相对于buffer中对应元素的可打印信息,它由函数名(如果可以确定)、函数的十六进制偏移量和实际返回地址(十六进制)组成。
    (需要注意的是backtrace_symbols的返回值是调用malloc申请的内存空间,调用者必须手动释放它,但是指针数组所指向的字符串不需要也不应该被释放)

2.3、__cxa_demangle函数

函数原型如下

#include  
     char* __cxa_demangle(const char *mangled_name, char *output_buffer, size_t *length, int *status) 
  • 功能描述:__cxa_demangle是C++的函数,而且是ABI(应用程序二进制接口),用于将已被编译器转换后的函数名给还原为原来的形式(编译的时候加上-rdunamic选项,加入函数符号表才能正确显示),即进行符号重组。第一个参数mangled_name就是要进行符号重组的名称,后三个参数用处不大,可以不作理会
  • 返回值:__cxa_demangle的返回值是一个字符串指针,内容就是函数的原始名称
    (需要注意的是__cxa_demangle的返回值是调用malloc申请的内存空间,调用者必须手动释放它)

3、测试各个函数的使用

测试程序如下,比较简单,递归5次之后调用Backtrace并结束递归

void fun() { 
    static int count = 0; if (++count == 5) { 
    Backtrace(50, 0); return; } fun(); } int main(int argc, char* argv[]) { 
    fun(); return 0; } 

3.1、使用backtrace()获取到的堆栈信息

0x401c43 0x4024da 0x 0x 0x 0x 0x40259f 0x7f0b40312b35 0x 

3.2、使用backtrace_symbols()转换后的堆栈信息

./all(_Z9BacktraceiiRKSs+0x58) [0x401c43] ./all(_Z3funv+0x48) [0x4024da] ./all(_Z3funv+0xd6) [0x] ./all(_Z3funv+0xd6) [0x] ./all(_Z3funv+0xd6) [0x] ./all(_Z3funv+0xd6) [0x] ./all(main+0x9) [0x40259f] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f0b40312b35] ./all() [0x] 

从结果可以看到,括号里面的字母即为被编译器转换后的函数名,我们需要将它提取出来,然后传给__cxa_demangle去生成真实的函数名
提取可以用如下代码实现,正则语法比较简单,大家不懂的可以自行百度查一下sscanf的正则规则

std::string sstr; sstr.resize(256); sscanf(str, "%*[^(]%*[^_]%255[^)+]", &sstr[0]); 

3.3、使用__cxa_demangle()获取原始的函数名

Backtrace(int, int, std::string const&) fun() fun() fun() fun() fun() ./all(main+0x9) /lib64/libc.so.6(__libc_start_main+0xf5) ./all() 

从结果上看,函数调用了5次fun(),符合预期

4、完整源代码展示

#include  
     #include  
     #include  
     #include  
     #include  
     #include  
     #include  
     #include  
     std::string demangle(const char* str) { 
    size_t size = 0; int status = 0; std::string sstr; sstr.resize(256); if (1 == sscanf(str, "%*[^(]%*[^_]%255[^)+]", &sstr[0])) { 
    char* tmp = abi::__cxa_demangle(&sstr[0], nullptr, &size, &status); if (tmp) { 
    std::string result(tmp); free(tmp); return result; } } if (1 == sscanf(str, "%255s", &sstr[0])) return sstr; return str; } / * brief: 获取当前栈信息的字符串 * param: size 栈的最大层数 * skip 跳过栈顶的层数 * prefix 栈信息前输出的内容 */ std::string Backtrace(int size = 64, int skip = 2, const std::string& prefix = "") { 
    std::vector<std::string> bt; void** array = (void **)malloc(sizeof(void*) * size); size_t s = backtrace(array, size); char** str = backtrace_symbols(array, s); std::stringstream ss; if (str == nullptr) { 
    std::cout << "backtrace_symbols fail" << std::endl; goto good; } for (size_t i = skip; i < s; i++) bt.push_back(demangle(str[i])); free(str); free(array); for (auto& b : bt) ss << prefix << b << std::endl; good: return ss.str(); } void fun() { 
    static int count = 0; if (++count == 5) { 
    std::cout << Backtrace(50, 0) << std::endl; return; } fun(); } int main(int argc, char* argv[]) { 
    fun(); return 0; } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/217084.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午10:23
下一篇 2026年3月18日 上午10:23


相关推荐

  • k8s pod控制器_k8s多人用吗

    k8s pod控制器_k8s多人用吗k8sPod控制器的介绍ReplicaSet(RS)Deployment(Deploy)扩缩容镜像更新版本回退金丝雀发布Horizontal Pod Autoscaler(HPA)DaemonSet(DS)JobCronJob(CJ)StatefulSet(有状态)StatefulSet的金丝雀发布k8s的Pod控制器详解主要介绍各种Pod控制器的详细使用。Pod控制器的介绍在kubernetes中,按照Pod的创建方式可以将其分为两类:自主式Pod:kubernetes直接创建出来的Pod,这

    2022年8月9日
    6
  • sql server 2008按条件筛选重复记录

    sql server 2008按条件筛选重复记录

    2021年9月9日
    68
  • java 卸载_java安装与卸载[通俗易懂]

    java 卸载_java安装与卸载[通俗易懂]1.安装JDK进行安装,安装过程中会有两次选择安装目录的时候,第一次选择的是jdk(java开发工具包),第二次选择的是jre(java的运行环境)我的安装目录:【我都使用的默认目录,占用内存并不大】jdk的C:\ProgramFiles\Java\jdk1.8.0_281\jre的C:\ProgramFiles\Java\jdk1.8.0_2812.设置环境变量1)JAVA_HO…

    2022年5月19日
    37
  • FlashFXP v3.5.4注册码+FlashFXP v3.6.0注册码+FlashFXP v3.7.2.build.1266…[通俗易懂]

    FlashFXP v3.5.4注册码+FlashFXP v3.6.0注册码+FlashFXP v3.7.2.build.1266…[通俗易懂]
    FlashFXP是功能强大的FXP/FTP软件,融合了一些其他优秀FTP软件的优点,如像CuteFTP一样可以比较文件夹,支持彩色文字显示;像BpFTP支持多文件夹选择文件,能够缓存文件夹;像LeapFTP一样的外观界面,甚至设计思路也差相仿佛。支持文件夹(带子文件夹)的文件传送、删除;支持上传、下载及第三方文件续传;可以跳过指定的文件类型,只传送需要的文件;可以自定义不同文件类型的显示颜色;可以缓存远端文件夹列表,支持FTP代理及Socks4&5;具有避免空闲功

    2022年7月26日
    10
  • Linux中curl命令详解

    Linux中curl命令详解curl CommandLineU 即在命令行中利用 URL 进行数据或者文件传输 https curl haxx se 这是 curl 的官网 可以从上面的官网地址下载最新的 curl 版本 同时可以在官网看出 curl 支持的各种协议 如 HTTP HTTPS IMAP IMAPS LDAP LDAPS POP3 POP3S 等 使用途径

    2025年11月22日
    5
  • 工业相机:传感器尺寸与像元尺寸的关系

    工业相机:传感器尺寸与像元尺寸的关系相同分辨率的工业相机,传感器面积越大,则其单位像素的面积也越大,成像质量也会越好。同样的500万像素的工业相机,2/3”的传感器成像质量就要优于1/2”的。一般来说,工业相机的靶面大小,如果要求不是太严格,可以直接按照公式计算。通常,像工业相机厂家会告诉你多少寸的传感器,那么就可以直接使用16mm*X”的传感器得到工业相机的图像传感器的靶面对角线长度。**工业相机使用16mm代表1寸,而不是使用25

    2022年6月9日
    92

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号