it-swarm-id.com

Cara menghasilkan stacktrace secara otomatis ketika program saya mogok

Saya bekerja di Linux dengan kompiler GCC. Ketika program C++ saya crash saya ingin secara otomatis menghasilkan stacktrace.

Program saya sedang dijalankan oleh banyak pengguna yang berbeda dan juga berjalan di Linux, Windows dan Macintosh (semua versi dikompilasi menggunakan gcc).

Saya ingin program saya dapat menghasilkan jejak stack ketika crash dan saat berikutnya pengguna menjalankannya, ia akan bertanya kepada mereka apakah boleh mengirimkan jejak stack kepada saya sehingga saya dapat melacak masalahnya. Saya dapat menangani pengiriman info kepada saya, tetapi saya tidak tahu cara membuat string jejak. Ada ide?

518
KPexEA

Untuk Linux dan saya percaya Mac OS X, jika Anda menggunakan gcc, atau kompiler apa pun yang menggunakan glibc, Anda dapat menggunakan fungsi backtrace () di execinfo.h untuk mencetak stacktrace dan keluar dengan anggun ketika Anda mendapatkan kesalahan segmentasi. Dokumentasi dapat ditemukan dalam manual libc .

Berikut adalah contoh program yang menginstal handler SIGSEGV dan mencetak stacktrace ke stderr ketika ia melakukan segmentasi. Fungsi baz() di sini menyebabkan segfault yang memicu penangan:

#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.
}

Mengkompilasi dengan -g -rdynamic membuat Anda mendapatkan informasi simbol dalam output Anda, yang dapat digunakan glibc untuk membuat stacktrace yang bagus:

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

Menjalankan ini membuat Anda mendapatkan hasil ini:

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

Ini menunjukkan modul muat, offset, dan fungsi yang berasal dari masing-masing frame dalam tumpukan. Di sini Anda dapat melihat penangan sinyal di atas tumpukan, dan fungsi libc sebelum main selain main, foo, bar, dan baz.

447
Todd Gamblin

Linux

Sementara penggunaan backtrace () berfungsi di execinfo.h untuk mencetak stacktrace dan keluar dengan anggun ketika Anda mendapatkan kesalahan segmentasi telah telah disarankan , saya tidak melihat menyebutkan seluk-beluk yang diperlukan untuk memastikan backtrace yang dihasilkan mengarah ke lokasi sebenarnya dari kesalahan (setidaknya untuk beberapa arsitektur - x86 & ARM).

Dua entri pertama dalam rantai bingkai tumpukan saat Anda masuk ke penangan sinyal berisi alamat pengirim di dalam penangan sinyal dan satu di dalam sigaction () di libc. Bingkai tumpukan fungsi terakhir yang dipanggil sebelum sinyal (yang merupakan lokasi kesalahan) hilang.

Kode

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

Keluaran

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]

Semua bahaya dari memanggil fungsi backtrace () dalam penangan sinyal masih ada dan tidak boleh diabaikan, tetapi saya menemukan fungsionalitas yang saya jelaskan di sini cukup membantu dalam debugging crash.

Penting untuk dicatat bahwa contoh yang saya berikan dikembangkan/diuji di Linux untuk x86. Saya juga berhasil menerapkan ini pada ARM menggunakan uc_mcontext.arm_pc alih-alih uc_mcontext.eip

Berikut tautan ke artikel tempat saya mempelajari detail untuk penerapan ini: http://www.linuxjournal.com/article/6391

115
jschmier

Ini bahkan lebih mudah daripada "man backtrace", ada perpustakaan kecil yang didokumentasikan (spesifik GNU) didistribusikan dengan glibc sebagai libSegFault.so, yang saya percaya ditulis oleh Ulrich Drepper untuk mendukung program catchsegv (lihat "man catchsegv").

Ini memberi kita 3 kemungkinan. Alih-alih menjalankan "program -o hai":

  1. Jalankan di dalam catchsegv:

    $ catchsegv program -o hai
    
  2. Tautkan dengan libSegFault saat runtime:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Tautan dengan libSegFault pada waktu kompilasi:

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

