当前位置:  开发笔记 > 编程语言 > 正文

我的程序崩溃时如何自动生成堆栈跟踪

如何解决《我的程序崩溃时如何自动生成堆栈跟踪》经验,为你挑选了17个好方法。

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

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

我希望我的程序能够在崩溃时生成堆栈跟踪,并且在用户下次运行它时,它会询问他们是否可以将堆栈跟踪发送给我,以便我可以追踪问题.我可以处理向我发送信息,但我不知道如何生成跟踪字符串.有任何想法吗?



1> Todd Gamblin..:

对于Linux而言我相信Mac OS X,如果您使用的是gcc,或者任何使用glibc的编译器,您可以使用backtrace()函数execinfo.h打印堆栈跟踪并在出现分段错误时正常退出.文档可以在libc手册中找到.

这是一个示例程序,它安装SIGSEGV处理程序并stderr在segfaults时打印堆栈跟踪.baz()这里的函数导致触发处理程序的段错误:

#include 
#include 
#include 
#include 
#include 


void handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

void baz() {
 int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo);       // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler
  foo(); // this will call foo, bar, and baz.  baz segfaults.
}

编译时-g -rdynamic会在输出中获取符号信息,glibc可以使用它来创建一个很好的堆栈跟踪:

$ gcc -g -rdynamic ./test.c -o test

执行此操作可以获得此输出:

$ ./test
Error: signal 11:
./test(handler+0x19)[0x400911]
/lib64/tls/libc.so.6[0x3a9b92e380]
./test(baz+0x14)[0x400962]
./test(bar+0xe)[0x400983]
./test(foo+0xe)[0x400993]
./test(main+0x28)[0x4009bd]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3a9b91c4bb]
./test[0x40086a]

这显示了堆栈中每个帧的加载模块,偏移量和函数.在这里你可以看到在堆栈的顶部信号处理程序,以及libc函数之前main除了main,foo,bar,和baz.


还有/lib/libSegFault.so可以与LD_PRELOAD一起使用.
如果崩溃来自malloc内部会发生什么?难道你不会持有一个锁,然后因为"回溯"试图分配内存而陷入困境吗?
`catchsegv`不是OP所需要的,但对于捕获分段错误和获取所有信息非常棒.
对于ARM,我还必须使用-funwind-tables进行编译.否则我的堆栈深度始终为1(空).
看起来你的backtrace输出中的前两个条目在信号处理程序中包含一个返回地址,并且可能在libc中的`sigaction()`中有一个.虽然你的回溯似乎是正确的,但我有时发现需要额外的步骤来确保故障的实际位置出现在回溯中,因为它可以被内核的`sigaction()`覆盖.
使用退出功能可以防止您获得核心转储并屏蔽退出父级等待呼叫的原因.我通常设置SA_RESETHAND来在运行后取消设置我的sigaction信号处理程序,然后调用raise(sig)来重新提升信号.如果启用了核心转储,那么您将同时获得回溯和核心转储.
@mic_e:真的!但这是一个在应用程序崩溃时调用的处理程序.换句话说,你的过程已经被污染了,你处于非常不安全的境地.你在信号处理程序中调用它,因为你想知道错误发生的位置,并且它在实践中有效.如果它失败了(我没有经常看到这种情况发生),你就没有失去任何东西,因为你的过程已经开始濒临死亡.
如果你打算在其他地方使用上面的代码而不是最终`exit()`的函数,请注意每个`backtrace_symbol_fd(3)`,你需要在完成后释放`array`.
根据`man 7 signal`,你的处理程序调用的每一个函数都不是异步信号安全的,导致未定义的行为.在实践中,您的进程可能会死锁(使用`-lpthread`),或者破坏其内存(没有).
然后,您可以尝试其他一些堆栈跟踪API,例如:DynInst的StackwalkerAPI http://www.dyninst.org/stackwalkerapi或http://www.nongnu.org/libunwind/.通常,如果您希望在malloc内部走出堆栈帧或中断帧,则需要执行特殊操作来处理它.许多工具使用自己的竞技场分配器来避免在这种情况下与libc malloc冲突.
backtrace和backtrace_symbols_fd不是异步信号安全的。您不应在信号处理程序中使用这些功能。
这将在多线程程序中工作吗?
我还必须包括:#include

