Question

Je sais qu’il n’ya pas de fonction C standard pour le faire. Je me demandais quelles sont les techniques pour cela sur Windows et * nix? (Windows XP est mon système d'exploitation le plus important pour le faire maintenant.)

Était-ce utile?

La solution

Nous l'avons utilisé pour nos projets:

https://www.codeproject.com/kb/threads/stackwalker.aspx

Le code est un peu désordonné à mon humble avis, mais cela fonctionne bien. Windows uniquement.

Autres conseils

Il y a backtrace () et backtrace_symbols ():

À partir de la page de manuel:

     #include <execinfo.h>
     #include <stdio.h>
     ...
     void* callstack[128];
     int i, frames = backtrace(callstack, 128);
     char** strs = backtrace_symbols(callstack, frames);
     for (i = 0; i < frames; ++i) {
         printf("%s\n", strs[i]);
     }
     free(strs);
     ...

Une façon de l'utiliser de manière plus pratique / POO consiste à enregistrer le résultat de backtrace_symbols () dans un constructeur de classe d'exceptions. Ainsi, chaque fois que vous lancez ce type d'exception, vous avez la trace de la pile. Ensuite, fournissez simplement une fonction pour l’imprimer. Par exemple:


class MyException : public std::exception {

    char ** strs;
    MyException( const std::string & message ) {
         int i, frames = backtrace(callstack, 128);
         strs = backtrace_symbols(callstack, frames);
    }

    void printStackTrace() {
        for (i = 0; i 

...


try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

Ta da!

Remarque: l'activation des indicateurs d'optimisation peut rendre la trace de pile résultante inexacte. Idéalement, on utiliserait cette fonctionnalité avec les indicateurs de débogage activés et les indicateurs d'optimisation désactivés.

Pour Windows, vérifiez l’API StackWalk64 () (également sous Windows 32 bits). Pour UNIX, vous devez utiliser la méthode native du système d’exploitation, ou vous référer à la fonction backtrace () de glibc, si disponible.

Notez cependant que prendre un Stacktrace en code natif est rarement une bonne idée - non pas parce que cela n’est pas possible, mais parce que vous essayez habituellement de réaliser la mauvaise chose.

La plupart du temps, les gens essaient de créer une pile, par exemple dans des circonstances exceptionnelles, comme lorsqu'une exception est capturée, une assertion échoue ou, pire ou pire, lorsque vous obtenez une "exception" fatale. ou signaler comme une violation de segmentation.

En ce qui concerne le dernier problème, la plupart des API exigent que vous allouiez explicitement de la mémoire ou que vous puissiez le faire en interne. Cela pourrait aggraver encore les choses dans l'état de fragilité dans lequel votre programme se trouve peut-être. Par exemple, le rapport d'incident (ou coredump) ne reflétera pas la cause réelle du problème, mais votre tentative infructueuse de le gérer).

Je suppose que vous essayez de résoudre ce problème de gestion des erreurs fatales, comme la plupart des gens semblent l’essayer lorsqu’il s’agit d’obtenir un stacktrace. Si tel est le cas, je m'appuierais sur le débogueur (pendant le développement) et laisserais le processus se transformer en production (ou mini-vidage sur les fenêtres). Avec une gestion des symboles appropriée, vous ne devriez pas avoir de difficulté à déterminer l’instruction qui cause l’instruction post-mortem.

Il n’existe aucun moyen indépendant de la plate-forme de le faire.

La chose la plus proche que vous puissiez faire est d’exécuter le code sans optimisation. De cette façon, vous pouvez vous connecter au processus (à l'aide du débogueur Visual C ++ ou de GDB) et obtenir une trace de pile utilisable.

Pour Windows, CaptureStackBackTrace () est également une option, qui nécessite moins de code de préparation de la part de l'utilisateur que StackWalk64 () . (En outre, pour un scénario similaire que j'ai eu, CaptureStackBackTrace () a fini par fonctionner mieux (plus fiable) que StackWalk64 () .)

Vous devriez utiliser la bibliothèque de déroulement .

unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;

while (unw_step(&cursor) > 0) {
  unw_get_reg(&cursor, UNW_REG_IP, &ip);
  unw_get_reg(&cursor, UNW_REG_SP, &sp);
  if (ctr >= 10) break;
  a[ctr++] = ip;
}

Votre approche fonctionnerait également correctement à moins que vous ne passiez un appel depuis une bibliothèque partagée.

Vous pouvez utiliser la commande addr2line sous Linux pour obtenir le numéro de fonction / ligne source du PC correspondant.

Solaris dispose de la commande pstack , qui a également été copiée sous Linux.

Puis-je vous indiquer mon article? Ce ne sont que quelques lignes de code.

Débogage post mortem

Bien que je rencontre actuellement des problèmes avec la implémentation x64 de cette .

Depuis quelques années, j'utilise la bibliothèque libbacktrace de Ian Lance Taylor. C'est beaucoup plus propre que les fonctions de la bibliothèque GNU C qui nécessitent l'exportation de tous les symboles. Il fournit plus d’utilité pour la génération de backtraces que libunwind. Enfin, ASLR n’est pas vaincu, contrairement aux approches nécessitant des outils externes tels que addr2line .

Libbacktrace faisait initialement partie de la distribution GCC, mais il est maintenant rendu disponible par l'auteur en tant que bibliothèque autonome sous une licence BSD:

https://github.com/ianlancetaylor/libbacktrace

Au moment de la rédaction de ce document, je n’utiliserais rien d’autre à moins d’avoir à générer des traces sur une plateforme non prise en charge par libbacktrace.

Vous pouvez le faire en faisant avancer la pile en arrière. En réalité, cependant, il est souvent plus facile d’ajouter un identifiant à une pile d’appels au début de chaque fonction et de l’afficher à la fin, puis de parcourir le contenu imprimé. C'est un peu un PITA, mais cela fonctionne bien et vous fera gagner du temps à la fin.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top