Dalam semua 3 kasus, Anda akan mendapatkan jejak balik yang lebih jelas dengan sedikit optimasi (gcc -O0 atau -O1) dan simbol debugging (gcc -g). Jika tidak, Anda mungkin berakhir dengan tumpukan alamat memori.

Anda juga dapat menangkap lebih banyak sinyal untuk jejak tumpukan dengan sesuatu seperti:

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

Outputnya akan terlihat seperti ini (perhatikan backtrace di bagian bawah):

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

Jika Anda ingin mengetahui detail berdarah, sayangnya sumber terbaik adalah sumber: Lihat http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c dan direktori induknya http://sourceware.org/git/?p=glibc.git;a=tree;f=debug

112
jhclark

Meskipun jawaban yang benar telah disediakan yang menjelaskan cara menggunakan fungsi GNU libc backtrace()1 dan saya memberikan jawaban saya sendiri yang menjelaskan bagaimana memastikan backtrace dari pengarah sinyal menunjuk ke lokasi sebenarnya dari kesalahan2, Saya tidak melihat penyebutan demangling C++ simbol output dari backtrace.

Ketika mendapatkan backtraces dari program C++, output dapat dijalankan melalui c++filt1 untuk menghilangkan simbol-simbol atau dengan menggunakan abi::__cxa_demangle1 langsung.

  • 1 Linux & OS X Perhatikan bahwa c++filt dan __cxa_demangle spesifik untuk GCC
  • 2 Linux

Contoh C++ Linux berikut menggunakan pengendali sinyal yang sama dengan jawaban saya yang lain dan menunjukkan bagaimana c++filt dapat digunakan untuk menghilangkan simbol.

Kode:

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

Output (./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]

Berikut ini dibangun pada penangan sinyal dari jawaban asli saya dan dapat menggantikan penangan sinyal dalam contoh di atas untuk menunjukkan bagaimana abi::__cxa_demangle dapat digunakan untuk menghilangkan simbol. Penangan sinyal ini menghasilkan output demangled yang sama seperti contoh di atas.

Kode:

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

Mungkin layak untuk dilihat di Google Breakpad , sebuah generator dan platform dump dump platform untuk memproses dump.

33
Simon Steele

Anda tidak menentukan sistem operasi Anda, jadi ini sulit dijawab. Jika Anda menggunakan sistem berdasarkan gnu libc, Anda mungkin dapat menggunakan fungsi libc backtrace().

GCC juga memiliki dua bawaan yang dapat membantu Anda, tetapi yang mungkin atau mungkin tidak diterapkan sepenuhnya pada arsitektur Anda, dan itu adalah __builtin_frame_address dan __builtin_return_address. Keduanya menginginkan tingkat integer langsung (dengan segera, maksud saya tidak bisa menjadi variabel). Jika __builtin_frame_address untuk level yang diberikan adalah bukan nol, harus aman untuk mengambil alamat pengirim dari level yang sama.

21
Brian Mitchell

ulimit -c <value> menetapkan batas ukuran file inti pada unix. Secara default, batas ukuran file inti adalah 0. Anda dapat melihat nilai ulimit Anda dengan ulimit -a.

juga, jika Anda menjalankan program Anda dari dalam gdb, itu akan menghentikan program Anda pada "pelanggaran segmentasi" (SIGSEGV, umumnya ketika Anda mengakses sepotong memori yang tidak Anda alokasikan) atau Anda dapat mengatur breakpoints.

ddd dan nemiver adalah ujung depan untuk gdb yang membuatnya bekerja lebih mudah untuk pemula.

12
Joseph

Saya sudah melihat masalah ini untuk sementara waktu.

Dan terkubur dalam di Google Performance Tools README

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

berbicara tentang libunwind

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

Senang mendengar pendapat perpustakaan ini.

