[LINUX] Suivi des programmes utilisateur avec SystemTap Partie 2

Ceci est une continuation du précédent programme utilisateur Trace avec SystemTap Partie 1.

La dernière fois, le but était "Voyons le mouvement sans changer la source existante", Cette fois, je décrirai comment utiliser SystemTap dans le but de "lors du développement de votre propre programme, traçons tout en sortant la valeur de variable du point d'intérêt comme outil de débogage".

Je veux l'utiliser comme mon propre outil de développement de programme

Les fonctionnalités suivantes ont été réalisées dans le script de trace précédent. ・ Indenté par la profondeur de la fonction -Liste des arguments de fonction et leurs valeurs -Valeur de retour de la fonction ・ Détection des appels système (ouverture / fermeture, etc.)

En plus de ce qui précède, je voulais que la sortie suivante suive le comportement de mon propre programme. ・ Je veux savoir où je suis passé quand il y avait une branche dans la fonction. ・ Je veux connaître la valeur de la variable d'intérêt.

J'ai pu le faire. Il y a des endroits où il semble possible d'en faire une méthode plus sophistiquée, mais je vais la publier comme méthode à ce stade.

Exemple avec exemple de code

La procédure de mise en œuvre est décrite. Les scripts SystemTap et leurs fichiers d'en-tête correspondants pour C ++, Affiche la méthode d'exécution et le résultat de l'exécution du code source C ++, SystemTap.

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

Script SystemTap pour le traçage

/home/TestProgs/tr2/ttr3.stp


#!/usr/bin/stap

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

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
	);
}


//define TR_FUNC()	STAP_PROBE2(myTrace,lineFunc,__LINE__,__PRETTY_FUNCTION__)
probe $1.provider("myTrace").mark("lineFunc")
{
	printf("%s%s:%d:%s\n",
		thread_indent(0),		
		usymfile(addr()),		//Nom du fichier de code source
		$arg1,					// __LINE__
		user_string($arg2)	//Chaîne d'espace utilisateur__PRETTY_FUNCTION__
	); 
}

//define TR_LINE()	STAP_PROBE1(myTrace,line,__LINE__)
probe $1.provider("myTrace").mark("line")
{
	printf("%s L:%d\n",
		thread_indent(0),		
		$arg1					// __LINE__
	);		
}

//define TR_LINE_MSG(a1)	STAP_PROBE2(myTrace,lineMsg,__LINE__,a1)			 
probe $1.provider("myTrace").mark("lineMsg")
{
	printf("%s L:%d, %s",
		thread_indent(0),		
		$arg1,					// __LINE__
		user_string($arg2)					
	);		
}

//define TR_MSG(a1) STAP_PROBE1(myTrace,msg,a1) 
probe $1.provider("myTrace").mark("msg")
{
	printf("%s",
		user_string($arg1)
	);		
}

//define TR_d(a1) STAP_PROBE2(myTrace,p_d,#a1,a1)
probe $1.provider("myTrace").mark("p_d")
{
	printf("%s=%d ",
		user_string($arg1),
		$arg2
	);		
}

//define TR_u(a1) STAP_PROBE2(myTrace,p_u,#a1,a1)
probe $1.provider("myTrace").mark("p_u")
{
	printf("%s=%u ",
		user_string($arg1),
		$arg2
	);		
}

//define TR_x(a1) STAP_PROBE2(myTrace,p_x,#a1,a1)
probe $1.provider("myTrace").mark("p_x")
{
	printf("%s=0x%x ",
		user_string($arg1),
		$arg2
	);		
}


//define TR_s(a1) STAP_PROBE2(myTrace,p_s,#a1,a1)
probe $1.provider("myTrace").mark("p_s")
{
	printf("%s='%s' ",
		user_string($arg1),
		user_string($arg2)
	);		
}


//define TR_LOCAL() STAP_PROBE(myTrace,local);
probe $1.provider("myTrace").mark("local")
{
	printf("[$$locals] %s\n",$$locals); //Toutes les variables locales dans la portée
}

Supplément à ttr3.stp

Nous avons utilisé provider (). Mark () pour sonder le code source. -La chaîne de caractères spécifiée par povider () est utilisée comme espace de noms, et la chaîne de caractères spécifiée par mark () est utilisée comme nom d'identification. -Du côté du code source C ++, le nom de la variable et sa valeur sont affichés avec une simple image printf.

Fichier d'en-tête C ++ associé à ttr3.stp

/home/TestProgs/tr2/ttr3.h


#ifndef __TTR3_MYTRACE_H_
#define __TTR3_MYTRACE_H_    1

