栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

从程序内部调用gdb以打印其stacktrace的最佳方法?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从程序内部调用gdb以打印其stacktrace的最佳方法?

您在我的另一个答案(现已删除)中提到,您还希望查看行号。从应用程序内部调用gdb时,我不确定该怎么做。

但是,我将与您分享几种 不使用gdb 即可打印具有函数名称及其相应行号的简单stacktrace的方法。其中大多数来自Linux
Journal的
一篇 非常好的
文章:

  • 方法1:

第一种方法是通过打印和日志消息进行传播,以查明执行路径。在复杂的程序中,即使借助某些特定于GCC的宏,此选项也可能变得繁琐而乏味。例如,考虑一个调试宏,例如:

 #define TRACE_MSG fprintf(stderr, __FUNCTION__         "() [%s:%d] here I amn",     __FILE__, __LINE__)

您可以通过剪切和粘贴该宏在整个程序中快速传播。当您不再需要它时,只需将其定义为无操作即可将其关闭。

  • 方法2 :( 它没有说明行号,但我对方法4进行了说明)

但是,获取堆栈回溯的一种更好的方法是使用glibc提供的某些特定支持功能。关键是backtrace(),它可以将堆栈帧从调用点导航到程序的开头,并提供返回地址数组。然后,您可以使用nm命令查看目标文件,从而将每个地址映射到代码中特定功能的主体。或者,您可以使用一种更简单的方法-
使用backtrace_symbols()。此函数将由backtrace()返回的返回地址列表转换为字符串列表,每个字符串包含函数内的函数名称偏移量和返回地址。字符串列表是从堆空间分配的(就像您调用了malloc()一样),因此一旦完成处理,就应该立即释放它。

我鼓励您阅读它,因为该页面有源代码示例。为了将地址转换为函数名,必须使用
-rdynamic 选项编译应用程序。

  • 方法3 :( 执行方法2的更好方法)

这项技术的一个更有用的应用是将堆栈回溯放入信号处理程序中,并让信号处理程序捕获程序可以接收的所有“不良”信号(SIGSEGV,SIGBUS,SIGILL,SIGFPE等)。这样,如果不幸的是程序崩溃了,并且您没有使用调试器运行它,则可以获取堆栈跟踪并知道错误发生在哪里。此技术还可以用于了解程序停止响应时在哪里循环

此处提供了此技术的实现。

  • 方法4:

我对方法3进行了一点改进,以打印行号。也可以将其复制以使用方法2。

基本上,我跟着一个尖端,它使用
addr2line

将地址转换为文件名和行号。

下面的源代码显示所有本地功能的行号。如果调用了另一个库中的函数,则可能会看到几个

??: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 %pn", sig, ctx.cr2, ctx.eip);  else    printf("Got signal %dn", sig);  trace_size = backtrace(trace, 16);    trace[1] = (void *)ctx.eip;  messages = backtrace_symbols(trace, trace_size);    printf("[bt] Execution path:n");  for (i=1; i<trace_size; ++i)  {    printf("[bt] #%d %sn", i, messages[i]);        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;    return 2*a;}int func_b() {  int res, a = 5;  res = 5 + func_a(a, 't');  return res;}int main() {    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);      printf("%dn", 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
签名已过时。我还通过从此答案中获取可执行文件的名称来对其进行了一些改进。这是最新版本:

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,"ERRORRRRRn");        exit(1);    }    printf("Executable name initialised: %sn",exe);}const char* getExecutableName(){    if (exe == 0)        initialiseExecutableName();    return exe;}#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;    if (sig == SIGSEGV)    printf("Got signal %d, faulty address is %p, ""from %pn", sig, info->si_addr, uc->uc_mcontext.gregs[REG_EIP]);  else    printf("Got signal %dn", sig);  trace_size = backtrace(trace, 16);    trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];  messages = backtrace_symbols(trace, trace_size);    printf("[bt] Execution path:n");  for (i=1; i<trace_size; ++i)  {    printf("[bt] %sn", messages[i]);        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() {    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);      printf("%dn", func_b());}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/371942.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号