2> jhclark..:

它比"man backtrace"更容易,有一个小文档库(GNU特定的)与glibc一起分发为libSegFault.so,我相信这是由Ulrich Drepper编写的,用于支持程序catchsegv(参见"man catchsegv").

这给了我们三种可能性.而不是运行"程序-o hai":

    在catchsegv中运行:

    $ catchsegv program -o hai
    

    在运行时与libSegFault链接:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    

    在编译时链接libSegFault:

    $ gcc -g1 -lSegFault -o program program.cc
    $ program -o hai
    

在所有3种情况下,您将获得更清晰的回溯,优化更少(gcc -O0或-O1)和调试符号(gcc -g).否则,您最终可能会得到一堆内存地址.

您还可以通过以下方式捕获更多堆栈跟踪信号:

$ export SEGFAULT_SIGNALS="all"       # "all" signals
$ export SEGFAULT_SIGNALS="bus abrt"  # SIGBUS and SIGABRT

输出看起来像这样(注意底部的回溯):

*** Segmentation fault Register dump:

 EAX: 0000000c   EBX: 00000080   ECX:
00000000   EDX: 0000000c  ESI:
bfdbf080   EDI: 080497e0   EBP:
bfdbee38   ESP: bfdbee20

 EIP: 0805640f   EFLAGS: 00010282

 CS: 0073   DS: 007b   ES: 007b   FS:
0000   GS: 0033   SS: 007b

 Trap: 0000000e   Error: 00000004  
OldMask: 00000000  ESP/signal:
bfdbee20   CR2: 00000024

 FPUCW: ffff037f   FPUSW: ffff0000  
TAG: ffffffff  IPOFF: 00000000  
CSSEL: 0000   DATAOFF: 00000000  
DATASEL: 0000

 ST(0) 0000 0000000000000000   ST(1)
0000 0000000000000000  ST(2) 0000
0000000000000000   ST(3) 0000
0000000000000000  ST(4) 0000
0000000000000000   ST(5) 0000
0000000000000000  ST(6) 0000
0000000000000000   ST(7) 0000
0000000000000000

Backtrace:
/lib/libSegFault.so[0xb7f9e100]
??:0(??)[0xb7fa3400]
/usr/include/c++/4.3/bits/stl_queue.h:226(_ZNSt5queueISsSt5dequeISsSaISsEEE4pushERKSs)[0x805647a]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/player.cpp:73(_ZN6Player5inputESs)[0x805377c]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:159(_ZN6Socket4ReadEv)[0x8050698]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:413(_ZN12ServerSocket4ReadEv)[0x80507ad]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:300(_ZN12ServerSocket4pollEv)[0x8050b44]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/main.cpp:34(main)[0x8049a72]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7d1b775]
/build/buildd/glibc-2.9/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8049801]

如果您想了解血腥细节,最好的来源是遗憾的来源:请参阅http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c及其父目录http://sourceware.org/git/?p=glibc.git;a=tree;f=debug


@crafter:你的意思是"不起作用".您尝试过什么语言/编译器/工具链/分发/硬件?它编译失败了吗?要发现错误?要产生输出?产生难以使用的输出?谢谢您的详细信息,它将帮助每个人.
@StéphaneGourichon@ HansKratz要与libSegFault链接,你必须在编译器标志中添加`-Wl, - no-as-needed`.否则,`ld`确实*不*链接`libSegFault`,因为它识别二进制文件不使用它的任何符号.

3> jschmier..:

Linux的