Masalahnya dengan -inamik adalah ia dapat meningkatkan ukuran biner secara signifikan dalam beberapa kasus

10
Gregory

Terima kasih untuk antusiasme telah menarik perhatian saya ke utilitas addr2line.

Saya telah menulis skrip cepat dan kotor untuk memproses output dari jawaban yang disediakan di sini : (Terima kasih banyak kepada jschmier!) Menggunakan utilitas addr2line.

Script menerima argumen tunggal: Nama file yang berisi output dari utilitas jschmier.

Keluaran harus mencetak sesuatu seperti berikut ini untuk setiap tingkat jejak:

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;

Kode:

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

Beberapa versi libc berisi fungsi yang berhubungan dengan jejak stack; Anda mungkin dapat menggunakannya:

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

Saya ingat menggunakan libunwind dahulu kala untuk mendapatkan jejak stack, tetapi mungkin tidak didukung pada platform Anda.

10
Stephen Deken

Penting untuk dicatat bahwa setelah Anda membuat file inti, Anda harus menggunakan alat gdb untuk melihatnya. Agar gdb memahami file inti Anda, Anda harus memberi tahu gcc untuk memasukkan biner dengan simbol debugging: untuk melakukan ini, Anda kompilasi dengan flag -g:

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

Kemudian, Anda dapat mengatur "ulimit -c unlimited" untuk membiarkannya membuang inti, atau jalankan saja program Anda di dalam gdb. Saya lebih suka pendekatan kedua: 

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

Saya harap ini membantu. 

10
Benson
ulimit -c unlimited

adalah variabel sistem, yang akan memungkinkan untuk membuat dump inti setelah aplikasi Anda crash. Dalam hal ini jumlah yang tidak terbatas. Cari file yang disebut core di direktori yang sama. Pastikan Anda mengkompilasi kode Anda dengan informasi debug yang diaktifkan!

salam

9
mana

Lupakan tentang mengubah sumber Anda dan lakukan beberapa peretasan dengan fungsi backtrace () atau macroses - ini hanyalah solusi yang buruk.

Sebagai solusi yang berfungsi dengan baik, saya akan menyarankan:

  1. Kompilasi program Anda dengan bendera "-g" untuk menyematkan simbol debug ke biner (jangan khawatir ini tidak akan mempengaruhi kinerja Anda). 
  2. Di linux jalankan perintah berikutnya: "ulimit -c unlimited" - untuk memungkinkan sistem membuat crash crash besar.
  3. Ketika program Anda macet, di direktori kerja Anda akan melihat file "core".
  4. Jalankan perintah berikutnya untuk mencetak backtrace ke stdout: gdb -batch -ex "backtrace" ./your_program_exe ./core

Ini akan mencetak jejak balik yang dapat dibaca yang tepat dari program Anda dengan cara yang dapat dibaca manusia (dengan nama file sumber dan nomor baris) ..__ Selain itu pendekatan ini akan memberi Anda kebebasan untuk mengotomatiskan sistem Anda: Memiliki skrip pendek yang memeriksa apakah proses dibuat dump inti, dan kemudian mengirim jejak balik melalui email ke pengembang, atau login ini ke beberapa sistem logging.

9
loopzilla

Anda dapat menggunakan DeathHandler - kelas C++ kecil yang melakukan segalanya untuk Anda, andal.

8
markhor

menang: Bagaimana dengan StackWalk64 http://msdn.Microsoft.com/en-us/library/ms680650.aspx

8
Roskoto

Melihat:

man 3 backtrace

Dan:

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

Ini adalah ekstensi GNU.

7
Stéphane

Lihat fasilitas Stack Trace di ACE (ADAPTIVE Communication Environment). Ini sudah ditulis untuk mencakup semua platform utama (dan banyak lagi). Pustaka tersebut berlisensi BSD-style sehingga Anda bahkan dapat menyalin/menempelkan kode jika Anda tidak ingin menggunakan ACE.

6
Adam Mitz

