Как автоматически создать трассировки стека, когда моя программа завершает работу


Я работаю на Linux с компилятором GCC. Когда моя программа C++ падает, я хотел бы, чтобы она автоматически генерировала stacktrace.

моя программа запускается на различных пользователей, а также работает на Linux, Windows и Macintosh (все версии компилируются с помощью gcc).

Я хотел бы, чтобы моя программа могла генерировать трассировку стека, когда она падает, и в следующий раз, когда пользователь запускает ее, он спросит их, нормально ли отправлять трассировку стека мне, чтобы я мог отследить проблему. Я могу обрабатывать отправку информации мне, но я не знаю, как создать строку трассировки. Есть идеи?

28 491

28 ответов:

для Linux и я считаю, что Mac OS X, если вы используете gcc или любой компилятор, который использует glibc, вы можете использовать функции backtrace () в execinfo.h чтобы напечатать трассировку стека и выйти изящно, когда вы получаете ошибку сегментации. Документация может быть найдена в руководстве libc.

вот пример программы, которая устанавливается SIGSEGV обработчик и выводит stacktrace в stderr когда это segfaults. Элемент 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 может использовать, чтобы сделать хороший stacktrace:

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

Linux

при использовании функций backtrace () в execinfo.h для печати stacktrace и выхода изящно, когда вы получаете ошибку сегментации имеет уже предложил, Я не вижу упоминания о тонкостях, необходимых для обеспечения того, чтобы полученная обратная трассировка указывала на фактическое местоположение ошибки (по крайней мере, для некоторых архитектур - x86 и ARM).

первые две записи в цепочке стека, когда вы попадаете в обработчик сигнала содержит возвращение адрес внутри обработчика сигнала и один внутри sigaction () в libc. Кадр стека последней функции, вызванной перед сигналом (который является местом ошибки), теряется.

код

#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 () в обработчике сигналов все еще существуют и не должны упускаться из виду, но я нахожу функциональность, которую я описал здесь, весьма полезной при отладке сбоев.

важно отметить, что пример, который я привел, разработан / протестирован на Linux для x86. Я также успешно реализовал это на ARM с помощью uc_mcontext.arm_pc вместо uc_mcontext.eip.

вот ссылка на статью, где я узнал подробности для этой реализации: http://www.linuxjournal.com/article/6391

это даже проще, чем" man backtrace", есть немного документированная библиотека (GNU specific), распространяемая с glibc как libSegFault.so, который, как я считаю, был написан Ульрихом Дреппером для поддержки программы catchsegv (см. "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
    

во всех 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;а=клякса;Ф=отладка/обработка выхода онлайн / оффлайн.с и его родительский каталог http://sourceware.org/git/?p=glibc.git;а=дерево;Ф=отладка

даже если a правильный ответ было предоставлено, что описывает, как использовать GNU libc backtrace() функции1, а я мой собственный ответ это описывает, как обеспечить обратную трассировку от обработчика сигнала указывает на фактическое местоположение ошибки2, я не вижу никакого упоминания о demangling символы C++, выводимые из backtrace.

при получении обратных следов из программы на C++ выводится можно запустить через c++filt1 в demangle символов или с помощью abi::__cxa_demangle1 напрямую.

  • 1 Linux & OS X обратите внимание, что c++filt и __cxa_demangle специфичны ли GCC
  • 2 Linux

в следующем примере c++ Linux используется тот же обработчик сигнала, что и my другого ответа и показано, как c++filt может использоваться для demangle символы.

код:

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]

следующее строится на обработчике сигнала от my оригинальный ответ и может заменить обработчик сигнала в приведенном выше примере, чтобы продемонстрировать, как abi::__cxa_demangle может быть использован для 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++ = '';
            *offset_begin++ = '';
            *offset_end++ = '';

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

может быть стоит посмотреть на Google Breakpad, кросс-платформенный генератор аварийного дампа и инструменты для обработки дампов.

вы не указали свою операционную систему, поэтому на это трудно ответить. Если вы используете систему, основанную на gnu libc, вы можете использовать функцию libc backtrace().

