linux backtrace()详细使用说明,分析Segmentation fault

在此之前,开发eCos应用程序时,经常碰到程序挂掉后,串口打印输出一大串让人看不懂的数据。今天才明白,原来这些数据是程序挂掉时的堆栈帧数据(stack frame data)。

通过这些堆栈帧数据可以分析出程序当时的运行状态和定位程序哪里出现了问题。

这就是本文要讲的—

backtrace()和backtrace_symbols()函数的使用。

backtrace()和backtrace_symbols()函数

backtrace()和backtrace_symbols()函数,包括另一个函数:backtrace_symbols_fd(),它们是GNU对程序调试的扩展支持。

头 文 件

#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中。参数size用来指定buffer中可以保存多少个void*元素(表示相应栈帧的地址,一个返回地址)。如果回溯的函数调用大于size,则size个函数调用地址被返回。为了取得全部的函数调用列表,应保证buffer和size足够大。

backtrace_symbols()函数,参数buffer是从backtrace()函数获取的数组指针,size是该数组中的元素个数(backtrace()函数的返回值)。该函数主要功能:将从backtrace()函数获取的地址转为描述这些地址的字符串数组。每个地址的字符串信息包含对应函数的名字、在函数内的十六进制偏移地址、以及实际的返回地址(十六进制)。需注意的是,当前,只有使用elf二进制格式的程序才能获取函数名称和偏移地址,此外,为支持函数名功能,可能需要添加相应的编译链接选项如-rdynamic;否则,只有十六进制的返回地址能被获取。backtrace_symbols()函数返回值是一个字符串指针,是通过malloc函数申请的空间,使用完后,调用者必需把它释放掉。注:如果不能为字符串获取足够的空间,该函数的返回值为NULL。

backtrace_symbols_fd()函数,与backtrace_symbols()函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行。它不会调用malloc函数,因此,它可以应用在函数调用可能失败的情况下。

返 回 值

backtrace()函数返回通过buffer返回的地址个数,这个数目不会超过size。如果这个返回值小于size,那么所有的函数调用列表都被保存;如果等于size,那么函数调用列表可能被截断,此时,一些最开始的函数调用没有被返回。

成功时,backtrace_symbols()函数返回一个由malloc分配的数组;失败时,返回NULL。

注意事项

这些函数对函数返回地址如何保存在栈内有一些假设,注意如下:

忽略帧指针(由gcc任何非零优化级别处理了)可能引起这些假设的混乱。

内联函数没有栈帧。

Tail-call(尾调用)优化会导致栈帧被其它调用覆盖。

为支持函数名功能,可能需要添加相应的编译链接选项如-rdynamic;否则,只有十六进制的返回地址能被获取。

“static”函数名是不会导出的,也不会出现在函数调用列表里,即使指定了-rdynamic链接选项。

程序用例

/**
* \brief backtrace测试程序
*
* 编译指令:gcc -g -rdynamic backtrace.c -o backtrace
*/ #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h> /* for signal */
#include <execinfo.h> /* for backtrace() */ #define SIZE 100 void dump(void)
{
int j, nptrs;
void *buffer[100];
char **strings; nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs); strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
} for (j = 0; j < nptrs; j++)
printf(" [%02d] %s\n", j, strings[j]); free(strings);
} void handler(int signo)
{
printf("\n=========>>>catch signal %d (%s) <<<=========\n",
signo, (char *)strsignal(signo));
printf("Dump stack start...\n");
dump();
printf("Dump stack end...\n"); /* 恢复并发送信号 */
signal(signo, SIG_DFL);
raise(signo);
} void printSigno(int signo)
{
static int i = 0; printf("\n=========>>>catch signal %d (%s) i = %d <<<=========\n",
signo, (char *)strsignal(signo), i++);
printf("Dump stack start...\n");
dump();
printf("Dump stack end...\n");
} void myfunc3(void)
{
/* 为SIGINT安装信号处理函数,通过Ctrl + C发出该信号 */
signal(SIGINT, handler);
signal(SIGSEGV, handler); /* 为安装SIGSEGV信号处理函数 */
signal(SIGUSR1, printSigno); /* 打印当前函数调用栈 */
printf("Current function calls list is: \n");
printf("----------------------------------\n");
dump();
printf("----------------------------------\n\n"); while (1) {
;/* 在这里通过Ctrl + C发出一个SIGINT信号来结束程序的运行 */
}
} /**
* \brief
* 使用static修饰函数,表明不导出这个符号。
* 即使用-rdynamic选项,看到的只能是个地址。
*/
static void myfunc2(void)
{
myfunc3();
} void myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls -1);
else
myfunc2();
} 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);
} // ----------------------------------------------------------------------------
// End of backtrace.c