Saya dapat membantu dengan versi Linux: fungsi backtrace, backtrace_symbols dan backtrace_symbols_fd dapat digunakan. Lihat halaman manual yang sesuai.

5
terminus

* nix: Anda dapat mencegat SIGSEGV (biasanya sinyal ini dinaikkan sebelum menabrak) dan menyimpan info ke dalam file. (selain file inti yang dapat Anda gunakan untuk debug menggunakan gdb misalnya).

win: Centang ini dari msdn.

Anda juga dapat melihat kode chrome google untuk melihat bagaimana menangani crash. Ini memiliki mekanisme penanganan pengecualian yang bagus.

4
INS

Saya menemukan bahwa solusi @tgamblin tidak lengkap . Tidak dapat menangani dengan stackoverflow . Saya pikir karena secara default signal handler dipanggil dengan stack yang sama dan. Untuk melindungi Anda perlu mendaftarkan tumpukan independen untuk penangan sinyal.

Anda dapat memeriksa ini dengan kode di bawah ini. Secara default pawang gagal. Dengan STACK_OVERFLOW makro yang didefinisikan, tidak apa-apa.

#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

Saya telah melihat banyak jawaban di sini melakukan penangan sinyal dan kemudian keluar . Itulah cara untuk pergi, tapi ingat fakta yang sangat penting: Jika Anda ingin mendapatkan dump inti untuk kesalahan yang dihasilkan, Anda tidak dapat menelepon exit(status). Panggil abort() sebagai gantinya!

3
jard18

Raja baru di kota telah tiba https://github.com/bombela/backward-cpp

1 tajuk untuk ditempatkan dalam kode Anda dan 1 pustaka untuk dipasang.

Secara pribadi saya menyebutnya menggunakan fungsi ini

#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

Saya akan menggunakan kode yang menghasilkan jejak stack untuk kebocoran memori di Visual Leak Detector . Ini hanya bekerja pada Win32.

3
Jim Buck

Sebagai solusi khusus Windows, Anda bisa mendapatkan yang setara dengan jejak tumpukan (dengan informasi jauh lebih banyak) menggunakan Windows Error Reporting . Dengan hanya beberapa entri registri, dapat diatur untuk mengumpulkan dump mode pengguna :

Dimulai dengan Windows Server 2008 dan Windows Vista dengan Paket Layanan 1 (SP1), Windows Error Reporting (WER) dapat dikonfigurasi sehingga dump mode pengguna penuh dikumpulkan dan disimpan secara lokal setelah aplikasi mode pengguna lumpuh. [...]

Fitur ini tidak diaktifkan secara default. Mengaktifkan fitur ini memerlukan hak administrator. Untuk mengaktifkan dan mengkonfigurasi fitur, gunakan nilai registri berikut di bawah kunci HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps.

Anda dapat mengatur entri registri dari penginstal Anda, yang memiliki hak istimewa yang diperlukan.

Membuat dump mode pengguna memiliki keuntungan sebagai berikut dibandingkan menghasilkan jejak stack pada klien:

  • Ini sudah diimplementasikan dalam sistem. Anda dapat menggunakan WER seperti yang diuraikan di atas, atau hubungi MiniDumpWriteDump sendiri, jika Anda memerlukan kontrol yang lebih baik atas jumlah informasi yang akan dibuang. (Pastikan untuk memanggilnya dari proses yang berbeda.)
  • Way lebih lengkap dari pada stack stack. Antara lain dapat berisi variabel lokal, argumen fungsi, tumpukan untuk utas lainnya, modul yang dimuat, dan sebagainya. Jumlah data (dan ukurannya) sangat dapat disesuaikan.
  • Tidak perlu mengirim simbol debug. Ini secara drastis mengurangi ukuran penyebaran Anda, serta membuatnya lebih sulit untuk merekayasa balik aplikasi Anda.
  • Sebagian besar independen dari kompiler yang Anda gunakan. Menggunakan WER bahkan tidak memerlukan kode apa pun. Either way, memiliki cara untuk mendapatkan basis data simbol (PDB) adalah sangat berguna untuk analisis offline. Saya percaya GCC dapat menghasilkan PDB, atau ada alat untuk mengkonversi basis data simbol ke format PDB.

