it-swarm-tr.com

Programım çöktüğünde otomatik olarak bir yığın oluşturma

Linux üzerinde GCC derleyicisiyle çalışıyorum. C++ programım çöktüğünde, otomatik olarak bir stacktrace oluşturmasını istiyorum.

Programım birçok farklı kullanıcı tarafından çalıştırılıyor ve aynı zamanda Linux, Windows ve Macintosh'ta da çalışıyor (tüm sürümler gcc kullanılarak derlendi).

Programımın çöktüğünde bir yığın izlemesi oluşturabilmesini ve kullanıcının bir sonraki çalıştırışında, sorunu izleyebilmem için yığın izini bana göndermenin uygun olup olmadığını soracak. Bana bilgi gönderme işini halledebilirim ama izleme dizesinin nasıl oluşturulacağını bilmiyorum. Herhangi bir fikir?

518
KPexEA

Linux için ve ben gcc veya glibc kullanan herhangi bir derleyiciyi kullanıyorsanız Mac OS X'e inanıyorum, bir segtratasyon hatası aldığınızda bir stant yazdırmak ve zarafetle çıkmak için backtrace () işlevlerini execinfo.h içinde kullanabilirsiniz. Belgeler libc manual içinde bulunabilir.

İşte SIGSEGV işleyicisini yükleyen ve segfaults olduğunda stderr dizinine baskı yapan bir örnek program. Buradaki baz() işlevi, işleyiciyi tetikleyen segfault'a neden olur:

#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 ile derlemek, çıktılarınızda glibc'nin bir Nice yığını oluşturmak için kullanabileceği sembol bilgisini alır:

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

Bunu çalıştırmak size bu çıktıyı verir:

$ ./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]

Bu, yığın modülündeki her karenin geldiği yük modülünü, ofseti ve işlevi gösterir. Burada sinyal işleyicisini yığının tepesinde görebilirsiniz ve libc main, main, foo ve bar öğelerine ek olarak baz öğesinden önce işlev görür.

447
Todd Gamblin

Linux

Backtrace () kullanımı bir stacktrace yazdırmak ve bir segmentasyon hatası aldığınızda zarafetle çıkmak için execinfo.h işlevini çalıştırırken zaten önerilmiş , sonuçta elde edilen backtrace işaretini sağlamak için gerekli olan karmaşıklıklardan bahsetmiyorum hatanın asıl konumu (en azından bazı mimariler için - x86 ve ARM).

Sinyal işleyicisine girdiğinizde yığın çerçeve zincirindeki ilk iki giriş, sinyal işleyicisinin içinde bir tane ve libc içindeki bir sigaction () içindeki bir dönüş adresini içerir. Sinyalden önce çağrılan en son fonksiyonun yığın çerçevesi (bu hatanın yeridir).

Kod

#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);
}

Çıktı

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]

Bir sinyal işleyicideki backtrace () işlevlerini çağırmanın tüm tehlikeleri hala mevcuttur ve göz ardı edilmemelidir, ancak burada tanımladığım işlevselliği çarpışmalarda hata ayıklama konusunda oldukça yararlı buluyorum.

Sağladığım örneğin Linux için x86 için geliştirildiğini/test edildiğini not etmek önemlidir. Bunu ayrıca ARM 'da uc_mcontext.arm_pc yerine uc_mcontext.eip kullanarak da başarıyla uyguladım. 

İşte bu uygulama için ayrıntıları öğrendiğim makaleye bir link: http://www.linuxjournal.com/article/6391

115
jschmier

Hatta "adam geri gösterimi" nden bile daha kolay, libSegFault.so olarak glibc ile dağıtılan ve GNUUS Drepper tarafından program catchsegv (bkz. "Man catchsegv") programını desteklemek için yazıldığına inanıyorum.

Bu bize 3 olasılık veriyor. "-O hai" programını çalıştırmak yerine:

  1. Catchsegv içinde çalıştır:

    $ catchsegv program -o hai
    
  2. Çalışma zamanında libSegFault ile bağlantı:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Derleme zamanında libSegFault ile bağlantı:

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

