Comment peut-on saisir une trace de stack dans C?

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

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

Il y a backtrace () et backtrace_symbols ():

De la page de manuel:

  #include  #include  ... 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 / OOP consiste à enregistrer le résultat de backtrace_symbols () dans un constructeur de classe d'exception. Ainsi, chaque fois que vous lancez ce type d'exception, vous avez la trace de la stack. Ensuite, il suffit de fournir une fonction pour l’imprimer. Par exemple:

class MyException : public std::exception { char ** strs; MyException( const std::ssortingng & message ) { int i, frames = backtrace(callstack, 128); strs = backtrace_symbols(callstack, frames); } void printStackTrace() { for (i = 0; i 

...


 essayez {
    lancer MyException ("Oops!");
 } catch (MyException e) {
     e.printStackTrace ();
 }

Ta da!

Remarque: l'activation des indicateurs d'optimisation peut rendre la trace de stack résultante imprécise. Idéalement, on pourrait utiliser cette fonctionnalité avec les indicateurs de débogage 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 de l’OS pour le faire, ou utiliser le backtrace de glibc (), si disponible.

Notez cependant que prendre un Stacktrace en code natif est rarement une bonne idée – non pas parce que ce n’est pas possible, mais parce que vous essayez habituellement d’obtenir la mauvaise chose.

La plupart du temps, les gens essaient d’obtenir une stack dans une circonstance exceptionnelle, par exemple lorsqu’une exception est interceptée, qu’une assertion échoue ou – pire et le plus faux – lorsque vous obtenez une “exception” fatale ou un signal comme violation de segmentation.

Compte tenu du dernier problème, la plupart des API nécessitent que vous allouiez explicitement de la mémoire ou que vous procédiez en interne. Si vous le faites dans l’état fragile dans lequel votre programme se trouve actuellement, vous risquez d’empirer les choses. Par exemple, le rapport de panne (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 d’obtenir cette erreur fatale, car la plupart des gens semblent essayer de le faire quand il s’agit d’obtenir une trace de stack. Si oui, je me fierais au débogueur (pendant le développement) et laisserais le processus se compacter en production (ou mini-vidage sur Windows). Avec la gestion correcte des symboles, vous ne devriez pas avoir de problème à déterminer l’instruction à l’origine de la mort.

Pour Windows, CaptureStackBackTrace() est également une option, qui nécessite moins de code de préparation à la fin de l’utilisateur que StackWalk64() . (Aussi, pour un scénario similaire que j’avais, CaptureStackBackTrace() fini par fonctionner mieux (de manière plus fiable) que StackWalk64() .)

Vous devriez utiliser la bibliothèque déroulante .

 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, sauf si vous appelez 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.

Il n’y a pas de moyen indépendant pour le faire.

La chose la plus proche que vous pouvez faire est d’exécuter le code sans optimisations. De cette façon, vous pouvez vous connecter au processus (en utilisant le débogueur Visual C ++ ou GDB) et obtenir une trace de stack utilisable.

Solaris a la commande pstack , qui a également été copiée dans Linux.

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

Le débogage post mortem

Bien que j’ai actuellement des problèmes avec l’ implémentation x64 de cela .

Depuis quelques années, j’utilise la libbacktrace de Ian Lance Taylor. Il est beaucoup plus propre que les fonctions de la bibliothèque GNU C qui nécessitent d’exporter tous les symboles. Il fournit plus d’utilité pour la génération de backtraces que libunwind. Et last but not least, il n’est pas vaincu par ASLR, tout comme les approches nécessitant des outils externes tels que addr2line .

Libbacktrace faisait initialement partie de la dissortingbution de GCC, mais elle est maintenant disponible sous forme de bibliothèque autonome sous licence BSD:

https://github.com/ianlancetaylor/libbacktrace

Au moment d’écrire ces lignes, je n’utiliserais rien d’autre à moins que je doive générer des traces sur une plate-forme non prise en charge par libbacktrace.

Vous pouvez le faire en marchant la stack en arrière. En réalité, il est souvent plus facile d’append un identifiant à une stack d’appels au début de chaque fonction et de le faire apparaître à la fin, puis il suffit de suivre cette impression. C’est un peu un PITA, mais cela fonctionne bien et vous permettra d’économiser du temps à la fin.