#ifndef NO_MYTRACE

	#include <stdio.h>
	#include <sys/sdt.h>

	//Retrait+Nom du fichier source+numéro de ligne+Nom de la fonction démêlé+nouvelle ligne
	#define TR_FUNC()	STAP_PROBE2(myTrace,lineFunc,__LINE__,__PRETTY_FUNCTION__)

	//Retrait+numéro de ligne+nouvelle ligne
	#define TR_LINE()	STAP_PROBE1(myTrace,line,__LINE__)

	//Retrait+numéro de ligne+Argument string
	#define TR_LINE_MSG(a1)	STAP_PROBE2(myTrace,lineMsg,__LINE__,a1)

	//Argument string
	#define TR_MSG(a1) STAP_PROBE1(myTrace,msg,a1) 

	//Nom de la variable et sa valeur(printf%format d)   
	#define TR_d(a1) STAP_PROBE2(myTrace,p_d,#a1,a1)

	//Nom de la variable et sa valeur(printf%format u)   
	#define TR_u(a1) STAP_PROBE2(myTrace,p_u,#a1,a1)

	//Nom de la variable et sa valeur(printf%format x)   
	#define TR_x(a1) STAP_PROBE2(myTrace,p_x,#a1,a1)

	//Nom de la variable et sa valeur(printf%format s)   
	#define TR_s(a1) STAP_PROBE2(myTrace,p_s,#a1,a1)

	//Noms de variables et leurs valeurs pour toutes les variables locales de la portée+nouvelle ligne
	#define TR_LOCAL() STAP_PROBE(myTrace,local);

	//Fonction de test factice
	// "> stap -v ttr3.stp "Quand, ttr3.marque définie dans stp()Cependant, s'il n'apparaît pas une seule fois dans le code CPP, l'exécution de stap entraînera une erreur.
	//Sert également de code de test ttr3.marque définie par stap()Tous ces éléments sont décrits dans cette fonction.
	void STAP_TRACE_defines(){
		int dmyI=0;
		const char *dmyS="dummy";
		TR_FUNC();
		TR_LINE();
		TR_LINE_MSG("test mesg\n"); 
		TR_MSG("test mesg\n");
		TR_d(dmyI);
		TR_u(dmyI);
		TR_x(dmyI);
		TR_s(dmyS);
		TR_LOCAL();
		printf("%s %d\n",dmyS,dmyI);
	}

#else

	#define TR_FUNC()			/**/
	#define TR_LINE()			/**/
	#define TR_LINE_MSG(a1)	/**/				 
	#define TR_MSG(a1)  		/**/
	#define TR_d(a1) 			/**/
	#define TR_u(a1) 			/**/
	#define TR_x(a1) 			/**/
	#define TR_s(a1) 			/**/
	#define TR_LOCAL() 		/**/

#endif // NO_MYTRACE

#endif // __TTR3_MYTRACE_H_

Supplément de ttr3.h

-Utilisez les fonctions système STAP_PROBE définies dans sys / sdt.h pour marquer la position de la sonde de SystemTap. -TR_d, TR_u_TR_x, TR_s ont un argument, mais le nom et la valeur de la variable sont obtenus en utilisant l'opérateur # de #define. -TR_d, TR_u_TR_x, TR_s sont définis sous la forme correspondant à% d% u% x% s de printf. Une minorité flottante ne suffit pas ... -Je veux aussi combiner TR_d, TR_u_TR_x et TR_s en un seul en utilisant un modèle, etc ... (Mais% d et% x sont difficiles à distinguer automatiquement car ils dépendent du but ...).

Exemple de code source C ++

/home/TestProgs/tr2/tr2.cpp


#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>

#include "ttr3.h"


class classSample1{
	public:
		classSample1(){};
		~classSample1(){};
		int func_C1(){
			printf("classSample1::func_C1()\n");
			TR_LINE(); //Retrait de ligne de numéro de ligne saut de ligne
			return 0;
		}
		int func_C2(int m);
		int cnt_func_C2 = 0;
};

