可以给自己的程序都加上这个东西,便于快速的找到错误吧,看到别人都是这么用的

#include

#include

#include

#include

//signal 函数用法参考http://www.kernel.org/doc/man-pages/online/pages/man2/signal.2.html

//backtrace ,backtrace_symbols函数用法参考 http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html

static void WidebrightSegvHandler(int signum) {

void *array[10];

size_t size;

char **strings;

size_t i, j;

signal(signum, SIG_DFL); /* 还原默认的信号处理handler */

size = backtrace (array, 10);

strings = (char **)backtrace_symbols (array, size);

fprintf(stderr, "widebright received SIGSEGV! Stack trace:\n");

for (i = 0; i < size; i++) {

fprintf(stderr, "%d %s \n",i,strings[i]);

}

free (strings);

exit(1);

}

int invalide_pointer_error(char * p)

{

*p = 'd'; //让这里出现一个访问非法指针的错误

return 0;

}

void error_2(char * p)

{

invalide_pointer_error(p);

}

void error_1(char * p)

{

error_2(p);

}

void error_0(char * p)

{

error_1(p);

}

int main()

{

//设置 信好的处理函数,各种 信号的定义见http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html

signal(SIGSEGV, WidebrightSegvHandler); // SIGSEGV      11       Core    Invalid memory reference

signal(SIGABRT, WidebrightSegvHandler); // SIGABRT       6       Core    Abort signal from

char *a = NULL;

error_0(a);

exit(0);

}

widebright@widebright:~/桌面$ gcc main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048580]

1 [0xb807a400]

2 ./a.out [0x8048636]

3 ./a.out [0x8048649]

4 ./a.out [0x804865c]

5 ./a.out [0x80486a9]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7f19775]

然后为了定位错误,我们需要加上-g参数重新编译一个带调试信息的版本

widebright@widebright:~/桌面$ gcc -g main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048580]

1 [0xb7fb3400]

2 ./a.out [0x8048636]

3 ./a.out [0x8048649]

4 ./a.out [0x804865c]

5 ./a.out [0x80486a9]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7e52775]

7 ./a.out [0x80484c1]

加上-rdynamic 参数的话,输出的符号更清楚一些,不过好像地址不一样了。

widebright@widebright:~/桌面$ gcc -g -rdynamic main.c

widebright@widebright:~/桌面$ ./a.out

widebright received SIGSEGV! Stack trace:

0 ./a.out [0x8048840]

1 [0xb7f3d400]

2 ./a.out(error_2+0x11) [0x80488f6]

3 ./a.out(error_1+0x11) [0x8048909]

4 ./a.out(error_0+0x11) [0x804891c]

5 ./a.out(main+0x4b) [0x8048969]

6 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5) [0xb7ddc775]

7 ./a.out [0x8048781]

可以看到有调试信息的时候,错误是一样的。然后就可以用gdb定位和调试错误了:

-----------------------

(gdb) info line *0x8048580

Line 19 of "main.c" starts at address 0x804856d

and ends at 0x8048583 .

(gdb) list *0x8048580

0x8048580 is in WidebrightSegvHandler (main.c:19).

14        char **strings;

15        size_t i, j;

16

17        signal(signum, SIG_DFL); /* 还原默认的信号处理handler */

18

19        size = backtrace (array, 10);

20        strings = (char **)backtrace_symbols (array, size);

21

22        fprintf(stderr, "widebright received SIGSEGV! Stack trace:\n");

