Comment générer automatiquement un stacktrace lorsque mon programme se bloque

Je travaille sur Linux avec le compilateur GCC. Lorsque mon programme C++ se bloque, je voudrais qu'il génère automatiquement un stacktrace.

Mon programme est exécuté par de nombreux utilisateurs différents et il fonctionne également sur Linux, Windows et Macintosh (toutes les versions sont compilées en utilisant gcc).

Je voudrais que mon programme puisse générer une trace de pile quand il se bloque et la prochaine fois que l'utilisateur l'exécute, il leur demandera s'il est correct de m'envoyer la trace de pile afin que je puisse traquer le problème. Je peux gérer l'envoi des informations mais je ne sais pas comment générer la chaîne de trace. Des idées?

491
demandé sur jww 2008-09-17 00:58:35

28 réponses

Pour Linux et je crois Mac OS X, si vous utilisez gcc, ou tout compilateur qui utilise glibc, vous pouvez utiliser les fonctions backtrace () dans execinfo.h pour imprimer une stacktrace et quitter gracieusement lorsque vous obtenez une erreur de segmentation. La Documentation se trouve dans le manuel de libc .

Voici un exemple de programme qui installe un gestionnaire SIGSEGV et imprime un stacktrace à stderr quand il segfaults. La fonction baz() provoque ici le segfault qui déclenche le gestionnaire:

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

Compiler avec -g -rdynamic vous obtient des informations de symbole dans votre sortie, que la glibc peut utiliser pour faire une belle stacktrace:

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

L'exécution de ceci vous obtient cette sortie:

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

Cela montre le module de charge, le décalage et la fonction d'où provient chaque image de la pile. Ici, vous pouvez voir le gestionnaire de signaux au-dessus de la pile, et les fonctions libc avant main en plus de main, foo, bar, et baz.

427
répondu tgamblin 2014-01-20 10:39:36

Linux

Alors que l'utilisation des fonctions backtrace () dans execinfo.h pour imprimer un stacktrace et quitter gracieusement quand vous obtenez un défaut de segmentation a déjà été suggéré , Je ne vois aucune mention des subtilités nécessaires pour assurer que le backtrace résultant pointe vers l'emplacement réel du défaut ( au moins pour certaines architectures-x86 & ARM).

Les deux premières entrées de la chaîne de trame de pile lorsque vous entrez dans le gestionnaire de signal contiennent une adresse de retour à l'intérieur de la gestionnaire de signal et un à l'intérieur de sigaction () dans la libc. La trame de pile de la dernière fonction appelée avant le signal (qui est l'emplacement du défaut) est perdue.

Code

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

Sortie

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]

Tous les dangers d'appeler les fonctions backtrace() dans un gestionnaire de signal existent toujours et ne doivent pas être négligés, mais je trouve la fonctionnalité que j'ai décrite ici très utile pour le débogage des plantages.

Il est important de noter que l'exemple que j'ai fourni est développé / testé sur Linux pour x86. J'ai également implémenté cela avec succès sur ARM en utilisant uc_mcontext.arm_pc au lieu de uc_mcontext.eip.

Voici un lien vers l'article où j'ai appris les détails de cette implémentation: http://www.linuxjournal.com/article/6391

110
répondu jschmier 2017-05-23 11:47:22

C'est encore plus facile que "man backtrace", il y a une bibliothèque peu documentée (spécifique à GNU) distribuée avec glibc comme libSegFault.so, qui était je crois a été écrit par Ulrich Drepper pour soutenir le programme catchsegv (voir "homme catchsegv").

Cela nous donne 3 possibilités. Au lieu d'exécuter "program-o hai":

  1. Exécuter dans catchsegv:

    $ catchsegv program -o hai
    
  2. Lien avec libSegFault au moment de l'exécution:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Lien avec libSegFault à la compilation temps:

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

Dans les 3 cas, vous obtiendrez des backtraces plus clairs avec moins d'optimisation (gcc-O0 ou-O1) et de symboles de débogage (gcc-g). Sinon, vous pouvez simplement vous retrouver avec une pile d'adresses mémoire.

Vous pouvez également attraper plus de signaux pour les traces de pile avec quelque chose comme:

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

La sortie ressemblera à ceci (notez le retour en bas):

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

Si vous voulez connaître les détails sanglants, la meilleure source est malheureusement l' source: Voir http://sourceware.org/git/?p=glibc.git;a = blob; f=debug / segfault. C et son répertoire parent http://sourceware.org/git/?p=glibc.git;a = Arbre; f = debug

105
répondu jhclark 2014-11-22 03:59:02

