J'ai un grand espace de travail qui contient de nombreux fichiers source de code C. Bien que je puisse voir les fonctions appelées à partir d'une fonction dans MS VS2005 à l'aide du navigateur d'objets, et également dans MSVC 6.0, cela ne montre que les fonctions appelées à partir d'une fonction particulière dans un type d'affichage non graphique. De plus, il ne montre pas la fonction appelée à partir de say main()
, puis les fonctions appelées à partir de celle-ci, et ainsi de suite, plus en profondeur dans la fonction de niveau feuille.
J'ai besoin d'un outil qui me donnera un graphe d'appel de fonction de manière imagée avec les fonctions callee
et caller
reliées par des flèches ou quelque chose comme ça, en partant de main()
jusqu'au dernier niveau de fonction, ou du moins en affichant un graphique d’appel de toutes les fonctions dans un fichier source C de manière imagée. Ce serait formidable si je pouvais imprimer ce graphique.
Tous les bons outils pour le faire (ne doivent pas être des outils gratuits)?
Egypte (logiciel libre)
KcacheGrind (GPL)
Graphviz (CPL)
CodeViz (GPL)
Méthodes d'analyse dynamique
Je décris ici quelques méthodes d’analyse dynamique.
Les méthodes dynamiques exécutent réellement le programme pour déterminer le graphe d'appels.
L'opposé des méthodes dynamiques sont les méthodes statiques, qui tentent de le déterminer à partir du source uniquement sans exécuter le programme.
Avantages des méthodes dynamiques:
Inconvénients des méthodes dynamiques:
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Programme de test:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
Usage:
Sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
Vous vous retrouvez maintenant dans un programme graphique impressionnant contenant de nombreuses données de performances intéressantes.
En bas à droite, sélectionnez l'onglet "Graphique des appels". Cela montre un graphe d'appels interactif qui est corrélé aux métriques de performance dans d'autres fenêtres lorsque vous cliquez sur les fonctions.
Pour exporter le graphique, cliquez dessus avec le bouton droit de la souris et sélectionnez "Exporter le graphique". Le fichier PNG exporté ressemble à ceci:
À partir de cela, nous pouvons voir que:
_start
, qui est le point d’entrée ELF réel et contient le passe-partout d’initialisation de glibcf0
, f1
et f2
sont appelés comme prévu les uns des autrespointed
est également affiché, même si nous l'avons appelé avec un pointeur de fonction. Il n'aurait peut-être pas été appelé si nous avions passé un argument de ligne de commande.not_called
n'est pas affiché car il n'a pas été appelé lors de l'exécution, car nous n'avons pas passé d'argument de ligne de commande supplémentaire.La bonne chose à propos de valgrind
est qu’il ne nécessite aucune option de compilation particulière.
Par conséquent, vous pouvez l'utiliser même si vous n'avez pas le code source, seulement l'exécutable.
valgrind
réussit à faire cela en exécutant votre code via une "machine virtuelle" légère.
Testé sur Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
ajoute des rappels , etrace analyse le fichier ELF et implémente tous les rappels.
Malheureusement, je n’ai pas réussi à le faire fonctionner: Pourquoi est-ce que `-finstrument-functions` ne fonctionne pas pour moi?
La sortie revendiquée est de format:
\-- main
| \-- Crumble_make_Apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
Probablement la méthode la plus efficace en plus du support de traçage matériel spécifique, mais elle a l'inconvénient de recompiler le code.
Comprendre fait un très bon travail de création de graphes d’appel.
Notre boîte à outils DMS Software Reengineering Toolkit a contrôle de flux statique/données/analyse de points/point d'appel qui a été appliqué à d'énormes systèmes (~ 25 millions de lignes) de code C, et produit de tels graphes d’appel , y compris des fonctions appelées via des pointeurs de fonction .
Vous pouvez consulter mon générateur d’arbre d’appel C basé sur bash ici . Il vous permet de spécifier une ou plusieurs fonctions C pour lesquelles vous souhaitez un appelant et/ou des informations appelées, ou vous pouvez spécifier un ensemble de fonctions et déterminer le graphe d'accessibilité des appels de fonction qui les connecte ... I.e. dites-moi tous les moyens de connexion entre main (), foo () et bar (). Il utilise graphviz/dot pour un moteur graphique.
Astrée est l'outil le plus robuste et le plus sophistiqué du marché, à mon humble avis.