backtrace()是glibc(>=2.1)提供的函数,用于跟踪函数的调用关系。

以下对backtrace()函数的说明以及实例,都来自其man page。

函数定义
       #include <execinfo.h>

int backtrace(void **buffer, int size);

char **backtrace_symbols(void *const *buffer, int size);

void backtrace_symbols_fd(void *const *buffer, int size, int fd);

函数说明

backtrace()函数用来获取程序中当前函数的回溯信息,即一系列的函数调用关系,获取到的信息被放在参数buffer中。buffer是一个数组指针,数组的每个元素保存着每一级被调用函数的返回地址。参数size指定了buffer中可存放的返回地址的数量。如果函数实际的回溯层级数大于size,则buffer中只能存放最近的函数调用关系,所以,想要得到完整的回溯信息,就要确保size参数足够大。
backtrace()函数的返回值为buffer中的条目数量,这个值不一定等于size,因为如果为得到完整回溯信息而将size设置的足够大,则该函数的返回值为buffer中实际得到的返回地址数量。
 
通过backtrace()函数得到buffer之后,backtrace_symbols()可以将其中的返回地址都对应到具体的函数名,参数size为buffer中的条目数。backtrace_symbols()函数可以将每一个返回值都翻译成“函数名+函数内偏移量+函数返回值”,这样就可以更直观的获得函数的调用关系。
经过翻译后的函数回溯信息放到backtrace_symbols()的返回值中,如果失败则返回NULL。需要注意,返回值本身是在backtrace_symbols()函数内部进行malloc的,所以必须在后续显式地free掉。
 
backtrace_symbols_fd()的buffer和size参数和backtrace_symbols()函数相同,只是它翻译后的函数回溯信息不是放到返回值中,而是一行一行的放到文件描述符fd对应的文件中。

注意,在编译的时候需要加上-rdynamic选项让链接器将所有符号添加到动态符号表中,这样才能将函数地址翻译成函数名。另外,这个选项不会处理static函数,所以,static函数的符号无法得到。

示例:

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void
myfunc3(void)
{int j, nptrs;
#define SIZE 100void *buffer[100];char **strings;nptrs = backtrace(buffer, SIZE);printf("backtrace() returned %d addresses\n", nptrs);/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)would produce similar output to the following: */strings = backtrace_symbols(buffer, nptrs);if (strings == NULL) {perror("backtrace_symbols");exit(EXIT_FAILURE);}for (j = 0; j < nptrs; j++)printf("%s\n", strings[j]);free(strings);
}static void   /* "static" means don't export the symbol... */
myfunc2(void)
{myfunc3();
}void
myfunc(int ncalls)
{if (ncalls > 1)myfunc(ncalls - 1);elsemyfunc2();
}int
main(int argc, char *argv[])
{if (argc != 2) {fprintf(stderr, "%s num-calls\n", argv[0]);exit(EXIT_FAILURE);}myfunc(atoi(argv[1]));exit(EXIT_SUCCESS);
}

进行编译:

cc -rdynamic prog.c -o prog
代码运行结果为:

$ ./prog 3
           backtrace() returned 8 addresses
           ./prog(myfunc3+0x5c) [0x80487f0]
           ./prog [0x8048871]
           ./prog(myfunc+0x21) [0x8048894]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(main+0x65) [0x80488fb]
           /lib/libc.so.6(__libc_start_main+0xdc) [0xb7e38f9c]
           ./prog [0x8048711]

采用信号量进行崩溃日志打印:

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>void trace(int signo)
{int j, nptrs;
#define SIZE 100void *buffer[100];char **strings;printf("signo: %d\n", signo);nptrs = backtrace(buffer, SIZE);printf("backtrace() returned %d addresses\n", nptrs);/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)*               would produce similar output to the following: */strings = backtrace_symbols(buffer, nptrs);if (strings == NULL) {perror("backtrace_symbols");exit(EXIT_FAILURE);}for (j = 0; j < nptrs; j++)printf("%s\n", strings[j]);free(strings);if (SIGSEGV == signo || SIGQUIT == signo) {exit(0);}
}void segfault(void)
{int *p = NULL;*p = 1;
}int main(int argc, char *argv[])
{signal(SIGSEGV, trace);signal(SIGINT, trace);signal(SIGQUIT, trace);while (1) {sleep(1);if (time(0) % 7 == 0) {segfault();}}return 0;
}

编译方法  gcc -rdynamic seg.c -o seg -g

段错信息

signo: 11
    backtrace() returned 6 addresses
    ./seg(trace+0x3c) [0x400aac]
    /lib64/libc.so.6() [0x3c39635690]
    ./seg(segfault+0x10) [0x400b64]
    ./seg(main+0x83) [0x400bef]
    /lib64/libc.so.6(__libc_start_main+0xf5) [0x3c39621b45]
    ./seg() [0x4009a9]

使用  objdump -d seg  查看出错地址  0x400b64  是一个赋值操作