Perhatikan, bahwa WER hanya dapat dipicu oleh kerusakan aplikasi (mis. Sistem mengakhiri proses karena pengecualian yang tidak ditangani). MiniDumpWriteDump dapat dipanggil kapan saja. Ini mungkin membantu jika Anda perlu membuang kondisi saat ini untuk mendiagnosis masalah selain macet.

Bacaan wajib, jika Anda ingin mengevaluasi penerapan dump mini:

2
IInspectable

Selain jawaban di atas, berikut cara Anda membuat Debian Linux OS menghasilkan dump inti 

  1. Buat folder "coredumps" di folder home pengguna
  2. Pergi ke /etc/security/limits.conf. Di bawah garis '', ketik “soft core unlimited”, dan “root soft core unlimited” jika memungkinkan core dumps untuk root, untuk memberikan ruang tak terbatas untuk core dumps. 
  3. CATATAN: "* soft core unlimited" tidak mencakup root, itulah sebabnya root harus ditentukan pada barisnya sendiri.
  4. Untuk memeriksa nilai-nilai ini, logout, masuk kembali, dan ketik "ulimit -a". "Ukuran file inti" harus ditetapkan ke tidak terbatas.
  5. Periksa file .bashrc (pengguna, dan root jika ada) untuk memastikan bahwa ulimit tidak diatur di sana. Jika tidak, nilai di atas akan ditimpa saat startup.
  6. Buka /etc/sysctl.conf. Masukkan yang berikut di bagian bawah: “kernel.core_pattern = /home//coredumps/%e_%t.dump”. (% e akan menjadi nama proses, dan% t akan menjadi waktu sistem)
  7. Keluar dan ketik "sysctl -p" untuk memuat konfigurasi baru Periksa/proc/sys/kernel/core_pattern dan verifikasi bahwa ini cocok dengan apa yang baru saja Anda ketik.
  8. Core dumping dapat diuji dengan menjalankan proses pada command line ("&"), dan kemudian membunuhnya dengan "kill -11". Jika core dumping berhasil, Anda akan melihat “(core dumped)” setelah indikasi kesalahan segmentasi.
2

Di Linux/unix/MacOSX gunakan file inti (Anda dapat mengaktifkannya dengan ulimit atau panggilan sistem yang kompatibel ). Di Windows gunakan pelaporan kesalahan Microsoft (Anda bisa menjadi mitra dan mendapatkan akses ke data kerusakan aplikasi Anda).

1
Kasprzol

Jika Anda masih ingin melakukannya sendiri seperti yang saya lakukan, Anda dapat menautkan bfd dan menghindari menggunakan addr2line seperti yang telah saya lakukan di sini:

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

Ini menghasilkan output:

[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

Saya lupa tentang teknologi "apport" GNOME, tetapi saya tidak tahu banyak tentang cara menggunakannya. Ini digunakan untuk menghasilkan stacktraces dan diagnostik lain untuk diproses dan dapat secara otomatis mengajukan bug. Ini tentu layak untuk dicoba.

0
Joseph

Jika program Anda macet, itu adalah sistem operasi itu sendiri yang menghasilkan informasi dump dump. Jika Anda menggunakan * nix OS, Anda tidak perlu mencegahnya melakukannya (lihat opsi 'coredump' perintah ulimit).

0
nsayer

Sepertinya di salah satu c ++ boost versi terakhir muncul library untuk memberikan apa yang Anda inginkan, mungkin kodenya akan multiplatform . Ini adalah boost :: stacktrace , yang dapat Anda gunakan seperti seperti dalam boost sampel :

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

Di Linux Anda mengkompilasi kode di atas:

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

Contoh backtrace disalin dari boost dokumentasi :

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