Jak automatycznie wygenerować stacktrace po awarii mojego programu

Pracuję na Linuksie z kompilatorem GCC. Gdy mój program C++ ulega awarii, chciałbym, aby automatycznie generował stacktrace.

Mój program jest uruchamiany przez wielu różnych użytkowników i działa również na Linuksie, Windows i Macintosh(wszystkie wersje są kompilowane przy użyciu gcc).

Chciałbym, aby mój program był w stanie wygenerować ślad stosu, gdy ulegnie awarii i następnym razem, gdy użytkownik uruchomi go, zapyta go, czy można wysłać ślad stosu do mnie, abym mógł wyśledzić problem. Mogę poradzić sobie z wysyłaniem informacji do mnie, ale nie wiem, jak wygenerować ciąg śladowy. Jakieś pomysły?

Author: jww, 2008-09-17

28 answers

Dla Linuksa i wierzę, że Mac OS X, Jeśli używasz gcc lub dowolnego kompilatora używającego glibc, możesz użyć funkcji backtrace() w execinfo.h, aby wydrukować stacktrace i wyjść z wdziękiem, gdy pojawi się błąd segmentacji. Dokumentację można znaleźć w podręczniku libc .

Oto przykładowy program, który instaluje funkcję obsługi SIGSEGV i wypisuje stacktrace do stderr, Gdy się segfaultuje. Funkcja baz() powoduje, że segfault uruchamia opiekun:

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

Kompilacja z -g -rdynamic pobiera informacje o symbolach w wyjściu, które glibc może użyć do utworzenia ładnego stosu:

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

Wykonanie tego daje Ci to wyjście:

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

Pokazuje moduł obciążenia, przesunięcie i funkcję, z której pochodzi każda ramka w stosie. Tutaj możesz zobaczyć obsługę sygnału na szczycie stosu, a funkcje libc przed main oprócz main, foo, bar, i baz.

 427
Author: tgamblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-20 10:39:36

Linux

Podczas korzystania z funkcji backtrace () w execinfo.h aby wydrukować stacktrace i wyjść z wdziękiem, gdy pojawi się błąd segmentacji został już zasugerowany , nie widzę wzmianki o zawiłościach niezbędnych do zapewnienia, że wynikająca z niego ścieżka wsteczna wskazuje na rzeczywistą lokalizację błędu(przynajmniej dla niektórych architektur - x86 & ARM).

Pierwsze dwa wpisy w łańcuchu ramek stosu po wejściu do obsługi sygnału zawierają adres zwrotny wewnątrz obsługa sygnału i jedna wewnątrz sigaction () w libc. Ramka stosu ostatniej funkcji wywołanej przed sygnałem (która jest lokalizacją błędu) jest tracona.

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

Wyjście

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]

Wszystkie zagrożenia związane z wywołaniem funkcji backtrace() w funkcji obsługi sygnału nadal istnieją i nie należy ich przeoczyć, ale funkcjonalność, którą tutaj opisałem, jest bardzo pomocna w debugowaniu awarii.

Należy pamiętać, że podany przeze mnie przykład został opracowany/przetestowany na Linuksie dla x86. Udało mi się również zaimplementować to na ARM używając uc_mcontext.arm_pc zamiast uc_mcontext.eip.

Oto link do artykułu, w którym zapoznałem się ze szczegółami tej implementacji: http://www.linuxjournal.com/article/6391

 110
Author: jschmier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22

Jest to nawet łatwiejsze niż "Man backtrace", istnieje trochę udokumentowana biblioteka (specyficzna dla GNU) dystrybuowana z glibc jako libSegFault.so, który według mnie został napisany przez Ulricha Dreppera do obsługi programu catchsegv (patrz "man catchsegv").

To daje nam 3 możliwości. Zamiast uruchomić "program-o hai":
  1. Run within catchsegv:

    $ catchsegv program -o hai
    
  2. Połączenie z libSegFault w czasie wykonywania:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Link z libSegFault przy kompilacji czas:

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

