Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5771892
  • 博文数量: 675
  • 博客积分: 20301
  • 博客等级: 上将
  • 技术积分: 7671
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 16:15
文章分类

全部博文(675)

文章存档

2012年(1)

2011年(20)

2010年(14)

2009年(63)

2008年(118)

2007年(141)

2006年(318)

分类: C/C++

2008-12-10 19:57:21

之前写了一篇文章使用gcc内建的builtin_return_address来得到函数堆栈情况,今天在代实验的时候,翻了一下之前下的《Linux Programmer's Toolbox》发现有一小节描述“运行时获得栈情况”,使用的是backtrace()函数。

《Linux Programmer's Toolbox》中的例子,使用backtrace得到地址,然后使用addr2line得到符号:
void print_trace(void)
{
    int i;
    const int NTRACE = 32; // Maximum call stack depth.
    void *traceback[NTRACE]; // Will hold instruction pointers

    // Use a shell command to lookup addresses.
    // Ref. addr2line(1), which is part of binutils.
    char cmd[128] = "addr2line -f -e ";
    // Use the symlink /proc/self/exe to get the name of the executable.
    // This prevents us from having to keep a copy of argv[0] somewhere.
    char *prog = cmd + strlen(cmd);
    int r = readlink("/proc/self/exe",prog,sizeof(cmd)-(prog-cmd)-1);
    printf("cmd: %s\n", cmd);
    // ... error checking omitted for brevity
    // Run the shell command - ref. popen(3)
    FILE *fp = popen(cmd, "w");
    // ... error checking omitted for brevity
    int depth = backtrace(traceback, NTRACE);
    for (i = 0; i < depth; i++)
    {
        // Send the addresses to addr2line.
        // addr2line prints to stdout.
        fprintf(fp, "%p\n", traceback[i]);
        // ... error checking omitted for brevity
    }
    fclose(fp);
}


gnu手册中的例子,使用backtrace_symbols()获得符号,但是需要在编译的时候加上-rdynamic选项:


#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>

/* Obtain a backtrace and print it to stdout. */
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 ("%s\n", strings[i]);

  free (strings);
}

/* A dummy function to make the backtrace more interesting. */
void
dummy_function (void)
{
  print_trace ();
}

int
main (void)
{
  dummy_function ();
  return 0;
}


参考中的《善用backtrace解决大问题》中除了采用backtrace_symbols()获得符号外,还使用了dladdr来获得符号:
#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#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 = %d\n", signum);
  fprintf(stderr, "info.si_errno = %d\n", info->si_errno);
  fprintf(stderr, "info.si_code = %d (%s)\n", info->si_code, si_codes[info->si_code]);
  fprintf(stderr, "info.si_addr = %p\n", info->si_addr);
  for(i = 0; i &lt; NGREG; i++)
    fprintf(stderr, "reg[%02d] = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# if defined(SIGSEGV_STACK_IA64)
  ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
  bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
  ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
  bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
# endif

  fprintf(stderr, "Stack trace:\n");
  while(bp && 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 && tmp)
      symname = tmp;
#endif

    fprintf(stderr, "% 2d: %p <%s+%u> (%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 && !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 < sz; ++i)
    fprintf(stderr, "%s\n", strings[i]);
#endif
  fprintf(stderr, "End of stack trace\n");
  exit (-1);
}



参考:
http://blog.chinaunix.net/u/3425/showart_263408.html


阅读(4963) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~