在muduo的CurrentThread.cc源文件中,有一个函数stackTrace,感觉很有意思,就研究了下是做什么的。

string stackTrace(bool demangle)
{string stack;const int max_frames = 200;void* frame[max_frames];int nptrs = ::backtrace(frame, max_frames);char** strings = ::backtrace_symbols(frame, nptrs);if (strings){size_t len = 256;char* demangled = demangle ? static_cast<char*>(::malloc(len)) : nullptr;for (int i = 1; i < nptrs; ++i)  // skipping the 0-th, which is this function{if (demangle){// https://panthema.net/2008/0901-stacktrace-demangled/// bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909]char* left_par = nullptr;char* plus = nullptr;for (char* p = strings[i]; *p; ++p){if (*p == '(')left_par = p;else if (*p == '+')plus = p;}if (left_par && plus){*plus = '\0';int status = 0;char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status);*plus = '+';if (status == 0){demangled = ret;  // ret could be realloc()stack.append(strings[i], left_par+1);stack.append(demangled);stack.append(plus);stack.push_back('\n');continue;}}}// Fallback to mangled namesstack.append(strings[i]);stack.push_back('\n');}free(demangled);free(strings);}return stack;
}

我并不打算详细的讲解函数的功能如何实现,因为我没有看,我只想说一下相关的接口、概念、以及实现的效果。

首先是两个函数backtrace和backtrace_symbols,在这篇博客中有说明和例子,https://www.cnblogs.com/fangyan5218/p/10686488.html。之后我会借用这里的例子,并做一些修改。例子如下:

#include <stdio.h>
#include <execinfo.h>
#include <unistd.h>
#include <stdlib.h>
#define BACKTRACE_SIZE 100void print_backtrace()
{void* buffer[BACKTRACE_SIZE]={0};int pointer_num = backtrace(buffer, BACKTRACE_SIZE);char** string_buffer = backtrace_symbols(buffer, pointer_num);if(string_buffer == NULL){printf("backtrace_symbols error");exit(-1);}printf("print backtrace begin\n");for(int i = 0; i < pointer_num; i++){printf("%s\n", string_buffer[i]);}printf("print backtrace end\n");free(string_buffer);return;
}void func(int num)
{if(num > 0){func(--num);}else{print_backtrace();}
}int main(int argc, char* argv[])
{if(argc != 2){printf("input param error");return -1;}int input_num = atoi(argv[1]);func(input_num);return 0;
}

我使用gcc命令编译了该程序,编译命令如下:

gcc backtrace.c -o backtrace -g -rdynamic -std=c99

运行结果如下:

print backtrace begin
./backtrace_c(print_backtrace+0x3a) [0x4008fe]
./backtrace_c(func+0x2b) [0x4009bf]
./backtrace_c(func+0x1f) [0x4009b3]
./backtrace_c(func+0x1f) [0x4009b3]
./backtrace_c(main+0x4e) [0x400a0f]
/lib64/libc.so.6(__libc_start_main+0xfd) [0x3cc8c1ed1d]
./backtrace_c() [0x400809]
print backtrace end

可以看到打印的堆栈信息,使用的函数名。但是如果我使用g++进行编译,修改编译命令为:

g++ backtrace.c -o backtrace -g -rdynamic

这时候执行后的打印信息变成了这样:

print backtrace begin
./backtrace_c(_Z15print_backtracev+0x3a) [0x4009ae]
./backtrace_c(_Z4funci+0x26) [0x400a6b]
./backtrace_c(_Z4funci+0x1f) [0x400a64]
./backtrace_c(_Z4funci+0x1f) [0x400a64]
./backtrace_c(main+0x4b) [0x400ab8]
/lib64/libc.so.6(__libc_start_main+0xfd) [0x3cc8c1ed1d]
./backtrace_c() [0x4008b9]
print backtrace end

可以看到函数的名字被修改了,这个修改就是mangle。而muduo库中stackTrace函数实现的就是demangle,将修改后的名字再改回去。

 将C++源程序标识符(original C++ source identifier)转换成C++ ABI标识符(C++ ABI identifier)
的过程称为mangle;相反的过程称为demangle。

现在我在源码中加入muduo库中的该函数,将代码修改为这样:

#include <stdio.h>
#include <cxxabi.h>
#include <execinfo.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
using namespace std;
#define BACKTRACE_SIZE 100string stackTrace(bool demangle)
{string stack;const int max_frames = 200;void* frame[max_frames];int nptrs = ::backtrace(frame, max_frames);char** strings = ::backtrace_symbols(frame, nptrs);if (strings){size_t len = 256;char* demangled = demangle ? static_cast<char*>(::malloc(len)) : NULL;for (int i = 1; i < nptrs; ++i)  // skipping the 0-th, which is this function{if (demangle){// https://panthema.net/2008/0901-stacktrace-demangled/// bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909]char* left_par = NULL;char* plus = NULL;for (char* p = strings[i]; *p; ++p){if (*p == '(')left_par = p;else if (*p == '+')plus = p;}if (left_par && plus){*plus = '\0';int status = 0;char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status);*plus = '+';if (status == 0){demangled = ret;  // ret could be realloc()stack.append(strings[i], left_par+1);stack.append(demangled);stack.append(plus);stack.push_back('\n');continue;}}}// Fallback to mangled namesstack.append(strings[i]);stack.push_back('\n');}free(demangled);free(strings);}return stack;
}void print_backtrace()
{// void* buffer[BACKTRACE_SIZE]={0};// int pointer_num = backtrace(buffer, BACKTRACE_SIZE);// char** string_buffer = backtrace_symbols(buffer, pointer_num);// if(string_buffer == NULL)// {//     printf("backtrace_symbols error");//     exit(-1);// }// printf("print backtrace begin\n");// for(int i = 0; i < pointer_num; i++)// {//     printf("%s\n", string_buffer[i]);// }// printf("print backtrace end\n");// free(string_buffer);// return;string s = stackTrace(1);printf("%s\n", s.c_str());
}void func(int num)
{if(num > 0){func(--num);}else{print_backtrace();}
}int main(int argc, char* argv[])
{if(argc != 2){printf("input param error");return -1;}int input_num = atoi(argv[1]);func(input_num);return 0;
}

编译后执行的打印为:

./backtrace(print_backtrace()+0x1c) [0x401002]
./backtrace(func(int)+0x26) [0x401072]
./backtrace(func(int)+0x1f) [0x40106b]
./backtrace(func(int)+0x1f) [0x40106b]
./backtrace(main+0x4b) [0x4010bf]
/lib64/libc.so.6(__libc_start_main+0xfd) [0x3cc8c1ed1d]
./backtrace() [0x400cd9]

哈哈,可以看到之前被修改的名称改为了函数名加参数的格式,看起来方便多了,而且有一个工具c++filt可以将刚才被mangle的名字改回来,例如执行命令“c++filt  _Z4funci”,结果为“func(int)”,可以看到跟后面使用stackTrace函数打印的名称是一样的。

最后想把《程序员的自我修养--链接、装载与库》中的一段文字摘抄在这:

C++符号修饰

众所周知,强大而复杂的C++拥有类、继承、虚机制、重载、名称空间等这些特性,它们使得符号管理更为复杂。最简单的例子,两个相同名字的函数func(int)和func(double),尽管函数名相同,但是参数列表不同,这是C++里面函数重载的最简单的一种情况,那么编译器和链接器在链接过程中如何区分这两个函数呢?为了支持C++这些复杂的特性,人们发明了符号修饰(Name Decoration)和符号改编(Name Mangling)的机制,下面我们来看看C++的符号修饰机制。

首先出现的一个问题是C++允许多个不同参数类型的函数拥有一样的名字,就是所谓的函数重载;另外C++还在语言级别支持名称空间,即允许在不同的名称空间有多个同样名字的符号。比如清单3-4这段代码。

清单3-4  C++函数的名称修饰

int func(int);
float func(float);class C {int func(int);class C2 {int func(int);};
};namespace N {int func(int);class C {int func(int);};
}

这段代码中有6个同名函数叫func,只不过它们的返回类型和参数及所在的名称空间不同。我们引入一个术语叫做函数签名(Function Signature),函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。函数签名用于识别不同的函数,就像签名用于识别不同的人一样,函数的名字只是函数签名的一部分。由于上面6个同名函数的参数类型及所处的类和名称空间不同,我们可以认为它们的函数签名不同。在编译器及链接器处理符号时,它们使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称(Decorated Name)。编译器在将C++源代码编译成目标文件时,会将函数和变量的名字进行修饰,形成符号名,也就是说,C++的源代码编译后的目标文件中所使用的符号名是相应的函数和变量的修饰后名称。C++编译器和链接器都使用符号来识别和处理函数和变量,所以对于不同函数签名的函数,即使函数名相同,编译器和链接器都认为它们是不同的函数。上面的6个函数签名在GCC编译器下,相对应的修饰后名称如表3-18所示。

表3-18

函数签名 修饰后名称(符号名)
int func(int) _Z4funci
float func(float) _Z4funcf
int C::func(int) _ZN1C4funcEi
int C::C2::func(int) _ZN1C2C24funcEi
int N::func(int) _ZN1N4funcEi
int N::C::func(int) _ZN1N1C4funcEi

stackTrace相关推荐

  1. 挖一挖C#中那些我们不常用的东西之系列(3)——StackTrace,Trim

    挖一挖C#中那些我们不常用的东西之系列(3)--StackTrace,Trim 原文:挖一挖C#中那些我们不常用的东西之系列(3)--StackTrace,Trim 时间太快了,三月又要过去了,告别一 ...

  2. boost::stacktrace::detail::void_ptr_cast相关的测试程序

    boost::stacktrace::detail::void_ptr_cast相关的测试程序 实现功能 C++实现代码 实现功能 boost::stacktrace::detail::void_pt ...

  3. boost::stacktrace模块实现终止处理程序的测试程序

    boost::stacktrace模块实现终止处理程序的测试程序 实现功能 C++实现代码 实现功能 boost::stacktrace模块实现终止处理程序的测试程序 C++实现代码 #include ...

  4. boost stacktrace堆栈打印

    在windows下最方便的是minidump,其他2个平台麻烦不少,google-breakpad使用起来又太麻烦. 最近boost1.65版本出了个stacktrace使用起来简单方便,只是无法看实 ...

  5. boost::stacktrace::detail相关的测试程序

    boost::stacktrace::detail相关的测试程序 实现功能 C++实现代码 实现功能 boost::stacktrace::detail相关的测试程序 C++实现代码 #include ...

  6. C# Environment.StackTrace、StackFrame、Tirm的使用技巧

    一.Tirm 这个我想没有程序员说不知道,但是里面有一个重载,这个不知道有多少程序员知道~ 可以看到,我可以去掉字符串前后的指定字符,只要我在char[]中申请即可,而不是仅仅去掉空格, var s ...

  7. boost::stacktrace::stacktrace相关的测试程序

    boost::stacktrace::stacktrace相关的测试程序 实现功能 C++实现代码 实现功能 boost::stacktrace::stacktrace相关的测试程序 C++实现代码 ...

  8. 记一个错误:Spark job failed during runtime. Please check stacktrace for the root cause.

    今天在跑任务的时候报了一个错误 Job failed with java.lang.ArrayIndexOutOfBoundsException: 1 FAILED: Execution Error, ...

  9. Stacktrace:] with root cause javax.el.PropertyNotFoundException: 类型[com.bean.Employee]上找不到属性[departm

    1.0 报错信息: 严重: Servlet.service() for servlet [jsp] in context with path [/ssm-crud3] threw exception ...

最新文章

  1. C语言标准库之strcat函数
  2. 大数据文字游戏_基于大数据的游戏化教学系统研究.docx
  3. [转]Java8-本地缓存
  4. TreeSet的定制排序
  5. SSM之一(使用idea创建一个Spring+SpringMVC的项目)
  6. Ubuntu的网络设置
  7. 每日程序C语言28-有序数组插入元素
  8. 防止Entity Framework重复插入关联对象
  9. jar包 jdk 停_一文读懂jar包的小秘密
  10. 苏宁大数据怎么运营_18个“硬核”数据告诉你,苏宁大数据如何火力全开护航618!...
  11. 中高级PHP程序员应该掌握哪些技术
  12. postgres数据库常见报错
  13. qpython3h手机版怎么发短信_python如何使用腾讯云发送短信
  14. 转 留美博士生写给后来人的辛酸回忆:你适合读博士和搞科研吗?
  15. FDD LTE B1是什么
  16. 红亚2015-3月杯季赛 CTF题部分writeup
  17. 抓包工具 HTTP Analyzer v7.5 的下载,安装,使用,破解说明
  18. MarkdownPad2安装教程
  19. ListView在工程中的详细应用(简易记账本)
  20. JVM_06 运行时数据区3-方法区

热门文章

  1. Ubuntu16.04安装使用STLINK(rtthread)
  2. BUU-MISC-[UTCTF2020]basic-forensics
  3. 开关电源的纹波和噪声电压-抑制方法
  4. 计算机毕设(附源码)JAVA-SSM辽宁省高考志愿智能辅助填报系统
  5. 译:3D Convolutional Neural Networks for Human Action Recognition
  6. uClinux 平台下的Flash存储技术(转)
  7. 磁盘泄密威胁和数据销毁技术综述
  8. 自定义控件之58同城加载
  9. (半搬)植物大战僵尸python代码
  10. 苹果邮件怎么添加qq邮箱_分别在win10自带邮件和iOS上添加QQ邮箱