linux gdb显示运行堆栈,从内部程序调用gdb打印其堆栈跟踪的最佳方...

您在我的其他答案(现已删除)中提到您还想查看行号.从应用程序内部调用gdb时,我不确定如何做到这一点.

但是我将与您分享几种方法来打印带有函数名称的简单堆栈跟踪及其各自的行号而不使用gdb.他们中的大多数来自Linux Journal的一篇非常好的文章:

>方法#1:

The first method is to disseminate it

with print and log messages in order

to pinpoint the execution path. In a

complex program, this option can

become cumbersome and tedious even if,

with the help of some GCC-specific

macros, it can be simplified a bit.

Consider, for example, a debug macro

such as:

#define TRACE_MSG fprintf(stderr, __FUNCTION__ \n "() [%s:%d] here I am

", \n __FILE__, __LINE__)

You can propagate this macro quickly

throughout your program by cutting and

pasting it. When you do not need it

anymore, switch it off simply by

defining it to no-op.

>方法#2 :(它没有说明行号,但是我对方法4做了什么)

A nicer way to get a stack backtrace,

however, is to use some of the

specific support functions provided by

glibc. The key one is backtrace(),

which navigates the stack frames from

the calling point to the beginning of

the program and provides an array of

return addresses. You then can map

each address to the body of a

particular function in your code by

having a look at the object file with

the nm command. Or, you can do it a

simpler way–use backtrace_symbols().

This function transforms a list of

return addresses, as returned by

backtrace(), into a list of strings,

each containing the function name

offset within the function and the

return address. The list of strings is

allocated from your heap space (as if

you called malloc()), so you should

free() it as soon as you are done with

it.

我鼓励你阅读它,因为该页面有source code个例子.要将地址转换为函数名,必须使用-rdynamic选项编译应用程序.

>方法#3 :(做方法2的更好方法)

An even more useful application for

this technique is putting a stack

backtrace inside a signal handler and

having the latter catch all the “bad”

signals your program can receive

(SIGSEGV, SIGBUS, SIGILL, SIGFPE and

the like). This way, if your program

unfortunately crashes and you were not

running it with a debugger, you can

get a stack trace and know where the

fault happened. This technique also

can be used to understand where your

program is looping in case it stops

responding

该技术的实现可用于here.

>方法#4:

我在方法#3上做了一个很小的改进来打印行号.这也可以复制到方法#2上.

基本上,我使用addr2line来followed a tip

convert addresses into file names and

line numbers.

下面的源代码打印所有本地函数的行号.如果调用另一个库中的函数,您可能会看到几个??:0而不是文件名.

#include

#include

#include

#include

#include

void bt_sighandler(int sig, struct sigcontext ctx) {

void *trace[16];

char **messages = (char **)NULL;

int i, trace_size = 0;

if (sig == SIGSEGV)

printf("Got signal %d, faulty address is %p, "

"from %p

", sig, ctx.cr2, ctx.eip);

else

printf("Got signal %d

", sig);

trace_size = backtrace(trace, 16);

/* overwrite sigaction with caller's address */

trace[1] = (void *)ctx.eip;

messages = backtrace_symbols(trace, trace_size);

/* skip first stack frame (points here) */

printf("[bt] Execution path:

");

for (i=1; i

{

printf("[bt] #%d %s

", i, messages[i]);

/* find first occurence of '(' or ' ' in message[i] and assume

* everything before that is the file name. (Don't go beyond 0 though

* (string terminator)*/

size_t p = 0;

while(messages[i][p] != '(' && messages[i][p] != ' '

&& messages[i][p] != 0)

++p;

char syscom[256];

sprintf(syscom,"addr2line %p -e %.*s", trace[i], p, messages[i]);

//last parameter is the file name of the symbol

system(syscom);

}

exit(0);

}

int func_a(int a, char b) {

char *p = (char *)0xdeadbeef;

a = a + b;

*p = 10; /* CRASH here!! */

return 2*a;

}

int func_b() {

int res, a = 5;

res = 5 + func_a(a, 't');

return res;

}

int main() {

/* Install our signal handler */

struct sigaction sa;

sa.sa_handler = (void *)bt_sighandler;

sigemptyset(&sa.sa_mask);

sa.sa_flags = SA_RESTART;

sigaction(SIGSEGV, &sa, NULL);

sigaction(SIGUSR1, &sa, NULL);

/* ... add any other signal here */

/* Do something */

printf("%d

", func_b());

}

此代码应编译为:gcc sighandler.c -o sighandler -rdynamic

该方案产出:

Got signal 11, faulty address is 0xdeadbeef, from 0x8048975

[bt] Execution path:

[bt] #1 ./sighandler(func_a+0x1d) [0x8048975]

/home/karl/workspace/stacktrace/sighandler.c:44

[bt] #2 ./sighandler(func_b+0x20) [0x804899f]

/home/karl/workspace/stacktrace/sighandler.c:54

[bt] #3 ./sighandler(main+0x6c) [0x8048a16]

/home/karl/workspace/stacktrace/sighandler.c:74

[bt] #4 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x3fdbd6]

??:0

[bt] #5 ./sighandler() [0x8048781]

??:0

对于最近的Linux内核版本更新2012/04/28,上述sigaction签名已过时.此外,我通过从this answer获取可执行文件名称来改进它.这是一个up to date version:

char* exe = 0;

int initialiseExecutableName()

{

char link[1024];

exe = new char[1024];

snprintf(link,sizeof link,"/proc/%d/exe",getpid());

if(readlink(link,exe,sizeof link)==-1) {

fprintf(stderr,"ERRORRRRR

");

exit(1);

}

printf("Executable name initialised: %s

",exe);

}

const char* getExecutableName()

{

if (exe == 0)

initialiseExecutableName();

return exe;

}

/* get REG_EIP from ucontext.h */

#define __USE_GNU

#include

void bt_sighandler(int sig, siginfo_t *info,

void *secret) {

void *trace[16];

char **messages = (char **)NULL;

int i, trace_size = 0;

ucontext_t *uc = (ucontext_t *)secret;

/* Do something useful with siginfo_t */

if (sig == SIGSEGV)

printf("Got signal %d, faulty address is %p, "

"from %p

", sig, info->si_addr,

uc->uc_mcontext.gregs[REG_EIP]);

else

printf("Got signal %d

", sig);

trace_size = backtrace(trace, 16);

/* overwrite sigaction with caller's address */

trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];

messages = backtrace_symbols(trace, trace_size);

/* skip first stack frame (points here) */

printf("[bt] Execution path:

");

for (i=1; i

{

printf("[bt] %s

", messages[i]);

/* find first occurence of '(' or ' ' in message[i] and assume

* everything before that is the file name. (Don't go beyond 0 though

* (string terminator)*/

size_t p = 0;

while(messages[i][p] != '(' && messages[i][p] != ' '

&& messages[i][p] != 0)

++p;

char syscom[256];

sprintf(syscom,"addr2line %p -e %.*s", trace[i] , p, messages[i] );

//last parameter is the filename of the symbol

system(syscom);

}

exit(0);

}

并初始化如下:

int main() {

/* Install our signal handler */

struct sigaction sa;

sa.sa_sigaction = (void *)bt_sighandler;

sigemptyset (&sa.sa_mask);

sa.sa_flags = SA_RESTART | SA_SIGINFO;

sigaction(SIGSEGV, &sa, NULL);

sigaction(SIGUSR1, &sa, NULL);

/* ... add any other signal here */

/* Do something */

printf("%d

", func_b());

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值