Her 3 durumda da, daha az optimizasyonla (gcc -O0 veya -O1) ve hata ayıklama sembolleriyle (gcc -g) daha net geri dönüşler elde edersiniz. Aksi takdirde, bir yığın bellek adresiyle bitebilirsiniz.

Ayrıca yığın izleri için daha fazla sinyal yakalayabilirsiniz:

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

Çıktı, bunun gibi bir şeye benzeyecektir (alttaki arkaya dikkat edin):

*** 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]

Kanlı ayrıntılarını bilmek istiyorsanız, en iyi kaynak ne yazık ki kaynaktır: Bkz. http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c ve üst dizini http://sourceware.org/git/?p=glibc.git;a=tree;f=debug

112
jhclark

GNU libc backtrace() işlevinin nasıl kullanılacağını açıklayan bir doğru cevap sağlanmış olmasına rağmen1 ve bir sinyal işleyiciden hatanın fiili yerini gösteren bir geri izlemenin nasıl sağlanacağını açıklayan kendi cevabımı verdim.2, Backtrace'den çıkarılan demangling C++ sembollerinden söz etmiyorum.

C++ programından backtraces alınırken, çıktı _c++filt_ ile çalıştırılabilir.1 Sembolleri demonte etmek veya abi::__cxa_demangle kullanarak1 direkt olarak.

  • 1 Linux ve OS X _c++filt_ ve ___cxa_demangle_ 'nin GCC'ye özgü olduğunu unutmayın.
  • 2 Linux

Aşağıdaki C++ Linux örneği, benim diğer cevabım ile aynı sinyal işleyicisini kullanır ve sembolleri dağıtmak için _c++filt_ 'nin nasıl kullanılabileceğini gösterir.

Kodu :

_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;
}
_

Çıktı (_./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]
_

Ayrılmış Çıktı (_./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]
_

Aşağıdakiler, orijinal cevabımdan sinyal işleyicisini oluşturur ve yukarıdaki örnekteki sinyal işleyicinin yerini alabilir abi::__cxa_demangle Sembolleri parçalamak için kullanılabilir. Bu sinyal işleyicisi, yukarıdaki örnektekiyle aynı ayrılmış çıkışı üretir.

Kodu :

_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);
}
_
81
jschmier

Çapraz platform çökmesine neden olan bir dökümü üreteci ve dökümü işleme araçlarına Google Breakpad , bakmaya değer.

33
Simon Steele

İşletim sisteminizi belirtmediniz, bu yüzden cevaplaması zor. Gnu libc'ye dayalı bir sistem kullanıyorsanız, libc işlevini backtrace() kullanabilirsiniz.

GCC ayrıca size yardımcı olabilecek, ancak mimarinize tamamen uygulanabilecek veya uygulanmayabilecek iki yapıya sahip ve bunlar __builtin_frame_address ve __builtin_return_address. Her ikisi de hemen bir tamsayı düzeyi ister (hemen, bunun bir değişken olamayacağı anlamına gelir). Belirli bir seviye için __builtin_frame_address sıfır değilse, aynı seviyenin dönüş adresini almak güvenli olmalıdır.

21
Brian Mitchell

ulimit -c <value>, çekirdek dosya boyutu sınırını unix'te ayarlar. Varsayılan olarak, çekirdek dosya boyutu sınırı 0'dır. ulimit değerlerinizi ulimit -a ile görebilirsiniz.

ayrıca, programınızı gdb içinden çalıştırırsanız, programınızı "segmentasyon ihlalleri" (SIGSEGV, genellikle tahsis etmediğiniz bir hafıza parçasına eriştiğinizde) durduracaktır veya sınır değerler koyabilirsiniz.

ddd ve nemiver, acemiler için daha kolay çalışmayı sağlayan gdb için ön uçlardır.

12
Joseph

Bir süredir bu soruna bakıyordum.

Ve Google Performans Araçları README’nin derinliklerine gömüldü

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

libunwind hakkında konuşur

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

Bu kütüphanenin görüşlerini duymak isterim.

-Dynamic ile ilgili sorun, bazı durumlarda ikili büyüklüğünü göreceli olarak önemli ölçüde artırabilmesidir.

