Derinimo programinės įrangos diegimas naudojant strace

Derinimo programinės įrangos diegimas naudojant strace

Mano kasdienis darbas daugiausia yra programinės įrangos diegimas, o tai reiškia, kad daug laiko praleidžiu bandydamas atsakyti į tokius klausimus:

  • Ši programinė įranga tinka kūrėjui, bet ne man. Kodėl?
  • Vakar ši programinė įranga man veikė, bet šiandien ne. Kodėl?

Tai tam tikras derinimo būdas, kuris šiek tiek skiriasi nuo įprasto programinės įrangos derinimo. Įprastas derinimas susijęs su kodo logika, o diegimo derinimas yra susijęs su kodo ir aplinkos sąveika. Net jei problemos šaknis yra loginė klaida, tai, kad viskas veikia vienoje mašinoje, o kitoje ne, reiškia, kad problema kažkaip yra aplinkoje.

Taigi vietoj įprastų derinimo įrankių kaip gdb Turiu kitokį derinimo diegimo įrankių rinkinį. Ir mano mėgstamiausias įrankis sprendžiant problemą, pvz., „Kodėl ši programinė įranga man neveikia? paskambino trace.

Kas yra strace?

trace yra „sistemos skambučių sekimo“ įrankis. Iš pradžių jis buvo sukurtas Linux, tačiau tuos pačius derinimo triukus galima atlikti naudojant kitų sistemų įrankius (DTrace arba kttrace).

Pagrindinė programa yra labai paprasta. Jums tereikia paleisti strace su bet kuria komanda ir ji išmes visus sistemos iškvietimus (nors pirmiausia tikriausiai turėsite ją įdiegti patys trace):

$ strace echo Hello
...Snip lots of stuff...
write(1, "Hellon", 6)                  = 6
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Kas yra šie sistemos iškvietimai? Tai kažkas panašaus į operacinės sistemos branduolio API. Kadaise programinė įranga turėjo tiesioginę prieigą prie aparatinės įrangos, kurioje ji veikė. Jei, pavyzdžiui, reikėjo ką nors parodyti ekrane, jis grojo su prievadais arba atmintyje susietais vaizdo įrenginių registrais. Kai išpopuliarėjo daugiafunkcinės kompiuterinės sistemos, įsivyravo chaosas, kai įvairios programos kovojo dėl techninės įrangos. Vienos programos klaidos gali sugadinti kitas, jei ne visą sistemą. Tada CPU atsirado privilegijų režimai (arba „žiedo apsauga“). Branduolys tapo labiausiai privilegijuotu: jis gavo visišką prieigą prie aparatinės įrangos, sukurdamas mažiau privilegijuotas programas, kurios jau turėjo prašyti prieigos iš branduolio, kad galėtų sąveikauti su aparatine įranga per sistemos skambučius.

Dvejetainiu lygmeniu sistemos iškvietimas šiek tiek skiriasi nuo paprasto funkcijos iškvietimo, tačiau dauguma programų standartinėje bibliotekoje naudoja įpakavimą. Tie. POSIX C standartinėje bibliotekoje yra funkcijos iškvietimas rašyti (), kuriame yra visas konkrečiai architektūrai skirtas sistemos skambučio kodas rašyti.

Derinimo programinės įrangos diegimas naudojant strace

Trumpai tariant, bet kokia programos ir jos aplinkos (kompiuterinių sistemų) sąveika vykdoma per sistemos skambučius. Todėl, kai programinė įranga veikia viename įrenginyje, bet ne kitame, būtų gerai pažvelgti į sistemos skambučių sekimo rezultatus. Tiksliau, čia pateikiamas tipiškų momentų, kuriuos galima analizuoti naudojant sistemos skambučio sekimą, sąrašas:

  • Konsolės I/O
  • Tinklo I/O
  • Prieiga prie failų sistemos ir failų I/O
  • Proceso gijos veikimo trukmės valdymas
  • Žemo lygio atminties valdymas
  • Prieiga prie konkrečių įrenginių tvarkyklių

Kada naudoti strace?