虽然已经建议在execinfo.h中使用backtrace()函数来打印堆栈跟踪并在出现分段错误时正常退出,但我没有提到确保所产生的回溯指向实际位置所需的复杂性.错误(至少对于某些架构 - x86和ARM).

进入信号处理程序时,堆栈帧链中的前两个条目包含信号处理程序内部的返回地址和libc中的一个sigaction()内部.在信号之前调用的最后一个函数的堆栈帧(即故障的位置)将丢失.

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
 unsigned long     uc_flags;
 struct ucontext   *uc_link;
 stack_t           uc_stack;
 struct sigcontext uc_mcontext;
 sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
 void *             array[50];
 void *             caller_address;
 char **            messages;
 int                size, i;
 sig_ucontext_t *   uc;

 uc = (sig_ucontext_t *)ucontext;

 /* Get the address at the time the signal was raised */
#if defined(__i386__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
#elif defined(__x86_64__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
#else
#error Unsupported architecture. // TODO: Add support for other arch.
#endif

 fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
  sig_num, strsignal(sig_num), info->si_addr, 
  (void *)caller_address);

 size = backtrace(array, 50);

 /* overwrite sigaction with caller's address */
 array[1] = caller_address;

 messages = backtrace_symbols(array, size);

 /* skip first stack frame (points here) */
 for (i = 1; i < size && messages != NULL; ++i)
 {
  fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
 }

 free(messages);

 exit(EXIT_FAILURE);
}

int crash()
{
 char * p = NULL;
 *p = 0;
 return 0;
}

int foo4()
{
 crash();
 return 0;
}

int foo3()
{
 foo4();
 return 0;
}

int foo2()
{
 foo3();
 return 0;
}

int foo1()
{
 foo2();
 return 0;
}

int main(int argc, char ** argv)
{
 struct sigaction sigact;

 sigact.sa_sigaction = crit_err_hdlr;
 sigact.sa_flags = SA_RESTART | SA_SIGINFO;

 if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
 {
  fprintf(stderr, "error setting signal handler for %d (%s)\n",
    SIGSEGV, strsignal(SIGSEGV));

  exit(EXIT_FAILURE);
 }

 foo1();

 exit(EXIT_SUCCESS);
}

产量

signal 11 (Segmentation fault), address is (nil) from 0x8c50
[bt]: (1) ./test(crash+0x24) [0x8c50]
[bt]: (2) ./test(foo4+0x10) [0x8c70]
[bt]: (3) ./test(foo3+0x10) [0x8c8c]
[bt]: (4) ./test(foo2+0x10) [0x8ca8]
[bt]: (5) ./test(foo1+0x10) [0x8cc4]
[bt]: (6) ./test(main+0x74) [0x8d44]
[bt]: (7) /lib/libc.so.6(__libc_start_main+0xa8) [0x40032e44]

在信号处理程序中调用backtrace()函数的所有危险仍然存在,不应该被忽视,但我发现我在这里描述的功能在调试崩溃时非常有用.

值得注意的是,我提供的示例是在Linux for x86上开发/测试的.我也使用uc_mcontext.arm_pc而不是在ARM上成功实现了这个uc_mcontext.eip.

以下是该文章的链接,其中我了解了此实现的详细信息:http: //www.linuxjournal.com/article/6391


