문제

저는 GCC 컴파일러를 사용하여 Linux에서 작업하고 있습니다.내 C++ 프로그램이 충돌할 때 자동으로 스택 추적을 생성하고 싶습니다.

내 프로그램은 다양한 사용자에 의해 실행되고 있으며 Linux, Windows 및 Macintosh에서도 실행됩니다(모든 버전은 다음을 사용하여 컴파일됩니다). gcc).

내 프로그램이 충돌할 때 스택 추적을 생성할 수 있고 다음에 사용자가 이를 실행할 때 문제를 추적할 수 있도록 스택 추적을 나에게 보내도 괜찮은지 묻는 메시지를 표시할 수 있기를 바랍니다.정보 전송을 처리할 수 있지만 추적 문자열을 생성하는 방법을 모르겠습니다.어떤 아이디어가 있나요?

도움이 되었습니까?

해결책

Linux와 Mac OS X의 경우 gcc나 glibc를 사용하는 컴파일러를 사용한다면 다음에서 backtrace() 함수를 사용할 수 있습니다. execinfo.h 분할 오류가 발생하면 스택 추적을 인쇄하고 정상적으로 종료합니다.문서를 찾을 수 있습니다 libc 매뉴얼에서.

다음은 SIGSEGV 핸들러를 실행하고 스택 추적을 인쇄합니다. stderr 세그폴트가 발생했을 때.그만큼 baz() 여기서 함수는 핸들러를 트리거하는 segfault를 발생시킵니다.

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


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.

다른 팁

리눅스

execinfo.h에서 backtrace() 함수를 사용하여 스택 추적을 인쇄하고 분할 오류가 발생할 때 정상적으로 종료하는 것은 이미 제안되었습니다, 결과 역추적이 오류의 실제 위치를 가리키는지 확인하는 데 필요한 복잡성에 대한 언급은 없습니다(적어도 일부 아키텍처의 경우 - x86 및 ARM).

신호 처리기에 들어갈 때 스택 프레임 체인의 처음 두 항목에는 신호 처리기 내부의 반환 주소와 libc의 sigaction() 내부에 하나가 포함되어 있습니다.신호(오류 위치) 이전에 호출된 마지막 함수의 스택 프레임이 손실됩니다.

암호

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

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