10
Gregory

Addr2line yardımcı programına dikkatimi çektiğiniz için enthusiasticgeek'e teşekkür ederiz.

Addr2line yardımcı programını kullanarak sağlanan here :

Komut tek bir argüman kabul eder: Jschmier'in yardımcı programından çıktıyı içeren dosyanın adı.

Çıktı, izin her düzeyi için aşağıdaki gibi bir şey yazdırmalıdır:

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;

Kod:

#!/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 
10
arr_sea

Libc'nin bazı versiyonları, yığın izlerini ele alan fonksiyonlar içerir; onları kullanmak mümkün olabilir:

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

Yığın izlerini almak için uzun zaman önce libunwind kullandığını hatırlıyorum, ancak platformunuzda desteklenmiyor olabilir.

10
Stephen Deken

Bir çekirdek dosyası oluşturduktan sonra gdb aracını kullanmanız gerekeceğini unutmayın. Gdb'nin çekirdek dosyanızı anlayabilmesi için, gcc'ye ikiliyi hata ayıklama sembolleriyle çalmasını söylemeniz gerekir: bunu yapmak için -g bayrağıyla derleyin:

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

Ardından, bir çekirdek dökmek için "ulimit -c sınırsız" ayarını ayarlayabilirsiniz veya sadece programınızı gdb içinde çalıştırın. İkinci yaklaşımı daha çok seviyorum: 

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

Umarım bu yardımcı olur. 

10
Benson
ulimit -c unlimited

bu, uygulamanız çöktükten sonra çekirdek dökümü oluşturmaya izin verecek bir sistem değişkenidir. Bu durumda sınırsız miktarda. Aynı dizinde çekirdek adında bir dosya arayın. Kodunuzu hata ayıklama bilgileri etkinken derlediğinizden emin olun!

saygılarımızla

9
mana

Kaynaklarınızı değiştirmeyi unutun ve backtrace () işlevi veya makroları ile bazı saldırılar yapın - bunlar yalnızca zayıf çözümler.

Düzgün çalışan bir çözüm olarak tavsiye ediyorum:

  1. Programınızı hata ayıklama simgelerini ikiliye katıştırmak için "-g" bayrağıyla derleyin (bunun performansınızı etkilemeyeceğinden endişelenmeyin). 
  2. Linux üzerinde sonraki komut çalıştırın: "ulimit -c sınırsız" - sistemin büyük çökme dökümü yapmasını sağlamak için.
  3. Programınız çöktüğünde, çalışma dizininde "core" dosyasını göreceksiniz.
  4. Backtrace komutunu stdout komutuna yazdırmak için sonraki komutu çalıştırın: gdb -batch -ex "backtrace" ./your_program_exe ./core

Bu, programınızın insan tarafından okunabilir bir şekilde (kaynak dosya adları ve satır numaraları ile) uygun bir şekilde okunabilir geri dönüşü yazdıracaktır. bir çekirdek dökümü yapın ve ardından geliştiricilere e-postayla geri gönderim gönderin veya bunu bazı günlük sistemlerine kaydedin.

9
loopzilla

DeathHandler - small C++ sınıfını, sizin için her şeyi yapan güvenilir bir şekilde kullanabilirsiniz.

8
markhor
8
Roskoto

Bakmak:

adam 3 backtrace

Ve:

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

Bunlar GNU uzantılarıdır.

7
Stéphane

ACE (ADAPTIVE Communication Environment) içindeki Stack Trace tesisine bakın. Tüm büyük platformları (ve daha fazlasını) kapsayacak şekilde yazılmıştır. Kütüphane BSD tarzı lisanslıdır, böylece ACE kullanmak istemiyorsanız kodu kopyalayıp yapıştırabilirsiniz.

6
Adam Mitz

Linux versiyonunda yardımcı olabilirim: backtrace, backtrace_symbols ve backtrace_symbols_fd işlevi kullanılabilir İlgili kılavuz sayfalarına bakın.

5
terminus

