So verfolgen Sie die Programmausführung mit dem Linux-Strace-Befehl

strace ist ein leistungsstarkes Befehlszeilentool für Prozessüberwachungs-, Diagnose- und Fehlerbehebungsprogramme unter Linux. Im Allgemeinen wird es verwendet, um die Systemaufrufe und die von einem Prozess empfangenen Signale abzufangen und aufzuzeichnen. Sie können strace verwenden, um zu analysieren, wie ein Programm mit dem System interagiert, um ein beliebiges Programm zu debuggen.

Dieses Tool ist sehr nützlich, wenn das Programm ständig abstürzt oder sich nicht wie erwartet verhält. Es bietet einen tiefen Einblick in die Funktionsweise des Systems. Jeder Benutzer kann seine eigenen laufenden Prozesse verfolgen.

In diesem Tutorial zeigen wir Ihnen, wie Sie das Befehlszeilentool strace unter Linux verwenden.

Installieren Sie Strace

Standardmäßig ist strace im Standard-Repository aller Linux-Betriebssysteme verfügbar.

Auf Debian- und Ubuntu-Betriebssystemen installieren Sie die strace mit dem folgenden Befehl:

apt-get install strace -y

Installieren Sie auf den Betriebssystemen RHEL und CentOS die strace mit dem folgenden Befehl:

dnf install strace -y

Nach der Installation von strace können Sie die strace-Version mit dem folgenden Befehl überprüfen:

strace -V

Sie sollten die folgende Ausgabe erhalten:

strace -- version 4.8

Sie können alle mit dem Befehl strace verfügbaren Optionen mit dem folgenden Befehl drucken:

strace -help

Output
usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...
[-a column] [-o file] [-s strsize] [-P path]...
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
or: strace -c[df] [-I n] [-e expr]... [-O overhead] [-S sortby]
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
-c -- count time, calls, and errors for each syscall and report summary
-C -- like -c but also print regular output
-d -- enable debug output to stderr
-D -- run tracer process as a detached grandchild, not as parent
-f -- follow forks, -ff -- with output into separate files
-i -- print instruction pointer at time of syscall
-q -- suppress messages about attaching, detaching, etc.
-r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs
-T -- print time spent in each syscall
-v -- verbose mode: print unabbreviated argv, stat, termios, etc. args
-x -- print non-ascii strings in hex, -xx -- print all strings in hex
-y -- print paths associated with file descriptor arguments
-h -- print help message, -V -- print version
-a column -- alignment COLUMN for printing syscall results (default 40)
-b execve -- detach on this syscall
-e expr -- a qualifying expression: option=[!]all or option=[!]val1[,val2]...
options: trace, abbrev, verbose, raw, signal, read, write
-I interruptible --
1: no signals are blocked
2: fatal signals are blocked while decoding syscall (default)
3: fatal signals are always blocked (default if '-o FILE PROG')
4: fatal signals and SIGTSTP (^Z) are always blocked
(useful to make 'strace -o FILE PROG' not stop on ^Z)
-o file -- send trace output to FILE instead of stderr
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs
-p pid -- trace process with process id PID, may be repeated
-s strsize -- limit length of print strings to STRSIZE chars (default 32)
-S sortby -- sort syscall counts by: time, calls, name, nothing (default time)
-u username -- run command as username handling setuid and/or setgid
-E var=val -- put var=val in the environment for command
-E var -- remove var from the environment for command
-P path -- trace accesses to path

1. Verfolgen Sie einen Systemaufruf

Wenn Sie die Systemaufrufe des Befehls ls verfolgen möchten, führen Sie den folgenden Befehl aus:

strace ls file1.txt

Ausgabe execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0 brk(0) = 0x1f2a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (Keine solche Datei oder Verzeichnis) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, - 1, 0) = 0x7f4dd0d30000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=114633, ...}) = 0 mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4dd0d14000
close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 lesen(3, "177ELF2113>1 ["..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=134296, ...}) = 0
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4dd08ed000
mprotect(0x7f4dd090d000, 2093056, PROT_NONE) = 0
mmap(0x7f4dd0b0c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f4dd0b0c000
mmap(0x7f4dd0b0e000, 5872, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4dd0b0e000
NLY|O_CLOEXEC) = 3
MAP_DENYWRITE, 3, 0x2000) = 0x7f4dd00e0000
close(3) = 0
.......
.......
) = 10
close(1) = 0
munmap(0x7f4dd0d2f000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++

In the above output, you can see the system call and result of the call of ls command. You should also see that exit status is 0. That means there was no error.

One use of strace (Except debugging some problem) is that you can find out which configuration files are read by a program.

For example:

strace php 2>&1 | grep php.ini

2. Filter Specific System Calls

Be default, strace displays all system calls for the given executable. If you want to display only a specific system call, you can use strace -e option.

For example, to displays only the write system call of the ls command run the following command:

strace -e write ls file1.txt file2.txt

Output
write(1, "file1.txt file2.txtn", 21file1.txt file2.txt
) = 21
+++ exited with 0 +++

To displays only the open system call of the ls command run the following command:

strace -e open ls file1.txt file2.txt

Output
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1.txt file2.txt
+++ exited with 0 +++

If you want to display files opened by a specific process like SSH, run the following command:

strace -f -e open /usr/sbin/sshd 2>&1 | grep ssh

Output
open("/etc/ssh/sshd_config", O_RDONLY) = 3
open("/etc/ssh/ssh_host_rsa_key", O_RDONLY) = -1 EACCES (Permission denied)
open("/etc/ssh/ssh_host_rsa_key", O_RDONLY) = -1 EACCES (Permission denied)
open("/etc/ssh/ssh_host_ecdsa_key.pub", O_RDONLY) = 3
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
open("/etc/ssh/ssh_host_ed25519_key.pub", O_RDONLY) = 3
Could not load host key: /etc/ssh/ssh_host_ed25519_key

To trace network-related system calls, run the following command:

strace -e network nc -v -n 127.0.0.1 80

Output
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 Verbindung zu 127.0.0.1 80 Port [tcp/*] gelungen!

3. An bereits laufenden Prozess anhängen

Wenn ein Prozess bereits läuft, können Sie ihn anhand seiner PID wie unten gezeigt verfolgen:

strace -p 5315

Output
Process 5315 attached
restart_syscall(<... resuming interrupted call ...>) = -1 ETIMEDOUT (Connection timed out)
futex(0x7ffdc25fd048, FUTEX_WAKE_PRIVATE, 1) = 0
lseek(31, 0, SEEK_SET) = 0
read(31, "1185755 22902 18214 39954 0 1079"..., 4095) = 38
lseek(32, 0, SEEK_SET) = 0
read(32, "Name:tchromenState:tR (running)n"..., 4095) = 1020
futex(0x7ffdc25fd074, FUTEX_WAIT_BITSET_PRIVATE, 1, {3799, 9175}, ffffffff) = -1 ETIMEDOUT (Connection timed out)
futex(0x7ffdc25fd048, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7ffdc25fd074, FUTEX_WAIT_BITSET_PRIVATE, 1, {3802, 10202}, ffffffff^CProcess 5315 detached

Dieser Befehl zeigt kontinuierlich Systemaufrufe an, die vom Prozess getätigt wurden. Sie können STRG+C drücken, um es zu stoppen.

wo:

5315 ist eine Prozess-ID des laufenden Prozesses.

4. Leiten Sie die Trace-Ausgabe in eine Datei um

Sie können das Flag -o mit dem Befehl strace verwenden, um die Ausgabe von strace in einer angegebenen Datei zu speichern.

strace -o file_out.txt ls file1.txt

Sie können nun den Inhalt der Datei file_out.txt mit folgendem Befehl anzeigen:

cat file_out.txt

5. Zeit für Systemaufrufe drucken

Um den relativen Zeitstempel jedes Aufrufs zu drucken, verwenden Sie das Flag -r wie unten gezeigt.

strace -r ls file1.txt

Ausgabe
0.000000 execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
0.000947 brk(0) = 0xaf3000
0.000450 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
0.000706 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f01b1ccb000
0.000319 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000093 close(1) = 0
0.000069 munmap(0x7f01b1cca000, 4096) = 0
0.000078 close(2) = 0
0.000104 exit_group(0) = ?
0.000184 +++ exited with 0 +++

Verwenden Sie die Option -T, um den Zeitunterschied zwischen dem Anfang und dem Ende jedes Systemaufrufs anzuzeigen, der mit dem Befehl ls durchgeführt wird.

strace -T ls file1.txt

Output
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0 <0.000908>
brk(0) = 0x1d72000 <0.000050>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000066>
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc895610000 <0.000059>
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000060>
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000072>
fstat(3, {st_mode=S_IFREG|0644, st_size=114633, ...}) = 0 <0.000052>
mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc8955f4000 <0.000062>
close(3) = 0 <0.000050>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000059>
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000068>
read(3, "177ELF2113>1["..., 832) = 832 <0.000057>
fstat(3, {st_mode=S_IFREG|0644, st_size=134296, ...}) = 0 <0.000052>
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc8951cd000 <0.000064>
mprotect(0x7fc8951ed000, 2093056, PROT_NONE) = 0 <0.000085>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89560f000 <0.000026>
write(1, "file1.txtn", 10file1.txt
) = 10 <0.000029>
close(1) = 0 <0.000021>
munmap(0x7fc89560f000, 4096) = 0 <0.000029>
close(2) = 0 <0.000022>
exit_group(0) = ?
+++ exited with 0 +++

Führen Sie den folgenden Befehl aus, um die Wanduhrzeit jedes Systemaufrufs zu drucken:

strace -t ls file1.txt

Output
10:58:25 execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
10:58:25 brk(0) = 0xc30000
10:58:25 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
10:58:25 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd4db396000
10:58:25 write(1, "file1.txtn", 10file1.txt
) = 10
10:58:25 close(1) = 0
10:58:25 exit_group(0) = ?
10:58:25 +++ exited with 0 +++

Die Option -tt zeigt den Zeitstempel gefolgt von der Mikrosekunde an.

strace -tt ls file1.txt

6. Anweisungszeiger des Systemaufrufs anzeigen

Sie können das Flag -i mit dem Befehl strace verwenden, um den Anweisungszeiger zum Zeitpunkt jedes Systemaufrufs auszugeben, der durch den Befehl ausgeführt wird:

strace -i ls file1.txt

Output
[00007efd8cfeb337] execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
[00007f5ab611e18c] brk(0) = 0x239e000
[00007f5ab611f537] access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
[00007f5ab611f65a] mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ab6326000
[00007f5ab5a01870] write(1, "file1.txtn", 10file1.txt
) = 10
[00007f5ab5a01f60] close(1) = 0
[00007f5ab5a0a9f7] munmap(0x7f5ab6325000, 4096) = 0
[00007f5ab5a01f60] close(2) = 0
[00007f5ab59d7309] exit_group(0) = ?
[????????????????] +++ exited with 0 +++

7. Generieren Sie einen Systemaufrufbericht

Sie können das Flag -c verwenden, um den nützlichen statistischen Bericht für den Ausführungs-Trace abzurufen.

strace -c ls /var/www/html

Output
index.html
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 8 read
0.00 0.000000 0 1 write
0.00 0.000000 0 9 open
0.00 0.000000 0 12 close
0.00 0.000000 0 1 stat
0.00 0.000000 0 10 fstat
0.00 0.000000 0 23 mmap
0.00 0.000000 0 14 mprotect
0.00 0.000000 0 3 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 2 ioctl
0.00 0.000000 0 8 8 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 getdents
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 openat
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 101 10 total

In der obigen Ausgabe zeigt die Spalte “Aufrufe” an, wie oft dieser bestimmte Systemaufruf ausgeführt wurde.

8. Drucken Sie die Debugging-Ausgabe von strace

Um Debug-Informationen für den Befehl strace auszugeben, verwenden Sie das Flag -d wie unten gezeigt:

strace -d ls file1.txt

Output
ptrace_setoptions = 0x11
new tcb for pid 6453, active tcbs:1
[wait(0x80137f) = 6453] ?? (128),PTRACE_EVENT_?? (128)
pid 6453 has TCB_STARTUP, initializing it
setting opts 11 on pid 6453
[wait(0x80057f) = 6453] ?? (128),PTRACE_EVENT_?? (128)
[wait(0x127f) = 6453] WIFSTOPPED,sig=SIGCONT
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */] [wait(0x4057f) = 6453] WIFSTOPPED,sig=SIGTRAP,PTRACE_EVENT_EXEC
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
) = 0
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
brk(0 [wait(0x857f) = 6453] WIFSTOPPED,sig=133
) = 0x25bc000
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
exit_group(0) = ?
[wait(0x0000) = 6453] WIFEXITED,exitcode=0
+++ exited with 0 +++
dropped tcb for pid 6453, 0 remain