GCC также имеет два встроенных интерфейса, которые могут помочь вам, но которые могут или не могут быть полностью реализованы на вашей архитектуре, и это __builtin_frame_address и __builtin_return_address. Оба из которых хотят непосредственный целочисленный уровень (под непосредственным, я имею в виду, что он не может быть переменной). Если __builtin_frame_address для данного уровня, отличного от нуля, он должен будьте осторожны, чтобы захватить обратный адрес того же уровня.

ulimit -c <value> устанавливает ограничение размера основного файла в unix. По умолчанию ограничение размера основного файла равно 0. Вы можете увидеть ваш ulimit значения ulimit -a.

кроме того, если вы запустите свою программу из gdb, она остановит вашу программу на "нарушениях сегментации" (SIGSEGV, как правило, когда вы обращались к части памяти, которую вы не выделили) или вы можете установить точки останова.

ddd и nemiver являются интерфейсами для gdb, которые делают работу с ним намного проще для новичок.

некоторые версии libc содержат функции, которые имеют дело с трассировками стека; вы можете использовать их:

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

Я помню, с помощью libunwind давным-давно, чтобы получить трассировку стека, но он не поддерживается на вашей платформе.

важно отметить, что как только вы создадите основной файл, вам нужно будет использовать инструмент 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 Performance Tools README

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

говорит о libunwind

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

хотелось бы услышать мнения о этой библиотеке.

проблема с -rdynamic, что он может увеличить размер исполняемого файла относительно значительно в некоторых случаях

спасибо enthusiasticgeek для привлечения моего внимания к утилите 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=

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 
ulimit -c unlimited

- Это системная переменная, которая позволит создать дамп памяти после сбоя приложения. При этом сумма неограниченная. Найдите файл с именем core в том же каталоге. Убедитесь, что вы скомпилировали свой код с включенной отладочной информацией!

в отношении

win: как насчет StackWalk64 http://msdn.microsoft.com/en-us/library/ms680650.aspx

можно использовать DeathHandler - небольшой класс C++, который делает все для вас, надежный.

забудьте об изменении ваших источников и сделать некоторые хаки с функцией backtrace() или макросы - это просто плохие решения.

Как правильно работающее решение, я бы посоветовал:

  1. скомпилируйте свою программу с флагом"- g " для встраивания отладочных символов в двоичный файл (не волнуйтесь, это не повлияет на вашу производительность).
  2. в linux выполните следующую команду: "ulimit-c unlimited" - чтобы система могла создавать большие аварийные дампы.
  3. когда ваша программа разбился, в рабочем каталоге вы увидите файл "core".
  4. выполните следующую команду для печати backtrace в stdout: gdb-batch-ex "backtrace" ./your_program_exe ./основных

Это будет печатать правильный читаемый backtrace вашей программы в удобочитаемом виде (с именами исходных файлов и номерами строк). Кроме того, этот подход даст вам свободу для автоматизации вашей системы: есть короткий скрипт, который проверяет, если процесс создан дамп, а затем отправлять отчеты по электронной почте разработчики, или войти в систему логирования.

посмотреть:

man 3 backtrace

и:

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

Это расширения GNU.

Я могу помочь с версией Linux: можно использовать функцию backtrace, backtrace_symbols и backtrace_symbols_fd. См. соответствующие страницы руководства.

см. средство трассировки стека в ACE (адаптивная коммуникационная среда). Это уже написано, чтобы охватить все основные платформы (и многое другое). Библиотека лицензирована в стиле BSD, поэтому вы можете даже скопировать/вставить код, если вы не хотите использовать ACE.

* nix: вы можете перехватить SIGSEGV (обычно этот сигнал поднимается до аварии) и сохранить информацию в файл. (помимо основного файла, который вы можете использовать для отладки с помощью gdb, например).

win: Проверьте этой от msdn.

