Ladenie nasadenia softvéru pomocou strace

Ladenie nasadenia softvéru pomocou strace

Mojou každodennou prácou je väčšinou nasadzovanie softvéru, čo znamená, že trávim veľa času hľadaním odpovedí na otázky ako:

  • Tento softvér funguje pre vývojárov, ale nie pre mňa. prečo?
  • Včera mi tento softvér fungoval, ale dnes už nie. prečo?

Ide o druh ladenia, ktorý sa mierne líši od bežného ladenia softvéru. Pravidelné ladenie je o logike kódu, ale ladenie nasadenia je o interakcii medzi kódom a prostredím. Aj keď je koreňom problému logická chyba, skutočnosť, že všetko funguje na jednom stroji a na druhom nie, znamená, že problém je nejakým spôsobom v prostredí.

Takže namiesto bežných nástrojov na ladenie, ako je gdb Mám inú sadu nástrojov na nasadenie ladenia. A môj obľúbený nástroj na riešenie problému typu „Prečo mi tento softvér nefunguje?“ volal strace.

Čo je to strace?

strace je nástroj na „sledovanie systémových hovorov“. Pôvodne bol vytvorený pre Linux, ale rovnaké triky na ladenie je možné vykonať pomocou nástrojov pre iné systémy (DTrace alebo ktrace).

Základná aplikácia je veľmi jednoduchá. Stačí spustiť strace s akýmkoľvek príkazom a vypíše všetky systémové volania (aj keď si ho budete musieť najskôr nainštalovať sami 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 +++

Aké sú tieto systémové volania? Toto je niečo ako API pre jadro operačného systému. Kedysi mal softvér priamy prístup k hardvéru, na ktorom bežal. Ak potreboval napríklad niečo zobraziť na obrazovke, pohral sa s portami alebo pamäťovo mapovanými registrami pre video zariadenia. Keď sa multitaskingové počítačové systémy stali populárnymi, vládol chaos, keď rôzne aplikácie bojovali o hardvér. Chyby v jednej aplikácii by mohli zničiť ostatné, ak nie celý systém. Potom sa v CPU objavili režimy privilégií (alebo „ochrana zvonenia“). Jadro sa stalo najprivilegovanejším: získalo plný prístup k hardvéru, čím sa vytvorili menej privilegované aplikácie, ktoré už museli požiadať o prístup z jadra, aby mohli interagovať s hardvérom prostredníctvom systémových volaní.

Na binárnej úrovni sa systémové volanie mierne líši od jednoduchého volania funkcie, ale väčšina programov používa obal v štandardnej knižnici. Tie. štandardná knižnica POSIX C obsahuje volanie funkcie napíš (), ktorý obsahuje celý kód špecifický pre architektúru pre systémové volanie písať.

Ladenie nasadenia softvéru pomocou strace

Stručne povedané, akákoľvek interakcia medzi aplikáciou a jej prostredím (počítačovými systémami) sa uskutočňuje prostredníctvom systémových volaní. Preto, keď softvér funguje na jednom počítači, ale nie na inom, bolo by dobré pozrieť sa na výsledky sledovania systémových hovorov. Presnejšie povedané, tu je zoznam typických bodov, ktoré možno analyzovať pomocou sledovania systémových volaní:

  • I/O konzoly
  • Sieťové I/O
  • Prístup k súborovému systému a súbor I/O
  • Správa životnosti procesného vlákna
  • Nízkoúrovňová správa pamäte
  • Prístup ku konkrétnym ovládačom zariadení

Kedy použiť strace?

Teoreticky, strace používa sa s akýmkoľvek programom v užívateľskom priestore, pretože každý program v užívateľskom priestore musí vykonávať systémové volania. Funguje efektívnejšie s kompilovanými programami na nízkej úrovni, ale funguje aj s jazykmi na vysokej úrovni, ako je Python, ak dokážete odstrániť dodatočný šum z prostredia runtime a tlmočníka.

V celej svojej kráse strace prejaví sa pri ladení softvéru, ktorý na jednom stroji funguje dobre, no na druhom zrazu prestane fungovať, čím vznikajú nejasné hlášky o súboroch, povoleniach, či neúspešných pokusoch o vykonanie nejakých príkazov alebo niečoho iného... Škoda, ale nejde sa tak dobre kombinujú s problémami na vysokej úrovni, ako sú chyby overovania certifikátov. Zvyčajne to vyžaduje kombináciu strace, Niekedy ltrace a nástroje vyššej úrovne (napríklad nástroj príkazového riadka OpenSSL na ladenie certifikátu).

Ako príklad použijeme samostatný server, ale sledovanie systémových hovorov možno často vykonať na zložitejších platformách nasadenia. Stačí si vybrať správne nástroje.

Jednoduchý príklad ladenia

Povedzme, že chcete spustiť úžasnú serverovú aplikáciu foo a skončíte s týmto:

$ foo
Error opening configuration file: No such file or directory

Zrejme nenašiel konfiguračný súbor, ktorý ste napísali. Stáva sa to preto, že niekedy, keď správcovia balíkov kompilujú aplikáciu, prepíšu očakávané umiestnenie súborov. A ak budete postupovať podľa inštalačného sprievodcu pre jednu distribúciu, v inej nájdete súbory úplne iné, ako ste očakávali. Problém by sa dal vyriešiť za pár sekúnd, ak by chybové hlásenie povedalo, kde hľadať konfiguračný súbor, ale nie. Kde teda hľadať?

Ak máte prístup k zdrojovému kódu, môžete si ho prečítať a všetko zistiť. Dobrý plán zálohovania, ale nie najrýchlejšie riešenie. Môžete sa uchýliť k ladiacemu nástroju krok za krokom gdb a uvidíte, čo program robí, ale oveľa efektívnejšie je použiť nástroj, ktorý je špeciálne navrhnutý na zobrazenie interakcie s prostredím: strace.

Výkon strace sa môže zdať nadbytočné, ale dobrou správou je, že väčšinu z nich možno pokojne ignorovať. Na uloženie výsledkov sledovania do samostatného súboru je často užitočné použiť operátor -o:

$ strace -o /tmp/trace foo
Error opening configuration file: No such file or directory
$ cat /tmp/trace
execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0
brk(NULL)                               = 0x56363b3fb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
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 (No such file or directory)
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, "Error opening configuration file"..., 60) = 60
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

