C 从程序内部调用gdb打印堆栈跟踪的最佳方法?
使用如下函数:C 从程序内部调用gdb打印堆栈跟踪的最佳方法?,c,linux,gdb,stack-trace,C,Linux,Gdb,Stack Trace,使用如下函数: #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> void print_trace() { char pid_buf[30]; sprintf(pid_buf, "--pid=%d", getpid()); char name_buf[512]; name_buf[readlink("/p
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
void print_trace() {
char pid_buf[30];
sprintf(pid_buf, "--pid=%d", getpid());
char name_buf[512];
name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
int child_pid = fork();
if (!child_pid) {
dup2(2,1); // redirect output to stderr
fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
abort(); /* If gdb failed to start */
} else {
waitpid(child_pid,NULL,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\n", func_b());
}
#包括
#包括
#包括
#包括
无效打印跟踪(){
char pid_buf[30];
sprintf(pid_buf,“--pid=%d”,getpid());
字符名_buf[512];
name_buf[readlink(“/proc/self/exe”,name_buf,511)]=0;
int child_pid=fork();
如果(!child_pid){
dup2(2,1);//将输出重定向到stderr
fprintf(标准输出,“%s pid=%s\n的堆栈跟踪”,名称\u buf,pid\u buf);
execlp(“gdb”、“gdb”、“--batch”、“-n”、“-ex”、“thread”、“-ex”、“bt”、name_buf、pid_buf、NULL);
中止();/*如果gdb启动失败*/
}否则{
waitpid(child_pid,NULL,0);
}
}
我在输出中看到了print_trace的详细信息
还有其他方法吗?中止()不是更简单吗
这样,如果它发生在现场,客户可以向您发送核心文件(我不知道有多少用户参与到我的应用程序中,希望我强制他们进行调试)。如果您使用Linux,标准C库包含一个名为
backtrace
的函数,该函数用帧的返回地址填充数组,另一个函数名为backtrace\u symbols
,它将从backtrace
获取地址并查找相应的函数名。这些记录在中
这些不会显示参数值、源代码行等,它们只应用于调用线程。然而,它们应该比以这种方式运行GDB快得多(也许也不那么古怪),所以它们有自己的位置。您在我的另一个答案(现在已删除)中提到,您也希望看到行号。当从应用程序内部调用gdb时,我不知道如何做到这一点 但我将与大家分享一些方法,可以打印一个简单的stacktrace,其中包含函数名及其各自的行号,而不使用gdb。他们中的大多数人都来自一篇非常好的文章:
- 方法#1:
- 方法#2:(它没有说明行号,但我在方法4中说明)
- 方法#3:(更好的方法2)
- 方法#4:
??:0
,而不是文件名
#include <stdio.h>
#include <signal.h>
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
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\n", sig, ctx.cr2, ctx.eip);
else
printf("Got signal %d\n", 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:\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] #%d %s\n", 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\n", func_b());
}
更新2012/04/28对于最新的linux内核版本,上述
sigaction
签名已过时。我还通过从中获取可执行文件名对其进行了一些改进。以下是一份:
张贴。简言之
因此,您需要一个独立函数,该函数打印一个堆栈跟踪,其中包含gdb堆栈跟踪所具有的所有功能,并且不会终止您的应用程序。答案是在非交互模式下自动启动gdb,以执行您想要的任务
这是通过在子进程中执行gdb,使用fork(),并在应用程序等待完成时编写脚本以显示堆栈跟踪来完成的。这可以在不使用核心转储和不中止应用程序的情况下执行
我相信这就是你要找的,@Vi有什么问题吗?“它做不到什么?”亚当·希姆克列出了一些问题。也许gdb可以用更合适的方式调用。也许我需要一些特殊的东西来支持多线程。也许有办法让它便携,或者有专门的“libstacktrace.so”
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
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\n");
exit(1);
}
printf("Executable name initialised: %s\n",exe);
}
const char* getExecutableName()
{
if (exe == 0)
initialiseExecutableName();
return exe;
}
/* get REG_EIP from ucontext.h */
#define __USE_GNU
#include <ucontext.h>
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\n", sig, info->si_addr,
uc->uc_mcontext.gregs[REG_EIP]);
else
printf("Got signal %d\n", 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:\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] %s\n", 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\n", func_b());
}