编译指令:gcc -g -rdynamic backtrace.c -o backtrace

运行结果:

reille@ubuntu:backtrace$ ./backtrace 3
Current function calls list is:
———————————-
backtrace() returned 9 addresses
[00] ./backtrace(dump+0x1f) [0x8048943]
[01] ./backtrace(myfunc3+0x4b) [0x8048a88]
[02] ./backtrace [0x8048aa1]
[03] ./backtrace(myfunc+0x21) [0x8048ac4]
[04] ./backtrace(myfunc+0x1a) [0x8048abd]
[05] ./backtrace(myfunc+0x1a) [0x8048abd]
[06] ./backtrace(main+0x52) [0x8048b18]
[07] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x644b56]
[08] ./backtrace [0x8048891]
———————————- ^C (按钮Ctrl + C键)
=========>>>catch signal 2 (Interrupt) <<<=========
Dump stack start…
backtrace() returned 10 addresses
[00] ./backtrace(dump+0x1f) [0x8048943]
[01] ./backtrace(handler+0x3c) [0x8048a11]
[02] [0x1f4400]
[03] ./backtrace [0x8048aa1]
[04] ./backtrace(myfunc+0x21) [0x8048ac4]
[05] ./backtrace(myfunc+0x1a) [0x8048abd]
[06] ./backtrace(myfunc+0x1a) [0x8048abd]
[07] ./backtrace(main+0x52) [0x8048b18]
[08] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x644b56]
[09] ./backtrace [0x8048891]
Dump stack end… reille@ubuntu:backtrace$
reille@ubuntu:backtrace$

分析Segmentation fault

对于Segmentation fault错误,有多种分析定位方法,如利用linux产生的core文件、使用gdb进行分析定位。但对于一直运行的程序,特别是大型程序,当意外出现Segmentation fault错误时,其分析定位,则比较棘手。

我们知道,产生Segmentation fault错误时,一般会产生一个SIGSEGV信号。利用这个机制,上述问题传统的做法是,在程序中安装SIGSEGV信号,然后在该信号处理函数中,回溯函数调用列表,从而分析定位错误,一劳永逸。

上面的程序用例中,已安装了SIGSEGV信号。

