strace ist unter Linux das Mittel der Wahl, wenn man verstehen will, was ein Prozess auf Kernel-Ebene treibt. Wenn ein Programm lautlos scheitert, hängt oder sich unerwartet verhält, zeigt strace die exakten System Calls — Dateiöffnungen, Netzwerkverbindungen, Speicherzuweisungen und Signal-Handling. Dieser Leitfaden behandelt strace von der einfachen Nutzung bis hin zu Debugging-Techniken für die Produktion, anhand realistischer Szenarien, die zeigen, wie man tatsächlich auftretende Probleme diagnostiziert.

Voraussetzungen

  • Ein Linux-System (beliebige Distribution — strace funktioniert überall)
  • Root- oder sudo-Zugriff (erforderlich, um Prozesse anderer Benutzer zu verfolgen)
  • Grundkenntnisse über Linux-Prozesse und Dateideskriptoren
  • strace installiert (wird im Installationsabschnitt unten behandelt)

strace unter Linux installieren

strace ist in den Paketrepositories jeder großen Distribution verfügbar und auf vielen Systemen bereits vorinstalliert.

# Debian / Ubuntu
sudo apt install strace

# RHEL / CentOS / Fedora
sudo dnf install strace

# Arch Linux
sudo pacman -S strace

# Alpine Linux
sudo apk add strace

# Installierte Version prüfen
strace --version

Auf minimalen Container-Images (Alpine, distroless) fehlt strace in der Regel. Für das Debugging temporär installieren und anschließend aus dem Produktions-Image entfernen.

Einen Befehl von Beginn an verfolgen

Der einfachste Einsatz von strace ist, es einem beliebigen Befehl voranzustellen:

strace ls /tmp

Damit werden alle System Calls ausgegeben, die ls von execve() bis exit_group() macht. Die Ausgabe geht nach stderr, sodass die normale Befehlsausgabe weiterhin auf stdout erscheint.

strace-Ausgabe lesen

Jede Zeile folgt diesem Format:

syscall_name(Argumente...) = Rückgabewert

Beispiel:

openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
getdents64(3, [{d_ino=2, d_off=1, d_type=DT_DIR, d_name="."},...], 32768) = 480
close(3)                                = 0

Das zeigt: ls hat /tmp als Dateideskriptor 3 geöffnet, Verzeichniseinträge gelesen und dann geschlossen. Ein Rückgabewert von -1 bedeutet, dass der Aufruf fehlgeschlagen ist — strace zeigt dann auch den errno:

openat(AT_FDCWD, "/etc/shadow", O_RDONLY) = -1 EACCES (Permission denied)

Diese einzelne Zeile erklärt oft, warum ein Programm scheitert — ohne Quellcode-Lektüre oder zusätzliche Debug-Ausgaben.

Nützliche Start-Flags

# Kind-Prozesse verfolgen (fork/clone)
strace -f ./my_app

# Zeitstempel für jeden syscall ausgeben
strace -t ls /tmp

# Relative Zeit zwischen syscalls (Hänger finden)
strace -r ls /tmp

# String-Argumente vollständig ausgeben (Standard: Abschneiden bei 32 Zeichen)
strace -s 256 ./my_app

# Ausgabe in Datei statt nach stderr schreiben
strace -o /tmp/trace.log ls /tmp

strace an einen laufenden Prozess anhängen

Ein Prozess muss nicht neu gestartet werden, um ihn zu verfolgen. Per PID an jeden laufenden Prozess anhängen:

# PID ermitteln
pidof nginx
# oder
ps aux | grep my_app

# An den Prozess anhängen
sudo strace -p 12345

# Anhängen und alle Threads/Kinder verfolgen
sudo strace -fp 12345

Mit Ctrl+C trennen. Der verfolgte Prozess läuft danach normal weiter — strace beendet ihn beim Abtrennen nicht.

Praxisszenario: Eine hängende Anwendung debuggen

Eine Produktions-Webanwendung reagiert gelegentlich nicht mehr auf Anfragen. Die Logs zeigen nichts. Statt blind neu zu starten:

# PID des feststeckenden Workers ermitteln
sudo strace -fp $(pidof my_app) -e trace=network,file -s 256 -o /tmp/hang_trace.log

Eine Testanfrage senden und dann den Trace prüfen:

grep -E 'futex|poll|select|epoll_wait' /tmp/hang_trace.log | tail -20

Steckt der Prozess in futex(FUTEX_WAIT) fest, wartet er auf einen Mutex (Deadlock). Steckt er in connect() oder poll() mit langem Timeout, wartet er auf einen vorgelagerten Dienst, der nicht antwortet.

System Calls mit strace filtern

Vollständige Traces sind unübersichtlich. Mit -e trace= gezielt auf das Wesentliche fokussieren:

# Nur Dateioperationen (open, read, write, close, stat, …)
strace -e trace=file ls /tmp