在使用GNU ld的系统上,记得使用`-rdynamic'进行编译,以指示链接器将所有符号(不仅是已使用的符号)添加到动态符号表中.这允许`backtrace_symbols()`将地址转换为函数名
对于ARM,我的回溯始终具有深度1,直到我将-funwind-tables选项添加到编译器.
在更新的`glibc``uc_mcontext`版本中,不包含名为`eip`的字段.现在有一个需要索引的数组,`uc_mcontext.gregs [REG_EIP]`是等价的.
这可能为时已晚,但是我们可以以某种方式使用`addr2line`命令来获取发生崩溃的确切行吗?

4> jschmier..:

即使提供了正确的答案,描述了如何使用GNU libc backtrace()函数1,我提供了自己的答案,描述了如何确保从信号处理程序的回溯指向故障的实际位置2,我看不到任何提及从回溯输出的demangling C++符号.

从C++程序获取回溯时,输出可以通过c++filt1运行以解码符号或直接使用1.abi::__cxa_demangle

1 Linux和OS X 请注意c++filt并且__cxa_demangle是特定于GCC的

2 Linux


下面的C++ Linux示例使用与我的其他答案相同的信号处理程序,并演示了如何c++filt使用它来解码符号.

代码:

class foo
{
public:
    foo() { foo1(); }

private:
    void foo1() { foo2(); }
    void foo2() { foo3(); }
    void foo3() { foo4(); }
    void foo4() { crash(); }
    void crash() { char * p = NULL; *p = 0; }
};

int main(int argc, char ** argv)
{
    // Setup signal handler for SIGSEGV
    ...

    foo * f = new foo();
    return 0;
}

输出(./test):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(crash__3foo+0x13) [0x8048e07]
[bt]: (2) ./test(foo4__3foo+0x12) [0x8048dee]
[bt]: (3) ./test(foo3__3foo+0x12) [0x8048dd6]
[bt]: (4) ./test(foo2__3foo+0x12) [0x8048dbe]
[bt]: (5) ./test(foo1__3foo+0x12) [0x8048da6]
[bt]: (6) ./test(__3foo+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

Demangled Output(./test 2>&1 | c++filt):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(foo::crash(void)+0x13) [0x8048e07]
[bt]: (2) ./test(foo::foo4(void)+0x12) [0x8048dee]
[bt]: (3) ./test(foo::foo3(void)+0x12) [0x8048dd6]
[bt]: (4) ./test(foo::foo2(void)+0x12) [0x8048dbe]
[bt]: (5) ./test(foo::foo1(void)+0x12) [0x8048da6]
[bt]: (6) ./test(foo::foo(void)+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

以下内容基于我原始答案中的信号处理程序,可以替换上例中的信号处理程序,以演示如何abi::__cxa_demangle使用它来解码符号.该信号处理程序产生与上例相同的去格式输出.

代码:

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " << caller_address 
              << std::endl << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);    

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i)
    {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // find parantheses and +address offset surrounding mangled name
        for (char *p = messages[i]; *p; ++p)
        {
            if (*p == '(') 
            {
                mangled_name = p; 
            }
            else if (*p == '+') 
            {
                offset_begin = p;
            }
            else if (*p == ')')
            {
                offset_end = p;
                break;
            }
        }

        // if the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end && 
            mangled_name < offset_begin)
        {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // if demangling is successful, output the demangled function name
            if (status == 0)
            {    
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << real_name << "+" << offset_begin << offset_end 
                          << std::endl;

            }
            // otherwise, output the mangled function name
            else
            {
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << mangled_name << "+" << offset_begin << offset_end 
                          << std::endl;
            }
            free(real_name);
        }
        // otherwise, print the whole line
        else
        {
            std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
        }
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}


别忘了#include

5> Simon Steele..:

可能值得一看的是Google Breakpad,一个跨平台的崩溃转储生成器和处理转储的工具.



6> Brian Mitche..:

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

GCC还有两个可以帮助你的内置版本,但是你的架构可能会或者可能不会完全实现,而且那些是__builtin_frame_address__builtin_return_address.两者都想要一个立即的整数级别(通过立即,我的意思是它不能是一个变量).如果__builtin_frame_address给定级别非零,则应该可以安全地获取相同级别的返回地址.



7> 小智..:

ulimit -c 在unix上设置核心文件大小限制.默认情况下,核心文件大小限制为0.您可以使用以下内容查看ulimitulimit -a.

另外,如果你从gdb中运行你的程序,它将停止你的程序"分段违规"(SIGSEGV通常当你访问一块你没有分配的内存时)或者你可以设置断点.

ddd和nemiver是gdb的前端,这使新手更容易使用它.


核心转储比堆栈跟踪更有用,因为您可以在调试器中加载核心转储,并在崩溃时查看整个程序及其数据的状态.

8> arr_sea..:

感谢热心人士将我的注意力吸引到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/\.*//' | 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 



9> Benson..:

重要的是要注意,一旦生成核心文件,您将需要使用gdb工具来查看它.要让gdb理解你的核心文件,你必须告诉gcc使用调试符号来检测二进制文件:为此,使用-g标志进行编译:

$ g++ -g prog.cpp -o prog

然后,你可以设置"ulimit -c unlimited"让它转储核心,或者只是在gdb中运行你的程序.我更喜欢第二种方法:

$ gdb ./prog
... gdb startup output ...
(gdb) run
... program runs and crashes ...
(gdb) where
... gdb outputs your stack trace ...

我希望这有帮助.


您也可以直接从崩溃的程序中调用`gdb`.SIGSEGV,SEGILL,SIGBUS,SIGFPE的设置处理程序,它将调用gdb.详细信息:http://stackoverflow.com/questions/3151779/how-its-better-to-invoke-gdb-from-program-to-print-its-stacktrace优势在于您可以获得美观​​,带注释的回溯bt full`,你也可以获得所有线程的堆栈跟踪.