Teoriškai, trace naudojamas su bet kuria vartotojo erdvėje esančia programa, nes bet kuri programa vartotojo erdvėje turi atlikti sistemos iškvietimus. Jis veikia efektyviau su kompiliuotomis žemo lygio programomis, bet taip pat veikia su aukšto lygio kalbomis, pvz., Python, jei galite sumažinti papildomą vykdymo laiko ir vertėjo triukšmą.

Visu savo puošnumu trace pasireiškia derinant programinę įrangą, kuri gerai veikia viename kompiuteryje, bet staiga nustoja veikti kitame, gamina neaiškius pranešimus apie failus, leidimus arba nesėkmingus bandymus vykdyti kažkokias komandas ar dar ką nors... Gaila, bet taip nėra puikiai dera su aukšto lygio problemomis, tokiomis kaip sertifikato tikrinimo klaidos. Paprastai tam reikia derinio tracekartais lttrace ir aukštesnio lygio įrankiai (pvz., komandinės eilutės įrankis $ openssl sertifikato derinimui).

Kaip pavyzdį naudosime atskirą serverį, tačiau sistemos skambučių sekimą dažnai galima atlikti sudėtingesnėse diegimo platformose. Jums tereikia pasirinkti tinkamas priemones.

Paprastas derinimo pavyzdys

Tarkime, kad norite paleisti nuostabią serverio programą foo, ir štai ką jūs baigsite:

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

Matyt, nepavyko rasti jūsų parašyto konfigūracijos failo. Taip atsitinka todėl, kad kartais, kai paketų tvarkytojai sudaro programą, jie nepaiso numatomų failų vietų. Ir jei laikotės vieno platinimo diegimo vadovo, kitame rasite visiškai kitokius failus, nei tikėjotės. Problema gali būti išspręsta per kelias sekundes, jei klaidos pranešime būtų nurodyta, kur ieškoti konfigūracijos failo, bet taip nėra. Taigi kur ieškoti?

Jei turite prieigą prie šaltinio kodo, galite jį perskaityti ir viską sužinoti. Geras atsarginis planas, bet ne greičiausias sprendimas. Galite naudoti nuoseklų derinimo priemonę, pvz., gdb ir pažiūrėkite, ką programa daro, tačiau daug efektyviau naudoti įrankį, kuris yra specialiai sukurtas sąveikai su aplinka parodyti: trace.

Produkcija trace gali atrodyti perteklinis, tačiau gera žinia ta, kad daugumą jų galima saugiai ignoruoti. Dažnai naudinga naudoti operatorių -o, kad išsaugotumėte sekimo rezultatus atskirame faile:

$ 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 +++

Apytiksliai visas pirmasis išvesties puslapis trace – Paprastai tai yra žemo lygio pasiruošimas startui. (Daug skambučių mmap, mprotect, plunksna tokiems dalykams kaip žemo lygio atminties aptikimas ir dinaminių bibliotekų rodymas.) Tiesą sakant, derinant išvestį trace Geriau skaityti nuo pat pabaigos. Žemiau bus iššūkis rašyti, kuriame rodomas klaidos pranešimas. Žiūrime aukščiau ir matome pirmąjį klaidingą sistemos iškvietimą – skambutį atidaryti, kuris pateikia klaidą ENOENT („Failas arba katalogas nerastas“) bando atidaryti /etc/foo/config.json. Čia turėtų būti konfigūracijos failas.

Tai buvo tik pavyzdys, bet sakyčiau, kad naudoju 90 % laiko trace, nėra nieko sudėtingesnio už tai. Žemiau yra išsamus žingsnis po žingsnio derinimo vadovas:

  • Nusiminkite dėl neaiškaus pranešimo apie sistemos y klaidą iš programos
  • Iš naujo paleiskite programą naudodami trace
  • Raskite klaidos pranešimą sekimo rezultatuose
  • Eikite aukščiau, kol pasieksite pirmąjį nesėkmingą sistemos skambutį

Labai tikėtina, kad sistemos skambutis 4 veiksme atskleis, kas nutiko.

Patarimai

Prieš parodydamas sudėtingesnio derinimo pavyzdį, parodysiu keletą efektyvaus naudojimo gudrybių trace:

vyras yra tavo draugas

