我使用GCC编译器在Linux上工作。当我的c++程序崩溃时,我希望它自动生成一个堆栈跟踪。

我的程序由许多不同的用户运行,它也可以在Linux、Windows和Macintosh上运行(所有版本都使用gcc编译)。

我希望我的程序能够在崩溃时生成堆栈跟踪,下次用户运行它时,它会问他们是否可以将堆栈跟踪发送给我,这样我就可以跟踪到问题。我可以处理发送信息给我,但我不知道如何生成跟踪字符串。什么好主意吗?


当前回答

看:

男人3回溯

And:

#include <exeinfo.h>
int backtrace(void **buffer, int size);

这些是GNU扩展。

其他回答

忘记改变你的源代码,用backtrace()函数或宏来做一些hack -这些只是糟糕的解决方案。

作为一个有效的解决方案,我的建议是:

用"-g"标志编译程序,用于将调试符号嵌入二进制(不用担心,这不会影响您的性能)。 在linux上运行下一个命令:"ulimit -c unlimited" -允许系统进行大崩溃转储。 当你的程序崩溃时,在工作目录中你会看到文件“core”。 gdb -batch -ex "backtrace" ./your_program_exe ./core . exe .执行下一个命令,将backtrace输出到标准输出

这将以人类可读的方式(包含源文件名和行号)打印程序的正确可读回溯。 此外,这种方法将给你自由自动化你的系统: 编写一个简短的脚本,检查进程是否创建了核心转储,然后通过电子邮件向开发人员发送回溯信息,或将其记录到一些日志系统中。

您没有指定您的操作系统,因此这个问题很难回答。如果你使用的是基于gnu libc的系统,你可以使用libc函数backtrace()。

GCC还有两个内置组件可以帮助你,但它们可能也可能不能在你的架构上完全实现,它们是__builtin_frame_address和__builtin_return_address。两者都需要一个即时整数级别(这里的即时是指它不能是一个变量)。如果给定级别的__builtin_frame_address非零,那么获取同一级别的返回地址应该是安全的。

* nix: 你可以拦截SIGSEGV(通常这个信号是在崩溃之前发出的),并将信息保存到一个文件中。(除了可以使用GDB进行调试的核心文件之外)。

赢得: 从msdn检查这个。

你也可以看看谷歌的chrome代码,看看它是如何处理崩溃的。它有一个很好的异常处理机制。

gdb -ex 'set confirm off' -ex r -ex bt -ex q <my-program>

感谢calorticgeek让我注意到addr2line实用程序。

我写了一个快速和肮脏的脚本来处理这里提供的答案的输出: (非常感谢jschmier!)使用addr2line实用程序。

脚本只接受一个参数:包含jschmier实用程序输出的文件名。

对于跟踪的每一层,输出应该打印如下内容:

BACKTRACE:  testExe 0x8A5db6b
FILE:       pathToFile/testExe.C:110
FUNCTION:   testFunction(int) 
   107  
   108           
   109           int* i = 0x0;
  *110           *i = 5;
   111      
   112        }
   113        return i;

代码:

#!/bin/bash

LOGFILE=$1

NUM_SRC_CONTEXT_LINES=3

old_IFS=$IFS  # save the field separator           
IFS=$'\n'     # new field separator, the end of line           

for bt in `cat $LOGFILE | grep '\[bt\]'`; do
   IFS=$old_IFS     # restore default field separator 
   printf '\n'
   EXEC=`echo $bt | cut -d' ' -f3 | cut -d'(' -f1`  
   ADDR=`echo $bt | cut -d'[' -f3 | cut -d']' -f1`
   echo "BACKTRACE:  $EXEC $ADDR"
   A2L=`addr2line -a $ADDR -e $EXEC -pfC`
   #echo "A2L:        $A2L"

   FUNCTION=`echo $A2L | sed 's/\<at\>.*//' | cut -d' ' -f2-99`
   FILE_AND_LINE=`echo $A2L | sed 's/.* at //'`
   echo "FILE:       $FILE_AND_LINE"
   echo "FUNCTION:   $FUNCTION"

   # print offending source code
   SRCFILE=`echo $FILE_AND_LINE | cut -d':' -f1`
   LINENUM=`echo $FILE_AND_LINE | cut -d':' -f2`
   if ([ -f $SRCFILE ]); then
      cat -n $SRCFILE | grep -C $NUM_SRC_CONTEXT_LINES "^ *$LINENUM\>" | sed "s/ $LINENUM/*$LINENUM/"
   else
      echo "File not found: $SRCFILE"
   fi
   IFS=$'\n'     # new field separator, the end of line           
done

IFS=$old_IFS     # restore default field separator