backtrace函数与堆栈 函数调用堆栈

一般察看函数运行时堆栈的方法是使用GDB之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。

在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈

Function: int backtrace(void **buffer,int size)

该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小

在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址

注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容

Function: char ** backtrace_symbols (void *const *buffer, int size)

backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值)

函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址

现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))

该函数的返回值是通过malloc函数申请的空间,因此调用这必须使用free函数来释放指针.

注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL

Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)

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

下面的例子显示了这三个函数的用法

#include <execinfo.h>

#include <stdio.h>

#include <stdlib.h>

void

print_trace (void)

{

void *array[10];

size_t size;

char **strings;

size_t i;

size = backtrace (array, 10);

strings = backtrace_symbols (array, size);

printf ("Obtained %zd stack frames.n", size);

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

printf ("%sn", strings);

free (strings);

}

void

dummy_function (void)

{

print_trace ();

}

int

main (void)

{

dummy_function ();

return 0;

}

备注:void *const *buffer -- buffer指向char类型的常量指针的指针(很是拗口)

善用backtrace解决大问题(转)

程序在得到一个Segmentation fault这样的错误信息毫无保留地就跳出来了,遇到这样的问题让人很痛苦,查找问题不亚于你N多天辛苦劳累编写代码的难度。那么有没有更好的方法可以在产生SIGSEGV信号的时候得到调试可用的信息呢?看看下面的例程吧!

sigsegv.h

#ifndef __sigsegv_h__

#define __sigsegv_h__

#ifdef __cplusplus

extern "C" {

#endif

int setup_sigsegv();

#ifdef __cplusplus

}

#endif

#endif

sigsegv.c

#define _GNU_SOURCE

#include &lt;memory.h&gt;

#include &lt;stdlib.h&gt;

#include &lt;stdio.h&gt;

#include &lt;signal.h&gt;

#include &lt;ucontext.h&gt;

#include &lt;dlfcn.h&gt;

#include &lt;execinfo.h&gt;

#ifndef NO_CPP_DEMANGLE

#include &lt;cxxabi.h&gt;

#endif

#if defined(REG_RIP)

# define SIGSEGV_STACK_IA64

# define REGFORMAT "6lx"

#elif defined(REG_EIP)

# define SIGSEGV_STACK_X86

# define REGFORMAT "x"

#else

# define SIGSEGV_STACK_GENERIC

# define REGFORMAT "%x"

#endif

static void signal_segv(int signum, siginfo_t* info, void*ptr) {

static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};

size_t i;

ucontext_t *ucontext = (ucontext_t*)ptr;

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)

int f = 0;

Dl_info dlinfo;

void **bp = 0;

void *ip = 0;

#else

void *bt[20];

char **strings;

size_t sz;

#endif

fprintf(stderr, "Segmentation Fault!n");

fprintf(stderr, "info.si_signo=%dn", signum);

fprintf(stderr, "info.si_errno=%dn", info-&gt;si_errno);

fprintf(stderr, "info.si_code=%d (%s)n", info-&gt;si_code, si_codes[info-&gt;si_code]);

fprintf(stderr, "info.si_addr=%pn", info-&gt;si_addr);

for(i = 0; i &lt; NGREG; i++)

fprintf(stderr, "reg[d]=0x" REGFORMAT "n", i, ucontext-&gt;uc_mcontext.gregs[i]);
backtrace函数与堆栈 函数调用堆栈

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)

# if defined(SIGSEGV_STACK_IA64)

ip = (void*)ucontext-&gt;uc_mcontext.gregs[REG_RIP];

bp = (void**)ucontext-&gt;uc_mcontext.gregs[REG_RBP];

# elif defined(SIGSEGV_STACK_X86)

ip = (void*)ucontext-&gt;uc_mcontext.gregs[REG_EIP];

bp = (void**)ucontext-&gt;uc_mcontext.gregs[REG_EBP];

# endif

fprintf(stderr, "Stack trace:n");

while(bp &amp;& ip) {

if(!dladdr(ip, &dlinfo))

break;

const char *symname = dlinfo.dli_sname;

#ifndef NO_CPP_DEMANGLE

int status;

char *tmp = __cxa_demangle(symname, NULL, 0, &status);

if(status == 0 &amp;& tmp)

symname = tmp;

#endif

fprintf(stderr, "% 2d: %p &lt;%s+%u&gt; (%s)n",

++f,

ip,

symname,

(unsigned)(ip - dlinfo.dli_saddr),

dlinfo.dli_fname);

#ifndef NO_CPP_DEMANGLE

if(tmp)

free(tmp);

#endif

if(dlinfo.dli_sname &amp;& !strcmp(dlinfo.dli_sname, "main"))

break;

ip = bp[1];

bp = (void**)bp[0];

}

#else

fprintf(stderr, "Stack trace (non-dedicated):n");

sz = backtrace(bt, 20);

strings = backtrace_symbols(bt, sz);

for(i = 0; i &lt; sz; ++i)

fprintf(stderr, "%sn", strings[i]);

#endif

fprintf(stderr, "End of stack tracen");

exit (-1);

}

int setup_sigsegv() {

struct sigaction action;

memset(&action, 0, sizeof(action));

action.sa_sigaction = signal_segv;

action.sa_flags = SA_SIGINFO;

if(sigaction(SIGSEGV, &action, NULL) &lt; 0) {

perror("sigaction");

return 0;

}

return 1;

}

#ifndef SIGSEGV_NO_AUTO_INIT

static void __attribute((constructor)) init(void) {

setup_sigsegv();

}