Daugelyje *nix sistemų visą sistemos iškvietimų į branduolį sąrašą galima gauti paleidus vyras syscals. Pamatysite tokius dalykus kaip brk (2), o tai reiškia, kad paleidus galite gauti daugiau informacijos vyras 2 brk.

Mažas grėblys: vyras 2 šakutė rodo man apvalkalo puslapį šakutė () в GNU libc, kuris, pasirodo, įgyvendinamas skambinant klonas (). Skambučių semantika šakutė išlieka toks pat, jei rašote programą naudodami šakutė (), ir paleisti pėdsaką – nerasiu jokių skambučių šakutė, vietoj jų bus klonas (). Tokie grėbliai tik suklaidina, jei pradėsite lyginti šaltinį su išvestimi trace.

Naudokite -o, kad įrašytumėte išvestį į failą

trace gali generuoti didelę išvestį, todėl dažnai naudinga sekimo rezultatus saugoti atskiruose failuose (kaip aukščiau pateiktame pavyzdyje). Tai taip pat padeda nepainioti programos išvesties su išvestimi trace konsolėje.

Norėdami peržiūrėti daugiau argumentų duomenų, naudokite -s

Galbūt pastebėjote, kad antroji klaidos pranešimo pusė nerodoma aukščiau esančiame pavyzdyje. Tai todėl trace pagal nutylėjimą rodo tik pirmuosius 32 eilutės argumento baitus. Jei norite pamatyti daugiau, pridėkite ką nors panašaus – 128 į skambutį trace.

-y leidžia lengviau sekti failus, lizdus ir kt.

„Viskas yra failas“ reiškia, kad *nix sistemos atlieka visus įvesties ir išvesties duomenis naudodamos failų deskriptorius, nesvarbu, ar tai taikoma failui, tinklui ar tarpprocesiniams vamzdžiams. Tai patogu programuojant, tačiau sunku sekti, kas iš tikrųjų vyksta, kai matote bendrą skaityti и rašyti sistemos skambučių sekimo rezultatuose.

Pridėjus operatorių taip, priversi trace komentuokite kiekvieną failo deskriptorių išvestyje su pastaba, ką jis nurodo.

Prijunkite prie jau veikiančio proceso naudodami -p**

Kaip matysite iš toliau pateikto pavyzdžio, kartais reikia atsekti jau veikiančią programą. Jei žinoma, kad jis veikia kaip procesas 1337 (tarkime, iš išvesties ps), galite jį atsekti taip:

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

Jums gali prireikti root teisių.

Naudokite -f norėdami stebėti vaikų procesus

trace Pagal numatytuosius nustatymus jis seka tik vieną procesą. Jei šis procesas sukuria antrinius procesus, gali būti matomas sistemos iškvietimas antriniam procesui sukurti, tačiau antrinio proceso sistemos iškvietimai nebus rodomi.

Jei manote, kad klaida yra antriniame procese, naudokite teiginį -f, tai leis jį sekti. Neigiamas dalykas yra tas, kad išvestis jus dar labiau suklaidins. Kada trace seka vieną procesą arba vieną giją, rodo vieną skambučio įvykių srautą. Kai vienu metu seka kelis procesus, galite matyti pranešimo nutrauktą skambučio pradžią , tada - krūva iškvietimų į kitas vykdymo šakas, ir tik tada - pirmosios pabaiga <…skambutis atnaujintas>. Arba padalinkite visus sekimo rezultatus į skirtingus failus, taip pat naudodami operatorių -ff (išsamiau vadovavimas apie trace).

Filtruokite pėdsakus naudodami -e

Kaip matote, sekimo rezultatas yra tikra visų galimų sistemos iškvietimų krūva. Vėliava -e Galite filtruoti pėdsaką (žr vadovas apie trace). Pagrindinis privalumas yra tai, kad greičiau paleisti filtruotą pėdsaką, nei atlikti visą sekimą ir tada grep'at. Tiesą sakant, man beveik visada nerūpi.

Ne visos klaidos yra blogos

Paprastas ir įprastas pavyzdys yra programa, kuri vienu metu ieško failo keliose vietose, pavyzdžiui, apvalkalas, ieškantis katalogo, kuriame yra vykdomasis failas:

$ 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
...

