Le strace est un outil de ligne de commande puissant pour les programmes de surveillance, de diagnostic et de dépannage des processus sous Linux. Généralement, il est utilisé pour intercepter et enregistrer les appels système et les signaux reçus par un processus. Vous pouvez utiliser strace pour analyser comment un programme interagit avec le système pour déboguer n’importe quel programme.
Cet outil est très utile si le programme plante continuellement ou ne se comporte pas comme prévu. Il fournit un aperçu approfondi du fonctionnement du système. Tout utilisateur peut tracer ses propres processus en cours d’exécution.
Dans ce tutoriel, nous allons vous montrer comment utiliser l’outil de ligne de commande strace sous Linux.
Installer Strace
Par défaut, strace est disponible dans le référentiel par défaut de tous les systèmes d’exploitation Linux.
Sur les systèmes d’exploitation Debian et Ubuntu, installez la strace avec la commande suivante :
apt-get install strace -y
Sur les systèmes d’exploitation RHEL et CentOS, installez la strace avec la commande suivante :
dnf install strace -y
Après avoir installé strace, vous pouvez vérifier la version de strace à l’aide de la commande suivante :
strace -V
Vous devriez obtenir la sortie suivante :
strace -- version 4.8
Vous pouvez imprimer toutes les options disponibles avec la commande strace avec la commande suivante :
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. Tracer un appel système
Si vous souhaitez tracer les appels système de la commande ls, exécutez la commande suivante :
strace ls file1.txt
Sortie execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0 brk(0) = 0x1f2a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (Aucun tel fichier ou répertoire) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, - 1, 0) = 0x7f4dd0d30000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun tel fichier ou répertoire) 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 (Aucun tel fichier ou répertoire) open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "177ELF211 3 > 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 Connexion au port 127.0.0.1 80 [tcp/*] réussi!
3. Attacher au processus déjà en cours
Si un processus est déjà en cours d’exécution, vous pouvez le tracer à l’aide de son PID comme indiqué ci-dessous :
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
Cette commande affichera en continu les appels système effectués par le processus. Vous pouvez appuyer sur CTRL+C pour l’arrêter.
où:
5315 est un ID de processus du processus en cours d’exécution.
4. Rediriger la sortie de trace vers un fichier
Vous pouvez utiliser l’indicateur -o avec la commande strace pour enregistrer la sortie strace dans le fichier spécifié.
strace -o file_out.txt ls file1.txt
Vous pouvez maintenant afficher le contenu du fichier file_out.txt avec la commande suivante :
cat file_out.txt
5. Imprimer le temps passé sur les appels système
Pour imprimer l’horodatage relatif de chaque appel, utilisez l’indicateur -r comme indiqué ci-dessous.
strace -r ls file1.txt
Sortir
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 +++
Pour afficher la différence de temps entre le début et la fin de chaque appel système effectué par la commande ls, utilisez l’option -T.
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, "177ELF211 3 >