вы также можете посмотреть на код chrome от 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.конф. Ниже линии'', тип "мягкий базовый безлимит", и "корень мягкий сердечник безлимитный" при включении дампы ядра для root, разрешить неограниченное пространство для дампов.
  3. Примечание: "* soft core unlimited " не охватывает root, поэтому root должен быть указан в своем собственном линия.
  4. чтобы проверить эти значения, выйдите из системы, войдите обратно и введите "ulimit-a". "Размер основного файла" должен быть установлен на неограниченный.
  5. проверяем .файлы bashrc (пользователь и корень, если применимо), чтобы убедиться, что ulimit не установлен там. В противном случае значение выше будет перезаписано при запуске.
  6. открыть /etc / sysctl.конф. Введите внизу следующее: "ядро.core_pattern = /home//coredumps / %e_%t.dump". (%e будет именем процесса, а %t-системой время)
  7. выйдите и введите "sysctl-p", чтобы загрузить новую конфигурацию Проверьте /proc/sys/kernel / core_pattern и убедитесь, что это соответствует тому, что вы только что ввели.
  8. сброс ядра можно проверить, запустив процесс в командной строке ( " & "), а затем убив его с помощью "kill -11". Если сброс ядра успешен, вы увидите "(Сброс ядра)" после индикации ошибки сегментации.

в Linux / unix / MacOSX используйте основные файлы (вы можете включить их с помощью ulimit или совместимый системный вызов). В Windows используйте Microsoft error reporting (вы можете стать партнером и получить доступ к данным сбоя приложения).

Если ваша программа аварийно завершает работу, это сама операционная система, которая генерирует дампе информации. Если вы используете ОС *nix, вам просто нужно не препятствовать этому (проверьте параметры команды ulimit "coredump").

Я забыл о технологии GNOME "apport", но я мало что знаю об ее использовании. Он используется для создания stacktraces и других диагностик для обработки и может автоматически записывать ошибки. Это, конечно, стоит проверить.

Как решение только для Windows, вы можете получить эквивалент трассировки стека (с гораздо большей информацией) с помощью Отчеты Об Ошибках Windows. С помощью всего нескольких записей реестра, он может быть настроен на сбор дампов пользовательского режима:

начиная с Windows Server 2008 и Windows Vista с пакетом обновления 1 (SP1), Windows Error Reporting (WER) можно настроить так, чтобы полные дампы пользовательского режима собирались и хранились локально после пользовательского режима сбой приложения. [...]

эта функция не включена по умолчанию. Для включения этой функции требуются права администратора. Чтобы включить и настроить эту функцию, используйте следующие значения реестра в разделе раздел HKEY_LOCAL_MACHINE\программное обеспечение\Майкрософт\Windows в\Windows\отчеты LocalDumps ключ.

вы можете установить записи реестра с вашего установщика, который имеет необходимые полномочия.

создание дампа пользовательского режима имеет следующие преимущества перед созданием трассировки стека на клиенте:

  • это уже реализовано в системе. Вы можете использовать WER, как описано выше, или вызвать MiniDumpWriteDump себя, если вам нужен более мелкозернистый контроль над количеством информации для сброса. (Обязательно вызовите его из другого процесса.)
  • путь более полный, чем трассировку стека. Среди прочего, он может содержать локальные переменные, аргументы функции , стеки для других потоков, загруженных модулей, и так далее. Объем данных (и, следовательно, размер) легко настраивается.
  • нет необходимости отправлять символы отладки. Это значительно уменьшает размер вашего развертывания, а также затрудняет обратное проектирование вашего приложения.
  • в значительной степени не зависит от используемого компилятора. Использование WER даже не требует никакого кода. В любом случае, имея способ получить базу данных символов (PDB) является очень полезно для автономный анализ. Я считаю, что GCC может либо генерировать PDB, либо есть инструменты для преобразования базы данных символов в формат PDB.

обратите внимание, что WER может быть вызван только сбоем приложения (т. е. системой, завершающей процесс из-за необработанного исключения). MiniDumpWriteDump можно вызвать в любое время. Это может быть полезно, если вам нужно сбросить текущее состояние для диагностики проблем, отличных от сбоя.

обязательное чтение, Если вы хотите оценить применимость мини-свалки: