Méthodes d'analyse dynamique
Je décris ici quelques méthodes d'analyse dynamique.
Les méthodes dynamiques exécutent en fait le programme pour déterminer le graphe d'appel.
Le contraire des méthodes dynamiques sont les méthodes statiques, qui tentent de le déterminer à partir de la source seule sans exécuter le programme.
Avantages des méthodes dynamiques:
- attrape les pointeurs de fonction et les appels virtuels C ++. Ceux-ci sont présents en grand nombre dans tout logiciel non trivial.
Inconvénients des méthodes dynamiques:
- vous devez exécuter le programme, ce qui peut être lent, ou nécessiter une configuration que vous n'avez pas, par exemple une compilation croisée
- seules les fonctions qui ont été réellement appelées s'affichent. Par exemple, certaines fonctions peuvent être appelées ou non en fonction des arguments de la ligne de commande.
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 êtes maintenant laissé dans un programme GUI génial qui contient beaucoup de données de performances intéressantes.
En bas à droite, sélectionnez l'onglet "Graphique d'appel". Cela affiche un graphique d'appel interactif qui correspond aux mesures de performances 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 PNG exporté ressemble à ceci:
À partir de là, nous pouvons voir que:
- le nœud racine est
_start
, qui est le point d'entrée ELF réel, et contient le passe-partout d'initialisation de la glibc
f0
, f1
et f2
sont appelés comme attendu l'un de l'autre
pointed
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.
Ce qui est cool, valgrind
c'est qu'il ne nécessite aucune option de compilation spéciale.
Par conséquent, vous pouvez l'utiliser même si vous n'avez pas le code source, seulement l'exécutable.
valgrind
parvient à le faire en exécutant votre code via une "machine virtuelle" légère. Cela rend également l'exécution extrêmement lente par rapport à l'exécution native.
Comme on peut le voir sur le graphique, des informations de synchronisation sur chaque appel de fonction sont également obtenues, et cela peut être utilisé pour profiler le programme, ce qui est probablement le cas d'utilisation d'origine de cette configuration, pas seulement pour voir les graphiques d'appel: Comment puis-je profiler Code C ++ fonctionnant sous Linux?
Testé sur Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
ajoute des callbacks , etrace analyse le fichier ELF et implémente tous les callbacks.
Malheureusement, je n'ai pas pu le faire fonctionner: pourquoi `-finstrument-functions` ne fonctionne-t-il 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 de la prise en charge du traçage matériel spécifique, mais présente l'inconvénient de devoir recompiler le code.