[LINUX] Suivi des programmes utilisateur avec SystemTap Partie 1

Tout d'abord, qu'est-ce que SystemTap?

Un outil pour étudier le comportement du noyau Linux. Il est également disponible pour les programmes utilisateur dans le noyau RHEL7 (CentOS7) et Systemtap.

La page Web officielle et Red Hat ont une documentation claire et détaillée. Documentation SystemTap (anglais officiel) SystemTap Examples. Guide du débutant SystemTap (Red Hat japonais) Référence SystemTap Tapset (Red Hat Japanese)

Article très utile sur Qiita Comment utiliser SystemTap (User-Space Probing) Comment utiliser SystemTap

Je veux tracer le programme avec le moins d'effort possible

Avant de lire la source, lors de l'étude du fonctionnement du programme cible Avez-vous déjà voulu voir le flux de la façon dont une fonction est appelée en affichant une liste d'arguments de fonction et leurs valeurs et valeurs de retour? j'ai Cela est particulièrement vrai lorsque C ++ utilise l'héritage de classe ou lors de la lecture de la source d'un paradigme inconnu.

Cette fois, je vais vous montrer comment tracer quand il a été compilé avec l'option -g ou quand il est recompilé avec l'option "-g -O0" mais le code source n'est pas changé. (La prochaine fois, je vais vous montrer le cas de l'édition du code source pour tracer le mouvement lors du développement de votre propre programme)

Exemples avec un exemple de code

Je décrirai la procédure de mise en œuvre sous forme de mémorandum. Affiche le script SystemTap, le code source C ++, la méthode d'exécution systemtap et le résultat de l'exécution.

Supposons que vous ayez deux fichiers, "tr1.cpp" et "ttr2.stp", affichés ci-dessous sous le répertoire / home / TestProgs / tr1 /.

Script System Tap pour le traçage

/home/TestProgs/tr1/ttr2.stp


#!/usr/bin/stap
//Exemple de méthode d'exécution> stap -v ttr2.stp 'process("/home/TestProgs/tr1/tr1")' 
// $1 indique le premier argument de démarrage. Dans l'exemple de processus ci-dessus("/home/TestProgs/tr1/tr1")Mais$Entrez 1.

//Sonde pour démarrer le processus
probe $1.begin
{
	printf("[proc begin] pid=%d,procname=%s\n", 
		pid(), 		//Numéro de processus
		execname()	//Nom du fichier d'exécution
	);
}

//Sonder jusqu'à la fin du processus
probe $1.end
{
	printf("[proc end] pid=%d,procname=%s\n", pid(), execname());
}


//Sonde à appel système
probe $1.syscall
{
	# open systemcall
	if( $syscall == 2) {
		printf("%s open(%s)\n",thread_indent(0),kernel_string($arg1))
	}
	# close systemcall
	else if ( $syscall == 3) {
		printf("%s close(%d)\n",thread_indent(0), $arg1)
	}
	//le numéro d'appel système est"ausyscall --dump"Il peut être obtenu avec la commande de.
}

//Sonder jusqu'au début de la fonction
probe $1.function("*").call
{
	printf("%s-->%s(%s) @%s %s\n",
		thread_indent(0),	
		ppfunc(),					//Nom de la fonction(Le nom de la classe et l'espace de noms ne sont pas affichés)
		$$parms, 					//Liste d'arguments
		usymfileline(addr()),		//Nom de fichier et numéro de ligne
		probefunc() 				//Nom de la fonction(Pas démêlé)
	);
	thread_indent(1);	//
} 

//Sonder jusqu'à la fin de la fonction
probe $1.function("*").return
{
	thread_indent(-1);
	printf("%s<--%s,%s\n",
		thread_indent(0),
		ppfunc(),
		$$return				//Valeur de retour
	);
}

-Le script SystemTap est similaire à C et awk.

Exemple de code source C ++

/home/TestProgs/tr1/tr1.cpp


