KCacheGrind : un outil simple mais diablement efficace pour analyser les callgraph de programmes en C (ou python, php, perl, ...) afin de faire du débogage ou encore de l'analyse de performance.
Situation actuelle avec un programme caml simple
Exemple utilisé :
let bar i = 1+i ;;let foo i = 2+(bar i ) ;;
let () = print_int ( foo 3 ) ;;
- D'abord nous compilons en désactivant les optimisations qui pourraient éliminer ces fonctions triviales
$ ocamlopt -inline 0 -o foo t.ml
- Puis nous exécutons ce test via valgrind qui génère un fichier au format "callgrind", contenant l'ordre d'appel des fonctions ainsi que les estimations du temps passé dans chacune d'entre elles :
$ valgrind --tool=callgrind ./foo
- Enfin, on regarde le résultat sous kcachegrind
$ kcachegrind callgrind.out.10624
Mais voilà, l'assembleur généré par le compilateur caml ne contient pas toutes les instructions utilisées par valgrind lors de l'analyse du programme. Ainsi, aucun label de fonction n'apparait et nous n'avons le droit qu'à des adresses mémoire en hexadécimal :(
/!\ Problème corrigé pour la version 3.11 : détails
Exemple du fonctionnement normal en C
Voyons comment valgrind fonctionne avec du C :
int bar(int a) { return 1+a; }int foo(int a) { return 2+bar(a); }
int main() { foo(3); }
$ gcc -O0 -o foo t.c $ valgrind --tool=callgrind ./foo $ kcachegrind callgrind.out.10719
Cette fois-ci, nous obtenons un graphe correct avec le nom des fonctions; Regardons maintenant l'assembleur généré par gcc
$gcc -O0 -S t.c
Assembleur de GCC :
.file "t.c" .text .globl bar .type bar, @function bar: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax incl %eax popl %ebp ret .size bar, .-bar .globl foo .type foo, @function foo: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %eax movl %eax, (%esp) call bar addl $2, %eax leave ret .size foo, .-foo
Comparons maintenant celui-ci à l'assembleur généré par le compilateur ocaml : Assembleur OCaml
.text .align 16 .globl camlT__bar_58 .type camlT__bar_58,@function camlT__bar_58: .L100: addl $2, %eax ret .text .align 16 .globl camlT__foo_60 .type camlT__foo_60,@function camlT__foo_60: .L101: call camlT__bar_58 .L102: addl $4, %eax ret
Dans les deux cas, nous retrouvons bien nos deux fonctions foo et bar avec les instructions : .globl, .type mais il manque .size à la fin des fonctions caml! Ceci est la source du problème pour valgrind, car après analyse de son code source, il ignore les fonctions de taille nulle ...
Solution
Il suffit d'appliquer ce minuscule patch sur le compilateur ocaml pour
générer des exécutables ELF valides aux yeux de valgrind : patch-alter_elf_for_valgrind-cvs-080620
(Patch réalisé sur la version CVS, qui correspond à la futur version 3.11)
Nous obtenons alors le callgraph suivant sur le premier exemple :
Comments
You can use your Fediverse (i.e. Mastodon, among many others) account to reply to this post
(Note that comments from locked accounts won't be visible on the blog, but only to me)