* nix: SIGSEGV (genellikle bu sinyal kilitlenmeden önce yükseltilir) ile etkileşime girebilir ve bilgileri bir dosyada tutabilirsiniz. (örneğin, gdb kullanarak hata ayıklamak için kullanabileceğiniz çekirdek dosyanın yanında).

win: this 'ı msdn'den kontrol edin.

Ayrıca çökmelerin nasıl işlendiğini görmek için google’ın krom koduna da bakabilirsiniz. Güzel bir istisna işleme mekanizmasına sahiptir.

4
INS

@Tgamblin çözümünün tamamlanmadığını buldum ... Korumak için sinyal işleyicisine bağımsız bir yığın kaydetmeniz gerekir.

Bunu aşağıdaki kodla kontrol edebilirsiniz. Varsayılan olarak işleyici başarısız olur. Tanımlanmış makro STACK_OVERFLOW ile sorun değil.

#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;
} 
4

Burada bir sinyal işleyicisi uygulayarak ve sonra çıkarken birçok cevap gördüm: .. Bu yol, ama çok önemli bir gerçeği hatırlayın: Oluşturulan hata için çekirdek dökümünü almak istiyorsanız, arayamazsınız. exit(status). Bunun yerine abort() arayın!

3
jard18

Kasabanın yeni kralı geldi https://github.com/bombela/backward-cpp

Kodunuza yerleştirmek için 1 başlık ve 1 kitaplık yükleyin.

Şahsen ben bu işlevi kullanarak onu ararım

#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);
}
3
Roy

Visual Leak Detector içindeki sızıntı belleği için yığın izi oluşturan kodu kullanırdım. Bu olsa da, sadece Win32 üzerinde çalışır.

3
Jim Buck

Yalnızca Windows çözümü olarak, Windows Hata Bildirimi komutunu kullanarak yığın izlemenin eşdeğerini (çok, daha fazla bilgi içeren) alabilirsiniz. Yalnızca birkaç kayıt defteri girişiyle, toplayıcı kullanıcı modu dökümlerini olarak ayarlayabilirsiniz.

Windows Server 2008 ve Windows Vista Service Pack 1'den (SP1) başlayarak, Windows Hata Bildirimi (WER), bir kullanıcı modu uygulaması çöktükten sonra tam kullanıcı modu dökümlerinin yerel olarak toplanması ve depolanması için yapılandırılabilir. [...]

Bu özellik varsayılan olarak etkin değildir. Özelliği etkinleştirmek için yönetici ayrıcalıkları gerekir. Özelliği etkinleştirmek ve yapılandırmak için, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Hata Bildirimi\LocalDumps tuşunun altındaki aşağıdaki kayıt defteri değerlerini kullanın.

Kayıt defteri girişlerini, gerekli ayrıcalıklara sahip yükleyicinizden ayarlayabilirsiniz.

Bir kullanıcı modu dökümü oluşturmak, istemcide yığın izi oluşturmaya göre aşağıdaki avantajlara sahiptir:

  • Sistemde zaten uygulandı. WER'i yukarıda belirtildiği gibi kullanabilir veya boşaltılacak bilgi miktarı üzerinde daha iyi kontrol sahibi olmanız gerekiyorsa MiniDumpWriteDump kendinizi arayabilirsiniz. (Farklı bir işlemden çağırdığınızdan emin olun.)
  • Yol bir yığın izlemeden daha eksiksiz. Diğerleri arasında yerel değişkenler, işlev argümanları, diğer iş parçacıkları için istifler, yüklü modüller vb. İçerebilir. Veri miktarı (ve sonuç olarak boyut) oldukça özelleştirilebilir.
  • Hata ayıklama sembolleri göndermenize gerek yok. Bu, dağıtımınızın boyutunu büyük ölçüde azaltır ve uygulamanızı tersine mühendislik uygulamanızı zorlaştırır.
  • Kullandığınız derleyiciden büyük ölçüde bağımsız. WER kullanmak bile kod gerektirmez. Her iki durumda da, bir sembol veritabanını (PDB) elde etmenin bir yolunu bulmak, çevrimdışı analiz için yararlı olan çok'dur. GCC'nin PDB'leri oluşturabileceğine veya sembol veritabanını PDB formatına dönüştürecek araçlar olduğuna inanıyorum.