10> Gregory..:

我一直在看这个问题.

并深深埋藏在Google Performance Tools自述文件中

http://code.google.com/p/google-perftools/source/browse/trunk/README

谈论libunwind

http://www.nongnu.org/libunwind/

很想听听这个图书馆的意见.

-rdynamic的问题在于它可以在某些情况下相对显着地增加二进制文件的大小


在x86/64上,我还没有看到-rdynamic增加二进制大小.添加-g会带来更大的增长.

11> Stephen Deke..:

某些版本的libc包含处理堆栈跟踪的函数; 你可以使用它们:

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

我记得很久以前使用libunwind来获取堆栈跟踪,但是你的平台可能不支持它.



12> markhor..:

您可以使用DeathHandler - 小型C++类,它可以为您提供一切可靠的服务.



13> 小智..:

忘记改变你的来源并使用backtrace()函数或宏来做一些黑客 - 这些只是糟糕的解决方案.

作为一个正常工作的解决方案,我建议:

    使用"-g"标志编译程序,以便将调试符号嵌入到二进制文件中(不要担心这不会影响您的性能).

    在linux上运行下一个命令:"ulimit -c unlimited" - 允许系统进行大型崩溃转储.

    当您的程序崩溃时,在工作目录中您将看到文件"core".

    运行下一个命令将backtrace打印到stdout:gdb -batch -ex"backtrace"./ your_program_exe ./core

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



14> mana..:
ulimit -c unlimited

是一个系统变量,它将允许在应用程序崩溃后创建核心转储.在这种情况下无限量.在同一目录中查找名为core的文件.确保在启用调试信息的情况下编译代码!

问候


用户不要求核心转储.他要求堆栈跟踪.请参阅http://www.delorie.com/gnu/docs/glibc/libc_665.html
你假设他是在Unix上,并使用Bash.
如果你使用tcsh,你必须做`limit coredumpsize unlimited`

15> Stéphane..:

看着:

男人3回溯

和:

#include 
int backtrace(void **buffer, int size);

这些是GNU扩展.



16> Adam Mitz..:

请参阅ACE中的堆栈跟踪工具(自适应通信环境).它已经编写为涵盖所有主要平台(以及更多).该库是BSD风格的许可证,因此如果您不想使用ACE,您甚至可以复制/粘贴代码.



17> terminus..:

我可以帮助Linux版本:可以使用函数backtrace,backtrace_symbols和backtrace_symbols_fd.请参阅相应的手册页.

推荐阅读
谢谢巷议
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有