Outil de suivi des appels de fonctions locales sous Linux

Je cherche un outil comme ltrace ou strace qui peut tracer des fonctions définies localement dans un exécutable. ltrace ne trace que les appels de bibliothèque dynamics et strace trace uniquement les appels système. Par exemple, compte tenu du programme C suivant:

#include  int sortingple ( int x ) { return 3 * x; } int main (void) { printf("%d\n", sortingple(10)); return 0; } 

L’exécution du programme avec ltrace affichera l’appel à printf car il s’agit d’une fonction de bibliothèque standard (qui est une bibliothèque dynamic sur mon système) et strace affichera tous les appels système du code de démarrage, les appels système utilisés pour implémenter le code d’arrêt, mais je veux quelque chose qui va me montrer que la fonction sortingple été appelée. En supposant que les fonctions locales n’ont pas été incorporées par un compilateur optimisant et que le binary n’a pas été supprimé (symboles supprimés), existe-t-il un outil capable de le faire?

modifier

Quelques précisions:

  • Il est normal que l’outil fournisse également des informations de trace pour les fonctions non locales.
  • Je ne veux pas avoir à recomstackr le (s) programme (s) avec un support pour des outils spécifiques, les informations de symbole dans l’exécutable devraient suffire.
  • Je serais vraiment bien si je pouvais utiliser l’outil pour attacher aux processus existants comme je peux le faire avec ltrace / strace.

En supposant que vous voulez seulement être averti pour des fonctions spécifiques, vous pouvez le faire comme ceci:

comstackr avec des informations de debug

donné

 #include  int fac(int n) { if(n == 0) return 1; return n * fac(n-1); } int main() { for(int i=0;i<4;i++) std::cout << fac(i) << std::endl; } 

Utilisez gdb pour tracer:

 [js@HOST2 cpp]$ g++ -g3 test.cpp [js@HOST2 cpp]$ gdb ./a.out (gdb) b fac Breakpoint 1 at 0x804866a: file test.cpp, line 4. (gdb) commands 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". >silent >bt 1 >c >end (gdb) run Starting program: /home/js/cpp/a.out #0 fac (n=0) at test.cpp:4 1 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 1 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 2 #0 fac (n=3) at test.cpp:4 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 6 Program exited normally. (gdb) 

Voici ce que je fais pour collecter les adresses de toutes les fonctions:

 tmp=$(mktemp) readelf -s ./a.out | gawk ' { if($4 == "FUNC" && $2 != 0) { print "# code for " $NF; print "b *0x" $2; print "commands"; print "silent"; print "bt 1"; print "c"; print "end"; print ""; } }' > $tmp; gdb --command=$tmp ./a.out; rm -f $tmp 

Notez qu'au lieu d'imprimer simplement l'image courante ( bt 1 ), vous pouvez faire ce que vous voulez, en imprimant la valeur de certains éléments globaux, en exécutant des commandes shell ou en envoyant quelque chose si la fonction fatal_bomb_exploded est fatal_bomb_exploded 🙂 Langue a changé "messages entre les deux. Mais ça se déroule facilement. Pas grand chose

System Tap peut être utilisé sur une machine Linux moderne (Fedora 10, RHEL 5, etc.).

Commencez par télécharger le script para-callgraph.stp .

Puis lancez:

 $ sudo stap para-callgraph.stp 'process("/bin/ls").function("*")' -c /bin/ls 0 ls(12631):->main argc=0x1 argv=0x7fff1ec3b038 276 ls(12631): ->human_options spec=0x0 opts=0x61a28c block_size=0x61a290 365 ls(12631): <-human_options return=0x0 496 ls(12631): ->clone_quoting_options o=0x0 657 ls(12631): ->xmemdup p=0x61a600 s=0x28 815 ls(12631): ->xmalloc n=0x28 908 ls(12631): <-xmalloc return=0x1efe540 950 ls(12631): <-xmemdup return=0x1efe540 990 ls(12631): <-clone_quoting_options return=0x1efe540 1030 ls(12631): ->get_quoting_style o=0x1efe540 

Voir aussi: Observer, mettre à jour le système et mettre à jour les fichiers

Utiliser des sondes (depuis Linux 3.5)

En supposant que vous vouliez tracer toutes les fonctions dans ~/Desktop/datalog-2.2/datalog en l’appelant avec les parameters -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl

  1. cd /usr/src/linux-`uname -r`/tools/perf
  2. for i in `./perf probe -F -x ~/Desktop/datalog-2.2/datalog`; do sudo ./perf probe -x ~/Desktop/datalog-2.2/datalog $i; done
  3. sudo ./perf record -agR $(for j in $(sudo ./perf probe -l | cut -d' ' -f3); do echo "-e $j"; done) ~/Desktop/datalog-2.2/datalog -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl
  4. sudo ./perf report -G