WER'in yalnızca bir uygulama çökmesiyle tetiklenebileceğini unutmayın (yani, işlenmeyen bir özel durum nedeniyle bir işlemi sonlandıran sistem). MiniDumpWriteDump herhangi bir zamanda çağrılabilir. Bir çökme dışındaki sorunları teşhis etmek için mevcut durumu boşaltmanız gerekirse, bu yararlı olabilir.

Mini dökümlerin uygulanabilirliğini değerlendirmek istiyorsanız, zorunlu okuma:

2
IInspectable

Yukarıdaki cevaplara ek olarak, burada Debian Linux işletim sisteminin çekirdek dökümü oluşturmasını nasıl sağlarsınız? 

  1. Kullanıcının giriş klasöründe bir "coredumps" klasörü oluşturun
  2. /Etc/security/limits.conf adresine gidin. '' Satırının altına, çekirdek dökümleri için sınırsız alan sağlamak üzere "yumuşak çekirdek sınırsız" ve "kök yumuşak çekirdek sınırsız" yazarak kök için dökümler düzenleyin. 
  3. NOT: “* yumuşak çekirdek sınırsız” kökü kapsamaz, bu nedenle kökün kendi satırında belirtilmesi gerekir.
  4. Bu değerleri kontrol etmek için oturumu kapatın, tekrar giriş yapın ve “ulimit -a” yazın. “Çekirdek dosya boyutu” sınırsız olarak ayarlanmalıdır.
  5. Ulimit'in ayarlanmadığından emin olmak için .bashrc dosyalarını (varsa kullanıcı ve varsa kök) kontrol edin. Aksi halde, yukarıdaki değer başlangıçta üzerine yazılacaktır.
  6. /Etc/sysctl.conf dosyasını açın. Aşağıdan aşağıdakini girin: “kernel.core_pattern = /home//coredumps/%e_%t.dump”. (% e, işlem adı,% t ise sistem zamanı olacaktır)
  7. Çıkın ve yeni konfigürasyonu yüklemek için “sysctl -p” yazın.
  8. Çekirdek boşaltma, komut satırında (“&”) bir işlem yürütülerek ve ardından “kill -11” ile öldürülerek test edilebilir. Çekirdek dökümü başarılı olursa, bölümlendirme hatası göstergesinden sonra “(çekirdeğin dökümü)” göreceksiniz.
2

Linux/unix/MacOSX'te çekirdek dosyalar kullanın (ulimit veya uyumlu sistem çağrısı ile etkinleştirebilirsiniz) Windows'ta Microsoft hata raporlamasını kullanın (bir ortak olabilir ve uygulama kilitlenme verilerinize erişebilirsiniz).

1
Kasprzol

Hala benim yaptığım gibi tek başına gitmek istersen bfd ile bağlantı kurabilir ve burada yaptığım gibi addr2line kullanmaktan kaçınabilirsiniz:

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

Bu çıktı üretir:

[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]
0
Geoffrey

GNOME "apport" teknolojisini unuttum, fakat kullanma hakkında pek bir şey bilmiyorum. İşleme yönelik yığınlar ve diğer teşhisler oluşturmak için kullanılır ve otomatik olarak hataları dosyalayabilir. Kesinlikle kontrol etmeye değer.

0
Joseph

Programınız çökerse, çökme dökümü bilgilerini üreten işletim sisteminin kendisidir. Bir * nix işletim sistemi kullanıyorsanız, bunu yapmasını engellemeniz gerekmez (ulimit komutunun 'coredump' seçeneklerini gözden geçirin).

0
nsayer

Tam olarak ne istediğinizi sağlamak için son c ++ boost sürüm kütüphanesinde göründüğü gibi görünüyor, muhtemelen kod multiplatform olur . Boost :: stacktrace , destek örneğinde olduğu gibi :

#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'ta Yukarıdaki kodu derleyin:

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

Dokümantasyonu artır 'dan kopyalanan örnek backtrace:

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
0
Grzegorz Bazior