We wszystkich 3 przypadkach otrzymasz wyraźniejsze ścieżki zwrotne z mniejszą optymalizacją (gcc-o0 lub-O1) i symbolami debugowania (gcc-g). W przeciwnym razie możesz po prostu skończyć ze stertą adresów pamięci.

Możesz również złapać więcej sygnałów dla śladów stosu za pomocą czegoś takiego:

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

Wynik będzie wyglądał mniej więcej tak (zwróć uwagę na backtrace na dole):

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

Jeśli chcesz poznać krwawe szczegóły, najlepszym źródłem jest niestety źródło: Zobacz http://sourceware.org/git/?p=glibc.git;a=blob; f=debug / segfault.C i jego Katalog nadrzędny http://sourceware.org/git/?p=glibc.git;a = tree; f=debug

 105
Author: jhclark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-22 03:59:02

Pomimo tego, że dostarczono poprawną odpowiedź opisującą jak korzystać z funkcji GNU libc backtrace() 1 i podałem moją własną odpowiedź , która opisuje, jak zapewnić backtrace z punktów obsługi sygnału do rzeczywistej lokalizacji błędu2, nie widzę żadnej wzmianki o demanglowaniu symboli C++ z backtrace.

Podczas uzyskiwania ścieżek wstecznych z programu C++, wyjście może być uruchamiane przez c++filt1 na demangle symbole lub za pomocą abi::__cxa_demangle1 bezpośrednio.

  • 1 Linux & OS X zauważ, że c++filt i __cxa_demangle są specyficzne dla GCC
  • 2 Linux

Poniższy przykład C++ Linuksa używa tej samej obsługi sygnału, co moja inna odpowiedź i pokazuje, w jaki sposób c++filt można użyć do demangle symboli.

Kod :

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

Wyjście (./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]

Poniższy przykład opiera się na obsłudze sygnału z mojej oryginalnej odpowiedzi i może zastąpić obsługę sygnału w powyższym przykładzie, aby zademonstrować, jak abi::__cxa_demangle może być używany do demangle symbole. Ta funkcja obsługi sygnału wytwarza takie samo demangled wyjście jak w powyższym przykładzie.

Kod :

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);
}
 73
Author: jschmier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22

Może warto przyjrzeć się Google Breakpad , wieloplatformowemu generatorowi zrzutów awaryjnych i narzędziom do przetwarzania zrzutów.

 33
Author: Simon Steele,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-13 14:15:07

Nie podałeś swojego systemu operacyjnego, więc trudno na to odpowiedzieć. Jeśli korzystasz z systemu opartego na gnu libc, możesz skorzystać z funkcji libc backtrace().

GCC ma również dwa wbudowane elementy, które mogą Ci pomóc, ale które mogą lub nie mogą być w pełni zaimplementowane na Twojej architekturze, a są to __builtin_frame_address i __builtin_return_address. Oba chcą natychmiastowego poziomu liczby całkowitej(przez natychmiastowy, mam na myśli, że nie może to być zmienna). Jeśli __builtin_frame_address dla danego poziomu jest niezerowe, powinno być bezpiecznie chwycić adres zwrotny tego samego poziomu.

 21
Author: Brian Mitchell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-25 00:19:03

ulimit -c <value> ustawia limit rozmiaru pliku rdzenia w systemie unix. Domyślnie limit rozmiaru pliku rdzenia wynosi 0. Możesz zobaczyć swoje ulimit wartości za pomocą ulimit -a.

Ponadto, jeśli uruchomisz swój program z poziomu gdb, zatrzyma on twój program na "naruszeniach segmentacji" (SIGSEGV, zazwyczaj gdy uzyskałeś dostęp do pamięci, której nie przydzieliłeś) lub możesz ustawić punkty przerwania.

Ddd i nemiver to front-endy dla gdb, które znacznie ułatwiają pracę z nim początkującym.

 12
Author: user,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-25 00:20:28

Niektóre wersje libc zawierają funkcje, które radzą sobie ze śladami stosu; możesz z nich korzystać:

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

