
Mon travail principal consiste principalement à déployer des systèmes logiciels, donc je passe beaucoup de temps à essayer de répondre à ce genre de questions :
- Le logiciel fonctionne pour le développeur, mais pas pour moi. Pourquoi ?
- Hier, le logiciel fonctionnait chez moi, mais aujourd'hui, il ne fonctionne pas. Pourquoi ?
C'est une sorte de débogage qui diffère un peu du débogage traditionnel. Le débogage traditionnel concerne la logique du code, tandis que le débogage de déploiement porte sur l'interaction entre le code et l'environnement. Même si la racine du problème est une erreur logique, le fait que cela fonctionne sur une machine mais pas sur une autre signifie que cela concerne d'une certaine manière l'environnement.
C'est pourquoi, au lieu des outils de débogage habituels comme gdb j'utilise un autre ensemble d'outils pour le débogage de déploiement. Et mon outil préféré pour résoudre le problème du type « Pourquoi ce logiciel ne fonctionne-t-il pas chez moi ? » s'appelle strace.
Qu'est-ce que strace ?
— c'est un outil de « traçage des appels système ». Il a été initialement conçu pour Linux, mais les mêmes fonctionnalités de débogage peuvent être réalisées avec des outils pour d'autres systèmes ( ou ).
Son utilisation principale est très simple. Il suffit de lancer strace avec n'importe quelle commande, et il va envoyer toutes les appels système dans un dump (il faudra probablement d'abord installer strace lui-même) strace):
$ strace echo Hello
...Snip lots of stuff...
write(1, "Hellon", 6) = 6
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++Qu'est-ce que ces appels système ? C'est quelque chose comme une API pour le noyau du système d'exploitation. Il y a longtemps, les logiciels avaient un accès direct au « matériel » sur lequel ils fonctionnaient. Par exemple, si quelque chose devait être affiché à l'écran, il interagissait avec les ports ou les registres de mémoire affichables pour les dispositifs vidéo. Lorsque les systèmes informatiques multitâches sont devenus populaires, le chaos s'est installé, car différentes applications se disputaient le « matériel ». Des erreurs dans une application pouvaient faire planter le fonctionnement des autres, voire de l'ensemble du système. C'est alors que les modes de privilège (ou « protection par anneau ») sont apparus dans le CPU. Le noyau devenait le plus privilégié : il avait un accès complet au « matériel », créant des applications moins privilégiées qui devaient demander l'accès au noyau pour interagir avec le « matériel » — via des appels système.
Au niveau binaire, l'appel système diffère légèrement d'un appel de fonction ordinaire, mais la plupart des programmes utilisent un wrapper dans la bibliothèque standard. C'est-à-dire que la bibliothèque POSIX C contient l'appel de fonction write(), qui contient tout le code dépendant de l'architecture pour l'appel système write.