# Nur Netzwerkoperationen (socket, connect, send, recv, …)
strace -e trace=network curl https://example.com

# Prozessverwaltung (fork, clone, execve, wait, exit)
strace -e trace=process bash -c "ls | grep foo"

# Speicheroperationen (mmap, mprotect, brk)
strace -e trace=memory ./my_app

# Bestimmte syscalls nach Name
strace -e trace=openat,read,write cat /etc/hostname

# Negation — alles AUSSER diesen verfolgen
strace -e trace=!mmap,mprotect,brk ./my_app

Nach Rückgabewert filtern

Nur fehlgeschlagene System Calls anzeigen — äußerst praktisch beim Debugging:

# Nur Aufrufe mit Fehler anzeigen
strace -Z ./my_app

# Nur erfolgreiche Aufrufe anzeigen
strace -z ./my_app

Das Flag -Z (strace 5.2+) ist ein echter Gamechanger für das Debugging in der Produktion. Statt tausende erfolgreiche Aufrufe durchzuwühlen, sieht man nur die Fehler.

Performance-Analyse mit strace

System Call-Zusammenfassung

Das Flag -c erstellt eine statistische Zusammenfassung statt eines Live-Traces:

strace -c ls /tmp
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 25.00    0.000045           5         9           mmap
 19.44    0.000035           5         7           close
 16.67    0.000030           5         6           openat
 11.11    0.000020           4         5           fstat
  8.33    0.000015           5         3           mprotect
  5.56    0.000010           3         3           read
  ...
------ ----------- ----------- --------- --------- ----------------
100.00    0.000180           4        42         2 total

Das zeigt, welche System Calls die meiste Zeit beanspruchen. Dominieren read oder write, ist der Prozess I/O-gebunden. Dominiert futex, liegt Lock Contention vor.

Einzelne Aufrufe zeitlich messen

# Wall-Clock-Zeit pro syscall
strace -T ./my_app

# Kombiniert: Zeitstempel + Dauer
strace -tT ./my_app

Das Flag -T hängt die Dauer in spitzen Klammern an:

openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY) = 3 <0.000024>
read(3, "nameserver 8.8.8.8\n", 4096)  = 19 <0.000011>
connect(4, {sa_family=AF_INET, sin_port=htons(443)}, 16) = -1 EINPROGRESS <0.000089>

89 Mikrosekunden für connect() sind in Ordnung. Dauert es 5+ Sekunden, liegt der Engpass bei DNS oder dem vorgelagerten Dienst.

strace im Vergleich zu alternativen Tracing-Tools

Featurestraceltraceperf tracebpftrace
VerfolgtKernel syscallsBibliotheksaufrufeKernel syscallsKernel + Userspace
OverheadHoch (10–100x)HochGering (~5 %)Sehr gering (~2 %)
An laufende PID anhängenJaJaJaJa
Nach syscall filternJa (-e trace=)Ja (-e)Ja (—filter)Ja (eigene Skripte)
Statistische ZusammenfassungJa (-c)Ja (-c)Ja (—summary)Eigene Skripte
Benötigte Kernel-VersionBeliebigBeliebig3.7+4.9+ (eBPF)
ProduktionssicherNur kurzNur kurzJaJa
Am besten fürSchnelles DebuggingBibliotheksaufruf-ProblemeProfiling mit geringem OverheadKomplexe Tracing-Logik

Faustregel: strace für die schnelle Diagnose einsetzen (anhängen, Problem finden, abtrennen). Für dauerhaftes Produktions-Monitoring, bei dem Overhead eine Rolle spielt, auf perf trace oder bpftrace wechseln.

Häufige strace-Debugging-Muster

Muster 1: Herausfinden, warum ein Programm eine Datei nicht findet

strace -e trace=openat,stat,access ./my_app 2>&1 | grep -i "no such file\|enoent\|eacces"

Das zeigt sofort jede Datei, die das Programm zu öffnen versucht und bei der es scheitert — ideal für Konfigurationsdatei-Probleme, fehlende Shared Libraries oder falsche Pfade.

Muster 2: Welche Konfigurationsdateien ein Programm liest

strace -e trace=openat ./my_app 2>&1 | grep -v ENOENT | grep '= [0-9]'

Filtert auf nur erfolgreiche Dateiöffnungen — zeigt genau, welche Config-Dateien, Bibliotheken und Datendateien das Programm tatsächlich verwendet.

Muster 3: DNS-Auflösungsprobleme debuggen

strace -e trace=network -s 256 curl https://example.com 2>&1 | grep -E 'connect|sendto|recvfrom'

Man sieht die DNS-Anfrage an die Nameserver aus /etc/resolv.conf, die Antwort und dann die eigentliche HTTPS-Verbindung. Dauert die DNS-Anfrage mehrere Sekunden, wurde die Latenzquelle gefunden.