Pamiętam, że używałem libunwind dawno temu, aby uzyskać ślady stosu, ale może to nie być obsługiwane na twojej platformie.

 10
Author: Stephen Deken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 20:51:24

Ważne jest, aby pamiętać, że po wygenerowaniu pliku core będziesz musiał użyć narzędzia gdb, aby go obejrzeć. Aby gdb miało sens dla Twojego pliku core, musisz powiedzieć gcc, aby ustawił plik binarny z symbolami debugowania: aby to zrobić, kompilujesz z flagą-g:

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

Następnie możesz ustawić "ulimit-C unlimited", aby pozwolić mu zrzucić rdzeń, lub po prostu uruchomić program wewnątrz gdb. Bardziej podoba mi się drugie podejście:

$ gdb ./prog
... gdb startup output ...
(gdb) run
... program runs and crashes ...
(gdb) where
... gdb outputs your stack trace ...
Mam nadzieję, że to pomoże.
 10
Author: Benson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 20:57:12

Przyglądałem się temu problemowi od jakiegoś czasu.

I zakopane głęboko w Google Performance Tools README

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

Rozmowy o libunwind

Http://www.nongnu.org/libunwind/

Chciałbym usłyszeć opinie o tej bibliotece.

Problem z-rdynamic polega na tym, że w niektórych przypadkach może stosunkowo znacznie zwiększyć rozmiar binarny

 10
Author: Gregory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 02:55:24

Dziękuję enthusiasticgeek za zwrócenie mojej uwagi na narzędzie addr2line.

Napisałem szybki i brudny skrypt, aby przetworzyć wyjście odpowiedzi podanej tutaj : (Wielkie dzięki dla jschmier!) za pomocą narzędzia addr2line.

Skrypt przyjmuje pojedynczy argument: nazwę pliku zawierającego wyjście z narzędzia jschmier.

Wyjście powinno wydrukować coś w stylu poniżej dla każdego poziomu ślad:

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
Author: arr_sea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22
ulimit -c unlimited

Jest zmienną systemową, która pozwoli na utworzenie zrzutu pamięci po awarii aplikacji. W tym przypadku nieograniczona ilość. Poszukaj pliku o nazwie core w tym samym katalogu. Upewnij się, że skompilowałeś kod z włączoną informacją o debugowaniu!

Pozdrawiam

 9
Author: mana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 20:52:38
 8
Author: Roskoto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:24:21

Możesz użyć DeathHandler - małej klasy C++, która robi wszystko za Ciebie, niezawodnie.

 8
Author: markhor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-01 06:38:05

Zapomnij o zmianie źródeł i zrób kilka hacków z funkcją backtrace() lub makrami - to tylko słabe rozwiązania.

Jako prawidłowo działające rozwiązanie radziłbym:

  1. Skompiluj swój program z flagą "- g " w celu osadzenia symboli debugowania w systemie binarnym(nie martw się, nie wpłynie to na wydajność).
  2. w Linuksie Uruchom następne polecenie: "ulimit-C unlimited" - aby umożliwić systemowi wykonywanie dużych zrzutów awaryjnych.
  3. po awarii programu, w katalogu roboczym zobaczysz plik "core".
  4. Uruchom następne polecenie, aby wydrukować backtrace na stdout: gdb-batch-ex "backtrace"./ your_program_exe ./ core

To wyświetli właściwą czytelną ścieżkę wsteczną Twojego programu w sposób czytelny dla człowieka(z nazwami plików źródłowych i numerami linii). Ponadto takie podejście daje swobodę automatyzacji systemu: mieć krótki skrypt, który sprawdza, czy proces stworzył zrzut pamięci, a następnie wysyła backtracks pocztą e-mail do programistów, lub zaloguj to do jakiegoś logowania system.

 8
Author: loopzilla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-06 12:37:15

Spójrz na:

Man 3 backtrace

I:

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

Są to rozszerzenia GNU.

 7
Author: Stéphane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 20:55:21

