Linux下手动获取当前调用栈
被问到如何手动获取当前的调用栈,之前碰到过一时没记起来,现在回头整理一下。
其原理是:使用backtrace()从栈中获取当前调用各层函数调用的返回地址,backtrace_symbols()将对应地址翻译成对应的符号信息,这两个函数在execinfo.h中声明。详细用法见后面的example。这里强调几处需要注意的地方,在man里头也有说明
1,inline函数无返回地址,因此在结果中不显示
2,需要给linker指定对应的参数,才能保证有对应的符号名称信息,GNU工具链是指定-rdynamic
3,尾调优化会使当前栈帧被新的栈帧覆盖,因此查询的到的信息,会与代码里调用关系不能一一对应
4,static函数由于其符号信息未输出,因此不能获取到具体的名称
example代码,编译指令gcc backtrace.c -o backtrace -g -rdynamic
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h> void bt(void)
{
#define MAX_DEPTH (20)
void *buffer[MAX_DEPTH];
int nptrs = backtrace(buffer, MAX_DEPTH);
char **stack = backtrace_symbols(buffer, nptrs);
int i; if (stack)
{
for (i = ; i < nptrs; ++i)
{
printf("%s\n", stack[i]);
} free(stack);
} return;
} static void func2(void)
{
bt();
} inline void func1(void)
{
func2();
} void func(void)
{
func1();
} int main(int argc, char *argv[])
{
func(); return ;
}
Linux arch 2.6.30-ARCH #1 SMP PREEMPT Fri Jul 31 18:10:38 UTC 2009 i686 Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz GenuineIntel GNU/Linux
gcc4.4.1 环境之行结果如下
[root@arch code]# make backtrace
gcc backtrace.c -o backtrace -g -rdynamic
[root@arch code]# ./backtrace
./backtrace(bt+0x19) [0x80486ed]
./backtrace [0x804874b]
./backtrace(func1+0xb) [0x8048758]
./backtrace(func+0xb) [0x8048765]
./backtrace(main+0xb) [0x8048772]
/lib/libc.so.6(__libc_start_main+0xe6) [0xb7f8da36]
./backtrace [0x8048641]
[root@arch code]# addr2line -e ./backtrace 0x8048765
/root/code/backtrace.c:40
[root@arch code]# addr2line -e ./backtrace 0x8048758
/root/code/backtrace.c:35
[root@arch code]# addr2line -e ./backtrace 0x804874b
/root/code/backtrace.c:30
[root@arch code]# addr2line -e ./backtrace 0x80486ed
/root/code/backtrace.c:10
[root@arch code]#
从实际验证结果可以看出static函数的确没有解析出对应的符号名,但是inline函数仍然有自己的调用栈,这应该是gcc没有实际将其优化展开,仍然将其当作普通函数所致。
并且根据addre2line的结果,我们可以看出backtrace()调用获取到的其实是各个函数调用的返回地址,可以自己根据行号进行一一比对。这里就不多重复了。
不过在Raspbian环境(Linux raspberrypi 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l GNU/Linux gcc 4.6.3)里,编译执行均没有问题,但是无任何输出,gdb跟踪的结果是backtrace()调用返回0,很奇怪。stackoverflow上有人说是根据GCC ARM Options documentation需要加上-mapcs-frame参数,以让gcc在ARM平台上产生栈帧,可是编译时加上该参数仍然无效。strace跟踪其执行过程发现其执行过程没有任何backtrace字样,如下
pi@raspberrypi ~/code $ strace ./backtrace
execve("./backtrace", ["./backtrace"], [/* 16 vars */]) =
brk() = 0x1082000
uname({sys="Linux", node="raspberrypi", ...}) =
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb6f0c000
access("/etc/ld.so.preload", R_OK) =
open("/etc/ld.so.preload", O_RDONLY) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE, , ) = 0xb6f0b000
close() =
open("/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so", O_RDONLY) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\270\4\0\0004\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "A.\0\0\0aeabi\0\1$\0\0\0\0056\0\6\6\10\1\t\1\n\2\22\4\24\1\25"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb6ee0000
mprotect(0xb6ee2000, , PROT_NONE) =
mmap2(0xb6ee9000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , 0x1) = 0xb6ee9000
close() =
munmap(0xb6f0b000, ) =
open("/etc/ld.so.cache", O_RDONLY) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ, MAP_PRIVATE, , ) = 0xb6ed5000
close() =
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
open("/lib/arm-linux-gnueabihf/libc.so.6", O_RDONLY) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\214y\1\0004\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "A.\0\0\0aeabi\0\1$\0\0\0\0056\0\6\6\10\1\t\1\n\2\22\4\24\1\25"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb6da6000
mprotect(0xb6ec8000, , PROT_NONE) =
mmap2(0xb6ecf000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , 0x121) = 0xb6ecf000
mmap2(0xb6ed2000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0xb6ed2000
close() =
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb6f0b000
set_tls(0xb6f0b4c0, 0xb6f0bb98, 0xb6f10048, 0xb6f0b4c0, 0xb6f10048) =
mprotect(0xb6ecf000, , PROT_READ) =
mprotect(0xb6f0f000, , PROT_READ) =
munmap(0xb6ed5000, ) =
open("/etc/ld.so.cache", O_RDONLY) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ, MAP_PRIVATE, , ) = 0xb6ed5000
close() =
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
open("/lib/arm-linux-gnueabihf/libgcc_s.so.1", O_RDONLY) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0`\364\0\0004\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., ) =
lseek(, , SEEK_SET) =
read(, "A2\0\0\0aeabi\0\1(\0\0\0\0056\0\6\6\10\1\t\1\n\2\22\4\24\1\25"..., ) =
brk() = 0x1082000
brk(0x10a3000) = 0x10a3000
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb6d7e000
mprotect(0xb6d9e000, , PROT_NONE) =
mmap2(0xb6da5000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , 0x1f) = 0xb6da5000
close() =
munmap(0xb6ed5000, ) =
exit_group() = ?
pi@raspberrypi ~/code $
对其原因待有待深究。
===== update 2019/5/5 ====
aarch64 linux 版本的 gcc有 -funwind-tables 编译参数,可以实现 backtrace()/backtrace_symbols 正常功能,需要新的 gcc 版本(>= gcc-4.5) 提供支持。原理是记录每个函数的 入栈指令(一般比APCS的入栈要少的多)到特殊的段.ARM.unwind_idx .ARM.unwind_tab。
详情见 http://www.alivepea.me/prog/how-backtrace-work/
Linux下手动获取当前调用栈的更多相关文章
- Linux下java获取CPU、内存、磁盘IO、网络带宽使用率
一.CPU 使用proc文件系统,"proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口.用户和应用程序可以通过proc得 ...
- 在linux下手动安装 apache, php, mysql--终极版
在linux下手动安装 apache, php, mysql: 参考: http://www.cnblogs.com/lufangtao/archive/2012/12/30/2839679.html ...
- 虚拟机Linux下一直获取不到ip怎么办
虚拟机Linux下一直获取不到ip怎么办 Ifconfig -a 只显示了本地的ip127.0.0.1 和另一个eth1 但是找不到ip地址. 需要做的是: 申请ipdhclient eth1 另外释 ...
- Linux下手动备份还原硬盘主引导记录MBR跟硬盘分区表DPT教程
Linux下手动备份还原硬盘主引导记录MBR跟硬盘分区表DPT教程 二 18 奶牛 Linux, Ubuntu, Windows 1,885 views查看评论 最近奶牛一直在折腾linux下的gru ...
- Linux下追踪函数调用,打印栈帧
事情的起因是这样的,之前同事的代码有一个内存池出现了没有回收的情况.也就是是Pop出来的对象没有Push回去,情况很难复现,所以在Pop里的打印日志,跟踪是谁调用了它,我想在GDB调试里可以追踪调用的 ...
- linux下自动获取并安装软件包 apt-get 的命令介绍
apt-cache search package 搜索包 apt-cache show package 获取包的相关信息,如说明.大小.版本等 sudo apt-get install p ...
- Linux下C/C++代码调用PHP代码(转)
Linux下C/C++代码可以通过popen系统函数调用PHP代码并通过fgets函数获取PHP代码echo输出的字符串. //main.c char str[1024] = {0}; char * ...
- Linux下C获取所有可用网卡信息
在Linux下开发网络程序时,经常会遇到需要取本地网络接口名.IP.广播地址.子网掩码或者MAC地址等信息的需求,最常见的办法是配合宏SIOCGIFHWADDR.SIOCGIFADDR.SIOCGIF ...
- [转]linux 下 使用 c / c++ 调用curl库 做通信开发
example: 1. http://curl.haxx.se/libcurl/c/example.html 2. http://www.libcurl.org/book: 1. http:/ ...
随机推荐
- 深入浅出设计模式——桥接模式(Bridge Pattern)
模式动机设想如果要绘制矩形.圆形.椭圆.正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色.绿色.蓝色等,此时至少有如下两种设计方案: 第一种设计方案是为每一种形状都提供一套 ...
- iOS - Mac 锁屏快捷键设置
Mac 锁屏快捷键设置 control + shift + Eject 锁屏快捷键 如果用户要离开电脑一段时间,可以选择直接把笔记本直接合上.但是这样原先在跑的进程就会挂起或者结束,如果正在下载,那么 ...
- 【HDU5955】Guessing the Dice Roll/马尔科夫
先从阿里机器学习算法岗网络笔试题说起:甲乙两人进行一个猜硬币的游戏.每个人有一个目标序列,由裁判来抛硬币.谁先得到裁判抛出的一串连续结果,谁赢. 甲的目标序列是正正正,乙的目标序列是反正正.那么如果裁 ...
- Less入门到上手——前端开发利器<二>深入了解
接着昨天的继续... ... 4.嵌套: HTML部分 <table> <tr> <th colspan="3">测试列表标题</th&g ...
- JVM学习——编译OpenJDK
最近在学习<深入理解java虚拟机 第二版>这本书.书中第一部分建议大家自己编译OpenJDK.抱着学习态度也来编译个玩一玩.下面进入正题. 1.编译环境介绍 操作系统 CentOS Li ...
- Java Performance - 如何调查解决 CPU 问题
随着硬件的发展,往往服务器会配置足够的 CPUs, Java Server/服务器不太有 CPU 问题:但是偶尔因为 代码海量循环 或者 线程安全性(thread safe), 还是会带来 CPU 问 ...
- mybatis 模糊查询 like
1. 参数中直接加入%% param.setUsername("%CD%"); param.setPassword("%11%"); <sel ...
- vue学习笔记之v-if
if顾名思义就是判断,v-if也是判断,举例如下 <p v-if="hello">Hello!</p> 这里 v-if 指令将根据表达式 hello 值的真 ...
- javascript的一些基础
当复制的两个变量的地址不同时他们是不相等的如下代码所示 function getFunction(value){ return function(value){ return value; } } v ...
- C++ template的一些高级用法(元编码,可变参数,仿函数,using使用方法,. C++ 智能指针)
1 . 通用函数可变参数模板 对于有些时候,我们无法确切的知道,函数的参数个数时,而又不想过多的使用所谓的函数重载,那么就可以效仿下面的例子: #include<iostream> #i ...