Približne celá prvá strana výstupu strace - Toto je zvyčajne príprava na spustenie na nízkej úrovni. (Veľa hovorov mmap, mprotect, brko na veci, ako je detekcia nízkoúrovňovej pamäte a zobrazenie dynamických knižníc.) V skutočnosti počas ladenia výstup strace Je lepšie čítať od konca. Nižšie bude výzva písať, ktorá zobrazí chybové hlásenie. Pozrieme sa hore a vidíme prvé chybné systémové volanie – volanie openat, čo vyvolá chybu ENOENT („súbor alebo adresár sa nenašiel“) pri pokuse o otvorenie /etc/foo/config.json. Tu by mal byť konfiguračný súbor.

Toto bol len príklad, ale povedal by som, že 90% času, ktorý používam strace, nie je nič oveľa ťažšie ako toto. Nižšie je uvedený úplný sprievodca ladením krok za krokom:

  • Rozčuľujte sa kvôli vágnej správe o systémovej chybe programu
  • Reštartujte program pomocou strace
  • Nájdite chybové hlásenie vo výsledkoch sledovania
  • Choďte vyššie, kým nezasiahnete prvé neúspešné systémové volanie

Je veľmi pravdepodobné, že systémové volanie v kroku 4 odhalí, čo sa pokazilo.

Tipy

Predtým, ako vám ukážem príklad zložitejšieho ladenia, ukážem vám pár trikov na efektívne využitie strace:

muž je tvoj priateľ

Na mnohých *nix systémoch možno úplný zoznam systémových volaní jadra získať spustením muž systémové volania. Uvidíte veci ako brk(2), čo znamená, že viac informácií je možné získať spustením muž 2 brk.

