栈调用关系跟踪
来源:互联网 发布:淘宝店家寄语 编辑:程序博客网 时间:2024/06/01 08:23
在发生段错误的时候,打印函数的调用栈信息是定位问题很好的手段,一般来讲,我们可以捕获SIGSEGV信号,在信号处理函数中将函数调用栈的关系打印出来。gdb调试中的backtrace,简称bt就是这个作用。
CU的二娃子前两天写了个Linux下进程崩溃时定位源代码位置,这篇文章写的很好,调用的GNU的backtrace函数,打印了函数的调用栈信息。我想补充一些内容,把这个话题补充的更加丰富一些。
我们遇到的很多难题,前辈都会遇到,很多有分享精神的前辈会写很多精彩的总结。国外布法罗大学的一个牛人总结了跟踪栈调用关系的文章,写了一篇博客。英文水平高的筒子,不要听我JJYY,直接跳转到http://www.acsu.buffalo.edu/~charngda/backtrace.html,去看这篇博文,当然本文提到的第二种方法还是值得一看的。作者提到了4种方法来解决栈调用关系其中二娃子用的是第二种方法。我阅读了self-service linux这本书,这本书也很详尽的描述了栈的结构。我们补充一种方法,自己实现backtrace。
我的栈调用关系如下:
第一种方法 : glibc提供的backtrace函数
先说二娃子的方法: GNU提供的backtrace函数
编译的时候,我们需要加上 -rdynamic 选项,否则的话,符号表信息打印不出来。
这种方法好是好,不过,需要加上-rdynamic选项。否则会出现如下打印:
第二种方法,自己动手丰衣足食的方法。
下面的图来自雨夜听声的博客,函数调用如下图所示。如果有N个参数,将N个参数压栈(顺序也很有意思,希望了解这个的可以看程序员的自我修养),然后是将返回地址压栈,最后是将ebp压栈保存起来。
如果我们只传递一个参数个某个函数,那么我们完全可以根据参数的地址推算出ebp存放的地址,进而得到ebp的值。参数地址-4(32位系统指针的长度为4Byte)可以得到返回地址的位置。参数的地址-8 得到ebp在栈存放的地址。我们一旦得到ebp,我们就可以回朔出整个栈调用。
先看第一步:getEBP
原理很简单,就是入参的地址下面是返回地址,返回地址的下面是被保存的ebp的地址。
第二步,有了ebp, 我们可以一步一步前回退,得到调用者的栈的ebp,调用者的调用者的栈的ebp,。。。。直到NULL
对这个过程不太理解的筒子可以看下我下面的实验:
光有这个也是不行的,只能拿到栈的信息,和返回地址的信息,拿不到函数名也是白扯。这时候我们可以利用libdl.so,我们用dladdr这个函数可以得到距离入参地址最近的符号表里面的symbol。
把整个函数书写一下:
注意两点:
1 头文件 dlfcn.h
2 编译的时候加上-rdynamic ,同时链接libdl.so 即加上-ldl选项
执行效果如下:
3 第三种是libunwind。
编译的时候加上 -lunwind -lunwind-x86 ,如果是X86_64,则是 -lunwind -lunwind-x86_64
优点是不需要-rdynamic选项,不需要-g选项。
执行结果如下:
参考文献1 提到了改进的backtrace,同时给出了cario的相关代码,很有意思,感兴趣的可以去读一下。
参考文献
1 http://www.acsu.buffalo.edu/~charngda/backtrace.html (强烈推荐)
2 程序员的自我修养
3 CU 二娃子的博客
4 Self-Service Linux chapter 5 :Stack(推荐)
CU的二娃子前两天写了个Linux下进程崩溃时定位源代码位置,这篇文章写的很好,调用的GNU的backtrace函数,打印了函数的调用栈信息。我想补充一些内容,把这个话题补充的更加丰富一些。
我们遇到的很多难题,前辈都会遇到,很多有分享精神的前辈会写很多精彩的总结。国外布法罗大学的一个牛人总结了跟踪栈调用关系的文章,写了一篇博客。英文水平高的筒子,不要听我JJYY,直接跳转到http://www.acsu.buffalo.edu/~charngda/backtrace.html,去看这篇博文,当然本文提到的第二种方法还是值得一看的。作者提到了4种方法来解决栈调用关系其中二娃子用的是第二种方法。我阅读了self-service linux这本书,这本书也很详尽的描述了栈的结构。我们补充一种方法,自己实现backtrace。
我的栈调用关系如下:
- int foo()
- {
- do_backtrace();
- }
- int bar( void )
- {
- foo();
- return 0;
- }
- int boo( void )
- {
- bar();
- return 0;
- }
- int baz( void )
- {
- boo();
- return 0;
- }
- int main( void )
- {
- baz();
- return 0;
- }
第一种方法 : glibc提供的backtrace函数
先说二娃子的方法: GNU提供的backtrace函数
#include <execinfo.h>
- void do_gnu_backtrace()
- {
- #define BACKTRACE_SIZ 100
- void *array[BACKTRACE_SIZ];
- size_t size, i;
- char **strings;
- size = backtrace(array, BACKTRACE_SIZ);
- strings = backtrace_symbols(array, size);
- for (i = 0; i < size; ++i) {
- printf("%p : %s\n", array[i], strings[i]);
- }
- printf("---------------------------------------------------------\n");
- free(strings);
- }
- 0x8048ae4 : ./bt_walk(do_gnu_backtrace+0x1f) [0x8048ae4]
- 0x8048c4f : ./bt_walk(foo+0x15) [0x8048c4f]
- 0x8048c66 : ./bt_walk(bar+0xb) [0x8048c66]
- 0x8048c78 : ./bt_walk(boo+0xb) [0x8048c78]
- 0x8048c8a : ./bt_walk(baz+0xb) [0x8048c8a]
- 0x8048c9c : ./bt_walk(main+0xb) [0x8048c9c]
- 0xb758e4d3 : /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb758e4d3]
- 0x8048901 : ./bt_walk() [0x8048901]
- 0x8048894 : ./bt_walk() [0x8048894]
- 0x80489ff : ./bt_walk() [0x80489ff]
- 0x8048a16 : ./bt_walk() [0x8048a16]
- 0x8048a28 : ./bt_walk() [0x8048a28]
- 0x8048a3a : ./bt_walk() [0x8048a3a]
- 0x8048a4c : ./bt_walk() [0x8048a4c]
- 0xb75144d3 : /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb75144d3]
- 0x80486b1 : ./bt_walk() [0x80486b1]
下面的图来自雨夜听声的博客,函数调用如下图所示。如果有N个参数,将N个参数压栈(顺序也很有意思,希望了解这个的可以看程序员的自我修养),然后是将返回地址压栈,最后是将ebp压栈保存起来。
如果我们只传递一个参数个某个函数,那么我们完全可以根据参数的地址推算出ebp存放的地址,进而得到ebp的值。参数地址-4(32位系统指针的长度为4Byte)可以得到返回地址的位置。参数的地址-8 得到ebp在栈存放的地址。我们一旦得到ebp,我们就可以回朔出整个栈调用。
先看第一步:getEBP
- void **getEBP( int dummy )
- {
- void **ebp = (void **)&dummy -2 ;
- return( *ebp );
- }
第二步,有了ebp, 我们可以一步一步前回退,得到调用者的栈的ebp,调用者的调用者的栈的ebp,。。。。直到NULL
- while( ebp )
- {
- ret = ebp + 1;
- dladdr( *ret, &dlip );
- printf("Frame %d: [ebp=0x%08x] [ret=0x%08x] %s\n",
- frame++, *ebp, *ret, dlip.dli_sname );
- ebp = (void**)(*ebp);
- /* get the next frame pointer */
- }
- (gdb) i b
- Num Type Disp Enb Address What
- 1 breakpoint keep y 0x0804858a in main at bt_walk.c:49
- 2 breakpoint keep y 0x08048578 in baz at bt_walk.c:44
- 3 breakpoint keep y 0x08048566 in boo at bt_walk.c:39
- 4 breakpoint keep y 0x08048554 in bar at bt_walk.c:34
- 5 breakpoint keep y 0x08048542 in foo at bt_walk.c:29
- 6 breakpoint keep y 0x080484ac in print_walk_backtrace at bt_walk.c:12
- 7 breakpoint keep y 0x0804849a in getEBP at bt_walk.c:6
- (gdb) r
- Starting program: /home/manu/code/c/self/calltrace/bt_walk
- Breakpoint 1, main () at bt_walk.c:49
- 49 baz();
- (gdb) p $ebp
- $1 = (void *) 0xbffff6d8
- (gdb) c
- Continuing.
- Breakpoint 2, baz () at bt_walk.c:44
- 44 boo();
- (gdb) p $ebp
- $2 = (void *) 0xbffff6c8
- (gdb) c
- Continuing.
- Breakpoint 3, boo () at bt_walk.c:39
- 39 bar();
- (gdb) p $ebp
- $3 = (void *) 0xbffff6b8
- (gdb) c
- Continuing.
- Breakpoint 4, bar () at bt_walk.c:34
- 34 foo();
- (gdb) p $ebp
- $4 = (void *) 0xbffff6a8
- (gdb) c
- Continuing.
- Breakpoint 5, foo () at bt_walk.c:29
- 29 print_walk_backtrace();
- (gdb) p $ebp
- $5 = (void *) 0xbffff698
- (gdb) c
- Continuing.
- Breakpoint 6, print_walk_backtrace () at bt_walk.c:12
- 12 int frame = 0;
- (gdb) p $ebp
- $6 = (void *) 0xbffff688
- (gdb) c
- Continuing.
- Breakpoint 7, getEBP (dummy=0x9ca212c) at bt_walk.c:6
- 6 void **ebp = (void **)&dummy -2 ;
- (gdb) p $ebp
- $7 = (void *) 0xbffff638
- (gdb) n
- 7 return( ebp );
- (gdb) p ebp
- $8 = (void **) 0xbffff638
- (gdb) x/40x 0xbffff638
- 0xbffff638: 0xbffff688 0x080484be 0x09ca212c 0xbffff68f
- 0xbffff648: 0x00000001 0xb7eac269 0xbffff68f 0xbffff68e
- 0xbffff658: 0x00000000 0xb7ff3fdc 0xbffff714 0x00000000
- 0xbffff668: 0x00000000 0xb7e47043 0x00000000 0x00000000
- 0xbffff678: 0x09ca212c 0x00000001 0xb7fb9ff4 0x00000000
- 0xbffff688: 0xbffff698 0x08048547 0x080485a0 0x08049ff4
- 0xbffff698: 0xbffff6a8 0x08048559 0xb7fba3e4 0x00008000
- 0xbffff6a8: 0xbffff6b8 0x0804856b 0xffffffff 0xb7e47196
- 0xbffff6b8: 0xbffff6c8 0x0804857d 0xb7fed270 0x00000000
- 0xbffff6c8: 0xbffff6d8 0x0804858f 0x080485a0 0x00000000
- (gdb) p &dummy
- $9 = (int **) 0xbffff640
- #ifdef __USE_GNU
- /* Structure containing information about object searched using
- ‘dladdr’. */
- typedef struct
- {
- __const char *dli_fname; /* File name of defining object. */
- void *dli_fbase; /* Load address of that object. */
- __const char *dli_sname; /* Name of nearest symbol. */
- void *dli_saddr; /* Exact value of nearest symbol. */
- } Dl_info;
/* Fill in *INFO with the following information about ADDRESS.
Returns 0 iff no shared object’s segments contain that address. */
extern int dladdr (__const void *__address, Dl_info *__info) __THROW;
- #include <dlfcn.h>
- void **getEBP( int dummy )
- {
- void **ebp = (void **)&dummy -2 ;
- return( *ebp );
- }
- void print_walk_backtrace( void )
- {
- int dummy;
- int frame = 0;
- Dl_info dlip;
- void **ebp = getEBP( dummy );
- void **ret = NULL;
- printf( "Stack backtrace:\n" );
- while( ebp )
- {
- ret = ebp + 1;
- dladdr( *ret, &dlip );
- printf("Frame %d: [ebp=0x%08x] [ret=0x%08x] %s\n",
- frame++, *ebp, *ret, dlip.dli_sname );
- ebp = (void**)(*ebp);
- /* get the next frame pointer */
- }
- printf("---------------------------------------------------------\n");
- }
1 头文件 dlfcn.h
2 编译的时候加上-rdynamic ,同时链接libdl.so 即加上-ldl选项
执行效果如下:
- Stack backtrace:
- Frame 0: [ebp=0xbfdbea38] [ret=0x08048c42] foo
- Frame 1: [ebp=0xbfdbea48] [ret=0x08048c63] bar
- Frame 2: [ebp=0xbfdbea58] [ret=0x08048c75] boo
- Frame 3: [ebp=0xbfdbea68] [ret=0x08048c87] baz
- Frame 4: [ebp=0xbfdbea78] [ret=0x08048c99] main
- Frame 5: [ebp=0x00000000] [ret=0xb75594d3] __libc_start_main
3 第三种是libunwind。
- #include <libunwind.h>
- void do_unwind_backtrace()
{
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
unw_word_t offset, pc;
char fname[64];
unw_get_reg(&cursor, UNW_REG_IP, &pc);
fname[0] = '\0';
(void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);
}
printf("---------------------------------------------------------\n");
}
优点是不需要-rdynamic选项,不需要-g选项。
执行结果如下:
- 0x8048a01 : (foo+0x1a) [0x8048a01]
- 0x8048a13 : (bar+0xb) [0x8048a13]
- 0x8048a25 : (boo+0xb) [0x8048a25]
- 0x8048a37 : (baz+0xb) [0x8048a37]
- 0x8048a49 : (main+0xb) [0x8048a49]
- 0xb75c14d3 : (__libc_start_main+0xf3) [0xb75c14d3]
- 0x80486b1 : (_start+0x21) [0x80486b1]
- ---------------------------------------------------------
参考文献1 提到了改进的backtrace,同时给出了cario的相关代码,很有意思,感兴趣的可以去读一下。
参考文献
1 http://www.acsu.buffalo.edu/~charngda/backtrace.html (强烈推荐)
2 程序员的自我修养
3 CU 二娃子的博客
4 Self-Service Linux chapter 5 :Stack(推荐)
0
上一篇:查看文件被占用
下一篇:修复sd卡的方法
相关热门文章
- 对象作为形参(压栈)...
- PMP总结之质量管理
- 逃避
- MongoDB之连接安全
- PDF格式在出版印刷行业的应用...
- linux 常见服务端口
- 【ROOTFS搭建】busybox的httpd...
- xmanager 2.0 for linux配置
- 什么是shell
- linux socket的bug??
- 系统提供的库函数存在内存泄漏...
- 初学UNIX环境高级编程的,关于...
- chinaunix博客什么时候可以设...
- 一个访问量较大网站的服务器。...
- 收音机驱动与v4l2架构的关系,...
给主人留下些什么吧!~~
评论热议
0 0
- 栈调用关系跟踪
- 栈调用关系跟踪
- 栈调用关系跟踪
- 用GCC来跟踪程序的函数调用关系
- 用GCC来跟踪程序的函数调用关系
- Linux C++ 跟踪程序奔溃及函数调用关系
- Java 调用栈跟踪办法
- 跟踪 API 调用--OllyDbg
- 函数调用跟踪
- Android调用堆栈跟踪
- Android 调用堆栈跟踪
- 跟踪Android调用堆栈
- strace跟踪系统调用
- Android 方法调用跟踪
- ftrace 跟踪函数调用
- Android 调用堆栈跟踪
- Android调用堆栈跟踪
- malloc 调用跟踪浅谈
- iboot调试
- ubifs烧写到nand flash中的几种方法
- 关于使用比较工具[找出你遗忘的地方]
- WPF(.net4.0)嵌入windows Media Player 运行出错
- 查看文件被占用
- 栈调用关系跟踪
- 修复sd卡的方法
- 查看arm平台下程序依赖的动态库有哪些
- 虚拟机下fedora与window共享文件夹
- QT程序打包发布 - 免安装
- Qt编译后无法debug时
- 设计log函数库的几个要点
- QTableView
- Java线程并发库
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
摩托车加油门发闷跑不起来
不加
到加油站千万不要说把油加满
为什么4s店说不让加壳牌汽油
gta5蓝宝儿全脱图不加马赛克
不动声色
不动声色意思
不动明王
岿然不动
人生不相见动如参与商
纹丝不动意思
叶子让哥哥动一动好不好
车钥匙拧不动
电脑鼠标不动怎么办
不动明王纹身
按兵不动
锁芯转不动小窍门
鲍曼不动杆菌
纹丝不动
岿然不动读音
汽车钥匙拧不动
不动明王纹身手稿
鼠标不动怎么办
原封不动
不动尊菩萨
不动
不要动
动了
么动
别动
战士站岗4小时不动
我就放在里面不动
日久生情总裁不许动
后面进去装睡不动
蔚领为什么卖不动
看着妻子被人弄得走不动路
一边骑马一边做不用动
妻子被小鬼弄得走不动路
战士连续站岗4小时纹丝不动
技能匡大师免费版
不匡