int classSample1::func_C2(int m){
	printf("classSample2::func_C2()\n");
	TR_FUNC();	//Indentation du nom de fichier numéro de ligne démêlé saut de ligne du nom de la fonction
	cnt_func_C2++;
	int l= cnt_func_C2 % 2;
	uint32_t u32=0xfffffffd + cnt_func_C2;
	uint64_t u64=0xfffffffffffffffd + cnt_func_C2;
	int32_t  i32=-3 + cnt_func_C2;
	int64_t  i64=-3 + cnt_func_C2;

	printf("cnt_func_C2 %d , l:%d ,u32:%u, u64:%lu\n",cnt_func_C2,l,u32,u64);	

	TR_LINE_MSG("");TR_d(cnt_func_C2);TR_d(l);TR_MSG("\n"); 				 	//Mettre en retrait le numéro de ligne cnt_func_Valeur C2 valeur l saut de ligne
	TR_LINE_MSG("");TR_d(i32);TR_u(u32);TR_d(i64);TR_u(u64);TR_MSG("\n");	//Indentation du numéro de ligne Valeur i32 Valeur u32 Valeur i64 Valeur u64 Saut de ligne
	TR_LINE_MSG("");TR_LOCAL();													//Mettre en retrait toutes les variables locales dans cette étendue()

	assert(cnt_func_C2<m);  //Suspendu à titre d'exemple.

	if (l == 0) {
		TR_LINE_MSG("even.\n");	//Mettre en retrait la chaîne de numéro de ligne
		return 0;
	}else{
		TR_LINE_MSG("odd.\n");	//Mettre en retrait la chaîne de numéro de ligne
		return 1;
	}
}



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

	classSample1 c1; 

	for(;;) {

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

		ret = c1.func_C2(5);
		sleep(2);

	}
	return ret;
}

Supplément

-TR _ *** est ajouté comme printf.

Compiler le code source C ++

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

Exécuter le script SystemTap

> stap -v ttr3.stp 'process("/home/TestProgs/tr2/tr2")'

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 243904virt/56396res/3316shr/53796data kb, in 790usr/40sys/837real ms.
WARNING: function _start return probe is blacklisted: keyword at ttr3.stp:45:1
 source: probe $1.function("*").return
         ^
Pass 2: analyzed script: 57 probes, 41 functions, 6 embeds, 2 globals using 246812virt/60696res/4680shr/56704data kb, in 120usr/110sys/228real ms.
Pass 3: translated to C into "/tmp/stap4b9QpY/stap_8a82d56cba8247f5ae53b508cd7dbe33_50483_src.c" using 246956virt/61192res/5028shr/56848data kb, in 50usr/110sys/159real ms.
Pass 4: compiled C into "stap_8a82d56cba8247f5ae53b508cd7dbe33_50483.ko" in 4050usr/640sys/4517real 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/tr2/
> ./tr2

Résultat de sortie du script SystemTap ttr3.stp

stdout


[proc begin] pid=16890,procname=tr2
     0 tr2(16890): open(/etc/ld.so.cache)
     0 tr2(16890): close(3)
     0 tr2(16890): open(/lib64/libstdc++.so.6)
     0 tr2(16890): close(3)
     0 tr2(16890): open(/lib64/libgcc_s.so.1)
     0 tr2(16890): close(3)
     0 tr2(16890): open(/lib64/libc.so.6)
     0 tr2(16890): close(3)
     0 tr2(16890): open(/lib64/libm.so.6)
     0 tr2(16890): close(3)
     0 tr2(16890):-->_start() @0x4006e0 _start
    19 tr2(16890):-->__libc_csu_init() @0x400a10 __libc_csu_init
    32 tr2(16890): -->_init() @0x400628 _init
    43 tr2(16890): <--_init,
    47 tr2(16890): -->frame_dummy() @0x4007a0 frame_dummy
    57 tr2(16890):  -->register_tm_clones() @0x400740 register_tm_clones
    68 tr2(16890):  <--register_tm_clones,
    70 tr2(16890): <--frame_dummy,
    73 tr2(16890):<--__libc_csu_init,
    86 tr2(16890):-->main(argc=0x1 argv=0x7ffc0ea27058) @/home/TestProgs/tr2/tr2.cpp:53 main
   100 tr2(16890): -->classSample1(this=0x0) @/home/TestProgs/tr2/tr2.cpp:11 _ZN12classSample1C2Ev
   113 tr2(16890): <--classSample1,
   116 tr2(16890): -->func_C1(this=0x7ffc0ea26f50) @/home/TestProgs/tr2/tr2.cpp:13 _ZN12classSample17func_C1Ev
   222 tr2(16890):   L:15
   227 tr2(16890): <--func_C1,return=0x0
