[LINUX] Rückverfolgung von Benutzerprogrammen mit SystemTap Teil 1

Was ist SystemTap?

Ein Tool zur Untersuchung des Verhaltens des Linux-Kernels. Es ist auch für Benutzerprogramme im RHEL7 (CentOS7) -Kernel und in Systemtap verfügbar.

Die offizielle Webseite und Red Hat haben eine klare und detaillierte Dokumentation. SystemTap-Dokumentation (offizielles Englisch) SystemTap Examples. SystemTap-Anfängerhandbuch (Red Hat Japanese) SystemTap Tapset-Referenz (Red Hat Japanese)

Sehr hilfreicher Artikel über Qiita Verwendung von SystemTap (User-Space Probing) Verwendung von SystemTap

Ich möchte das Programm mit so wenig Aufwand wie möglich verfolgen

Vor dem Lesen der Quelle, wenn Sie untersuchen, wie das Zielprogramm funktioniert Wollten Sie schon immer den Ablauf des Aufrufs einer Funktion sehen, indem Sie eine Liste der Funktionsargumente sowie deren Werte und Rückgabewerte anzeigen? ich habe Dies gilt insbesondere dann, wenn C ++ die Klassenvererbung verwendet oder wenn die Quelle eines unbekannten Paradigmas gelesen wird.

Dieses Mal werde ich Ihnen zeigen, wie Sie nachverfolgen können, wann es mit der Option -g kompiliert wurde oder wann es mit der Option "-g -O0" neu kompiliert wurde, der Quellcode jedoch nicht geändert wurde. (Das nächste Mal werde ich Ihnen den Fall zeigen, dass Sie den Quellcode bearbeiten, um die Bewegung bei der Entwicklung Ihres eigenen Programms zu verfolgen.)

Beispiele mit Beispielcode

Ich werde das Umsetzungsverfahren als Memorandum beschreiben. Zeigt das SystemTap-Skript, den C ++ - Quellcode, die Systemtap-Ausführungsmethode und das Ausführungsergebnis an.

Angenommen, Sie haben zwei Dateien im Verzeichnis / home / TestProgs / tr1 /, "tr1.cpp" und "ttr2.stp" (siehe unten).

System Tippen Sie auf das Skript zur Ablaufverfolgung

/home/TestProgs/tr1/ttr2.stp


#!/usr/bin/stap
//Beispiel für eine Ausführungsmethode> stap -v ttr2.stp 'process("/home/TestProgs/tr1/tr1")' 
// $1 gibt das erste Startargument an. Im obigen Beispielprozess("/home/TestProgs/tr1/tr1")Aber$Geben Sie 1 ein.

//Sonde zum Prozessstart
probe $1.begin
{
	printf("[proc begin] pid=%d,procname=%s\n", 
		pid(), 		//Prozessnummer
		execname()	//Name der Ausführungsdatei
	);
}

//Sonde bis zum Ende des Prozesses
probe $1.end
{
	printf("[proc end] pid=%d,procname=%s\n", pid(), execname());
}


//Probe zum Systemaufruf
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)
	}
	//Systemrufnummer ist"ausyscall --dump"Es kann mit dem Befehl von erhalten werden.
}

//Sonde zum Start der Funktion
probe $1.function("*").call
{
	printf("%s-->%s(%s) @%s %s\n",
		thread_indent(0),	
		ppfunc(),					//Funktionsname(Klassenname und Namespace werden nicht angezeigt.
		$$parms, 					//Argumentliste
		usymfileline(addr()),		//Dateiname und Zeilennummer
		probefunc() 				//Funktionsname(Nicht entwirrt)
	);
	thread_indent(1);	//
} 

//Sonde bis zum Ende der Funktion
probe $1.function("*").return
{
	thread_indent(-1);
	printf("%s<--%s,%s\n",
		thread_indent(0),
		ppfunc(),
		$$return				//Rückgabewert
	);
}

-Das SystemTap-Skript ähnelt C und awk.

Beispiel für einen C ++ - Quellcode

/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);	//Als Beispiel ausgesetzt.
	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;
}

Kompilieren Sie den C ++ - Quellcode

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

Führen Sie das Systap-Skript aus

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

Geben Sie dem Argument eine Zeichenfolge, die den vollständigen Pfad zur ausführbaren Datei enthält. Das unten gezeigte Parser-Ergebnis wird ausgegeben.

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.

Öffnen Sie eine andere Shell und führen Sie das Zielprogramm aus

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

Ausgabeergebnis des SystemTap-Skripts 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

Eine Ablaufverfolgung mit den folgenden Eigenschaften wird durchgeführt. ・ Einrücken um die Tiefe der Funktion -Liste der Funktionsargumente und ihrer Werte

Ich denke, es gibt genug Informationen, um zu sehen, wie das Programm funktioniert.

Ich wollte es tun, aber ich wusste nicht, ob es möglich war

-Ich möchte den Mitgliedsfunktionsnamen der Klasse als entwirrt anzeigen. -Ich möchte anzeigen, welche Rückgabe in einer Funktion mit mehreren Rückgaben beendet wurde. -Ich möchte einen Core Dump erkennen und eine Rückverfolgung anzeigen.

Wenn jemand weiß, würde ich es begrüßen, wenn Sie mich unterrichten könnten.

Vielen Dank, dass Sie so weit gelesen haben.

Recommended Posts

Rückverfolgung von Benutzerprogrammen mit SystemTap Teil 1
Benutzerprogrammverfolgung mit SystemTap Teil 2
Sandkasten mit neo4j Teil 10
Bildverarbeitung mit Python (Teil 2)
Python mit freeCodeCamp Teil1 studieren
Angrenzende Bilder mit Python Teil 1
Schaben mit Selen + Python Teil 1
Python studieren mit freeCodeCamp part2
Bildverarbeitung mit Python (Teil 1)
Nampre mit Python lösen (Teil 2)
Bildverarbeitung mit Python (3)
Schaben mit Selen + Python Teil 2