Même si une réponse correcte a été fournie qui décrit comment utiliser la fonction GNU libc backtrace()1 et j'ai fourni ma propre réponse qui décrit comment s'assurer qu'un retour en arrière d'un gestionnaire de signal pointe vers l'emplacement réel de la panne2, Je ne vois aucune mention dedemangling symboles c++ sortie de la backtrace.

Lors de l'obtention de backtraces à partir D'un programme C++, la sortie peut être exécutée c++filt1 pour démangeaisons des symboles ou en utilisant abi::__cxa_demangle1 directement.

  • 1 Linux et OS X notez que c++filt et __cxa_demangle sont spécifiques à GCC
  • 2 Linux

L'exemple C++ Linux suivant utilise le même gestionnaire de signal que mon autre réponse et montre comment c++filt peut être utilisé pour démanguler les symboles.

Code:

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

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

Ce qui suit s'appuie sur le gestionnaire de signal de ma réponse originale et peut remplacer le gestionnaire de signal dans l'exemple ci-dessus pour démontrer comment abi::__cxa_demangle peut être utilisé pour démanguler les symboles. Ce gestionnaire de signal produit la même sortie démangée que l'exemple ci-dessus.

Code:

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
répondu jschmier 2017-05-23 11:47:22

Pourrait être intéressant de regarder Google Breakpad , un générateur de crash dump multi-plateforme et des outils pour traiter les vidages.

33
répondu Simon Steele 2016-07-13 14:15:07

Vous n'avez pas spécifié votre système d'exploitation, il est donc difficile de répondre. Si vous utilisez un système basé sur gnu libc, vous pourrez peut-être utiliser la fonction libc backtrace().

GCC a également deux builtins qui peuvent vous aider, mais qui peuvent ou non être implémentés complètement sur votre architecture, et ceux-ci sont __builtin_frame_address et __builtin_return_address. Les deux veulent un niveau entier immédiat (par immédiat, je veux dire que ce ne peut pas être une variable). Si __builtin_frame_address pour un niveau donné est non nul, il devrait être sûr de saisir le adresse de retour du même niveau.

21
répondu Brian Mitchell 2014-03-25 00:19:03

ulimit -c <value> définit la limite de taille de fichier de base sous unix. Par défaut, la limite de taille de fichier de base est 0. Vous pouvez voir vos valeurs ulimit avec ulimit -a.

