输出程序的堆栈

backtrace函数

在linux系统中,我们可以通过如下三个库函数来获取程序的调用堆栈,可以通过man 3 backtrace查看。它们由GUN C Library提供。

#include <execinfo.h>/* Store up to SIZE return address of the current program state inARRAY and return the exact number of values stored.  */
int backtrace(void **array, int size);/* Return names of functions from the backtrace list in ARRAY in a newlymalloc()ed memory block.  */
char **backtrace_symbols(void *const *array, int size);/* This function is similar to backtrace_symbols() but it writes the resultimmediately to a file.  */
void backtrace_symbols_fd(void *const *array, int size, int fd);

在程序中使用backtrace定位异常

通常,在我们的服务程序中都会设置异常退出后自动重启,当程序出现异常退出后,我们需要程序同时输出异常的堆栈来告诉开发人员程序程序出现过异常,以及出现异常的位置在哪里。

在linux中,程序异常退出一般都会收到相关的信号,例如出现了内存错误,进程会收到一个SIGSEGV的信号,然后会执行默认操作退出程序。为此我们可以利用信号的捕捉的方式,在程序退出的前一刻输出其堆栈数据。

示例代码如下,

//main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <execinfo.h> //下一小节介绍
void start_gperftools(int sig)
{HeapProfilerStart("heapfile.heap");
}
void stop_gperftools(int sig)
{HeapProfilerStop();
}string str_crash_log_path = "xxx.dump";
void dump_crash_statck_no_malloc(const int & i_frame_size)
{void *array[i_frame_size];char **strings;int i_fd = open(str_crash_log_path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO);if (i_fd == -1) {exit(1);}size_t i_ret_frame_size = backtrace (array, i_frame_size);backtrace_symbols_fd (array, i_ret_frame_size,i_fd);close(i_fd);exit(1);
}void recv_signal(int sig)
{dump_crash_statck_no_malloc(100);/*这里也可以恢复默认动作,然后重新发送改信号给自己,这样在dump_crash_statck_no_malloc中就无需使用exit函数signal(sig, SIG_DFL);raise(sig);*/
}
void task()
{char* g;printf("%c\n", g[1]); //模拟段错误return ;
}
int main()
{signal(SIGSEGV, recv_signal);  signal(SIGABRT , recv_signal);//下一小节介绍,本小节可以去掉signal(SIGUSR1, start_gperftools);signal(SIGUSR2, stop_gperftools);task();//业务代码
}//编译 g++ main.cpp -o a.out -g -rdynamic

如上所示,当我们的业务代码task中出现了内存等异常错误时,就会产生一个xxx.dump文件,里面记录了程序退出时的堆栈信息(如下所示)。如果需要定位具体的位置,可使用addr2line命令,例如,addr2line -e a.out 0x400ed7,即可输出g[1]出现的行;需要注意是,这里的地址需要是在静态链接的情况下(动态链接情况下也是可以获取,但是稍复杂点,感兴趣可以百度了解下)。

 1 ./a.out(_Z27dump_crash_statck_no_mallocRKi+0xd0)[0x400e4d]2 ./a.out(_Z11recv_signali+0x28)[0x400eb0]3 /lib64/libc.so.6(+0x36400)[0x7f23d9f21400]4 ./a.out(_Z4taskv+0xc)[0x400ed7]  #出问题的点5 ./a.out(main+0x31)[0x400f0e]6 /lib64/libc.so.6(__libc_start_main+0xf5)[0x7f23d9f0d555]7 ./a.out[0x400cb9]

backtrace函数注意点

(1)backtrace的实现依赖于栈指针(fp寄存器),在gcc编译过程中任何非零的优化等级(-On参数)或加入了栈指针优化参数-fomit-frame-pointer后多将不能正确得到程序栈信息;
(2)backtrace_symbols的实现需要符号名称的支持,在gcc编译过程中需要加入-rdynamic参数;
(3)内联函数没有栈帧,它在编译过程中被展开在调用的位置;
(4)尾调用优化(Tail-call Optimization)将复用当前函数栈,而不再生成新的函数栈,这将导致栈信息不能正确被获取。

使用gperftools来获取程序的状态

在进程的运行过程,想要获取进程的各种状态,例如内存分配,cpu使用情况等等。gperftools是一个很好的工具。这里简单的介绍下在程序中加入gperftools,正好我们公司的服务器程序都是这么做的,简要记录下步骤,感兴趣可以自行查找,相关内容在网上有很详细的介绍。
(1)在github/gperftools获取源码,编译,获取工具相关依赖库;
(2)在程序编译时加入-ltcmalloc选项(分析进程不同的状态链接不同的库,使用不同api,这里的状态指内存分配,cpu使用状况等,具体识业务而定);
(3)程序中加入相关api,如上节中示例代码中的一样(上节中为开启和关闭内存检测api);
(4)通过kill -USER1/USER2 PID的方式开启和关闭检测;
(5)通过检测输出文件分析结果。

