143 votes

imprimer la pile d'appels en C ou C++

Existe-t-il un moyen de vider la pile d'appels d'un processus en cours d'exécution en C ou C++ chaque fois qu'une certaine fonction est appelée ? Ce que j'ai en tête est quelque chose comme ceci :

void foo()
{
   print_stack_trace();

   // foo's body

   return
}

print_stack_trace fonctionne de la même manière que caller en Perl.

Ou quelque chose comme ça :

int main (void)
{
    // will print out debug info every time foo() is called
    register_stack_trace_function(foo); 

    // etc...
}

register_stack_trace_function met une sorte de point d'arrêt interne qui provoquera l'impression d'une trace de la pile à chaque fois que foo s'appelle.

Est-ce que quelque chose de ce genre existe dans une bibliothèque C standard ?

Je travaille sur Linux, en utilisant GCC.


Contexte

J'ai une exécution de test qui se comporte différemment en fonction de certains commutateurs de ligne de commande qui ne devraient pas affecter ce comportement. Mon code comporte un générateur de nombres pseudo-aléatoires qui, je suppose, est appelé différemment en fonction de ces commutateurs. Je veux pouvoir exécuter le test avec chaque jeu de commutateurs et voir si le générateur de nombres aléatoires est appelé différemment pour chacun d'eux.

0 votes

@Nathan : Pour autant que je sache, il n'y a pas de moyen STANDARD de le faire, mais il existe peut-être des bibliothèques qui peuvent vous aider.

1 votes

Armen, connaissez-vous l'un de ces produits ?

0 votes

Cela ne serait-il pas non plus à réaliser dans un débogueur ?

87voto

Idan K Points 10037

Pour une solution exclusivement linux, vous pouvez utiliser backtrace(3) qui renvoie simplement un tableau de void * (en fait, chacune d'entre elles pointe vers l'adresse de retour de la trame de pile correspondante). Pour les traduire en quelque chose d'utile, il y a symboles de retour en arrière(3) .

Faites attention à la section notes dans backtrace(3) :

Les noms des symboles peuvent être indisponibles sans l'utilisation d'options spéciales spéciales de l'éditeur de liens. Pour les systèmes utilisant l'éditeur de liens GNU, il est nécessaire d'utiliser l'option linker -rdynamique de l'éditeur de liens. Notez que les noms des fonctions "statiques" ne sont pas exposés, et ne seront pas disponibles dans le backtrace.

10 votes

Pour information, cette fonctionnalité existe également sur Mac OS X : developer.apple.com/library/mac/#documentation/Darwin/Reference/

10 votes

2 votes

8voto

Existe-t-il un moyen de vider la pile d'appels d'un processus en cours d'exécution en C ou C++ chaque fois qu'une certaine fonction est appelée ?

Vous pouvez utiliser une fonction macro au lieu de l'instruction de retour dans la fonction spécifique.

Par exemple, au lieu d'utiliser return,

int foo(...)
{
    if (error happened)
        return -1;

    ... do something ...

    return 0
}

Vous pouvez utiliser une fonction macro.

#include "c-callstack.h"

int foo(...)
{
    if (error happened)
        NL_RETURN(-1);

    ... do something ...

    NL_RETURN(0);
}

Lorsqu'une erreur se produit dans une fonction, vous verrez une pile d'appels de style Java, comme indiqué ci-dessous.

Error(code:-1) at : so_topless_ranking_server (sample.c:23)
Error(code:-1) at : nanolat_database (sample.c:31)
Error(code:-1) at : nanolat_message_queue (sample.c:39)
Error(code:-1) at : main (sample.c:47)

Le code source complet est disponible ici.

c-callstack sur https://github.com/Nanolat

6voto

Paul Michalik Points 3060

Il n'existe pas de méthode normalisée pour ce faire. Pour Windows, la fonctionnalité est fournie dans le fichier DbgHelp bibliothèque

0 votes

Des exemples ? Tout ce que j'ai trouvé sur msdn, c'est que la seule fonctionnalité est la récupération des symboles, rien sur la pile d'appel.

3voto

slashmais Points 3924

Vous pouvez mettre en œuvre cette fonctionnalité vous-même :

Utilisez une pile globale (chaîne) et, au début de chaque fonction, placez le nom de la fonction et les autres valeurs (par exemple les paramètres) sur cette pile ; à la fin de la fonction, placez-la à nouveau.

Écrivez une fonction qui imprimera le contenu de la pile lorsqu'elle est appelée, et utilisez-la dans la fonction où vous voulez voir la pile d'appels.

Cela peut sembler être beaucoup de travail, mais c'est très utile.

2 votes

Je ne le ferais pas. Je créerais plutôt un wrapper qui utilise les API spécifiques de la plate-forme sous-jacente (voir ci-dessous). La quantité de travail serait probablement la même, mais l'investissement devrait être rentabilisé plus rapidement.

3 votes

@paul : votre réponse fait référence à Windows alors que l'OP spécifie clairement linux ... mais pourrait être utile pour les Windows-guys qui se présentent ici.

0 votes

C'est vrai, je n'ai pas vu ça Hm, c'est la dernière phrase de la question, donc peut-être que le posteur devrait modifier sa demande pour mentionner sa plateforme cible à un endroit plus important.

-6voto

sbi Points 100828

Existe-t-il un moyen de vider la pile d'appels d'un processus en cours d'exécution en C ou C++ chaque fois qu'une certaine fonction est appelée ?

Non, il n'y en a pas, bien que des solutions dépendantes de la plateforme puissent exister.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X