En outre, si vous exécutez votre programme à partir de gdb, il arrêtera votre programme sur les "violations de segmentation" (SIGSEGV, généralement lorsque vous accédez à un morceau de mémoire que vous n'avez pas alloué) ou vous pouvez définir des points d'arrêt.

Ddd et nemiver sont des front-ends pour gdb qui rendent le travail avec beaucoup plus facile pour le novice.

12
répondu user 2014-03-25 00:20:28

Certaines versions de libc contiennent des fonctions qui traitent des traces de pile; vous pouvez peut-être les utiliser:

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

Je me souviens avoir utilisé libunwind Il y a longtemps pour obtenir des traces de pile, mais il se peut qu'il ne soit pas supporté sur votre plate-forme.

10
répondu Stephen Deken 2008-09-16 20:51:24

Il est important de noter qu'une fois que vous générez un fichier de base, vous devrez utiliser l'outil gdb pour le regarder. Pour que gdb donne un sens à votre fichier de base, vous devez dire à gcc d'Instrumenter le binaire avec des symboles de débogage: pour ce faire, vous compilez avec l'indicateur-g:

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

Ensuite, vous pouvez soit définir "ulimit-c unlimited" pour le laisser vider un noyau, soit simplement exécuter votre programme dans gdb. J'aime plus la deuxième approche:

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

J'espère que cela aide.

10
répondu Benson 2008-09-16 20:57:12

J'ai regardé ce problème pendant un moment.

Et enterré profondément dans les outils de Performance de Google README

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

Parle de libunwind

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

Aimerait entendre les opinions de cette bibliothèque.

Le problème avec -rdynamic est qu'il peut augmenter la taille du binaire relativement significative, dans certains cas,

10
répondu Gregory 2008-09-18 02:55:24

Merci à enthusiasticgeek d'avoir attiré mon attention sur l'utilitaire addr2line.

J'ai écrit un script rapide et sale pour traiter la sortie de la réponse fournie ici : (un grand merci à jschmier!) en utilisant l'utilitaire addr2line.

Le script accepte un seul argument: le nom du fichier contenant la sortie de l'utilitaire de jschmier.

La sortie doit imprimer quelque chose comme ce qui suit pour chaque niveau du trace:

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;

Code:

#!/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
répondu arr_sea 2017-05-23 11:47:22
ulimit -c unlimited

Est une variable système, qui permettra de créer un vidage de base après que votre application se bloque. Dans ce cas, un montant illimité. Recherchez un fichier appelé noyau dans le même répertoire. Assurez-vous que vous avez compilé votre code avec les informations de débogage activées!

Cordialement

9
répondu mana 2008-09-16 20:52:38
8
répondu Roskoto 2008-09-16 21:24:21

Vous pouvez utiliser DeathHandler - petite Classe C++ qui fait tout pour vous, fiable.

8
répondu markhor 2013-03-01 06:38:05

Oubliez de changer vos sources et faites quelques hacks avec la fonction backtrace() ou macros - ce ne sont que de mauvaises solutions.

Comme une solution qui fonctionne correctement, je conseillerais:

  1. compilez votre programme avec l'indicateur "- g " pour intégrer les symboles de débogage au binaire (ne vous inquiétez pas, cela n'aura pas d'impact sur vos performances).
  2. sous linux, exécutez la commande suivante: "ulimit - c unlimited" - pour permettre au système de faire de gros crash dumps.
  3. Lorsque votre programme s'est écrasé, dans le répertoire de travail vous verrez le fichier "core".
  4. exécutez la commande suivante pour imprimer backtrace à stdout: gdb-batch-ex "backtrace"./ your_program_exe ./ noyau

Cela imprimera la trace arrière lisible correcte de votre programme de manière lisible par l'homme (avec les noms de fichiers source et les numéros de ligne). De plus cette approche vous donnera la liberté d'automatiser votre système: ayez un script court qui vérifie si le processus a créé un vidage de base, puis envoyez des backtraces par e-mail aux développeurs, ou enregistrez-le dans une journalisation système.

8
répondu loopzilla 2016-07-06 12:37:15

Regardez:

Man 3 backtrace

Et:

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

Ce sont des extensions GNU.

7
répondu Stéphane 2008-09-16 20:55:21

Je peux vous aider avec la version Linux: la fonction backtrace, backtrace_symbols et backtrace_symbols_fd peut être utilisée. Voir les pages de manuel correspondantes.

5
répondu terminus 2008-09-16 21:01:04

Voir la fonction Stack Trace dans ACE (ADAPTIVE Communication Environment). C'est déjà écrit pour couvrir toutes les principales plates-formes (et plus). La Bibliothèque est sous licence de style BSD, donc vous pouvez même copier / coller le code si vous ne voulez pas utiliser ACE.

5
répondu Adam Mitz 2008-09-17 00:07:57

* nix: vous pouvez intercepter SIGSEGV (habituellement ce signal est déclenché avant de planter) et conserver les informations dans un fichier. (outre le fichier de base que vous pouvez utiliser pour déboguer en utilisant gdb par exemple).

Gagner: Vérifiez CE depuis msdn.

Vous pouvez également regarder le code chrome de google pour voir comment il gère les plantages. Il a un bon mécanisme de gestion des exceptions.

4
répondu INS 2008-09-16 21:09:52

J'ai trouvé que la solution @tgamblin n'est pas complète. Il ne peut pas gérer avec stackoverflow. Je pense que parce que par défaut le gestionnaire de signal est appelé avec la même pile et SIGSEGV est lancé deux fois. Pour vous protéger, vous devez enregistrer une pile indépendante pour le gestionnaire de signaux.

Vous pouvez vérifier cela avec le code ci-dessous. Par défaut, le gestionnaire ne. Avec la macro définie STACK_OVERFLOW, tout va bien.

#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
répondu Daneel S. Yaitskov 2014-03-20 11:59:22

J'utiliserais le code qui génère une trace de pile pour la mémoire divulguée dans Visual Leak Detector . Cela ne fonctionne que sur Win32, cependant.

3
répondu Jim Buck 2008-09-16 21:00:42

J'ai vu beaucoup de réponses ici effectuer un gestionnaire de signal puis quitter. C'est la voie à suivre, mais rappelez-vous un fait très important: si vous voulez obtenir le vidage de base pour l'erreur générée, vous ne pouvez pas appeler exit(status). Appelez abort() à la place!

3
répondu jard18 2013-12-12 23:23:10

Le nouveau roi en ville est arrivé https://github.com/bombela/backward-cpp

1 en-tête à placer dans votre code et 1 bibliothèque à installer.

Personnellement, je l'appelle en utilisant cette fonction

#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
répondu Roy 2018-01-10 22:29:34

En plus des réponses ci-dessus, voici comment faire en sorte que Debian Linux OS génère core dump

  1. Créez un dossier "coredumps" dans le dossier personnel de l'utilisateur
  2. Allez dans /etc / security / limits.conf. Sous la ligne'', tapez "soft core unlimited "et" root soft core unlimited " si vous activez les vidages de base pour root, pour permettre un espace illimité pour les vidages de base.
  3. NOTE:" * soft core unlimited " ne couvre pas root, c'est pourquoi root doit être spécifié dans sa propre ligne.
  4. pour vérifier ces valeurs, déconnectez-vous, reconnectez-vous et tapez "ulimit-a". "Taille du fichier de base" doit être définie sur Illimité.
  5. Vérifiez le.bashrc fichiers (utilisateur, et root le cas échéant) pour vous assurer que ulimit n'est pas défini là. Sinon, la valeur ci-dessus sera écrasée au démarrage.
  6. Ouvrez /etc / sysctl.conf. Entrez ce qui suit en bas: "noyau.core_pattern = / accueil/ / coredumps / %e_%T. dump". (%e sera le nom du processus et % t l'heure système)
  7. quittez et tapez "sysctl-p" pour charger la nouvelle configuration Vérifiez /proc / sys/kernel / core_pattern et vérifiez que cela correspond à ce que vous venez de taper.
  8. core dumping peut être testé en exécutant un processus sur la ligne de commande ( " & "), puis en le tuant avec "kill -11". Si le dumping de base est réussi, vous verrez "(Core dumped) " après l'indication de défaut de segmentation.
2
répondu enthusiasticgeek 2013-05-08 19:13:58

Sous Linux / unix / MacOSX, utilisez les fichiers de base (vous pouvez les activer avec ulimit ou appel système compatible ). Sous Windows, utilisez Microsoft error reporting (vous pouvez devenir partenaire et accéder aux données de plantage de votre application).

1
répondu Kasprzol 2008-09-16 21:16:42

Si votre programme se bloque, c'est le système d'exploitation lui-même qui génère des informations de vidage sur plantage. Si vous utilisez un système d'exploitation *nix, vous devez simplement ne pas l'empêcher de le faire (consultez les options 'coredump' de la commande ulimit).

0
répondu nsayer 2008-09-16 21:01:26

J'ai oublié la technologie GNOME de "apport", mais je ne sais pas grand-chose sur l'utilisation. Il est utilisé pour générer des stacktraces et d'autres diagnostics pour le traitement et peut automatiquement déposer des bogues. C'est certainement la peine de vérifier dans.

0
répondu 2008-09-16 21:24:29

En tant que solution Windows uniquement, vous pouvez obtenir l'équivalent d'une trace de pile (avec beaucoup, beaucoup plus d'informations) en utilisant Windows Error Reporting. Avec seulement quelques entrées de registre, il peut être configuré pour collect vidages en mode utilisateur :

À partir de Windows Server 2008 et Windows Vista avec Service Pack 1 (SP1), les rapports D'erreurs Windows (WER) peuvent être configurés de sorte que les vidages en mode utilisateur complets soient collectés et stockés localement après qu'une application en mode utilisateur se bloque. [...]

Cette fonctionnalité n'est pas activée par défaut. L'activation de la fonctionnalité nécessite des privilèges d'administrateur. Pour activer et configurer la fonctionnalité, utilisez les valeurs de Registre suivantes sous la cléHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps .

Vous pouvez définir les entrées de Registre à partir de votre programme d'installation, qui dispose des privilèges requis.

La création d'un vidage en mode utilisateur présente les avantages suivants par rapport à la génération d'une trace de pile sur le client:

  • Il est déjà implémenté dans le système. Vous pouvez soit utiliser WER comme indiqué ci-dessus, soit appeler MiniDumpWriteDump vous-même, si vous avez besoin d'un contrôle plus précis sur la quantité d'informations à vider. (Assurez-vous d'appeler à partir d'un processus différent.)
  • façon plus complète qu'une trace de pile. Entre autres, il peut contenir des variables locales, des arguments de fonction, des piles pour d'autres threads, des modules chargés, etc. La quantité de données (et par conséquent taille) est hautement personnalisable.
  • Pas besoin d'expédier des symboles de débogage. Cela réduit considérablement la taille de votre déploiement et rend plus difficile l'ingénierie inverse de votre application.
  • , Largement indépendant du compilateur que vous utilisez. L'utilisation de WER ne nécessite même pas de code. De toute façon, avoir un moyen d'obtenir une base de données de symboles (PDB) est très utile pour l'analyse hors ligne. Je crois que GCC peut générer des PDB, ou il existe des outils pour convertir le symbole base de données au format PDB.

Prenez note que WER ne peut être déclenché que par un plantage d'application (c'est-à-dire que le système termine un processus en raison d'une exception non gérée). MiniDumpWriteDump peut être appelé à tout moment. Cela peut être utile si vous devez vider l'état actuel pour diagnostiquer des problèmes autres qu'un plantage.

Lecture Obligatoire, si vous souhaitez évaluer l'applicabilité des mini décharges:

0
répondu IInspectable 2018-03-01 13:00:47