Mogę pomóc z wersją Linuksa: można użyć funkcji backtrace, backtrace_symbols i backtrace_symbols_fd. Zobacz odpowiednie strony podręcznika.

 5
Author: terminus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:01:04

Zobacz funkcję śledzenia stosu w ACE (adaptacyjne środowisko komunikacyjne). Jest już napisany, aby objąć wszystkie główne platformy (i nie tylko). Biblioteka jest licencjonowana w stylu BSD, więc możesz nawet skopiować / wkleić kod, jeśli nie chcesz używać ACE.

 5
Author: Adam Mitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 00:07:57

*nix: możesz przechwycić SIGSEGV (Zwykle ten sygnał jest wywoływany przed awarią) i zachować informacje w pliku. (oprócz pliku core, którego można użyć do debugowania np. za pomocą gdb).

Win: Sprawdź to {[2] } z msdn.

Możesz również spojrzeć na kod Google chrome, aby zobaczyć, jak radzi sobie z awariami. Ma ładny mechanizm obsługi wyjątków.

 4
Author: INS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:09:52

Stwierdziłem, że rozwiązanie @tgamblin nie jest kompletne. Nie poradzi sobie ze stackoverflow. Myślę, że ponieważ domyślnie program obsługi sygnału jest wywoływany z tym samym stosem i SIGSEGV jest rzucany dwa razy. Aby chronić musisz zarejestrować niezależny stos dla obsługi sygnału.

Możesz to sprawdzić poniższym kodem. Domyślnie funkcja obsługi nie działa. Przy zdefiniowanym makrze STACK_OVERFLOW wszystko jest w porządku.

#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
Author: Daneel S. Yaitskov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-20 11:59:22

Użyłbym kodu, który generuje ślad stosu dla wycieku pamięci w Visual Leak Detector . Działa to tylko na Win32.

 3
Author: Jim Buck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:00:42

Widziałem wiele odpowiedzi tutaj wykonując obsługę sygnału, a następnie wychodząc. To jest droga do zrobienia, ale pamiętaj o bardzo ważnym fakcie: jeśli chcesz uzyskać zrzut pamięci dla wygenerowanego błędu, nie możesz wywołać exit(status). Zamiast tego zadzwoń abort()!

 3
Author: jard18,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-12 23:23:10

Przybył nowy król w mieście https://github.com/bombela/backward-cpp

1 Nagłówek do umieszczenia w kodzie i 1 biblioteka do zainstalowania.

Osobiście nazywam to za pomocą tej funkcji

#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
Author: Roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-10 22:29:34

Oprócz powyższych odpowiedzi, tutaj jak zrobić Debian Linux OS generować zrzut pamięci

  1. Utwórz folder "coredumps" w folderze domowym użytkownika
  2. Przejdź do /etc / security / limits.conf. Poniżej linii ''wpisz" soft core unlimited " i "root soft core unlimited", jeśli włączysz zrzuty rdzeni dla roota, aby umożliwić nieograniczone miejsce na zrzuty rdzeni.
  3. Uwaga: "* soft core unlimited " nie obejmuje roota, dlatego root musi być podany we własnej linii.
  4. aby sprawdzić te wartości, Wyloguj się, Zaloguj się ponownie i wpisz "ulimit-a". "Rozmiar pliku rdzenia" powinien być ustawiony na nieograniczony.
  5. Sprawdź .pliki bashrc (użytkownik i root, jeśli dotyczy), aby upewnić się, że ulimit nie jest tam ustawiony. W przeciwnym razie powyższa wartość zostanie nadpisana podczas uruchamiania.
  6. Open / etc / sysctl.conf. Na dole wpisz: "kernel.core_pattern = / home/ / coredumps / %e_%t.dump". (%e będzie nazwą procesu, a %t będzie czasem systemowym)
  7. Wyjdź i wpisz "sysctl-p", aby załadować nowa konfiguracja Sprawdź /proc / sys / kernel / core_pattern i sprawdź, czy pasuje do tego, co właśnie wpisałeś.
  8. dumping rdzenia można przetestować uruchamiając proces z linii poleceń (" &"), a następnie zabijając go za pomocą "kill -11 ". Jeśli core dumping się powiedzie, zobaczysz "(core dumping) " po wskazaniu błędu segmentacji.
 2