9. Systemaufrufe basierend auf einer bestimmten Bedingung verfolgen

Sie können die Systemaufrufe auch basierend auf der spezifischen Bedingung verfolgen. Für example, verfolgen Sie alle Systemaufrufe im Zusammenhang mit der Speicherverwaltung und führen Sie den folgenden Befehl aus:

strace -q -e memory ls file1.txt

Output
brk(0) = 0x248d000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac4000
mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f68e9aa8000
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e9681000
mprotect(0x7f68e96a1000, 2093056, PROT_NONE) = 0
mmap(0x7f68e98a0000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f68e98a0000
mmap(0x7f68e98a2000, 5872, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f68e98a2000
mmap(NULL, 2126336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e9479000
mmap(0x7f68e8e74000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f68e8e74000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9aa6000
mmap(NULL, 2113760, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e8a6d000
mprotect(0x7f68e8a71000, 2093056, PROT_NONE) = 0
mmap(0x7f68e8c70000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f68e8c70000
brk(0) = 0x248d000
brk(0x24ae000) = 0x24ae000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac3000
munmap(0x7f68e9ac3000, 4096) = 0
mmap(NULL, 7216688, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f68e838b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac3000
file1.txt
munmap(0x7f68e9ac3000, 4096) = 0
+++ exited with 0 +++

Führen Sie den folgenden Befehl aus, um die signalbedingten Systemaufrufe zu verfolgen:

strace -e signal ls file1.txt

Output
file1.txt
+++ exited with 0 +++

Führen Sie den folgenden Befehl aus, um die prozessbezogenen Systemaufrufe zu verfolgen:

strace -e process ls file1.txt

Output
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fb8196a8840) = 0
file1.txt
exit_group(0) = ?
+++ exited with 0 +++

Fazit

In der obigen Anleitung haben Sie anhand mehrerer Beispiele gelernt, wie Sie den Befehl strace verwenden. Dieses Tool ist sehr nützlich für Systemadministratoren und Programmierer, um jedes Programm zu debuggen und Fehler zu beheben.