2003172 tr2(16890): -->func_C2(this=0x7ffc0ea26f50 m=0x5) @/home/TestProgs/tr2/tr2.cpp:23 _ZN12classSample17func_C2Ei
2003220 tr2(16890):  /home/TestProgs/tr2/tr2.cpp:24:int classSample1::func_C2(int)
2003281 tr2(16890):   L:34, cnt_func_C2=1 l=1 
2003299 tr2(16890):   L:35, i32=-2 u32=4294967294 i64=-2 u64=18446744073709551614 
2003324 tr2(16890):   L:36, [$$locals] __PRETTY_FUNCTION__=[...] l=0x1 u32=0xfffffffe u64=0xfffffffffffffffe i32=0xfffffffffffffffe i64=0xfffffffffffffffe
2003337 tr2(16890):   L:44, odd.
2003345 tr2(16890): <--func_C2,return=0x1
4003668 tr2(16890): -->func_C1(this=0x4006e0) @/home/TestProgs/tr2/tr2.cpp:13 _ZN12classSample17func_C1Ev
4003729 tr2(16890):   L:15
4003738 tr2(16890): <--func_C1,return=0x0
6006538 tr2(16890): -->func_C2(this=0x7ffc0ea26f50 m=0x5) @/home/TestProgs/tr2/tr2.cpp:23 _ZN12classSample17func_C2Ei
6006587 tr2(16890):  /home/TestProgs/tr2/tr2.cpp:24:int classSample1::func_C2(int)
6006612 tr2(16890):   L:34, cnt_func_C2=2 l=0 
6006630 tr2(16890):   L:35, i32=-1 u32=4294967295 i64=-1 u64=18446744073709551615 
6006655 tr2(16890):   L:36, [$$locals] __PRETTY_FUNCTION__=[...] l=0x0 u32=0xffffffff u64=0xffffffffffffffff i32=0xffffffffffffffff i64=0xffffffffffffffff
6006668 tr2(16890):   L:41, even.
6006676 tr2(16890): <--func_C2,return=0x0
8008347 tr2(16890): -->func_C1(this=0x4006e0) @/home/TestProgs/tr2/tr2.cpp:13 _ZN12classSample17func_C1Ev
8008409 tr2(16890):   L:15
8008418 tr2(16890): <--func_C1,return=0x0
10009956 tr2(16890): -->func_C2(this=0x7ffc0ea26f50 m=0x5) @/home/TestProgs/tr2/tr2.cpp:23 _ZN12classSample17func_C2Ei
10010004 tr2(16890):  /home/TestProgs/tr2/tr2.cpp:24:int classSample1::func_C2(int)
10010028 tr2(16890):   L:34, cnt_func_C2=3 l=1 
10010046 tr2(16890):   L:35, i32=0 u32=0 i64=0 u64=0 
10010070 tr2(16890):   L:36, [$$locals] __PRETTY_FUNCTION__=[...] l=0x1 u32=0x0 u64=0x0 i32=0x0 i64=0x0
10010081 tr2(16890):   L:44, odd.
10010089 tr2(16890): <--func_C2,return=0x1
12014178 tr2(16890): -->func_C1(this=0x4006e0) @/home/TestProgs/tr2/tr2.cpp:13 _ZN12classSample17func_C1Ev
12014238 tr2(16890):   L:15
12014246 tr2(16890): <--func_C1,return=0x0
14014842 tr2(16890): -->func_C2(this=0x7ffc0ea26f50 m=0x5) @/home/TestProgs/tr2/tr2.cpp:23 _ZN12classSample17func_C2Ei
14014888 tr2(16890):  /home/TestProgs/tr2/tr2.cpp:24:int classSample1::func_C2(int)
14014912 tr2(16890):   L:34, cnt_func_C2=4 l=0 
14014930 tr2(16890):   L:35, i32=1 u32=1 i64=1 u64=1 
14014953 tr2(16890):   L:36, [$$locals] __PRETTY_FUNCTION__=[...] l=0x0 u32=0x1 u64=0x1 i32=0x1 i64=0x1
14014965 tr2(16890):   L:41, even.
14014972 tr2(16890): <--func_C2,return=0x0
16015484 tr2(16890): -->func_C1(this=0x4006e0) @/home/TestProgs/tr2/tr2.cpp:13 _ZN12classSample17func_C1Ev
16015579 tr2(16890):   L:15
16015588 tr2(16890): <--func_C1,return=0x0
18020986 tr2(16890): -->func_C2(this=0x7ffc0ea26f50 m=0x5) @/home/TestProgs/tr2/tr2.cpp:23 _ZN12classSample17func_C2Ei
18021033 tr2(16890):  /home/TestProgs/tr2/tr2.cpp:24:int classSample1::func_C2(int)
18021057 tr2(16890):   L:34, cnt_func_C2=5 l=1 
18021074 tr2(16890):   L:35, i32=2 u32=2 i64=2 u64=2 
18021098 tr2(16890):   L:36, [$$locals] __PRETTY_FUNCTION__=[...] l=0x1 u32=0x2 u64=0x2 i32=0x2 i64=0x2
[proc end] pid=16890,procname=tr2

De la fonctionnalité précédente, ・ Indenté par la profondeur de la fonction -Liste des arguments de fonction et leurs valeurs -Valeur de retour de la fonction ・ Détection d'appel système En plus de, -Sortir le numéro de ligne où la branche est passée -Afficher le nom de la variable et sa valeur pour la variable d'intérêt Avec l'ajout de Je pense qu'il peut être utilisé de manière pratique pour tracer des gabarits de mon propre programme, générer des incidents pendant les tests, etc.

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