使用BackTrace查看调用堆栈
有时我们遇到问题去想查看call stack时,一般利用gdb工具,断点再bt即可查看,但是很多时候也许没有条件去使用gdb工具,这时就可以利用backtrace函数。下面就对该函数进行简单的原理分析和方法介绍
用法介绍
按照下图的code写即可显示出调用堆栈
不过需要注意的是,如果在gcc的时候 没有加上-rdynamic选项,那么在显示调用堆栈的时候,是没有显示那个函数调用的,像下图
如果加上了-rdynamic选项,就会显示出对应的函数了,我们project Makefile都有加这个选项
-rdynamic选项 主要是将所有的链接符号加到动态链接表(.dynsym)里,但不包含static修饰的函数
可利用readelf -s 命令查看dynsym表
对每个API进行分析
int backtrace(void **buffer, int size);
返回调用堆栈
buffer :提供一个指针的数组
size :指定缓冲区的个数,即设置的调用深度
int : 返回实际返回的调用深度
每个地址指针由 函数名、地址偏移、返回地址组成
char **backtrace_symbols(void *const *buffer, int size);
字符串结果通过该API返回,会在该函数中malloc,由我们free
一般的程序员写到上面就结束了或者看到上面就结束了,不一般的还会继续下面的内容
原理分析
想要弄清backtrace函数是怎么实现,需要先弄清调用栈
因为不同的芯片架构,指令、寄存器表示均不同,这里用ARM架构去看效果
将之前测试code全部贴出来如下,以这个code为例子
将main反汇编
lr寄存器,Link register,记录之前的执行位置,在退出时,赋给pc寄存器,返回执行
pc寄存器,Program counter,当前程序执行位置,随程序执行变化
sp寄存器,Stack pointer,当前栈指针的位置,随栈变化,每次PUSH -4 ;POP +4
r11寄存器,用来记录栈帧底部的地址
分析上面的main汇编语句
<+0> push {r11, lr}
将r11 和 lr寄存器推入至栈中
就是将上一个程序的栈帧底部位置保存至栈里,退出程序的时候好恢复,上一个程序的栈位置
<+4> add r11, sp, #4
将 sp + 4 赋给 r11
将该程序的栈帧底部位置保存至r11寄存器,
<+8> bl 0x89c4<fun1>
跳转至fun1函数
<+12> mov r3, #0
赋给r3寄存器 3 值
<+16> mov r0, r3
将r3寄存器赋给r0
<+20> pop {r11, pc}
从栈中恢复r11寄存器,并将之前保存的lr寄存器的值赋给pc,以便恢复到之前的函数运行的状态
有点糊涂,没关系,再看一下fun1函数的汇编语句
再分析下fun1的汇编语句
<+0> push {r11, lr}
同样地,将r11寄存器和lr寄存器压入栈,这里的r11寄存器的值,就是在main函数中的栈帧底部的位置,这里的lr寄存器的值,就是在main函数执行bl命令时,将pc的值赋给了lr寄存器
<+4> add r11, sp, #4
就是将 sp + 4 赋给r11,现在r11的值就为fun1函数栈帧底部地址
<+8> bl 0x89b4 <fun2>
跳转至fun2函数
<+12> pop {r11, pc}
从栈中取出 之前保存的main函数的r11 和 lr,赋值给r11 和 pc,这样就恢复了main函数的运行
晓得上述流程之后,就可以实现backtrace函数,
首先,拿到本函数的r11寄存器,所指示的栈地址,出栈,就能得到调用函数的lr寄存器的值,然后就能通过dynsym动态链接表,找到对应的函数名
再出栈,就能得到调用函数的r11寄存器的值,以此类推,最终得到整个调用栈
本来想把glibc库中的实现秀出来,不过这个库是有调到so文件实现
延伸
利用该API还可以迅速定位出段错误来,段错误时,会发送SIGSEGV信号,重载信号处理程序,将上面code加入进去,就能够得到调用堆栈
接着利用objdump 反编译出来objdump -d test > test.s,即可大致推测哪个语句导致的段错误
甚至,巧妙的利用send SIGTSTP信号和上述函数,就能制造出断点,和gdb一样的效果调试
使用BackTrace查看调用堆栈相关推荐
- 一种获取过程调用堆栈信息的简单方法
在程序崩溃或出现异常时,通常需要给开发人员提供基本的过程调用的信息,这里给出一个简单的C++实现.主要思路是:过程调用的开始时,在栈上创建一个类,利用类的构造函数记录相关信息,在过程调用完毕时会自动调 ...
- 转:eclipse 调用堆栈 快捷键
转自:http://www.verydemo.com/demo_c288_i65529.html Eclipse 修改Eclipse堆栈大小 修改Eclipse堆栈大小 -Xmx512M-Xms512 ...
- linux中追踪函数backtrace调用堆栈
From: http://www.embeddedlinux.org.cn/html/jishuzixun/201211/19-2388.html 一般察看函数运行时堆栈的方法是使用GDB之类的外部调 ...
- linux c 用户态 调试追踪函数 调用堆栈 定位段错误
一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...
- 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误
嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报 分类: 嵌入式(928) 一般察看函数运行时堆栈的 ...
- 【听歌】GDB入门教程之查看函数调用堆栈
写在前面:又到周末啦~上上周忍痛买了个雅马哈声卡和 AKG 话筒,这周六才正式打开试用了下,效果还不错,我自己还挺享受的.不过这玩意儿太高端,还不会用 AI 调音.小伙伴们感觉下这首加了一点点电音效果 ...
- 程序退出前的遗言----linux下利用backtrace追踪函数调用堆栈以及定位段错误
一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...
- libuv 编译使用,打印调用堆栈
libuv 编译选项: CFLAGS='-g -O0 -funwind-tables' ./configure --disable-silent-rules --disable-udev --enab ...
- javascript 堆栈_JavaScript调用堆栈-它是什么以及为什么它是必需的
javascript 堆栈 The JavaScript engine (which is found in a hosting environment like the browser), is a ...
最新文章
- 卡联科技与正元地理合作 打造智慧城市
- SPQuery 查询知多少
- Eclipse中修改tomcat内存大小
- java里面运行js_在java中利用rhino执行javascript
- android程序中关于webview加载html文件
- 百度C2C对决淘宝的两把利器
- php 分表 实战,PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP)
- Arquillian和Jboss的版本问题
- 设计模式之抽象工厂模式(Java实现)
- 监视Python程式自动退出,并重新启动程式
- html回调函数,JS 回调函数
- 特教学校计算机课,特教学校引入编程课 为听障孩子打开智能之门
- OEM 11g在win7 ie11下报错“证书错误,导航已阻止”的恢复方法
- 蓝光光盘的区域位置代码
- 微信公众平台的运营管理
- JSON和list之间的转换
- Tuscany SCA软件架构设计理念分析(二)
- beecloud对接——微信支付
- Unity—JsonFx序列化场景
- 粒子背景php,html5+canvas圆形粒子移动背景动画特效