Malé hrable: muž 2 vidlička ukazuje mi stránku shellu vidlička() в GNU libc, ktorý, ako sa ukázalo, sa realizuje volaním klon(). Sémantika hovoru vidlica zostáva rovnaký, ak napíšete program pomocou vidlička()a spustiť sledovanie – nenájdem žiadne hovory vidlica, namiesto nich tam bude klon(). Takéto hrable vás zmiatnu len vtedy, ak začnete porovnávať zdroj s výstupom strace.

Použite -o na uloženie výstupu do súboru

strace môže generovať rozsiahly výstup, takže je často užitočné ukladať výsledky sledovania do samostatných súborov (ako v príklade vyššie). To tiež pomáha vyhnúť sa zámene výstupu programu s výstupom strace v konzole.

Na zobrazenie ďalších údajov argumentov použite -s

Možno ste si všimli, že druhá polovica chybového hlásenia nie je zobrazená vo vyššie uvedenom príklade sledovania. Je to pretože strace predvolená hodnota zobrazuje iba prvých 32 bajtov argumentu reťazca. Ak chcete vidieť viac, pridajte niečo podobné -s 128 na hovor strace.

-y uľahčuje sledovanie súborov, soketov atď.

"Všetko je súbor" znamená, že systémy *nix vykonávajú všetky I/O pomocou deskriptorov súborov, či už sa to týka súboru alebo siete alebo medziprocesových kanálov. To je vhodné na programovanie, ale sťažuje sledovanie toho, čo sa skutočne deje, keď vidíte spoločné čítať и písať vo výsledkoch sledovania systémových volaní.

Pridaním operátora , prinútite strace označte každý deskriptor súboru vo výstupe poznámkou, na čo ukazuje.

Pripojte k už spustenému procesu pomocou -p**

Ako uvidíte z príkladu nižšie, niekedy potrebujete sledovať program, ktorý už beží. Ak je známe, že beží ako proces 1337 (povedzme z výstupu ps), potom to môžete sledovať takto:

$ strace -p 1337
...system call trace output...

Možno budete potrebovať práva root.

Na monitorovanie podriadených procesov použite -f

strace Štandardne sleduje iba jeden proces. Ak tento proces vytvorí podriadené procesy, potom je možné vidieť systémové volanie na vytvorenie podriadeného procesu, ale systémové volania podriadeného procesu sa nezobrazia.

Ak si myslíte, že chyba je v podradenom procese, použite príkaz -f, umožní to jeho sledovanie. Nevýhodou je, že výstup vás ešte viac zmätie. Kedy strace sleduje jeden proces alebo jedno vlákno, zobrazuje jeden prúd udalostí hovorov. Keď sleduje viacero procesov naraz, môže sa stať, že začiatok hovoru preruší správa , potom - kopa výziev pre ďalšie exekučné pobočky a až potom - koniec prvej <...foocall obnovený>. Alebo rozdeľte všetky výsledky sledovania do rôznych súborov aj pomocou operátora -ff (podrobnosti v vedenie na strace).

Filtrujte stopy pomocou -e

Ako vidíte, výsledkom sledovania je skutočná kopa všetkých možných systémových volaní. Vlajka -e Stopu môžete filtrovať (pozri sprievodca na strace). Hlavnou výhodou je, že je rýchlejšie spustiť filtrované sledovanie ako vykonať úplné sledovanie a potom greppri. Úprimne povedané, takmer vždy mi je to jedno.

Nie všetky chyby sú zlé

Jednoduchým a bežným príkladom je program, ktorý hľadá súbor na niekoľkých miestach naraz, napríklad shell hľadajúci adresár, ktorý obsahuje spustiteľný súbor:

$ strace sh -c uname
...
stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0
...

Heuristiky ako „posledná neúspešná požiadavka pred nahlásením chyby“ sú dobré pri hľadaní relevantných chýb. Nech je to akokoľvek, je logické začať úplne od konca.

