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)