En résumé, toute interaction d'une application avec son environnement (systèmes informatiques) se fait par le biais d'appels systèmes. Ainsi, lorsque le logiciel fonctionne sur une machine mais pas sur une autre, il est judicieux de jeter un œil aux résultats de la trace des appels systèmes. Plus précisément, voici une liste de points typiques que l'on peut analyser à l'aide d'une trace d'appels système :
- Entrée-sortie console
- Entrée-sortie réseau
- Accès au système de fichiers et entrée-sortie de fichiers
- Gestion de la durée de vie des processus/threads
- Gestion basse niveau de la mémoire
- Accès aux pilotes de dispositifs spéciaux
Quand utiliser strace ?
En théorie, strace il est utilisé avec n'importe quel programme dans l'espace utilisateur, car tout programme dans l'espace utilisateur doit effectuer des appels systèmes. Il fonctionne plus efficacement avec des programmes compilés et bas niveau, mais il fonctionne également avec des langages de haut niveau comme Python, à condition de pouvoir se frayer un chemin à travers le bruit supplémentaire provenant de l'environnement d'exécution et de l'interpréteur.
Dans toute sa splendeur strace il se révèle lors du débogage de logiciels qui fonctionnent bien sur une machine, mais qui cessent soudainement de fonctionner sur une autre, donnant des messages vagues concernant des fichiers, des permissions ou des tentatives échouées d'exécuter certaines commandes ou autres... Malheureusement, il ne s'associe pas aussi bien avec des problèmes de haut niveau tels que les erreurs de vérification des certificats. Généralement, une combinaison de strace, parfois et d'outils de niveau supérieur (comme l'outil en ligne de commande openssl pour le débogage des certificats).
Prenons par exemple le travail sur un serveur isolé, mais la trace des appels systèmes peut souvent être exécutée sur des plateformes de déploiement plus complexes. Il suffit de choisir les outils appropriés.
Exemple de débogage simple
Disons que vous souhaitez lancer une application serveur extraordinaire foo, et voici ce qui se passe :
$ foo
Erreur lors de l'ouverture du fichier de configuration : Aucun fichier ou répertoire de ce typeIl est évident qu'il n'a pas réussi à trouver le fichier de configuration que vous avez écrit. Cela se produit parfois, car lorsque les gestionnaires de paquets compilent des applications, ils redéfinissent l'emplacement attendu des fichiers. Si l'on suit le guide d'installation pour une distribution, on trouve souvent les fichiers complètement ailleurs que prévu dans une autre. Le problème pourrait être résolu en quelques secondes si le message d'erreur indiquait où chercher le fichier de configuration, mais il ne le fait pas. Alors, où chercher ?
S'il y a accès au code source, on peut le lire et tout comprendre. C'est un bon plan de secours, mais ce n'est pas la solution la plus rapide. On pourrait utiliser un débogueur pas à pas comme gdb et voir ce que fait le programme, mais il est bien plus efficace d'utiliser un outil spécialement conçu pour montrer l'interaction avec l'environnement : strace.
Sortie strace cela peut sembler excessif, mais la bonne nouvelle est que la plus grande partie peut être ignorée sans souci. Il est souvent utile d'utiliser l'option -o pour enregistrer les résultats de la trace dans un fichier séparé :
$ strace -o /tmp/trace foo
Erreur lors de l'ouverture du fichier de configuration : Aucun fichier ou dossier de ce type
$ cat /tmp/trace
execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0
brk(NULL) = 0x56363b3fb000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun fichier ou dossier de ce type)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=25186, ...}) = 0
mmap(NULL, 25186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f12cf1000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 > 1 260A2 "..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f12cef000
mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f12b2e000
mprotect(0x7f2f12b50000, 1658880, PROT_NONE) = 0
mmap(0x7f2f12b50000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f12b50000
mmap(0x7f2f12c98000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7f2f12c98000
mmap(0x7f2f12ce5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7f2f12ce5000
mmap(0x7f2f12ceb000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f12ceb000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f2f12cf0500) = 0
mprotect(0x7f2f12ce5000, 16384, PROT_READ) = 0
mprotect(0x56363b08b000, 4096, PROT_READ) = 0
mprotect(0x7f2f12d1f000, 4096, PROT_READ) = 0
munmap(0x7f2f12cf1000, 25186) = 0
openat(AT_FDCWD, "/etc/foo/config.json", O_RDONLY) = -1 ENOENT (Aucun fichier ou dossier de ce type)
dup(2) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
brk(NULL) = 0x56363b3fb000
brk(0x56363b41c000) = 0x56363b41c000
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x8), ...}) = 0
write(3, "Erreur lors de l'ouverture du fichier de configuration"..., 60) = 60
close(3) = 0
exit_group(1) = ?
+++ sorti avec 1 +++Environ toute la première page de sortie strace — c'est généralement une préparation de bas niveau au lancement. (Beaucoup d'appels mmap, mprotect, brk pour les éléments de type détection de la mémoire basse niveau et chargement de bibliothèques dynamiques.) En fait, lors du débogage, les sorties strace se lisent mieux depuis la fin. En bas, il y aura un appel write, affichant un message d'erreur. Regardons au-dessus, et voyons le premier appel système échoué — l'appel openat, produisant l'erreur ENOENT (« fichier ou répertoire introuvable »), tentant d'ouvrir /etc/foo/config.json. Voici où le fichier de configuration devrait se trouver.
C'était juste un exemple, mais je dirais que 90 % du temps, lorsque j'utilise strace, il n'est pas nécessaire d'exécuter quelque chose de beaucoup plus compliqué que cela. Ci-dessous — un guide étape par étape complet pour le débogage :
- Ne pas se décourager à cause d'un message d'erreur non clair de system-y provenant du programme
- Redémarrer le programme avec strace
- Rechercher dans les résultats de traçage le message d'erreur
- Monter, jusqu'à ce que vous rencontriez le premier appel système échoué
Il est très probable que l'appel système à la 4ème étape montre ce qui s'est mal passé.
Indices
Avant de montrer un exemple de débogage plus complexe, je vais vous donner quelques astuces pour une utilisation efficace. strace:
man — votre ami
Sur de nombreux systèmes *nix, vous pouvez obtenir la liste complète des appels système au noyau en exécutant man syscalls. Vous verrez des éléments comme brk(2), ce qui signifie que vous pouvez obtenir plus d'informations en exécutant man 2 brk.
Petits pièges : man 2 fork me montre la page pour le shell fork() dans GNU libc, qui, apparemment, est implémentée à l'aide de l'appel clone(). La sémantique de l'appel fork reste la même si vous écrivez un programme utilisant fork(), et lancez le traçage — je ne trouverai pas les appels fork, à la place, il y aura clone(). De tels pièges ne font que semer la confusion si l'on commence à comparer le code source avec la sortie strace.
Utilisez -o pour enregistrer la sortie dans un fichier
strace peut générer une sortie vaste, il est donc souvent utile de conserver les résultats du traçage dans des fichiers séparés (comme dans l'exemple ci-dessus). Cela aide également à ne pas confondre la sortie du programme avec la sortie strace dans le terminal.
Utilisez -s pour voir plus de données sur l'argument
Vous avez sûrement remarqué que la seconde moitié du message d'erreur n'est pas affichée dans l'exemple de traçage ci-dessus. C'est parce que strace affiche par défaut seulement les premiers 32 octets de la chaîne d'arguments. Si vous voulez voir plus, ajoutez quelque chose comme -s 128 à l'appel strace.
-u facilite le suivi des fichiers, des sockets, etc.
«Tout est fichier» signifie que les systèmes *nix effectuent toutes les entrées-sorties en utilisant des descripteurs de fichiers, qu'ils soient appliqués à des fichiers, à un réseau ou à des canaux interprocessus. C'est pratique pour la programmation, mais cela complique le suivi de ce qui se passe réellement lorsque vous voyez des éléments communs read et write dans les résultats du traçage des appels système.
En ajoutant l'opérateur -u, vous ferez en sorte que strace annote chaque descripteur de fichier dans la sortie avec une note indiquant à quoi il se réfère.
Attachez-vous à un processus déjà en cours avec -p**
Comme illustré dans l'exemple ci-dessous, il arrive parfois que vous ayez besoin de traquer un programme qui est déjà en cours d'exécution. Si vous savez qu'il est en cours en tant que processus 1337 (par exemple, d'après les sorties de ps), vous pouvez le traquer comme ceci :
$ strace -p 1337
...sortie de traçage des appels système...Vous aurez peut-être besoin des droits root.
Utilisez -f pour suivre les processus enfants
strace Par défaut, il suit un seul processus. Si ce processus engendre des processus enfants, vous pouvez voir l'appel système pour créer le processus enfant, mais les appels système du processus enfant ne seront pas affichés.
Si vous pensez que l'erreur se situe dans le processus enfant, utilisez l'opérateur -f, cela activera son suivi. Le désavantage est que la sortie vous embrouillera encore plus. Quand strace vous suivez un processus ou un seul chemin, il montre un flux unique d'appels d'événements. Quand il suit plusieurs processus à la fois, vous pourriez voir le début d'un appel, interrompu par le message <unfinished …>, puis un tas d'appels pour d'autres branches d'exécution, et seulement ensuite la fin du premier avec <… foocall resumed>. Ou séparez tous les résultats de suivi dans différents fichiers, en utilisant également l'opérateur -ff (détails dans pour strace).
Filtrez le suivi à l'aide de -e
Comme vous pouvez le voir, le résultat du suivi est un véritable mélange de tous les appels système possibles. Avec le drapeau -e vous pouvez filtrer le suivi (voir pour strace). L'avantage principal est que lancer un suivi avec filtrage est plus rapide que de faire un suivi complet, puis grepde filtrer. Pour être honnête, je m'en fiche presque toujours.
Toutes les erreurs ne sont pas mauvaises.
Un exemple simple et courant est un programme qui cherche un fichier à plusieurs endroits, comme un shell qui cherche où se trouve le fichier exécutable dans la corbeille :
$ strace sh -c uname
...
stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (Aucun fichier ou annuaire)
stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (Aucun fichier ou annuaire)
stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0
...L'heuristique du type « dernier appel échoué avant le message d'erreur » est utile pour trouver des erreurs pertinentes. Quoi qu'il en soit, il est logique de commencer par la fin.
Comprendre les appels système est facilité par les guides de programmation en langage C.
Les appels standards aux bibliothèques C ne sont pas des appels système, mais seulement une fine couche superficielle. Donc, si vous comprenez un peu comment et quoi faire en C, il vous sera plus facile de comprendre les résultats du suivi des appels systèmes. Par exemple, si vous avez des problèmes avec le débogage des appels aux systèmes réseau, consultez le classique .
Un exemple de débogage un peu plus complexe.
J'ai déjà dit que l'exemple de débogage simple est un exemple de ce avec quoi je suis, pour la plupart, confronté dans mon travail avec strace. Cependant, il arrive parfois qu'une véritable enquête soit nécessaire, alors voici un véritable exemple de débogage un peu plus complexe.
— un planificateur de traitement des tâches, une autre implémentation du démon *nix cron. Il est installé sur le serveur, mais lorsque quelqu'un essaie d'éditer le programme, voici ce qui se passe :
# crontab -e -u logs
bcrontab: Fatal: Could not create temporary fileTrès bien, donc bcron il a essayé d'écrire un certain fichier, mais cela n'a pas fonctionné, et il ne sait pas pourquoi. Passons à strace:
# strace -o /tmp/trace crontab -e -u logs
bcrontab: Fatal: Could not create temporary file
# cat /tmp/trace
...
openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
read(3, "#Ansible: logsaggn20 14 * * * lo"..., 8192) = 150
read(3, "", 8192) = 0
munmap(0x7f82049b4000, 8192) = 0
close(3) = 0
socket(AF_UNIX, SOCK_STREAM, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
write(3, "156:Slogs #Ansible: logsaggn20 1"..., 161) = 161
read(3, "32:ZCould not create temporary f"..., 8192) = 36
munmap(0x7f82049b4000, 8192) = 0
close(3) = 0
write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49
unlink("bcrontab.14779.1573691864.847933") = 0
exit_group(111) = ?
+++ exited with 111 +++Vers la fin, il y a un message d'erreur write, mais cette fois, quelque chose est différent. Tout d'abord, il n'y a pas d'erreur pertinente de l'appel système qui se produit généralement avant cela. Deuxièmement, il est évident que quelqu'un a déjà lu le message d'erreur quelque part. Il semble que le véritable problème soit ailleurs, et bcrontab simplement reproduit le message.
Si l'on regarde man 2 read, on peut voir que le premier argument (3) est le descripteur de fichier que *nix utilise pour tous les traitements d'entrée-sortie. Comment savoir ce que représente le descripteur de fichier 3 ? Dans ce cas particulier, on peut exécuter strace avec l'opérateur -u (voir ci-dessus), et il racontera automatiquement, cependant, pour calculer ce genre de choses, il est utile de savoir comment lire et analyser les résultats de la traçabilité.
La source du descripteur de fichier peut être l'un des nombreux appels système (cela dépend de l'usage du descripteur — pour la console, une socket réseau, un fichier proprement dit ou autre chose), mais quoi qu'il en soit, nous cherchons les appels qui retournent 3 (c'est-à-dire que nous cherchons « = 3 » dans les résultats de la traçabilité). Dans ce résultat, il y en a 2 : openat en haut et socket au milieu. openat ouvre le fichier, mais close(3) après cela montrera qu'il se ferme à nouveau. (Attention : les descripteurs de fichiers peuvent être réutilisés lorsqu'ils sont ouverts et fermés). L'appel socket() est pertinent car c'est le dernier avant read(), et il s'avère que bcrontab travaille avec quelque chose via une socket. La ligne suivante montre que le descripteur de fichier est lié à unix domain socket au chemin /var/run/bcron-spool.
Donc, il faut trouver le processus lié à unix socket de l'autre côté. Pour cela, il existe quelques astuces élégantes, et les deux seront utiles pour déboguer les déploiements de serveurs. La première consiste à utiliser netstat ou un plus récent ss (statut du socket). Les deux commandes montrent les connexions réseau actives du système et prennent l'opérateur -l pour décrire les sockets à l'écoute, ainsi que l'opérateur -p pour afficher les programmes connectés au socket en tant que clients. (Il existe de nombreuses options utiles, mais pour cette tâche, celles-ci suffisent.)
# ss -pl | grep /var/run/bcron-spool
u_str LISTEN 0 128 /var/run/bcron-spool 1466637 * 0 users:(("unixserver",pid=20629,fd=3))Cela signifie que l'écoute est la commande inixserver, fonctionnant avec l'ID de processus 20629. (Et, par coïncidence, elle utilise le descripteur de fichiers 3 comme socket.)
Le deuxième outil vraiment utile pour trouver la même information s'appelle lsof. Il répertorie tous les fichiers ouverts (ou descripteurs de fichiers) dans le système. Ou il est possible d'obtenir des informations sur un fichier spécifique :
# lsof /var/run/bcron-spool
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
unixserve 20629 cron 3u unix 0x000000005ac4bd83 0t0 1466637 /var/run/bcron-spool type=STREAMLe processus 20629 est un serveur de longue durée, donc il est possible de s'y attacher strace avec quelque chose comme strace -o /tmp/trace -p 20629. Si vous modifiez la tâche cron dans un autre terminal, vous obtiendrez la sortie des résultats de traçage avec l'erreur apparente. Voici le résultat :
accept(3, NULL, NULL) = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181
close(4) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (à redémarrer si SA_RESTART est réglé)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21181, si_uid=998, si_status=0, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED, NULL) = 21181
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (Aucun processus fils)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]}) = 43
accept(3, NULL, NULL) = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200
close(4) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (à redémarrer si SA_RESTART est réglé)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21200, si_uid=998, si_status=111, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 111}], WNOHANG|WSTOPPED, NULL) = 21200
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (Aucun processus fils)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]}) = 43
accept(3, NULL, NULL)(Le dernier accept() ne sera pas terminé lors du traçage.) Et encore une fois, malheureusement, ce résultat ne contient pas l'erreur que nous recherchons. Nous ne voyons aucun message que bcrontag envoie au socket ou reçoit de lui. Au lieu de cela, il s'agit uniquement de gestion de processus (clone, wait4, SIGCHLD Et autres.) Ce processus génère un processus enfant qui, comme on peut le deviner, effectue le véritable travail. Et si nous devons capturer son empreinte, ajoutez au rappel strace -f. Voici ce que nous trouverons en cherchant un message d'erreur dans le nouveau résultat avec strace -f -o /tmp/trace -p 20629:
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission refusée)
21470 write(1, "32:ZImpossible de créer fichier temporaire f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (Aucun fichier ou répertoire de ce type)
21470 exit_group(111) = ?
21470 +++ sorti avec 111 +++Voilà, cela commence à avoir du sens. Le processus 21470 rencontre une erreur « accès refusé » en tentant de créer un fichier à l'emplacement tmp/spool.21470.1573692319.854640 (relatif au répertoire de travail actuel). Si nous savions simplement quel est le répertoire de travail actuel, nous saurions aussi le chemin complet et pourrions découvrir pourquoi le processus ne peut pas créer son fichier temporaire. Malheureusement, le processus est déjà sorti, donc nous ne pouvons pas simplement utiliser lsof -p 21470 pour trouver le répertoire actuel, mais nous pouvons travailler en arrière — chercher les appels système du PID 21470 qui changent de répertoire. (S'il n'y en a pas, le PID 21470 doit les avoir hérités de son parent, et cela est déjà passé par lsof -p ne le permettra pas.) Cet appel système — chdir (ce qui n'est pas difficile à déterminer avec des moteurs de recherche modernes). Et voici le résultat de la recherche inversée à partir des résultats de traçage, jusqu'au serveur PID 20629 :
20629 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21470
...
21470 execve("/usr/sbin/bcron-spool", ["bcron-spool"], 0x55d2460807e0 /* 27 vars */) = 0
...
21470 chdir("/var/spool/cron") = 0
...
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission refusée)
21470 write(1, "32:ZImpossible de créer fichier temporaire f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (Aucun fichier ou répertoire de ce type)
21470 exit_group(111) = ?
21470 +++ sorti avec 111 +++(Si vous êtes perdu, vous devriez peut-être lire mon précédent article .) Donc, le serveur PID 20629 n'a pas obtenu la permission de créer un fichier à l'emplacement /var/spool/cron/tmp/spool.21470.1573692319.854640. Très probablement, cela est dû aux réglages classiques des permissions du système de fichiers. Vérifions :
# ls -ld /var/spool/cron/tmp/
drwxr-xr-x 2 root root 4096 Nov 6 05:33 /var/spool/cron/tmp/
# ps u -p 20629
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
cron 20629 0.0 0.0 2276 752 ? Ss Nov14 0:00 unixserver -U /var/run/bcron-spool -- bcron-spoolVoilà où le bât blesse ! Le serveur fonctionne comme un cron utilisateur, mais seul root a la permission d'écrire dans le répertoire /var/spool/cron/tmp/. Une simple commande chown cron /var/spool/cron/tmp/ fera en sorte que bcron travailler correctement. (Si le problème ne provient pas de cela, le prochain suspect probable est un module de sécurité du noyau comme SELinux ou AppArmor, donc je vérifierais le journal des messages du noyau avec dmesg.)
Au total
Pour un débutant dans les résultats de trace de système d'appels, il peut être submergé, mais j'espère avoir montré qu'ils sont un moyen rapide de déboguer toute une classe de problèmes de déploiement courants. Imaginez que vous essayez de déboguer une application multiprocessus bcron, en utilisant un débogueur pas à pas.
Analyser les résultats de trace en remontant la chaîne des appels système nécessite de la compétence, mais comme je l'ai dit, en général, en utilisant strace, je prends simplement le résultat de la trace et cherche les erreurs en commençant par la fin. Quoi qu'il en soit, strace cela m'aide à gagner un temps précieux lors du débogage. J'espère que cela vous sera également utile.
Source : habr.com
