如何得到当前程序执行的堆栈
如何得到当前程序执行的堆栈
1. 背景
通常我们只是在调试程序的时候,才用 gdb bt命令显示当前进程(或线程)所在的堆栈。实际代码开发中,一般较少需要得到当前程序的堆栈。但是,在调试一些不容易复现、gdb难以跟踪的bug时,或在需要记录部分代被执行的上下文的情景下,就非常有必要得到当前程序运行的堆栈。
2. 原理
要实现上面得到当前程序堆栈的功能,需要依赖glibc中execinfo.h声明的backtrace()函数族。为此我们需要先了解glibc中backtrace是如何实现的。
2.1 底层库的实现
可以参考glibc-2.17/debug/backtrace.c,执行过程是从栈顶遍历到栈底,一层层根据调用关系,取得当前sp的值,并保存在指定的数组里面。
/* By default assume the `next' pointer in struct layout points to thenext struct layout. */
#ifndef ADVANCE_STACK_FRAME
# define ADVANCE_STACK_FRAME(next) BOUNDED_1 ((struct layout *) (next))
#endif/* By default, the frame pointer is just what we get from gcc. */
#ifndef FIRST_FRAME_POINTER
# define FIRST_FRAME_POINTER __builtin_frame_address (0)
#endifint
__backtrace (array, size)void **array;int size;
{struct layout *current;void *__unbounded top_frame;void *__unbounded top_stack;int cnt = 0;top_frame = FIRST_FRAME_POINTER;top_stack = CURRENT_STACK_FRAME;/* We skip the call to this function, it makes no sense to record it. */current = BOUNDED_1 ((struct layout *) top_frame);while (cnt < size)
{if ((void *) current INNER_THAN top_stack|| !((void *) current INNER_THAN __libc_stack_end))/* This means the address is out of range. Note that for thetoplevel we see a frame pointer with value NULL which clearly isout of range. */
break;array[cnt++] = current->return_address;current = ADVANCE_STACK_FRAME (current->next);}return cnt;
}
weak_alias (__backtrace, backtrace)
libc_hidden_def (__backtrace)
2.2 用户态的使用过程
这样,程序开发者就可以直接include execinfo.h头文件,然后调用backtrace()函数。execinfo.h中列出了实现类似功能的一组函数族:
/* Store up to SIZE return address of the current program state inARRAY and return the exact number of values stored. */extern int backtrace (void **__array, int __size) __nonnull ((1));/* Return names of functions from the backtrace list in ARRAY in a newlymalloc()ed memory block. */extern char **backtrace_symbols (void *const *__array, int __size)__THROW __nonnull ((1));/* This function is similar to backtrace_symbols() but it writes the resultimmediately to a file. */extern void backtrace_symbols_fd (void *const *__array, int __size, int __fd)__THROW __nonnull ((1));
从参数中可以想到,需要为array预备一部分存储调用栈的存储空间,后面调用的backtrace()把会进程当前执行的栈信息写到这个array里面去。
3. 实例分析
3.1 测试程序及其输出
具体示例如下execinfo.c:
#include <stdio.h>
#include <execinfo.h>int main(int argc, char * argv[])
{
int i = 0;
void * stack[1024] = {NULL,};backtrace(stack, 1024);for (i = 0; i < 32; i++) {printf("stack %d: %p\n", i, stack[i]);
}return 0;
}
gcc -g -o execinfo execinfo.c 完成之后,可以看一下这个程序的运行结果:
[root@localhost test]# ./execinfo
stack 0: 0x4005cd
stack 1: 0x7fced5bb6c05
stack 2: 0x4004b9
stack 3: (nil)
.......
3.2 反汇编二进制及其分析
我们接着对execinfo反汇编: objdump -alDS ./execinfo >> execinfo.S,得到execinfo.S之后,
看看0x4005cd 对应到哪一行代码:
/home/qxi/test/execinfo.c:34
backtrace(stack, 1024);4005b9: 48 8d 85 f0 df ff ff lea -0x2010(%rbp),%rax4005c0: be 00 04 00 00 mov $0x400,%esi4005c5: 48 89 c7 mov %rax,%rdi4005c8: e8 83 fe ff ff callq 400450 <backtrace@plt>
/home/qxi/test/execinfo.c:36for (i = 0; i < 32; i++) {4005cd: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)4005d4: eb 25 jmp 4005fb <main+0x7b>
/home/qxi/test/execinfo.c:37 (discriminator 2)
可以看到它指向的是backtrace()执行之后的程序地址,也就是最后一个入栈的值。接着看0x40004b9分别对应哪个函数:
Disassembly of section .text:
0000000000400490 <_start>:
_start():400490: 31 ed xor %ebp,%ebp400492: 49 89 d1 mov %rdx,%r9400495: 5e pop %rsi400496: 48 89 e2 mov %rsp,%rdx400499: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp40049d: 50 push %rax40049e: 54 push %rsp40049f: 49 c7 c0 80 06 40 00 mov $0x400680,%r84004a6: 48 c7 c1 10 06 40 00 mov $0x400610,%rcx4004ad: 48 c7 c7 80 05 40 00 mov $0x400580,%rdi4004b4: e8 b7 ff ff ff callq 400470 <__libc_start_main@plt>4004b9: f4 hlt4004ba: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
可以看到4004b9记录的是从glibc跳到main()函数之后执行的第一条指令的位置,也就是最早入栈的值。
4. 总结
函数调栈中压栈的值,记录着当前调用返回后会执行的下一个指令(函数)的地址. 结合上面的原理和示例分析,可以看到在应用程序中得到当前程序的调用栈的过程,就是把函数调用过程中一层层入栈的值,从栈顶一个个再次读出的过程。因此利用这个特性,再结合一些其他技术,我们可以用来实现跟踪资源泄漏、锁申请而没有释放等高级功能。
本文转自存储之厨51CTO博客,原文链接:http://blog.51cto.com/xiamachao/2064805 ,如需转载请自行联系原作者
如何得到当前程序执行的堆栈相关推荐
- javascript之执行上下文堆栈
执行上下文堆栈 有三种类型的ECMAScript代码:全局代码,函数代码和eval代码.代码执行在它的执行上下文里. 有唯一的全局上下文,以及可能有多个函数和eval上下文.每一个函数调用,进入到函数 ...
- 菜鸟学习笔记:Java基础篇3(面向对象思想、程序执行过程内存分析、面向对象重要概念)
菜鸟学习笔记:Java面向对象篇上 Java面向对象的思想 Java程序执行过程内存分析 Java垃圾回收机制 构造方法 方法重载(overload) static关键字 this关键字 Java面向 ...
- 如何让java程序执行一段时间后停止
如何让java程序执行一段时间后停止 1.概述 在本文中,我们将学习如何在一段时间后结束长时间运行的任务.我们将探讨这个问题的各种解决方案.此外,还将介绍各种方案缺点. 2.使用循环 假设我们在一个循 ...
- JVM原理(二)执行引擎篇(JVM程序执行流程、JIT编译器、JIT编译器优化)
一.JVM程序执行流程 上一章我们介绍过程序执行通常分为解释执行和编译执行,而Java两种方式都采用了,下面是Java编译成字节码.动态编译和解释为机器码的过程分析: 编译器和解释器的协调工作流程: ...
- ucosii任务三要素---执行代码 堆栈 任务控制块
使用ucosii也有一段时间了,把学习到的总结一下.这篇文章不是对ucosii如何使用的讲解,而是主要看看ucosii内核实现的原理,或者说讲一些RTOS种通用的知识.对于RTOS基础知识的讲解,暂时 ...
- 终止js程序执行的方法
js终止程序执行的方法共有三种 (一)在function里面(普通js方法) (1)return; (2)return false; (二)非function方法里面(如ajax方法) alert(& ...
- linux下程序执行的步骤及其作用
程序执行的步骤及其作用 在linux下使用gcc编程时,从表面上看是简单的命令的执行,但实际上,程序的执行分为四个步骤:预编译,编译,汇编,链接. 预编译 在linux系统下,一个.c文件经过预编译生 ...
- 页面动态显示程序执行结果-append
页面动态显示程序执行结果-append 一般CMS程序安装时会看到: 原理就是用:jquery的append属性:[ajax配合使用即可] 自己简单写了个:知道原理即可: <script src ...
- python导包顺序_2019-03-21 python导入包以及Python程序执行顺序理解
http://codingpy.com/article/python-import-101/ https://segmentfault.com/a/1190000009842139 (一)Python ...
- python读文件路径-python获取程序执行文件路径的方法(推荐)
1.获取当前执行主脚本方法:sys.argv[0]和_ file _ (1)sys.argv 一个传给Python脚本的指令参数列表.sys.argv[0]是脚本的名字.一般得到的是相对路径,用os. ...
最新文章
- echarts 地图 scatter点击事件_React实现高亮可点击地图
- 【数据分析】多场景下的算法构建
- php浏览器类型检测工具,php检测客户端浏览器类型的简单示例
- html日期英文状态显示不出来,html 时间控件插件laydate, 显示时分,不显示秒
- Iptalbes自动封杀暴力破解(Qmail邮件系统)者的IP地址
- Sentinel 1.8.0 年度版本发布,熔断降级重构升级!
- Diskpart命令安装系统小结
- 关于稀疏矩阵转化为稠密矩阵问题 (scipy.sparse格式和tensor稀疏张量格式)
- Halcon 4点单标相机外参
- docker安装指定版本的tag镜像
- Solana 海湾流(Gulf Stream)海平面(Sealevel)区别
- 串口通信以及波特率计算方法
- 抖音seo/抖音搜索排名系统/抖音矩阵优化/抖音seo源码开发,轻松进前十
- 蓝桥杯之桥本分数式(全排列函数应用)
- 电力系统非线性控制_电力系统保护与控制2020年第13期目录
- 云分众享,阿里云盘资源搜索工具
- 解决通过硬盘或U盘安装ubuntu server出现无法挂载光盘的问题教程
- 介绍Zbrush是什么软件
- Android Studio 4.1没有GsonFormat插件
- 神奇的 Excel 插件:Azure DevOps 插件
热门文章
- jsp和java一样具有平台独立性._web开发技术总复习题
- 无人驾驶插秧机智能辅助系统_北斗年会 | 智慧农业:插秧“神器”大显身手——雷科防务致力于高精度无人驾驶插秧机前装应用...
- soapui返回值类型都有哪些_小程序都有哪些类型,开发小程序效果如何
- android 百度地图 64位,百度地图 Android SDK
- 添加halcon图像显示控件_Halcon的C#二次开发及经验分享
- flutter怎么手动刷新_Flutter 怎样更新?怎样升级? - Flutter - Angular 教程网
- Egg 2.20.0 发布,阿里开源的企业级 Node.js 框架
- UVA 10733 - The Colored Cubes(Ploya)
- 个人Web自动化测试学习点总结
- String类型的方法总结