使用backtrace追踪程序的异常退出相关推荐

  1. 详谈 UNIX 环境进程异常退出

    原文链接:http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/ 详谈 UNIX 环境进程异常退出 本文详 ...

  2. Nginx问题定位之监控进程异常退出

    nginx在运行过程中是否稳定,是否有异常退出过?这里总结几项平时会用到的小技巧. 1. 在error.log中查看是否有signal项,如果有,看看signal是多少. 比如,这是一个异常退出的情况 ...

  3. golang--监控goroutine异常退出

    在golang中,我们可以很轻易产生数以万计的goroutine,不过这也带来了麻烦:在运行中某一个goroutine异常退出,怎么办? 在erlang中,有link原语,2个进程可以链接在一起,一个 ...

  4. python 实现异常退出

    python 实现异常退出 参考文章: (1)python 实现异常退出 (2)https://www.cnblogs.com/ivyharding/p/11277999.html (3)https: ...

  5. linux c 调试 strace 诊断 调试程序 异常退出 崩溃

    目录 strace是什么? strace能做什么? strace怎么用? strace问题定位案例 1.定位进程异常退出 2.定位共享内存异常 3. 性能分析 跟踪系统调用和信号 strace是什么? ...

  6. java 线程崩溃_java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理...

    1.application应用程序注入自定义钩子程序 java语言本身提供一个很好的Runtime类,可以使我们很好的获取运行时信息.其中有一个方法是 public void addShutdownH ...

  7. 【Linux 内核】进程管理 ( 进程状态 | 进程创建 | 进程终止 | 调用 exit 系统调用函数主动退出 | main 函数返回自动退出 | kill 杀死进程 | 执行异常退出 )

    文章目录 一.进程状态 二.进程创建 三.进程终止 ( 调用 exit 系统调用函数主动退出 | main 函数返回自动退出 | kill 杀死进程 | 执行异常退出 ) 一.进程状态 Linux 进 ...

  8. MHA监控进程异常退出(MHA版本:0.56)

    最近遇到一个非常诡异的问题,mha后台进程自己中断退出了.以下是报错: Mon Dec 21 20:16:07 2015 - [info] OK. Mon Dec 21 20:16:07 2015 - ...

  9. 关于一个局部变量未初始化引发的项目异常退出问题

    类似如图一个函数,i为局部变量,未初始化,引起查询至此异常退出,偶尔加个打印,异常又没了. 拜托朋友查了许久,无意打印dst数组的i时发现越界到3000了,深刻教训:局部变量务必初始化,否则带来后续排 ...

最新文章

  1. 工作流引擎 Activiti 实战系列
  2. jenkins安装和使用
  3. python 登陆网站图片验证_登陆需要密码以及图片验证的网站 如知乎
  4. Distributed Systems笔记-middlewares
  5. 《linux内核设计与实现》第一章
  6. android之微信分享图片
  7. Dart编译技术在服务端的探索和应用
  8. %3c大自然的语言%3e竺可桢题目,大自然的语言竺可桢阅读答案
  9. Arccatalog连接到postgresql失败问题
  10. 数字信号处理:时域采样定理与频域采样定理
  11. rimraf与windows的rmdir简单使用命令方法
  12. [IPhone] 如何将制作图片放大缩小的动作
  13. Android SystemUI之NavigationBar,导航栏(四)
  14. 【编码】数据库编码报错
  15. 英语影视台词---绿皮书(1)
  16. iOS进阶 - pod install 与 pod update 读这一篇就够了
  17. Python入门神图一张
  18. 单相LCL型并网逆变器电容电流反馈有源阻尼控制实现
  19. CorelDRAW CORE压缩包X4教你快速入门制作立体五角星
  20. 类和对象系列教材 (一)- 什么是Java中的引用?

热门文章

  1. 如何利用「心理账户」提高用户付费和留存?
  2. 【linux】linux虚机修改固定ip地址的方式
  3. 音频转换m4a软件哪个好?三招教你转换格式
  4. 怎么用javascript做表格
  5. 浅谈VUE全家桶(Vue、VueX、Vue-Router、axios、Vue-cli)的理解与认识
  6. XML是什么的通俗理解
  7. 计算机2级可以搞小抄吗,份计算机二级C语言上机题库可缩印做小抄百分百准确.doc...
  8. 模拟集成电路与数字集成电路_通过可组合集成进行数字化转型
  9. java军棋源代码,java军旗源码 --第一讲
  10. WAV文件格式分析(附AVI文件格式分析)