Author: enthusiasticgeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-08 19:13:58

Na Linuksie / unix/MacOSX używaj plików core (możesz je włączyć za pomocą ulimit lub zgodnego wywołania systemowego ). W systemie Windows użyj raportowania błędów firmy Microsoft(możesz zostać partnerem i uzyskać dostęp do danych awarii aplikacji).

 1
Author: Kasprzol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:16:42

Jeśli twój program ulegnie awarii, to sam system operacyjny generuje informacje o zrzutie awarii. Jeśli używasz *nix OS, po prostu nie musisz mu tego uniemożliwiać(sprawdź opcje "coredump" polecenia ulimit).

 0
Author: nsayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:01:26

Zapomniałem o technologii GNOME "apport", ale nie znam się na jej używaniu. Służy do generowania ścieżek stosowych i innych diagnostycznych do przetwarzania i może automatycznie zapisywać błędy. Na pewno warto to sprawdzić.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 21:24:29

Jako rozwiązanie tylko dla Windows, możesz uzyskać odpowiednik śledzenia stosu (z dużo, dużo więcej informacji) za pomocą Raportowanie błędów systemu Windows. Wystarczy kilka wpisów rejestru, aby ustawić zbierać zrzuty trybu użytkownika :

Począwszy od systemu Windows Server 2008 i Windows Vista z dodatkiem Service Pack 1 (SP1), Raportowanie błędów systemu Windows (Wer) można skonfigurować tak, aby pełne zrzuty trybu użytkownika były gromadzone i przechowywane lokalnie po awarii aplikacji trybu użytkownika. [...]

Ta funkcja nie jest domyślnie włączona. Włączenie tej funkcji wymaga uprawnień administratora. Aby włączyć i skonfigurować tę funkcję, użyj następujących wartości rejestru w kluczu HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps.

Możesz ustawić wpisy rejestru z instalatora, który ma wymagane uprawnienia.

Tworzenie zrzutu trybu użytkownika ma następujące zalety w porównaniu z generowaniem śladu stosu na kliencie:

  • jest już zaimplementowany w systemie. Możesz użyć WER, jak opisano powyżej, lub wywołać Minidump Writedump samodzielnie, jeśli potrzebujesz bardziej drobnoziarnistej kontroli nad ilością informacji do zrzutu. (Upewnij się, aby wywołać go z innego procesu.)
  • sposób bardziej kompletny niż ślad stosu. Między innymi może zawierać zmienne lokalne, argumenty funkcji, stosy dla innych wątków, załadowane moduły i tak dalej. Ilość danych (i w związku z tym rozmiar) jest wysoce konfigurowalny.
  • Nie ma potrzeby wysyłania symboli debugowania. To zarówno drastycznie zmniejsza rozmiar wdrożenia, jak i utrudnia inżynierię wsteczną aplikacji.
  • W dużej mierze niezależny od używanego kompilatora. Używanie WER nie wymaga nawet żadnego kodu. Tak czy inaczej, posiadanie sposobu na uzyskanie bazy danych symboli (PDB) jest Bardzo przydatne do analizy offline. Wierzę, że GCC może albo generować PDB, albo są narzędzia do konwersji symbolu bazy danych do formatu PDB.

Zwróć uwagę, że WER może być wywołane tylko przez awarię aplikacji (tj. system kończący proces z powodu nieobsługiwanego wyjątku). MiniDumpWriteDump może być wywołany w każdej chwili. Może to być pomocne, jeśli musisz zrzucić aktualny stan, aby zdiagnozować problemy inne niż awaria.

Lektura obowiązkowa, jeśli chcesz ocenić przydatność mini zrzutów:

 0
Author: IInspectable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-01 13:00:47