Muster 4: Warum ein Dienst beim Start scheitert

sudo strace -f -o /tmp/service_trace.log systemctl start my_service
# Dann den Trace nach Fehlern durchsuchen
grep '= -1' /tmp/service_trace.log | grep -v 'ENOENT.*locale\|ENOENT.*lib' | head -30

Der grep filtert harmlose „Datei nicht gefunden”-Fehler bei Locale- und Bibliotheks-Probing heraus (die normal sind) und lässt nur die echten Fehler übrig.

Muster 5: Datei-Schreibvorgänge in Echtzeit beobachten

sudo strace -fp $(pidof my_app) -e trace=write -s 1024 2>&1 | grep 'write([0-9]*,'

Jedes Byte, das der Prozess in einen Dateideskriptor schreibt, wird sichtbar — nützlich für das Debugging von Logging-Problemen oder unerwarteten Dateiänderungen.

Fallstricke und Sonderfälle

Setuid-Binaries verfolgen: strace kann sich nicht an setuid-Programme anhängen, sofern strace nicht selbst als root läuft. Der Kernel entzieht aus Sicherheitsgründen die ptrace-Berechtigungen.

Mehrthreadige Anwendungen: Bei Multithreading immer -f (follow forks) verwenden. Ohne dieses Flag werden nur die syscalls des Haupt-Threads erfasst, und die Worker-Threads — wo das eigentliche Problem steckt — bleiben unsichtbar.

Container-Umgebungen: Innerhalb von Docker-Containern benötigt strace die SYS_PTRACE-Capability. Mit --cap-add=SYS_PTRACE oder --privileged ausführen:

docker run --cap-add=SYS_PTRACE my_image strace ./my_app

In Kubernetes die Capability im Security Context des Pods ergänzen:

securityContext:
  capabilities:
    add: ["SYS_PTRACE"]

Der Performance-Overhead ist real: strace fängt jeden System Call über ptrace ab, was zwei Kontextwechsel pro Aufruf erfordert. Ein Prozess mit 100.000 syscalls/Sekunde wird dramatisch verlangsamt. strace niemals länger als nötig an einen Produktionsprozess angehängt lassen.

strace -c verschleiert einzelne langsame Aufrufe: Die -c-Zusammenfassung zeigt Durchschnittswerte. Ein Prozess könnte 10.000 schnelle read()-Aufrufe und einen 30-Sekunden-read() machen — der Durchschnitt sieht dann unauffällig aus. Mit -C (Großbuchstabe) erhält man sowohl die Zusammenfassung als auch den Live-Trace, um Ausreißer zu erkennen.

Fehlerbehebung bei häufigen strace-Problemen

„Operation not permitted” beim Anhängen

# ptrace-Scope prüfen (Ubuntu/Debian)
cat /proc/sys/kernel/yama/ptrace_scope

Ist der Wert 1 (Standard unter Ubuntu), können nur eigene Prozesse verfolgt werden. Um das Verfolgen beliebiger Prozesse vorübergehend zu erlauben:

# Temporär — wird beim Neustart zurückgesetzt
sudo sysctl kernel.yama.ptrace_scope=0

# Oder einfach sudo mit strace verwenden
sudo strace -p 12345

Die Trace-Ausgabe ist überwältigend

# Filter kombinieren: nur fehlgeschlagene Dateioperationen mit Zeitangabe
strace -Z -e trace=file -T -s 256 ./my_app 2>&1 | head -50

Einen kurzlebigen Prozess verfolgen

Für Prozesse, die schnell starten und beenden (z. B. Cron-Jobs):

# Den Befehl einwickeln
strace -f -o /tmp/cron_trace.log /path/to/cron_script.sh

# Oder den Elternprozess verfolgen, der ihn startet
sudo strace -fp $(pidof crond) -o /tmp/cron_trace.log

Zusammenfassung

  • strace verfolgt Kernel-System Calls — einsetzen, wenn ein Prozess lautlos scheitert, hängt oder sich falsch verhält und die Logs keine Hinweise liefern
  • An laufende Prozesse anhängen mit strace -p PID ohne Neustart — -f für mehrthreadige Anwendungen hinzufügen
  • Mit -e trace= filtern, um sich auf Datei-, Netzwerk-, Prozess- oder Speicheroperationen zu konzentrieren, statt in der Ausgabe zu ertrinken
  • -Z für nur fehlgeschlagene Aufrufe — der schnellste Weg, den Grund für ein Problem zu finden
  • -c für Performance-Zusammenfassungen — zeigt, welche System Calls die meiste Zeit beanspruchen
  • Der Performance-Overhead ist erheblich — in der Produktion kurz anhängen und dann wieder trennen; für dauerhaftes Tracing perf trace oder bpftrace verwenden
  • Container benötigen die SYS_PTRACE-Capability — in Docker oder Kubernetes explizit hinzufügen

Verwandte Artikel