/* 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() 함수를 호출할 때 발생하는 모든 위험은 여전히 ​​존재하므로 간과해서는 안 됩니다. 그러나 여기서 설명한 기능은 충돌 디버깅에 매우 도움이 됩니다.

내가 제공한 예제는 x86용 Linux에서 개발/테스트되었다는 점에 유의하는 것이 중요합니다.나는 또한 이것을 사용하여 ARM에서 이것을 성공적으로 구현했습니다. uc_mcontext.arm_pc 대신에 uc_mcontext.eip.

다음은 이 구현에 대한 세부 정보를 배운 기사에 대한 링크입니다.http://www.linuxjournal.com/article/6391

"man backtrace"보다 훨씬 쉽습니다. libSegFault.so와 같이 glibc와 함께 배포되는 약간 문서화된 라이브러리(GNU 전용)가 있습니다. 저는 이 라이브러리가 catchsegv 프로그램을 지원하기 위해 Ulrich Drepper에 의해 작성된 것이라고 생각합니다("man catchsegv" 참조).

이는 우리에게 3가지 가능성을 제공합니다."program -o hai"를 실행하는 대신:

  1. catchsegv 내에서 실행:

    $ catchsegv program -o hai
    
  2. 런타임 시 libSegFault와 연결:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. 컴파일 타임에 libSegFault와 연결:

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

세 가지 경우 모두 최적화(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

비록 정답 GNU libc 사용 방법을 설명하는 내용이 제공되었습니다. backtrace() 기능1 그리고 나는 제공했다 내 자신의 대답 신호 처리기의 역추적이 오류의 실제 위치를 가리키는지 확인하는 방법을 설명합니다.2, 언급된 내용이 없습니다. 해체 역추적에서 출력되는 C++ 기호입니다.

C++ 프로그램에서 역추적을 얻을 때 출력은 다음을 통해 실행될 수 있습니다. c++filt1 기호를 분해하거나 다음을 사용하여 abi::__cxa_demangle1 곧장.

  • 1 리눅스 및 OS X참고하세요 c++filt 그리고 __cxa_demangle GCC에만 해당
  • 2 리눅스

다음 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]

망가진 출력 (./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);
}

볼만한 가치가 있겠네요 구글 브레이크패드, 크로스 플랫폼 크래시 덤프 생성기 및 덤프를 처리하는 도구입니다.

운영 체제를 지정하지 않으셨기 때문에 답변이 어렵습니다.gnu libc 기반 시스템을 사용하는 경우 libc 기능을 사용할 수 있습니다. backtrace().

GCC에는 또한 여러분을 도울 수 있지만 여러분의 아키텍처에 완전히 구현될 수도 있고 구현되지 않을 수도 있는 두 가지 내장 기능이 있습니다. __builtin_frame_address 그리고 __builtin_return_address.둘 다 즉시 정수 수준을 원합니다(즉시라는 것은 변수가 될 수 없음을 의미합니다).만약에 __builtin_frame_address 주어진 레벨이 0이 아니기 때문에 동일한 레벨의 반환 주소를 가져오는 것이 안전해야 합니다.

ulimit -c <value> Unix에서 코어 파일 크기 제한을 설정합니다.기본적으로 코어 파일 크기 제한은 0입니다.당신은 당신의 볼 수 있습니다 ulimit 가치 ulimit -a.

또한, gdb 내에서 프로그램을 실행하면 "세그먼트 위반"으로 인해 프로그램이 중단됩니다(SIGSEGV, 일반적으로 할당하지 않은 메모리에 액세스할 때) 또는 중단점을 설정할 수 있습니다.

ddd와 nemiver는 gdb의 프런트엔드이므로 초보자가 훨씬 쉽게 작업할 수 있습니다.

addr2line 유틸리티에 관심을 가져주신 passiongeek에게 감사드립니다.

제공된 답변의 출력을 처리하기 위해 빠르고 더러운 스크립트를 작성했습니다. 여기:(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 

코어 파일을 생성한 후에는 이를 확인하기 위해 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 ...

이게 도움이 되길 바란다.

나는 이 문제를 한동안 살펴보았습니다.

그리고 Google 성능 도구 README에 깊이 묻혀있습니다.

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

libunwind에 대해 이야기합니다

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

이 도서관에 대한 의견을 듣고 싶습니다.

-rdynamic의 문제점은 경우에 따라 바이너리 크기를 상대적으로 크게 늘릴 수 있다는 것입니다.

일부 libc 버전에는 스택 추적을 처리하는 함수가 포함되어 있습니다.당신은 그것을 사용할 수 있습니다 :

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

사용했던 기억이 나네요 libunwind 오래 전에 스택 추적을 얻으려고 노력했지만 플랫폼에서 지원되지 않을 수 있습니다.

소스를 변경하는 것을 잊어버리고 backtrace() 함수나 매크로를 사용하여 몇 가지 해킹을 수행하십시오. 이것은 단지 형편없는 해결책입니다.

제대로 작동하는 솔루션으로서 다음과 같이 조언하고 싶습니다.

  1. 디버그 기호를 바이너리에 포함시키기 위해 "-g" 플래그를 사용하여 프로그램을 컴파일하십시오(성능에 영향을 주지 않으니 걱정하지 마십시오).
  2. Linux에서는 다음 명령을 실행합니다."ulimit -c unlimited" - 시스템이 큰 크래시 덤프를 만들 수 있도록 합니다.
  3. 프로그램이 충돌하면 작업 디렉터리에 "core" 파일이 표시됩니다.
  4. 다음 명령을 실행하여 역추적을 stdout으로 인쇄합니다.gdb -batch -ex "역추적" ./your_program_exe ./core

이렇게 하면 사람이 읽을 수 있는 방식(소스 파일 이름 및 줄 번호 포함)으로 적절하게 읽을 수 있는 프로그램 역추적을 인쇄합니다.게다가 이 접근 방식을 사용하면 시스템을 자유롭게 자동화할 수 있습니다.프로세스가 코어 덤프를 생성했는지 확인한 다음 이메일로 개발자에게 역추적을 보내거나 이를 일부 로깅 시스템에 기록하는 짧은 스크립트가 있습니다.

ulimit -c unlimited

애플리케이션이 충돌한 후 코어 덤프를 생성할 수 있는 시스템 변수입니다.이 경우 금액은 무제한입니다.동일한 디렉토리에서 core라는 파일을 찾으십시오.디버깅 정보를 활성화하여 코드를 컴파일했는지 확인하세요!

문안 인사

이기다:StackWalk64는 어떻습니까? http://msdn.microsoft.com/en-us/library/ms680650.aspx

당신이 사용할 수있는 데스핸들러 - 당신을 위해 모든 것을 해 주는 신뢰할 수 있는 작은 C++ 클래스입니다.

보다:

남자 3 역추적

그리고:

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

이것은 GNU 확장입니다.

스택 추적 기능을 참조하세요. 에이스 (적응형 통신 환경).모든 주요 플랫폼(및 그 이상)을 포괄하도록 이미 작성되었습니다.라이브러리는 BSD 스타일 라이센스이므로 ACE를 사용하지 않으려는 경우 코드를 복사/붙여넣을 수도 있습니다.

Linux 버전에 대해 도움을 드릴 수 있습니다.backtrace, backtrace_symbols 및 backtrace_symbols_fd 함수를 사용할 수 있습니다.해당 매뉴얼 페이지를 참조하십시오.

*아니야:가로챌 수 있어 SIGSEGV (보통 이 신호는 충돌 전에 발생합니다.) 정보를 파일에 보관합니다.(예를 들어 gdb를 사용하여 디버깅하는 데 사용할 수 있는 코어 파일 외에)

이기다:확인하다 이것 msdn에서.

또한 Google의 크롬 코드를 보고 충돌을 처리하는 방법을 확인할 수도 있습니다.훌륭한 예외 처리 메커니즘이 있습니다.

@tgamblin 솔루션이 완전하지 않은 것으로 나타났습니다.stackoverflow로는 처리할 수 없습니다.기본적으로 신호 핸들러가 동일한 스택으로 호출되고 Sigsegv가 두 번 던져지기 때문에 생각합니다.보호하려면 신호 처리기에 대한 독립 스택을 등록해야 합니다.

아래 코드로 이를 확인할 수 있습니다.기본적으로 핸들러는 실패합니다.정의된 매크로 STACK_OVERFLOW를 사용하면 괜찮습니다.

#include <iostream>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <cassert>

using namespace std;

//#define STACK_OVERFLOW

#ifdef STACK_OVERFLOW
static char stack_body[64*1024];
static stack_t sigseg_stack;
#endif

static struct sigaction sigseg_handler;

void handler(int sig) {
  cerr << "sig seg fault handler" << endl;
  const int asize = 10;
  void *array[asize];
  size_t size;

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

  // print out all the frames to stderr
  cerr << "stack trace: " << endl;
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  cerr << "resend SIGSEGV to get core dump" << endl;
  signal(sig, SIG_DFL);
  kill(getpid(), sig);
}

void foo() {
  foo();
}

int main(int argc, char **argv) {
#ifdef STACK_OVERFLOW
  sigseg_stack.ss_sp = stack_body;
  sigseg_stack.ss_flags = SS_ONSTACK;
  sigseg_stack.ss_size = sizeof(stack_body);
  assert(!sigaltstack(&sigseg_stack, nullptr));
  sigseg_handler.sa_flags = SA_ONSTACK;
#else
  sigseg_handler.sa_flags = SA_RESTART;  
#endif
  sigseg_handler.sa_handler = &handler;
  assert(!sigaction(SIGSEGV, &sigseg_handler, nullptr));
  cout << "sig action set" << endl;
  foo();
  return 0;
} 

누수된 메모리에 대한 스택 추적을 생성하는 코드를 사용하겠습니다. 시각적 누출 감지기.그러나 이것은 Win32에서만 작동합니다.

여기에서 신호 처리기를 수행한 다음 종료하는 데 대한 많은 답변을 보았습니다.그렇게 해야 합니다. 그러나 매우 중요한 사실을 기억하십시오.생성된 오류에 대한 코어 덤프를 얻으려면 다음을 호출할 수 없습니다. exit(status).부르다 abort() 대신에!

마을의 새로운 왕이 도착했습니다https://github.com/bombela/backward-cpp

코드에 배치할 헤더 1개와 설치할 라이브러리 1개.

개인적으로 나는 이 기능을 사용하여 호출합니다.

#include "backward.hpp"
void stacker() {

using namespace backward;
StackTrace st;


st.load_here(99); //Limit the number of trace depth to 99
st.skip_n_firsts(3);//This will skip some backward internal function from the trace

Printer p;
p.snippet = true;
p.object = true;
p.color = true;
p.address = true;
p.print(st, stderr);
}

위의 답변 외에도 Debian Linux OS에서 코어 덤프를 생성하는 방법은 다음과 같습니다.

  1. 사용자의 홈 폴더에 "coredumps" 폴더를 만듭니다.
  2. /etc/security/limits.conf로 이동합니다.' ' 줄 아래에 "soft core unlimited"를 입력하고 루트에 대해 코어 덤프를 활성화하는 경우 "root Soft core unlimited"를 입력하여 코어 덤프에 무제한 공간을 허용합니다.
  3. 메모:"* 소프트 코어 무제한"에는 루트가 포함되지 않으므로 루트를 자체 줄에 지정해야 합니다.
  4. 이 값을 확인하려면 로그아웃했다가 다시 로그인한 후 "ulimit -a"를 입력하세요."코어 파일 크기"는 무제한으로 설정되어야 합니다.
  5. .bashrc 파일(해당되는 경우 사용자 및 루트)을 확인하여 ulimit가 설정되어 있지 않은지 확인하세요.그렇지 않으면 시작 시 위의 값을 덮어쓰게 됩니다.
  6. /etc/sysctl.conf를 엽니다.하단에 다음을 입력합니다.“kernel.core_pattern = /home//coredumps/%e_%t.dump”.(%e는 프로세스 이름이고, %t는 시스템 시간입니다)
  7. 종료 및 "sysctl -p"를 입력하여 새 구성 확인/proc/sys/kernel/core_pattern을로드하고 방금 입력 한 내용과 일치하는지 확인하십시오.
  8. 코어 덤핑은 명령줄(" &")에서 프로세스를 실행한 다음 "kill -11 "을 사용하여 프로세스를 종료하여 테스트할 수 있습니다.코어 덤핑이 성공하면 분할 오류 표시 뒤에 "(core dumped)"가 표시됩니다.

Windows 전용 솔루션으로서 다음을 사용하여 스택 추적과 동등한 정보(훨씬 더 많은 정보 포함)를 얻을 수 있습니다. Windows 오류 보고.몇 가지 레지스트리 항목만으로 다음과 같이 설정할 수 있습니다. 사용자 모드 덤프 수집:

Windows Server 2008 및 Windows Vista SP1(서비스 팩 1)부터 사용자 모드 응용 프로그램이 충돌한 후 전체 사용자 모드 덤프가 로컬로 수집되고 저장되도록 WER(Windows 오류 보고)을 구성할 수 있습니다.[...]

이 기능은 기본적으로 활성화되어 있지 않습니다.이 기능을 활성화하려면 관리자 권한이 필요합니다.기능을 활성화하고 구성하려면 다음 레지스트리 값을 사용하십시오. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows 오류 보고\LocalDumps 열쇠.

필요한 권한이 있는 설치 프로그램에서 레지스트리 항목을 설정할 수 있습니다.

사용자 모드 덤프를 생성하면 클라이언트에서 스택 추적을 생성하는 것보다 다음과 같은 이점이 있습니다.

  • 이미 시스템에 구현되어 있습니다.위에 설명된 대로 WER을 사용하거나 다음을 호출할 수 있습니다. 미니 덤프쓰기 덤프 덤프할 정보의 양을 보다 세밀하게 제어해야 하는 경우.(다른 프로세스에서 호출해야 합니다.)
  • 방법 스택 추적보다 더 완벽합니다.무엇보다도 여기에는 지역 변수, 함수 인수, 다른 스레드에 대한 스택, 로드된 모듈 등이 포함될 수 있습니다.데이터의 양(및 그에 따른 크기)은 고도로 사용자 정의 가능합니다.
  • 디버그 기호를 제공할 필요가 없습니다.이로 인해 배포 크기가 크게 줄어들 뿐만 아니라 애플리케이션을 리버스 엔지니어링하기가 더 어려워집니다.
  • 사용하는 컴파일러와는 거의 독립적입니다.WER을 사용하면 코드가 필요하지 않습니다.어느 쪽이든 기호 데이터베이스(PDB)를 얻는 방법은 다음과 같습니다. 매우 오프라인 분석에 유용합니다.나는 GCC가 PDB를 생성할 수 있거나 기호 데이터베이스를 PDB 형식으로 변환하는 도구가 있다고 생각합니다.

WER은 애플리케이션 충돌(예:처리되지 않은 예외로 인해 시스템이 프로세스를 종료하는 경우) MiniDumpWriteDump 언제든지 호출할 수 있습니다.이는 충돌 이외의 문제를 진단하기 위해 현재 상태를 덤프해야 하는 경우 도움이 될 수 있습니다.

미니 덤프의 적용 가능성을 평가하려는 경우 필수 읽기:

Linux/unix/MacOSX에서는 코어 파일을 사용합니다(ulimit 또는 호환 가능한 시스템 호출).Windows에서는 Microsoft 오류 보고를 사용합니다(파트너가 되어 애플리케이션 충돌 데이터에 액세스할 수 있습니다).

"apport"라는 그놈 기술을 잊어버렸는데, 그것을 사용하는 방법에 대해서는 잘 모릅니다.이는 처리를 위한 스택 추적 및 기타 진단을 생성하는 데 사용되며 자동으로 버그를 신고할 수 있습니다.확실히 확인해 볼 가치가 있습니다.

그것은 당신이 원하는 것을 정확하게 제공하기 위해 마지막 C++ 부스트 버전 라이브러리 중 하나에서 나온 것처럼 보입니다. 아마도 코드는 다중 플랫폼일 것입니다.그것은 부스트::스택트레이스, 다음과 같이 사용할 수 있습니다. 부스트 샘플에서와 같이:

#include <filesystem>
#include <sstream>
#include <fstream>
#include <signal.h>     // ::signal, ::raise
#include <boost/stacktrace.hpp>

const char* backtraceFileName = "./backtraceFile.dump";

void signalHandler(int)
{
    ::signal(SIGSEGV, SIG_DFL);
    ::signal(SIGABRT, SIG_DFL);
    boost::stacktrace::safe_dump_to(backtraceFileName);
    ::raise(SIGABRT);
}

void sendReport()
{
    if (std::filesystem::exists(backtraceFileName))
    {
        std::ifstream file(backtraceFileName);

        auto st = boost::stacktrace::stacktrace::from_dump(file);
        std::ostringstream backtraceStream;
        backtraceStream << st << std::endl;

        // sending the code from st

        file.close();
        std::filesystem::remove(backtraceFileName);
    }
}

int main()
{
    ::signal(SIGSEGV, signalHandler);
    ::signal(SIGABRT, signalHandler);

    sendReport();
    // ... rest of code
}

Linux에서는 위의 코드를 컴파일합니다.

g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace

다음에서 복사된 역추적 예시 부스트 문서화:

0# bar(int) at /path/to/source/file.cpp:70
1# bar(int) at /path/to/source/file.cpp:70
2# bar(int) at /path/to/source/file.cpp:70
3# bar(int) at /path/to/source/file.cpp:70
4# main at /path/to/main.cpp:93
5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
6# _start

내가 그랬던 것처럼 여전히 혼자 가고 싶다면 다음과 연결할 수 있습니다. bfd 그리고 사용을 피하세요 addr2line 내가 여기서 했던 것처럼:

https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c

그러면 다음과 같은 출력이 생성됩니다.

[E]        crash.linux.c:170  | crit_err_hdlr                  | ==== FATAL CRASH (a12-151-g28b12c85f4+1) ====
[E]        crash.linux.c:171  | crit_err_hdlr                  | signal 11 (Segmentation fault), address is (nil)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (0) /home/geoff/Projects/LookingGlass/client/src/main.c:936 (register_key_binds)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (1) /home/geoff/Projects/LookingGlass/client/src/main.c:1069 (run)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (2) /home/geoff/Projects/LookingGlass/client/src/main.c:1314 (main)
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (3) /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f8aa65f809b]
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (4) ./looking-glass-client(_start+0x2a) [0x55c70fc4aeca]
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top