Kurzy programovania v C vám môžu pomôcť pochopiť systémové volania.

Štandardné volania do C knižníc nie sú systémové volania, ale len tenká povrchová vrstva. Ak teda aspoň trochu rozumiete, ako a čo robiť v C, ľahšie pochopíte výsledky sledovania systémových volaní. Napríklad máte problém s ladením hovorov do sieťových systémov, pozrite sa na rovnakú klasiku Bija's Guide to Network Programming.

Zložitejší príklad ladenia

Už som povedal, že príklad jednoduchého ladenia je ukážkou toho, s čím sa väčšinou musím pri práci potýkať strace. Niekedy je však potrebné skutočné vyšetrovanie, takže tu je skutočný príklad pokročilejšieho ladenia.

bcron - plánovač spracovania úloh, ďalšia implementácia démona *nix cron. Je nainštalovaný na serveri, ale keď sa niekto pokúsi upraviť plán, stane sa toto:

# crontab -e -u logs
bcrontab: Fatal: Could not create temporary file

Dobre, to znamená bcron pokúsil napísať určitý súbor, ale nevyšlo to a neprizná prečo. Odkrývanie 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 +++

Na samom konci je chybové hlásenie písať, ale tentoraz je niečo iné. Po prvé, neexistuje žiadna relevantná chyba systémového volania, ktorá sa zvyčajne vyskytuje predtým. Po druhé, je jasné, že niekde už niekto čítal chybové hlásenie. Zdá sa, že skutočný problém je niekde inde a bcrontab jednoducho prehrá správu.

Ak sa pozriete na muž 2 čítal, môžete vidieť, že prvý argument (3) je deskriptor súboru, ktorý *nix používa na všetko I/O spracovanie. Ako zistím, čo predstavuje deskriptor súboru 3? V tomto konkrétnom prípade môžete bežať strace s operátorom (pozri vyššie) a automaticky vám to povie, ale ak chcete zistiť takéto veci, je užitočné vedieť, ako čítať a analyzovať výsledky sledovania.

Zdrojom deskriptora súboru môže byť jedno z mnohých systémových volaní (všetko závisí od toho, na čo je deskriptor určený – konzola, sieťový soket, samotný súbor alebo niečo iné), ale nech je to akokoľvek, hľadáme volá vrátením 3 (t. j. hľadáme „= 3“ vo výsledkoch sledovania). V tomto výsledku sú 2 z nich: openat na samom vrchole a zásuvka V strede. openat otvorí súbor ale zavrieť(3) potom ukáže, že sa opäť zatvára. (Rake: deskriptory súborov môžu byť znovu použité, keď sú otvorené a zatvorené). Zavolajte zásuvka() vhodné, pretože je to posledné predtým čítať()a ukázalo sa, že bcrontab s niečím pracuje cez zásuvku. Ďalší riadok ukazuje, že je priradený deskriptor súboru soket domény unix na ceste /var/run/bcron-spool.

Musíme teda nájsť proces, ktorý je s tým spojený unixová zásuvka na druhej strane. Na tento účel existuje niekoľko šikovných trikov, z ktorých oba sú užitočné pri ladení nasadení servera. Prvým je použitie netstat alebo novšie ss (stav zásuvky). Oba príkazy zobrazujú aktívne sieťové pripojenia systému a prijímajú výpis -l popísať počúvacie zásuvky, ako aj operátora -p na zobrazenie programov pripojených k soketu ako klienta. (Existuje mnoho ďalších užitočných možností, ale tieto dve sú na túto úlohu dostatočné.)

# ss -pl | grep /var/run/bcron-spool
u_str LISTEN 0   128   /var/run/bcron-spool 1466637   * 0   users:(("unixserver",pid=20629,fd=3))

To naznačuje, že poslucháč je príkazom inixserver, beží s ID procesu 20629. (A zhodou okolností používa deskriptor súboru 3 ako soket.)

