17.7 STRACE – Systemaufrufe verfolgen
STRACE ist ein Werkzeug, das konsequent die Systemaufrufe anzeigt, die ein laufendes Programm ausführt. Dabei befindet sich STRACE praktisch als Schnittstelle zwischen dem Programm und dem Kernel. Wird beispielsweise ein einfacher printf()-Aufruf verwendet, können Sie sich mit STRACE anzeigen lassen, wie die Ausgabe eigentlich stattfindet – sprich, wie es z. B. von der Funktion printf() zum eigentlichen Systemcall kommt. Sie wissen zwar wahrscheinlich, dass printf() mit write() realisiert wurde, aber dessen sind sich viele Programmierer nicht bewusst. STRACE wird gerne für folgende Aufgabe verwendet:
|
Personen, die sich mit der Kernel-Programmierung auseinander setzen wollen, lernen mit STRACE die Systemaufrufe und Programmiertechniken kennen, die für das Zusammenspiel von Software und dem Betriebssystem (Kernel) nötig sind. |
|
Debuggen von Software, wenn sich diese ein wenig seltsam verhält oder dauernd abstürtzt – was mit STRACE auch ohne den Quelltext möglich ist! |
|
Herausfinden von Geschwindigkeitsbremsen von Software, deren Quelltext nicht vorhanden ist. |
Hinweis Damit STRACE auch die Systemaufrufe, die eine Software abgibt, protokollieren kann, muss dies der Kernel natürlich auch unterstützen. Diese Unterstützung wird letztendlich auch nur wieder mit einem Systemaufruf (ptrace()) realisiert.
|
Die Ausgabe des STRACE-Protokolls kann enorm lang werden, weshalb hier nur als Beispiel die Ausgabe des Protokolls vom Hallo Welt-Programm erfolgt.
#include <stdio.h>
int main(void) {
printf("Hallo Welt\n");
return 0;
}
Hierzu das STRACE-Protokoll:
$ strace hallo
execve("./hallo", ["./hallo"], [/* 65 vars */]) = 0
uname({sys="Linux", node="linux", ...}) = 0
brk(0) = 0x8049548
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70568, ...}) = 0
old_mmap(NULL, 70568, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0pY\1\000"..., 1024) = 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1491599, ...}) = 0
old_mmap(NULL, 1268004, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40027000
mprotect(0x40156000, 26916, PROT_NONE) = 0
old_mmap(0x40156000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED,
3, 0x12f000) = 0x40156000
old_mmap(0x4015a000, 10532, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|
MAP_ANONYMOUS, -1, 0) = 0x4015a000
close(3) = 0
munmap(0x40015000, 70568) = 0
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 4), ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0) = 0x40015000
write(1, "Hallo Welt\n", 11) = 11
munmap(0x40015000, 4096) = 0
exit_group(0) = ?
Mit diesem Aufruf bekommen Sie eine Menge Systemaufrufe angezeigt, die sich gar nicht unmittelbar im Quellcode befinden und dennoch ausgeführt werden. Die komplette Ausgabe wurde auf die Standardfehlerausgabe gemacht, womit Sie die Möglichkeit haben, diese Meldungen von der Standardfehlerausgabe des Programms in eine Datei zu schreiben. Bei der Ausgabe können Sie auch erkennen, dass STRACE nicht nur die Namen der Systemcalls ausgibt, sondern auch alle Parameter und sogar die Rückgabewerte. Sie sehen dabei auch, welch ein Aufwand betrieben wird, bis es zur Ausgabe des Strings mittels
write(1, "Hallo Welt\n", 11) = 11
kommt. Ganz zu Beginn finden Sie gewöhnlich immer den execve()-Aufruf, der das Programm selbst startet. Der komplette zusätzliche Code wird zum Starten und Ausführen des Programms benötigt. So wird z. B. die C-Standardbibliothek dynamisch in dem Speicherbereich des Prozesses eingeblendet:
open("/lib/libc.so.6", O_RDONLY) = 3
Die anderen Systemaufrufe wie old_mmap und munmap werden für die Verwaltung von dynamischem Speicher verwendet, der für die Anwendung nötigt ist.
Neben der eben gezeigten Verwendung von STRACE, lässt sich mithilfe von verschiedenen Optionen eine noch detailliertere Ausgabe erreichen. Einige mögliche Optionen hierzu wären:
Tabelle 17.5
Einige von vielen strace-Aufrufen
Aufruf
|
Bedeutung
|
strace hallo
|
Startet hallo und gibt dessen Systemaufrufe auf dem Bildschirm aus
|
strace -o hallo.log hallo
|
Ausgabe in hallo.log-Logdatei
|
strace -f -o hallo.log hallo
|
Verfolgt auch Kindprozesse
|
strace -p pid
|
Verfolgt die Aufrufe des laufenden Prozesses mit der PID pid
|
strace -e trace=open,write hallo
|
Gibt nur Systemaufrufe aus, die das Dateimanagement betreffen (hier open und write)
|
Als Ausblick lässt sich zu strace sagen, dass dieses Werkzeug sehr hilfreich ist, um gewisse Abläufe zwischen dem Kernel und dem Prozess zu verstehen. Ganz klarer Vorteil von diesem Tool ist, dass hierbei der Quelltext nicht vorliegen muss, um damit arbeiten zu können. Allerdings stellt das Tool eine ziemlich hohe Anforderung an den Entwickler und richtet sich vorwiegend an Programmierer, die sich mit der Kernel-Programmierung auseinander setzen müssen.
|