linux backtrace()详细使用说明,分析Segmentation fault的更多相关文章

  1. linux backtrace()详细使用说明,分析Segmentation fault【转】

    转自:http://velep.com/archives/1032.html 在此之前,开发eCos应用程序时,经常碰到程序挂掉后,串口打印输出一大串让人看不懂的数据.今天才明白,原来这些数据是程序挂 ...

  2. Linux下的段错误(Segmentation fault)

    Linux开发中常见段错误问题原因分析 1 使用非法的内存地址(指针),包括使用未经初始化及已经释放的指针.不存在的地址.受系统保护的地址,只读的地址等,这一类也是最常见和最好解决的段错误问题,使用G ...

  3. Linux下报错:Segmentation fault.

    遇到的问题:程序在读文件之后,准备执行fclose(fp);时,出现了如下错误: Program received signal SIGSEGV, Segmentation fault. 解决方法:倒 ...

  4. linux segmentation fault记录

    文章将记录linux学习使用中出现的各种segmentation fault,持续更新,希望对看到人有所帮助 1. linux pcap segmentation fault -- 2013.11.2 ...

  5. Linux程序Segmentation fault (core dumped)

    1 问题原因 Segmentation fault (core dumped)多为内存不当操作造成.空指针.野指针的读写操作,数组越界访问,破坏常量等.对每个指针声明后进行初始化为NULL是避免这个问 ...

  6. linux Ubuntu(Segmentation fault)段错误出现原因及调试方法

      在linux下编译了一个程序,尝试运行的时候出现: Segmentation fault (core dumped) 初步确认为...完全不知道是什么玩意. 于是找度娘了. ----------- ...

  7. linux C++ 莫名奇异的段错误(segmentation fault),无法调用其他函数

    进来在linux下开发C++项目,遇到了非常奇怪的bug. 项目须要多线程实现,在写好代码后,每当执行到线程函数内部,当内部调用其他函数如printf.fopen等时就会提示段错误(segmentat ...

  8. 【error】segmentation fault分析

    前言 调试代码的时候,可能会出现segmentation fault的bug,很难找到原因,在此总结一下可能的原因. SIGSEGV 原因分析 1.程序中的变量没有进行检查: 比如,没有对变量的大小进 ...

  9. linux: QT安装时出现段错误segmentation fault

    环境:macOS 10.14.6 VMware Fusion版本:11.0.1 QT版本:qt-creator-linux-x86_64-opensource-2.5.2.bin 安装时出现:segm ...

随机推荐

  1. nyoj891(区间上的贪心)

    题目意思: 给一些闭区间,求最少须要多少点,使得每一个区间至少一个点. http://acm.nyist.net/JudgeOnline/problem.php?pid=891 例子输入 4 1 5 ...

  2. 【转】php ob_start()、ob_end_flush和ob_end_clean()多级缓冲

    原文:https://my.oschina.net/CuZn/blog/68650 当php.ini配置文件中的  设置开启的时候,就相当于PHP已经打开了最顶层的 一级缓存 (等价于调用了一次 ob ...

  3. SegmentFault 巨献 1024 程序猿游戏「红岸的呼唤」第一天任务攻略

    今天一不小心在微博上看到了SegmentFault的一条微博: 眼看今天就要过去了,那在这里说一下我的解题过程(事实上大家都知道了吧-=). 高速传送门:http://segmentfault.com ...

  4. getAttribute for IE7

    getAttribute 大部分介绍都说仅仅有一个.包含w3cschool. 事实上这种方法在iE7下有两个參数. msdn 上查到的. 简单翻一下 0 是默认情况,不区分大写和小写! 1 区分大写和 ...

  5. C++对象模型——关键词所带来的差异(第一章)

    1.2    关键词所带来的差异 (A Keyword Distinction) 假设不是为了努力维护与C之间的兼容性.C++能够比方今更简单.举个样例,假设没有八种整数须要支持的话,overload ...

  6. CreateDialog Win32 API调用的一个小问题

    在老版本号的VC编译器上.关键调用是下面2句: InitCommonDialogs(); HWND hwndDialog = CreateDialog(hInstance, "IDD_XXX ...

  7. c/c++ 数据结构之位图(bitmap)具体解释

    1.  概述 位图(bitmap)是一种很经常使用的结构,在索引.数据压缩等方面有广泛应用. 本文介绍了位图的实现方法及其应用场景. 2. 位图实现 2014728101320" alt=& ...

  8. android 自己定义ViewGroup实现可记载并呈现选择的ListView

    转载请注明出处:王亟亟的大牛之路 之前也做过一些用TextView之类的记录ListView选项的东西.可是总认为好难看.发现个不错的实现就贴给大家. 项目文件夹 执行效果: 自己定义视图: @Tar ...

  9. (转)dp动态规划分类详解

    dp动态规划分类详解 转自:http://blog.csdn.NET/cc_again/article/details/25866971 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间 ...

  10. .NET通用基本权限系统框架源代码

    DEMO下载地址: http://download.csdn.net/detail/shecixiong/5372895 一.开发技术:B/S(.NET C# ) 1.Windows XP以上 (支援 ...