Euristika, pvz., „paskutinė nesėkminga užklausa prieš pranešant apie klaidą“, padeda rasti atitinkamas klaidas. Kad ir kaip būtų, logiška pradėti nuo pačios pabaigos.

C programavimo vadovėliai gali padėti suprasti sistemos skambučius.

Standartiniai iškvietimai į C bibliotekas nėra sistemos skambučiai, o tik plonas paviršinis sluoksnis. Taigi, jei bent šiek tiek suprasite, kaip ir ką daryti C, jums bus lengviau suprasti sistemos skambučio sekimo rezultatus. Pavyzdžiui, jums kyla problemų derinant skambučius į tinklo sistemas, pažiūrėkite į tą pačią klasiką Bija tinklo programavimo vadovas.

Sudėtingesnis derinimo pavyzdys

Jau sakiau, kad paprasto derinimo pavyzdys yra pavyzdys to, su kuo dažniausiai tenka susidurti dirbant trace. Tačiau kartais reikia atlikti tikrą tyrimą, todėl čia pateikiamas realus pažangesnio derinimo pavyzdys.

bcron - užduočių apdorojimo planuoklis, kitas *nix demono įgyvendinimas cron. Jis įdiegtas serveryje, bet kai kas nors bando redaguoti tvarkaraštį, nutinka taip:

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

Gerai, tai reiškia bcron bandė parašyti tam tikrą failą, bet nepavyko, ir jis nepripažins kodėl. Atskleidimas trace:

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

Pačioje pabaigoje yra klaidos pranešimas rašyti, bet šį kartą kažkas kitaip. Pirma, nėra atitinkamos sistemos skambučio klaidos, kuri paprastai įvyksta prieš tai. Antra, aišku, kad kažkur kažkas jau perskaitė klaidos pranešimą. Atrodo, kad tikroji problema yra kažkur kitur, ir bcrontab tiesiog atkuria pranešimą.

Jei pažiūrėsite vyras 2 perskaitė, galite matyti, kad pirmasis argumentas (3) yra failo deskriptorius, kurį *nix naudoja visam įvesties / išvesties apdorojimui. Kaip sužinoti, ką reiškia 3 failo aprašas? Šiuo konkrečiu atveju galite paleisti trace su operatoriumi taip (žr. aukščiau) ir jis automatiškai jums praneš, bet norint išsiaiškinti tokius dalykus, naudinga žinoti, kaip nuskaityti ir išanalizuoti pėdsakų rezultatus.

Failo deskriptoriaus šaltinis gali būti vienas iš daugelio sistemos iškvietimų (viskas priklauso nuo to, kam deskriptorius skirtas – konsolei, tinklo lizdui, pačiam failui ar dar kam), bet kad ir kaip būtų, ieškome skambina grąžindami 3 (t. y. ieškome „= 3“ sekimo rezultatuose). Šiame rezultate yra 2 iš jų: atidaryti pačiame viršuje ir lizdas Viduryje. atidaryti atidaro failą, bet close(3) parodys, kad jis vėl užsidaro. (Rake: failų aprašai gali būti naudojami pakartotinai, kai jie atidaromi ir uždaromi). Skambinti lizdas() tinka, nes tai paskutinis skaityti (), ir pasirodo, kad bcrontab su kažkuo dirba per lizdą. Kita eilutė rodo, kad failo aprašas yra susietas su unix domeno lizdas pakeliui /var/run/bcron-spool.

Taigi, turime rasti procesą, susijusį su unix lizdas kitoje pusėje. Tam tikslui yra keletas puikių gudrybių, kurios abi yra naudingos derinant serverio diegimą. Pirmasis yra naudoti netstat ar naujesni ss (lizdo būsena). Abi komandos rodo aktyvius sistemos tinklo ryšius ir priima pareiškimą -l apibūdinti klausymosi lizdus, ​​taip pat operatorių -p rodyti programas, prijungtas prie lizdo kaip klientą. (Yra daug daugiau naudingų parinkčių, tačiau šių dviejų pakanka šiai užduočiai atlikti.)

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

Tai rodo, kad klausytojas yra komanda inixserveris, veikia su proceso ID 20629. (Ir, atsitiktinai, kaip lizdą naudojamas failo deskriptorius 3.)