0000000000400b54 <segfault>:
      400b54:       55                      push   %rbp
      400b55:       48 89 e5                mov    %rsp,%rbp
      400b58:       48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
      400b5f:       00
      400b60:       48 8b 45 f8             mov    -0x8(%rbp),%rax
      400b64:       c7 00 01 00 00 00       movl   $0x1,(%rax)
      400b6a:       5d                      pop    %rbp
      400b6b:       c3                      retq

使用  addr2line 0x400b64 -e seg -afs  查看段错函数和对应的代码行数

0x0000000000400b64
    segfault
    seg.c:41

backtrace和backtrace_symbols函数原理解析相关推荐

  1. php 打印函数调用栈,利用backtrace和backtrace_symbols函数打印调用栈信息

    本帖最后由 kylin_try 于 2017-2-6 08:41 编辑 在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈. #include int ...

  2. python构造callable_Python callable内置函数原理解析

    python内置函数 callable用于检查一个对象是否是可调用的,如果函数返回True,object 仍然可能调用失败:但如果返回 False,调用对象 object 绝对不会成功. 一.call ...

  3. python中f点flush是什么函数_Python文件操作及内置函数flush原理解析

    1.打开文件得到文件句柄并赋值给一个变量 2.通过句柄对文件进行操作 3.关闭文件 示例文件 '你好呀' 我是于超 嗯 再见 文件操作基本流程 f=open('chenli',encoding='ut ...

  4. vue render函数_Vue原理解析(一):Vue到底是什么?

    Vue,现在前端的当红炸子鸡,随着热度指数上升,实在是有必要从源码的角度,对它功能的实现原理一窥究竟.个人觉得看源码主要是看两样东西,从宏观上来说是它的设计思想和实现原理:微观上来说就是编程技巧,也就 ...

  5. “有意思的前端函数面试题”第一题答案原理解析

    if(a == 1 && a == 2 && a == 3){console.log("我走进来了"); }<!--答案1:--> va ...

  6. php函数unset,PHP unset函数 PHP unset函数原理及使用方法解析

    想了解PHP unset函数原理及使用方法解析的相关内容吗,小猿笔记在本文为您仔细讲解PHP unset函数的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:PHP,unset函数,下面大家 ...

  7. opencv焦距估计函数cvInitIntrinsicParams2D原理解析

    背景介绍 在进行相机标定时,通常会包括内参初始化估计.外参初始化估计,以及非线性优化参数.在opencv中内参数估计主要在cvInitIntrinsicParams2D函数中实现,外参数估计在cvFi ...

  8. 秋色园QBlog技术原理解析:性能优化篇:数据库文章表分表及分库减压方案(十五)...

    文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色 ...

  9. 深度学习- Dropout 稀疏化原理解析

    搬运原文链接:https://zhuanlan.zhihu.com/p/38200980 深度学习中 Dropout 原理解析 文章目录 深度学习中 Dropout 原理解析 1. Dropout 简 ...

  10. Android微信小程序原理,微信小程序事件流原理解析

    这篇文章主要介绍了微信小程序事件流原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.什么是事件? 事件是视图层到逻辑层的通讯方式: 事件可 ...

最新文章

  1. python-文件基本操作(二)
  2. 69. Sqrt(x)(python)
  3. 限制oracle数据库表的输出记录条数
  4. lnp和mysql分开安装_毕业设计之LNP+DISCUZ +分离的数据库操作
  5. 断点续传的原理剖析与实例讲解
  6. 七、制作主题(二) Anatomy of a theme
  7. java 如何去掉http debug日志_谈谈日志的最佳实践
  8. 【Django 2021年最新版教程27】数据库model 查询2个日期范围内的所有日期
  9. python自动下载邮件附件
  10. The NMEA 0183 Protocol
  11. mujoco_py中文文档
  12. html地图周边搜索,高德地图API实现定位、地点搜索和周边搜索(H5/Vue/微信小程序)...
  13. 虚幻引擎图文笔记:使用MixamoConverter对Mixamo动画重定向成UESkeleton匹配动画
  14. [树莓派] 轻松制作一个遥控小车(C++,Socket)
  15. 我和 flow.ci 的第一次亲密接触
  16. 【zz北邮人】[经历][完稿]cs硕士妹子找工作经历【阿里人搜等互联网】
  17. k线顶分型 python_顶底分型的实战操作要点(转)
  18. 开发一套软件的成本费用?这里有八个要点
  19. uniapp 封装导航栏
  20. 高精度AOA定位,给你带来不一样的室内外定位技术-新导智能

热门文章

  1. 你需要知道的关于元宇宙NFT平台艺术数字藏品交易的一切
  2. 云学python (第二章用编程改造世界·小练习)《vamei-从Python开始学编程》
  3. 提取win10 锁屏壁纸
  4. Hackintosh_guide黑苹果
  5. 【zznu-2174】
  6. 三维绕任意轴旋转矩阵
  7. android中如何让动画停止,让Android AnimatorSet停止制作动画
  8. GUI简介、AWT、Frame、Penal
  9. 能不能算是PLSQL Developer的锅?
  10. c语言贪吃蛇游戏视频,贪吃蛇游戏,贪吃蛇游戏视频