23        for (i = 0; i < size; i++) {

-----------------

(gdb) list *0x8048636

0x8048636 is in error_2 (main.c:41).

36

37

38    void error_2(char * p)

39    {

40        invalide_pointer_error(p);

41    }

42

43    void error_1(char * p)

44    {

45         error_2(p);

--------------

(gdb) list *0x8048649

0x8048649 is in error_1 (main.c:46).

41    }

42

43    void error_1(char * p)

44    {

45         error_2(p);

46    }

47

48    void error_0(char * p)

49    {

50         error_1(p);

=============

(gdb) br main.c:40

Breakpoint 1 at 0x804862b: file main.c, line 40.

(gdb) run

Starting program: /home/widebright/桌面/a.out

Breakpoint 1, error_2 (p=0x0) at main.c:40

40        invalide_pointer_error(p);

(gdb) stepi

0x0804862e    40        invalide_pointer_error(p);

(gdb) stepi

0x08048631    40        invalide_pointer_error(p);

(gdb) stepi

invalide_pointer_error (p=0x0) at main.c:32

32    {

(gdb) stepi

0x08048616    32    {

(gdb) stepi

33        *p = 'd'; //让这里出现一个访问非法指针的错误

(gdb) stepi

0x0804861b    33        *p = 'd'; //让这里出现一个访问非法指针的错误

(gdb) stepi

Program received signal SIGSEGV, Segmentation fault.

0x0804861b in invalide_pointer_error (p=0x0) at main.c:33

33        *p = 'd'; //让这里出现一个访问非法指针的错误

(gdb) print p

$1 = 0x0

(gdb) print *p

Cannot access memory at address 0x0

===============================================

好像使用

int sigaction(int signum, const struct sigaction *act,

struct sigaction *oldact);

http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html

这个函数注册信号的处理函数的话,可以得到更多的信息,比如出错 时候的寄存器的值等等。

因为他函数 最后一个参数传过来一个ucontext_t *ucontext 的指针

可以看到 “善用backtrace解决大问题” http://blog.chinaunix.net/u/3425/showart_263408.html 这个网页上有给出一个例子。

最初看到这个用法的的在redhat的安装程序的anaconda里面的。

------------------------

关于backtrack的原理 的解释,参考这个:

从别人blog上拷来的,地址:http://blog.csdn.net/absurd/archive/2005/12/13/551585.aspx

开发嵌入式软件通常是比较麻烦的事,一些常用的工具往往无法使用,在开发PC软件时简单的任务,此时变得很复杂。今天就遇到了这样一件事,折腾了几个小时,仅仅是为知道call stack。

我编译了一个程序放到PDA(ARM9+LINUX+UCLIBC)上面运行,出现了一个ASSERT,并显示了文件名和行号,原来是调用了一个没有实现 的函数,我很想知道是谁调用了它,这看似简单的问题却让我很头疼,如果有gdb,那好办-用bt命令就可以搞定,如果用的libc,那也好办-用 backtrace函数就可以搞定,问题是两者都没有。

想来想去只有自己写一个backtrace,要实现这个功能并不难,如果我们知道调用堆栈的格式,就可以很容易取出上层调用者的指令地址,有了这些上层调用者的指令地址,我们可以通过MAP文件找到指令地址对应的源文件名和行号。

下面简要介绍一下实现原理:

要获得调用者的地址,有必要介绍一下堆栈的格式:

+---------------------------+ (高地址)

+_参数1__________+

+---------------------------+

+_参数2__________+

+---------------------------+ 参数的顺序依赖于调用方式

+_参数.__________+

+---------------------------+

+_参数N__________+

+---------------------------+

+_eip____________+ 返回本次调用后,下一条指令的地址

+----------------------------+

+_ebp____________+ 这里保存的调用者的ebp

+----------------------------+

(ebp 指向这里:相当于调用者和被调用者的分界线)

+----------------------------+

+_临时变量1_______+

+----------------------------+

+_临时变量2_______+

+----------------------------+

+_临时变量.________+

+----------------------------+

+----------------------------+

+_临时变量N_______+

+----------------------------+(低地址)

由于优化、调用方式、编译器的不同,上述布局部可能有所不同,但一般来说,第一个局部变量前是调用者的ebp,ebp前是返回后下一条指令的地址。

知道了这个结构,要获得上层调用的者指令地址就容易了,我们可以用如下代码模拟glibc提供的backtrace的功能:

int backtrace (void **BUFFER, int SIZE)

{

int n = 0;

int *p = &n;

int i = 0;

int ebp = p[1];

int eip = p[2];

for(i = 0; i < SIZE; i++)

{

BUFFER[i] = (void*)eip;

p = (int*)ebp;

ebp = p[0];

eip = p[1];

}

return SIZE;

}

附:

通过addr2line可以找到地址对应的文件名和行号,不用手动去查MAP文件了。

=======================

windows系统上面要实现同样的功能,可能要调用

Debug Help Library 里面的StackWalk64 等函数。

http://msdn.microsoft.com/en-us/library/ms680650(VS.85).aspx

找到一个使用StackWalk64 的例子http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html

这里又是一个模拟backtrace(stackwalk)函数的例子

http://www.cnblogs.com/lbq1221119/archive/2008/04/18/1159956.html

其实你可以在程序的任何地方调用backtrace和 stackwalk函数的,呵呵

linux backtrack函数,Linux调用backtrack函数打印程序崩溃时的调用堆栈相关推荐

  1. linux程序崩溃时调用链,Linux 获取并分析程序崩溃时的调用堆栈

    下面是一个小例子,说明了程序出现段错误时,如何打印程序的堆栈信息. #include #include #include #include static void WidebrightSegvHand ...

  2. 栈windows linux,Linux+Windows: 程序崩溃时,在 C++ 代码中,如何获取函数调用栈信息...

    一.前言 程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段. 因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获 ...

  3. 基于HTTP可供浏览器调用的本地打印程序

    之前给公司做打印都是用ActiveX控件,只支持IE浏览器,最近需要支持谷歌,又不想去学谷歌插件编写,于是就用本地启动一个http服务器来供浏览器调用(写成windows服务更好),同事用了都说好(笑 ...

  4. 生产服务器怎么dmp堆栈信息,如何根据程序崩溃时的DMP文件使用WinDbg查找调用堆栈...

    在 Windbg.exe,打开进程的.dmp 文件. 请确保您符号路径指向正确的位置. 有关如何执行此操作,请访问下面的 Microsoft Web 站点: 如何获得符号 http://www.mic ...

  5. 解决三星note3调用系统拍照后程序崩溃或无法获取图片

    三星note3上调用拍照时,发现调用相机拍完照片返回时,导致系统崩溃或者无法获取到图片,仔细研究发现,拍完照后触发它的activity重新走了onCreate()方法. 解决方法: 1.刚开始调用了o ...

  6. chrome插件中调用ajax,Chrome扩展程序中的Ajax调用无效

    尝试在Chrome扩展程序中发送ajax请求.我已经确认请求返回200响应,它应该只是console.log来测试'.我不确定这里是否存在异常问题?我已经阅读了Chrome扩展程序' addListe ...

  7. Linux汇编代码中加打印,汇编语言中调用C函数打印“hello world”

    linux中的汇编语言开发一般采用的是AT&T语法,而一些老版本的as对于intel的语法支持还不是很好,保险起见还是用AT&T语法才是王道啊 呵呵 # filename:hello. ...

  8. Linux C/C++程序崩溃bug调试方法

    C,C++程序最常见的崩溃问题就是内存问题,内存越界,访问空指针,野指针等都会造成程序崩溃.Linux系统中当程序运行过程中出现非法操作,系统会先发送对应的错误信号,每种错误信号都有默认的处理方式,比 ...

  9. linux下开启程序崩溃生成core文件开关之ulimit详解

    运行服务器程序经常会出现崩溃,而我们不可能一天24小时都等着他出现.在实际运行中也不能总是使用gdb启动,毕竟gdb绑定运行会消耗很大一部分性能. 不过linux系统在程序崩溃时会生成一个coredu ...

最新文章

  1. 策略梯度搜索:不使用搜索树的在线规划和专家迭代 | 技术头条
  2. Nature:要想真正研究宿主-肠道微生物的相互作用,必须将相对定量变成绝对定量...
  3. 数据库索引的工作原理及其种类
  4. python基础:条件循环字符串
  5. java6 disable ssl2.0_SpringBoot2.0如何启用https协议
  6. Redis开发:hash存储自定义Java对象及value的序列化器设置
  7. (*长期更新)软考网络工程师学习笔记——Section 17 交换技术原理
  8. Android vector矢量图应用实例
  9. Qt文档阅读笔记-C++与QML混合编程(QML画饼状图)【通过信号与槽交互】
  10. DFMZ-开发过程中遇到的错误-01
  11. 如何设计实现一个证书加密签名工具包-极客大学架构师训练营(架构师 黄燧)
  12. BUUCTF [CISCN2019 总决赛 Day2 Web1] Easyweb
  13. 梨花带雨html音乐播放器源码,梨花带雨 - 雨陌文化传媒 - 5SING中国原创音乐基地...
  14. SpringMVC框架中@Controller类的方法的返回值的详细介绍
  15. nodejs对PDF合并的几种方法
  16. 电动汽车的数据记录 BMS
  17. AppBarLayoutCoordinatorLayoutBehavior
  18. Jenkins构建从github上克隆时,报Host key verification failed.
  19. ih5连接mysql数据库_iH5高级教程:H5数据应用,多种数据的判断
  20. 171031 Matlab数字图像处理-01-绪言

热门文章

  1. 宝塔Linux面板安装教程
  2. 【问题3】:Kaggle练习题《房价预测》----分别采用的岭回归,随机森林,bagging模型,AdaBoost,XgBoost等。
  3. deepspeech 2 (百度 2016 论文解读 )
  4. 读《深陷平庸的SaaS如何自救?》有感
  5. java JPanel设置边框和标题
  6. Golang 执行go build -buildmode=plugin命令构建插件出现 can‘t load package错误
  7. 深度学习论文笔记(知识蒸馏)—— FitNets: Hints for Thin Deep Nets
  8. 【THUSC2017】【LOJ2977】巧克力 斯坦纳树
  9. 【Linux】文件查找、权限设置以及综合应用
  10. 初识GeneXus产品