Antrasis tikrai naudingas įrankis ieškant tos pačios informacijos yra vadinamas lof. Jame pateikiami visi sistemoje atidaryti failai (arba failų aprašai). Arba galite gauti informacijos apie vieną konkretų failą:

# 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

Process 20629 yra ilgaamžis serveris, todėl galite jį prijungti trace naudojant kažką panašaus strace -o /tmp/trace -p 20629. Jei redaguosite cron užduotį kitame terminale, gausite sekimo išvestį su klaida. Ir štai rezultatas:

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

(Paskutinis priimti() nebus baigtas atsekant.) Vėlgi, deja, šiame rezultate nėra klaidos, kurios ieškome. Nematome jokių pranešimų, kuriuos bcrontag siunčia į lizdą arba gauna iš jo. Vietoj to visiškai valdykite procesą (klonuoti, laukti 4, SIGCHLD ir tt) Šis procesas sukuria antrinį procesą, kuris, kaip galite spėti, atlieka tikrąjį darbą. Ir jei jums reikia sugauti jos pėdsaką, pridėkite prie skambučio strace -f. Tai rasime, kai naujame rezultate su strace ieškosime klaidos pranešimo -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 +++

Dabar tai kažkas. Procesas 21470 gauna klaidą „prieiga uždrausta“, kai bandoma sukurti failą kelyje tmp/spool.21470.1573692319.854640 (susijęs su dabartiniu darbo katalogu). Jei tik žinotume dabartinį darbo katalogą, taip pat žinotume visą kelią ir galėtume išsiaiškinti, kodėl procesas negali jame sukurti laikinojo failo. Deja, procesas jau baigtas, todėl jūs negalite tiesiog naudoti lsof -p 21470 norėdami rasti dabartinį katalogą, bet galite dirbti ir priešinga kryptimi – ieškokite PID 21470 sistemos iškvietimų, kurie keičia katalogą. (Jei jų nėra, PID 21470 turėjo juos paveldėti iš pirminio, ir tai jau baigta lsof -p nepavyko sužinoti.) Šis sistemos iškvietimas yra chdir (ką nesunku sužinoti šiuolaikinių internetinių paieškos sistemų pagalba). Ir štai atvirkštinių paieškų, pagrįstų sekimo rezultatais, rezultatas iki serverio 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 +++

(Jei pasiklydote, galbūt norėsite perskaityti mano ankstesnį įrašą apie *nix procesų valdymą ir apvalkalus.) Taigi, serverio PID 20629 negavo leidimo sukurti failo kelyje /var/spool/cron/tmp/spool.21470.1573692319.854640. Labiausiai tikėtina, kad to priežastis yra klasikiniai failų sistemos leidimų nustatymai. Patikrinkime:

# 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

Štai kur šuo palaidotas! Serveris veikia kaip vartotojo cron, bet tik root turi teisę rašyti į katalogą /var/spool/cron/tmp/. Paprasta komanda chown cron /var/spool/cron/tmp/ privers bcron dirbti teisingai. (Jei tai nebuvo problema, kitas labiausiai tikėtinas įtariamasis yra branduolio saugos modulis, pvz., SELinux arba AppArmor, todėl aš patikrinčiau branduolio pranešimų žurnalą su dmesg.)

Iš viso

Sistemos iškvietimų pėdsakai pradedantiesiems gali būti didžiuliai, bet tikiuosi, kad įrodžiau, kad jie yra greitas būdas derinti visą klasę įprastų diegimo problemų. Įsivaizduokite, kad bandote derinti daugiaprocesį bcronnaudojant žingsnis po žingsnio derintuvą.

Norint išanalizuoti pėdsakų rezultatus atgal išilgai sistemos skambučių grandinės, reikia įgūdžių, tačiau, kaip sakiau, beveik visada, naudojant trace, aš tiesiog gaunu pėdsakų rezultatą ir ieškau klaidų pradedant nuo pabaigos. bet kokiu atveju trace padeda sutaupyti daug laiko derinant. Tikiuosi, kad tai bus naudinga ir jums.

Šaltinis: www.habr.com

Добавить комментарий