liste des fonctions en binaire datalogcall tree lors de la sélection de dl_pushlstring, montrant comment principal appelé loadfile appelé dl_load appelé programme appelé règle qui appelle à son tour les autres fonctions qui ont fini par appeler dl_pushlstring, analyser (parent: programme, c.-à-d. dl_pushstring et ainsi de suite

En supposant que vous pouvez recomstackr (pas de changement de source requirejs) le code que vous voulez tracer avec l’option -finstrument-functions option gcc, vous pouvez utiliser etrace pour obtenir le graphe d’appel de fonction.

Voici à quoi ressemble la sortie:

 \-- 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 

Sur Solaris, truss (équivalent à strace) peut filtrer la bibliothèque à tracer. Je suis surpris quand j’ai découvert que strace n’avait pas une telle capacité.

 $ sudo yum install frysk $ ftrace -sym:'*' -- ./a.out 

Plus: ftrace.1

Si vous externalisez cette fonction dans une bibliothèque externe, vous devriez également pouvoir la voir s’appeler (avec ltrace).

La raison pour laquelle cela fonctionne est que ltrace se place entre votre application et la bibliothèque et que lorsque tout le code est internalisé avec un fichier, il ne peut pas intercepter l’appel.

ie: ltrace xterm

crache des trucs des librairies X, et X est loin d’être un système.

En dehors de cela, la seule façon réelle de le faire est l’interception à la compilation via des indicateurs de prof ou des symboles de débogage.

Je viens de parcourir cette application, qui semble intéressante:

http://www.gnu.org/fr/software/cflow/

Mais je ne pense pas que ce soit ce que vous voulez.

Si les fonctions ne sont pas intégrées, vous pourriez même avoir de la chance en utilisant objdump -d .

Pour un exemple, prenons un butin au début de la routine main de GCC 4.3.2:

 $ objdump `which gcc` -d | grep '\(call\|main\)' 08053270 
: 8053270: 8d 4c 24 04 lea 0x4(%esp),%ecx -- 8053299: 89 1c 24 mov %ebx,(%esp) 805329c: e8 8f 60 ff ff call 8049330 80532a1: 8d 04 03 lea (%ebx,%eax,1),%eax -- 80532cf: 89 04 24 mov %eax,(%esp) 80532d2: e8 b9 c9 00 00 call 805fc90 80532d7: 8b 5d 9c mov 0xffffff9c(%ebp),%ebx -- 80532e4: 89 04 24 mov %eax,(%esp) 80532e7: e8 b4 a7 00 00 call 805daa0 80532ec: 8b 55 9c mov 0xffffff9c(%ebp),%edx -- 8053302: 89 0c 24 mov %ecx,(%esp) 8053305: e8 d6 2a 00 00 call 8055de0 805330a: e8 71 ac 00 00 call 805df80 805330f: e8 4c 2f 00 00 call 8056260 8053314: c7 44 24 04 01 00 00 movl $0x1,0x4(%esp) -- 805331c: c7 04 24 02 00 00 00 movl $0x2,(%esp) 8053323: e8 78 5e ff ff call 80491a0 8053328: 83 e8 01 sub $0x1,%eax

Il faut un peu d’effort pour parcourir l’ensemble de l’assembleur, mais vous pouvez voir tous les appels possibles d’une fonction donnée. Il n’est pas aussi facile à utiliser que gprof ou certains des autres utilitaires mentionnés, mais il présente plusieurs avantages distincts:

  • Vous n’avez généralement pas besoin de recomstackr une application pour l’utiliser
  • Il montre tous les appels de fonctions possibles, alors que quelque chose comme gprof affichera uniquement les appels de fonctions exécutés.

Il existe un script shell pour automatiser les appels de fonctions de suivi avec gdb. Mais il ne peut pas s’attacher au processus en cours.

blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

Copie de la page – http://web.archive.org/web/20090317091725/http://blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

Copie de l’outil – callgraph.tar.gz

http://web.archive.org/web/20090317091725/http://superadditive.com/software/callgraph.tar.gz

Il vide toutes les fonctions du programme et génère un fichier de commandes gdb avec des points d’arrêt sur chaque fonction. A chaque point d’arrêt, “backtrace 2” et “continue” sont exécutés.

Ce script est plutôt lent sur big porject (~ des milliers de fonctions), donc j’ajoute un filtre sur la liste des fonctions (via egrep). C’était très facile et j’utilise ce script presque tous les jours.

Gprof pourrait être ce que vous voulez

Voir les traces, un framework de traçage pour les applications Linux C / C ++: https://github.com/baruch/traces#readme

Cela nécessite de recomstackr votre code avec son instrument, mais il fournira une liste de toutes les fonctions, de leurs parameters et de leurs valeurs de retour. Il y a un interactif pour permettre la navigation facile des échantillons de données volumineux.

J’espère que les outils callgrind ou cachegrind de Valgrind vous donneront les informations recherchées.

NOTE: Ce n’est pas le ftrace basé sur le kernel Linux, mais plutôt un outil que j’ai récemment conçu pour effectuer le traçage et le contrôle des fonctions locales. Linux ELF x86_64 / x86_32 sont pris en charge publiquement.

https://github.com/leviathansecurity/ftrace