Druhý skutočne užitočný nástroj na vyhľadávanie rovnakých informácií je tzv tiež. Uvádza zoznam všetkých otvorených súborov (alebo deskriptorov súborov) v systéme. Alebo môžete získať informácie o jednom konkrétnom súbore:

# 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=STREAM

Proces 20629 je server s dlhou životnosťou, takže ho môžete pripojiť strace pomocou niečoho podobného strace -o /tmp/trace -p 20629. Ak upravíte úlohu cron v inom termináli, dostanete výstup sledovania s chybou. A tu je výsledok:

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 (To be restarted if SA_RESTART is set)
--- 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 (No child processes)
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 (To be restarted if SA_RESTART is set)
--- 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 (No child processes)
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

(Posledný súhlasiť() sa pri sledovaní nedokončí.) Tento výsledok opäť bohužiaľ neobsahuje chybu, ktorú hľadáme. Nevidíme žiadne správy, ktoré bcrontag odosiela alebo prijíma zo zásuvky. Namiesto toho dokončite riadenie procesu (klonovať, počkaj4, SIGCHLD atď.) Tento proces vytvára podriadený proces, ktorý, ako by ste mohli hádať, robí skutočnú prácu. A ak potrebujete zachytiť jej stopu, pridajte sa k hovoru strace -f. To je to, čo nájdeme, keď hľadáme chybové hlásenie v novom výsledku so 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 denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

Tak to je niečo. Proces 21470 dostane chybu „prístup odmietnutý“ pri pokuse o vytvorenie súboru na ceste tmp/spool.21470.1573692319.854640 (týka sa aktuálneho pracovného adresára). Ak by sme len poznali aktuálny pracovný adresár, poznali by sme aj úplnú cestu a boli by sme schopní zistiť, prečo v ňom proces nemôže vytvoriť svoj dočasný súbor. Bohužiaľ, proces už skončil, takže nemôžete len použiť lsof -p 21470 aby ste našli aktuálny adresár, ale môžete pracovať aj opačným smerom - hľadajte systémové volania PID 21470, ktoré menia adresár. (Ak žiadne neexistujú, PID 21470 ich musel zdediť od svojho rodiča, a to už cez lsof -p sa nedá zistiť.) Toto systémové volanie je chdir (čo sa dá ľahko zistiť pomocou moderných online vyhľadávačov). A tu je výsledok spätného vyhľadávania na základe výsledkov sledovania až po server 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 denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

(Ak ste sa stratili, možno si budete chcieť prečítať môj predchádzajúci príspevok o *nix riadení procesov a shelloch.) Server PID 20629 teda nedostal povolenie na vytvorenie súboru na ceste /var/spool/cron/tmp/spool.21470.1573692319.854640. S najväčšou pravdepodobnosťou je to spôsobené klasickým nastavením povolení súborového systému. Skontrolujme to:

# 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-spool

Tam je pes zakopaný! Server beží ako užívateľský cron, ale iba root má oprávnenie zapisovať do adresára /var/spool/cron/tmp/. Jednoduchý príkaz chown cron /var/spool/cron/tmp/ prinúti bcron pracovať správne. (Ak to nebol problém, potom ďalším najpravdepodobnejším podozrivým je bezpečnostný modul jadra ako SELinux alebo AppArmor, takže by som skontroloval protokol správ jadra pomocou dmesg.)

Celkom

Sledovanie systémových volaní môže byť pre začiatočníka zdrvujúce, ale dúfam, že som ukázal, že ide o rýchly spôsob odladenia celej triedy bežných problémov s nasadením. Predstavte si, že sa pokúšate odladiť multiproces bcronpomocou ladiaceho nástroja krok za krokom.

Analýza výsledkov sledovania spätne pozdĺž reťazca systémových volaní vyžaduje zručnosť, ale ako som povedal, takmer vždy používanie strace, práve dostanem výsledok sledovania a hľadám chyby od konca. každopádne, strace pomáha mi ušetriť veľa času pri ladení. Dúfam, že to bude užitočné aj pre vás.

Zdroj: hab.com

Pridať komentár