#endif

main.c

#include "sigsegv.h"

#include &lt;string.h&gt;

int die() {

char *err = NULL;

strcpy(err, "gonner");

return 0;

}

int main() {

return die();

}

下面来编译上面的main.c程序看看将会产生什么样的信息呢,不过要注意的就是如果要在你的程序里引用sigsegv.h、sigsegv.c得到堆栈信息的话记得加上-rdynamic -ldl参数。

/data/codes/c/test/backtraces $ gcc -o test -rdynamic -ldl -ggdb -g sigsegv.c main.c

/data/codes/c/test/backtraces $ ./test

Segmentation Fault!

info.si_signo = 11

info.si_errno = 0

info.si_code = 1 (SEGV_MAPERR)

info.si_addr = (nil)

reg[00] = 0x00000033

reg[01] = 0x00000000

reg[02] = 0xc010007b

reg[03] = 0x0000007b

reg[04] = 0x00000000

reg[05] = 0xb7fc8ca0

reg[06] = 0xbff04c2c

reg[07] = 0xbff04c1c

reg[08] = 0xb7f8cff4

reg[09] = 0x00000001

reg[10] = 0xbff04c50

reg[11] = 0x00000000

reg[12] = 0x0000000e

reg[13] = 0x00000006

reg[14] = 0x080489ec

reg[15] = 0x00000073

reg[16] = 0x00010282

reg[17] = 0xbff04c1c

reg[18] = 0x0000007b

Stack trace:

1: 0x80489ec &lt;die+16&gt; (/data/codes/c/test/backtraces/test)

2: 0x8048a16 &lt;main+19&gt; (/data/codes/c/test/backtraces/test)

End of stack trace

/data/codes/c/test/backtraces $

下面用gdb来看看出错的地方左右的代码:

/data/codes/c/test/backtraces $ gdb ./test

gdb&gt; disassemble die+16

Dump of assembler code for function die:

0x080489dc &lt;die+0&gt;: push ?p

0x080489dd &lt;die+1&gt;: mov %esp,?p

0x080489df &lt;die+3&gt;: sub $0x10,%esp

0x080489e2 &lt;die+6&gt;: movl $0x0,0xfffffffc(?p)

0x080489e9 &lt;die+13&gt;: mov 0xfffffffc(?p),?x

0x080489ec &lt;die+16&gt;: movl $0x6e6e6f67,(?x)

0x080489f2 &lt;die+22&gt;: movw $0x7265,0x4(?x)

0x080489f8 &lt;die+28&gt;: movb $0x0,0x6(?x)

0x080489fc &lt;die+32&gt;: mov $0x0,?x

0x08048a01 &lt;die+37&gt;: leave

0x08048a02 &lt;die+38&gt;: ret

End of assembler dump.

gdb&gt;

也可以直接break *die+16进行调试,看看在出错之前的堆栈情况,那么下面我们再来看看代码问题到底出在什么地方了。

/data/codes/c/test/backtraces $ gdb ./test

gdb&gt; break *die+16

Breakpoint 1 at 0x80489f2: file main.c, line 6.

gdb&gt; list *die+16

0x80489f2 is in die (main.c:6).

1 #include "sigsegv.h"

2 #include &lt;string.h&gt;

3

4 int die() {

5 char *err = NULL;

6 strcpy(err, "gonner");

7 return 0;

8 }

9

10 int main() {

gdb&gt;

现在看看定位错误将会多么方便,上面的调试指令中list之前break不是必须的,只是让你可以看到break其实就已经指出了哪一行代码导致 Segmentation fault了。如果你要发布你的程序你一般会为了减少体积不会附带调试信息的(也就是不加-ggdb -g参数),不过没关系,你一样可以得到上面stack-trace信息,然后你调试之前只要加上调试信息即可。

  

爱华网本文地址 » http://www.413yy.cn/a/25101011/72803.html

更多阅读

win7待机时莫名死机的原因疑难杂症 电脑待机后死机

??? 前一段时间比较难熬,因为我的电脑在win7旗舰版平台上无故频繁死机。??? 死机的症状比较蹊跷:应用程序打开一段时间,只要大约5分钟以上没理它,程序就如同心肌梗塞一般梗死,鼠标处于僵死中。打开浏览器,只要看稍长一些,再移动鼠标,系统就

裸拍 与堆栈 堆栈进行拍摄

【裸拍】与【堆栈】——无ND慢门/终极降噪/丝状白云/雾化海浪/浪漫星轨...不借助任何滤镜系统,只有裸机裸镜,在强光下玩超长慢门效果让夜景、星轨的噪点彻底消失,从此不再惧怕高ISO和长曝噪点这并非空穴来风,也不是什么神机妖镜,您和您

C ++ 中对象或其对象指针的赋值 c语言指针赋值

C++中对象或其对象指针的赋值C++中成员函数的动态绑定:C++中要实现函数的动态绑定,必须在其基类中将函数声明为virtual且在子类中对函数加以实现。然后用一个基类指针指向某一个子类对象,这样才会在函数调用时实现动态绑定。在C++中,只

VB实用小程序 vb创意小程序

用VB实现编程离不开函数调用及Windows API函数的调用,以下是笔者收集的一些实用的小例程,它们可以直接用在你的实际编程中,也可以根据实际应用加以扩充完善。其中涉及Windows API函数调用的代码你可以从VB5.0系统的API函数查看器中复制

声明:《backtrace函数与堆栈 函数调用堆栈》为网友岁月了然分享!如侵犯到您的合法权益请联系我们删除