#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <assert.h>

class classSample1{
	public:
		classSample1(){};
		~classSample1(){};
		int func_C1()  
		{
			printf("classSample1::func_C1()\n");
			return 0;
		}
		int func_C2();
		int cnt_func_C2 = 0;
};

int classSample1::func_C2(){
	printf("classSample2::func_C2()\n");
	cnt_func_C2++;
	int l= cnt_func_C2 % 2;
	printf("cnt_func_C2 %d, l:%d\n",cnt_func_C2,l);
	assert(cnt_func_C2<5);	//Suspendu à titre d'exemple.
	return 0;
}


int funcSub1()
{
	printf("funcSub1\n");
	return 1;
}

int func1(int x)
{
	int fd;

	fd = open("/tmp/test1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
	printf("func1\n");
	close(fd);
	funcSub1();
	return 1;
}

int func2(int x)
{
	int fd;

	fd = open("/tmp/test2.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
	printf("func2\n");
	close(fd);
	return 2;
}

int main(int argc, char *argv[])
{
	int ret=0;

	classSample1 c1; 

	for(;;) {
		ret = func1(10);
		sleep(2);

		ret = func2(20);
		sleep(2);

		ret = c1.func_C1();
		sleep(2);

		ret = c1.func_C2();
		sleep(2);
	}
	return ret;
}

Compiler le code source C ++

> cd /home/TestProgs/tr1/
> gcc -Wall -g3 -O0 -std=c++11 tr1.cpp -o tr1 -lstdc++

Exécuter le script Systap

> stap -v ttr2.stp 'process("/home/TestProgs/tr1/tr1")'

Donnez à l'argument une chaîne contenant le chemin complet du fichier exécutable. Le résultat de l'analyseur comme indiqué ci-dessous est sorti.

stdout


Pass 1: parsed user script and 491 library scripts using 243892virt/56392res/3316shr/53784data kb, in 790usr/30sys/826real ms.
WARNING: function _start return probe is blacklisted: keyword at ttr2.stp:45:1
 source: probe $1.function("*").return
         ^
Pass 2: analyzed script: 36 probes, 27 functions, 6 embeds, 2 globals using 246148virt/60044res/4600shr/56040data kb, in 100usr/100sys/195real ms.
Pass 3: translated to C into "/tmp/stapa1Sf5u/stap_174029a96504ce3d524d3ac8b34d4f54_38391_src.c" using 246296virt/60520res/4968shr/56188data kb, in 50usr/100sys/149real ms.
Pass 4: compiled C into "stap_174029a96504ce3d524d3ac8b34d4f54_38391.ko" in 3590usr/540sys/3979real ms.
Pass 5: starting run.

-En attente de l'exécution du programme cible.

Ouvrez un autre shell et exécutez le programme cible

> cd /home/TestProgs/tr1/
> ./tr1

Résultat de sortie du script SystemTap ttr2.stp

stdout


[proc begin] pid=20693,procname=tr1
     0 tr1(20693): open(/etc/ld.so.cache)
     0 tr1(20693): close(3)
     0 tr1(20693): open(/lib64/libstdc++.so.6)
     0 tr1(20693): close(3)
     0 tr1(20693): open(/lib64/libgcc_s.so.1)
     0 tr1(20693): close(3)
     0 tr1(20693): open(/lib64/libc.so.6)
     0 tr1(20693): close(3)
     0 tr1(20693): open(/lib64/libm.so.6)
     0 tr1(20693): close(3)
     0 tr1(20693):-->_start() @0x400770 _start
    19 tr1(20693):-->__libc_csu_init() @0x400a60 __libc_csu_init
    46 tr1(20693): -->_init() @0x400698 _init
    57 tr1(20693): <--_init,
    60 tr1(20693): -->frame_dummy() @0x400830 frame_dummy
    68 tr1(20693):  -->register_tm_clones() @0x4007d0 register_tm_clones
    78 tr1(20693):  <--register_tm_clones,
    80 tr1(20693): <--frame_dummy,
    83 tr1(20693):<--__libc_csu_init,
    96 tr1(20693):-->main(argc=0x1 argv=0x7ffd32486188) @/home/TestProgs/tr1/tr1.cpp:59 main
   113 tr1(20693): -->classSample1(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:9 _ZN12classSample1C2Ev
   123 tr1(20693): <--classSample1,
   131 tr1(20693): -->func1(x=0xa) @/home/TestProgs/tr1/tr1.cpp:40 _Z5func1i
   144 tr1(20693):   open(/tmp/test1.txt)
   215 tr1(20693):   close(3)
   229 tr1(20693):  -->funcSub1() @/home/TestProgs/tr1/tr1.cpp:32 _Z8funcSub1v
   246 tr1(20693):  <--funcSub1,return=0x1
   250 tr1(20693): <--func1,return=0x1
2000657 tr1(20693): -->func2(x=0x14) @/home/TestProgs/tr1/tr1.cpp:51 _Z5func2i
2000684 tr1(20693):   open(/tmp/test2.txt)
2000762 tr1(20693):   close(3)
2000826 tr1(20693): <--func2,return=0x2
4002721 tr1(20693): -->func_C1(this=0x400770) @/home/TestProgs/tr1/tr1.cpp:11 _ZN12classSample17func_C1Ev
4002778 tr1(20693): <--func_C1,return=0x0
6002907 tr1(20693): -->func_C2(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:21 _ZN12classSample17func_C2Ev
6002994 tr1(20693): <--func_C2,return=0x0
8003121 tr1(20693): -->func1(x=0xa) @/home/TestProgs/tr1/tr1.cpp:40 _Z5func1i
8003149 tr1(20693):   open(/tmp/test1.txt)
8003228 tr1(20693):   close(3)
8003279 tr1(20693):  -->funcSub1() @/home/TestProgs/tr1/tr1.cpp:32 _Z8funcSub1v
8003310 tr1(20693):  <--funcSub1,return=0x1
8003317 tr1(20693): <--func1,return=0x1
10004527 tr1(20693): -->func2(x=0x14) @/home/TestProgs/tr1/tr1.cpp:51 _Z5func2i
10004555 tr1(20693):   open(/tmp/test2.txt)
10004633 tr1(20693):   close(3)
10004698 tr1(20693): <--func2,return=0x2
12004785 tr1(20693): -->func_C1(this=0x400770) @/home/TestProgs/tr1/tr1.cpp:11 _ZN12classSample17func_C1Ev
12004839 tr1(20693): <--func_C1,return=0x0
14005308 tr1(20693): -->func_C2(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:21 _ZN12classSample17func_C2Ev
14005365 tr1(20693): <--func_C2,return=0x0
16006063 tr1(20693): -->func1(x=0xa) @/home/TestProgs/tr1/tr1.cpp:40 _Z5func1i
16006091 tr1(20693):   open(/tmp/test1.txt)
16006169 tr1(20693):   close(3)
16006191 tr1(20693):  -->funcSub1() @/home/TestProgs/tr1/tr1.cpp:32 _Z8funcSub1v
16006220 tr1(20693):  <--funcSub1,return=0x1
16006227 tr1(20693): <--func1,return=0x1
18009385 tr1(20693): -->func2(x=0x14) @/home/TestProgs/tr1/tr1.cpp:51 _Z5func2i
18009412 tr1(20693):   open(/tmp/test2.txt)
18009488 tr1(20693):   close(3)
18009503 tr1(20693): <--func2,return=0x2
20010037 tr1(20693): -->func_C1(this=0x400770) @/home/TestProgs/tr1/tr1.cpp:11 _ZN12classSample17func_C1Ev
20010093 tr1(20693): <--func_C1,return=0x0
22011732 tr1(20693): -->func_C2(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:21 _ZN12classSample17func_C2Ev
22011789 tr1(20693): <--func_C2,return=0x0
24012358 tr1(20693): -->func1(x=0xa) @/home/TestProgs/tr1/tr1.cpp:40 _Z5func1i
24012385 tr1(20693):   open(/tmp/test1.txt)
24012460 tr1(20693):   close(3)
24012599 tr1(20693):  -->funcSub1() @/home/TestProgs/tr1/tr1.cpp:32 _Z8funcSub1v
24012750 tr1(20693):  <--funcSub1,return=0x1
24012757 tr1(20693): <--func1,return=0x1
26012971 tr1(20693): -->func2(x=0x14) @/home/TestProgs/tr1/tr1.cpp:51 _Z5func2i
26012999 tr1(20693):   open(/tmp/test2.txt)
26013074 tr1(20693):   close(3)
26013089 tr1(20693): <--func2,return=0x2
28013574 tr1(20693): -->func_C1(this=0x400770) @/home/TestProgs/tr1/tr1.cpp:11 _ZN12classSample17func_C1Ev
28013630 tr1(20693): <--func_C1,return=0x0
30015263 tr1(20693): -->func_C2(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:21 _ZN12classSample17func_C2Ev
30015319 tr1(20693): <--func_C2,return=0x0
32015746 tr1(20693): -->func1(x=0xa) @/home/TestProgs/tr1/tr1.cpp:40 _Z5func1i
32015773 tr1(20693):   open(/tmp/test1.txt)
32015848 tr1(20693):   close(3)
32015871 tr1(20693):  -->funcSub1() @/home/TestProgs/tr1/tr1.cpp:32 _Z8funcSub1v
32015900 tr1(20693):  <--funcSub1,return=0x1
32015907 tr1(20693): <--func1,return=0x1
34017409 tr1(20693): -->func2(x=0x14) @/home/TestProgs/tr1/tr1.cpp:51 _Z5func2i
34017436 tr1(20693):   open(/tmp/test2.txt)
34017515 tr1(20693):   close(3)
34017592 tr1(20693): <--func2,return=0x2
36018061 tr1(20693): -->func_C1(this=0x400770) @/home/TestProgs/tr1/tr1.cpp:11 _ZN12classSample17func_C1Ev
36018218 tr1(20693): <--func_C1,return=0x0
38020672 tr1(20693): -->func_C2(this=0x7ffd32486080) @/home/TestProgs/tr1/tr1.cpp:21 _ZN12classSample17func_C2Ev
[proc end] pid=20693,procname=tr1

Une trace avec les caractéristiques suivantes est effectuée. ・ Indenté par la profondeur de la fonction -Liste des arguments de fonction et leurs valeurs -Valeur de retour de la fonction -Détection de fichier ouvert et fermé

Je pense qu'il y a suffisamment d'informations pour voir comment le programme fonctionne.

Je voulais le faire, mais je ne savais pas si c'était possible

-Je veux afficher le nom de la fonction membre de la classe comme démêlé. -Je veux afficher quel retour une fonction a plusieurs retours. -Je veux détecter un vidage de mémoire et afficher une trace arrière.

Si quelqu'un sait, je vous serais reconnaissant si vous pouviez m'apprendre.

Merci d'avoir lu jusqu'ici.

Recommended Posts

Suivi des programmes utilisateur avec SystemTap Partie 1
Suivi des programmes utilisateur avec SystemTap Partie 2
bac à sable avec neo4j partie 10
Traitement d'image avec Python (partie 2)
Etudier Python avec freeCodeCamp part1
Images en bordure avec python Partie 1
Grattage avec Selenium + Python Partie 1
Etudier Python avec freeCodeCamp part2
Traitement d'image avec Python (partie 1)
Résolution de Nampre avec Python (partie 2)
Traitement d'image avec Python (3)
Grattage avec Selenium + Python Partie 2