Ohjelmiston käyttöönoton virheenkorjaus stracen avulla

Ohjelmiston käyttöönoton virheenkorjaus stracen avulla

Päivätyöni on enimmäkseen ohjelmistojen käyttöönottoa, mikä tarkoittaa, että käytän paljon aikaa yrittääkseni vastata kysymyksiin, kuten:

  • Tämä ohjelmisto toimii kehittäjälle, mutta ei minulle. Miksi?
  • Eilen tämä ohjelmisto toimi minulle, mutta tänään ei. Miksi?

Tämä on eräänlainen virheenkorjaus, joka eroaa hieman tavallisesta ohjelmiston virheenkorjauksesta. Säännöllinen virheenkorjaus koskee koodin logiikkaa, mutta käyttöönoton virheenkorjaus koskee koodin ja ympäristön välistä vuorovaikutusta. Vaikka ongelman syy on looginen virhe, se, että kaikki toimii yhdellä koneella eikä toisella, tarkoittaa, että ongelma on jotenkin ympäristössä.

Joten tavallisten virheenkorjaustyökalujen sijaan gdb Minulla on erilaiset työkalut käyttöönoton virheenkorjaukseen. Ja suosikkityökaluni ongelman käsittelemiseen, kuten "Miksi tämä ohjelmisto ei toimi minulle?" nimeltään ytsakki.

Mikä on strace?

ytsakki on työkalu "järjestelmäpuhelujen jäljittämiseen". Se luotiin alun perin Linuxille, mutta samat virheenkorjaustemput voidaan tehdä muiden järjestelmien työkaluilla (DTrace tai ktrace).

Perussovellus on hyvin yksinkertainen. Sinun tarvitsee vain ajaa strace millä tahansa komennolla ja se tyhjentää kaikki järjestelmäkutsut (vaikka joudut todennäköisesti ensin asentamaan sen itse ytsakki):

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

Mitä nämä järjestelmäkutsut ovat? Tämä on jotain kuin API käyttöjärjestelmän ytimelle. Olipa kerran ohjelmistolla oli suora pääsy laitteistoon, jolla se juoksi. Jos esimerkiksi sen piti näyttää jotain näytöllä, se soitti videolaitteiden porteilla tai muistikartoitetuilla rekistereillä. Kun moniajoista tuli suosittuja tietokonejärjestelmiä, kaaos vallitsi, kun erilaiset sovellukset taistelivat laitteistosta. Yhden sovelluksen virheet voivat kaataa muita, ellei koko järjestelmän. Sitten etuoikeustilat (tai "soittosuojaus") ilmestyivät suorittimeen. Ytimestä tuli etuoikeutetuin: se sai täyden pääsyn laitteistoon, mikä synnytti vähemmän etuoikeutettuja sovelluksia, joiden oli jo pyydettävä pääsyä ytimeltä ollakseen vuorovaikutuksessa laitteiston kanssa järjestelmäkutsujen kautta.

Binääritasolla järjestelmäkutsu on hieman erilainen kuin yksinkertainen funktiokutsu, mutta useimmat ohjelmat käyttävät käärettä vakiokirjastossa. Nuo. POSIX C -standardikirjasto sisältää funktiokutsun kirjoittaa(), joka sisältää kaiken järjestelmäkutsun arkkitehtuurikohtaisen koodin kirjoittaa.

Ohjelmiston käyttöönoton virheenkorjaus stracen avulla

Lyhyesti sanottuna mikä tahansa vuorovaikutus sovelluksen ja sen ympäristön (tietokonejärjestelmien) välillä tapahtuu järjestelmäkutsujen kautta. Siksi, kun ohjelmisto toimii yhdellä koneella, mutta ei toisella, olisi hyvä tarkastella järjestelmäkutsujen jäljityksen tuloksia. Tarkemmin sanottuna tässä on luettelo tyypillisistä hetkistä, jotka voidaan analysoida järjestelmäkutsujäljityksen avulla:

  • Konsolin I/O
  • Verkko I/O
  • Tiedostojärjestelmän käyttöoikeus ja tiedostojen I/O
  • Prosessilangan käyttöiän hallinta
  • Matalan tason muistin hallinta
  • Pääsy tiettyihin laiteajureihin

Milloin stracea käytetään?

Teoriassa, ytsakki käytetään minkä tahansa käyttäjätilan ohjelman kanssa, koska minkä tahansa käyttäjätilassa olevan ohjelman on tehtävä järjestelmäkutsuja. Se toimii tehokkaammin käännetyillä, matalan tason ohjelmilla, mutta se toimii myös korkean tason kielten, kuten Pythonin, kanssa, jos voit leikata yli ajonajan ja tulkin ylimääräisen melun.

Kaikessa loistossaan ytsakki ilmenee virheenkorjauksen aikana, kun ohjelmisto toimii hyvin yhdellä koneella, mutta lakkaa yhtäkkiä toimimasta toisella ja tuottaa epämääräisiä viestejä tiedostoista, käyttöoikeuksista tai epäonnistuneista yrityksistä suorittaa komentoja tai jotain muuta... Harmi, mutta se ei toimi yhdistää niin hyvin korkean tason ongelmiin, kuten varmenteen vahvistusvirheisiin. Yleensä tämä vaatii yhdistelmän ytsakkijoskus lttrace ja korkeamman tason työkalut (kuten komentorivityökalu openssl sertifikaatin virheenkorjaus).

Käytämme esimerkkinä itsenäistä palvelinta, mutta järjestelmäkutsujen jäljitys voidaan usein tehdä monimutkaisemmilla käyttöönottoalustoilla. Sinun tarvitsee vain valita oikeat työkalut.

Yksinkertainen virheenkorjausesimerkki

Oletetaan, että haluat käyttää hämmästyttävää palvelinsovellusta foo, ja tähän päädyt:

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

Ilmeisesti se ei löytänyt kirjoittamaasi asetustiedostoa. Tämä johtuu siitä, että joskus pakettien hallintaohjelmat kääntäessään sovelluksen ohittavat odotetut tiedostojen sijainnit. Ja jos noudatat yhden jakelun asennusopasta, toisesta löydät tiedostoja, jotka ovat täysin erilaisia ​​kuin odotit. Ongelma voitaisiin ratkaista muutamassa sekunnissa, jos virheilmoitus kertoisi, mistä konfiguraatiotiedostoa etsitään, mutta se ei tee sitä. Joten mistä etsiä?

Jos sinulla on pääsy lähdekoodiin, voit lukea sen ja selvittää kaiken. Hyvä varasuunnitelma, mutta ei nopein ratkaisu. Voit turvautua vaiheittaiseen debuggeriin, kuten gdb ja katso mitä ohjelma tekee, mutta on paljon tehokkaampaa käyttää työkalua, joka on erityisesti suunniteltu näyttämään vuorovaikutusta ympäristön kanssa: ytsakki.

johtopäätös ytsakki saattaa tuntua tarpeettomalta, mutta hyvä uutinen on, että suurin osa niistä voidaan jättää huomiotta. Usein on hyödyllistä käyttää -o-operaattoria jäljitystulosten tallentamiseen erilliseen tiedostoon:

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

Suunnilleen koko tulosteen ensimmäinen sivu ytsakki - Tämä on yleensä matalan tason valmistautumista laukaisuun. (Paljon puhelua mmap, mprotect, BRK esimerkiksi alhaisen muistin havaitsemiseen ja dynaamisten kirjastojen näyttämiseen.) Itse asiassa virheenkorjauksen aikana tulos ytsakki On parempi lukea loppuun asti. Alla on haaste kirjoittaa, joka näyttää virheilmoituksen. Katsomme yllä ja näemme ensimmäisen virheellisen järjestelmäkutsun - puhelun openat, joka aiheuttaa virheen ENOENT ("tiedostoa tai hakemistoa ei löydy") yrittää avata /etc/foo/config.json. Tässä asetustiedoston pitäisi olla.

Tämä oli vain esimerkki, mutta sanoisin, että 90 % ajasta käytän ytsakki, ei ole mitään paljon vaikeampaa tehdä kuin tämä. Alla on täydellinen vaiheittainen virheenkorjausopas:

  • Suututtele ohjelman järjestelmä-y-virheestä kertovasta epämääräisestä viestistä
  • Käynnistä ohjelma uudelleen komennolla ytsakki
  • Etsi virheilmoitus jäljitystuloksista
  • Mene korkeammalle, kunnes osut ensimmäiseen epäonnistuneeseen järjestelmäkutsuun

On hyvin todennäköistä, että järjestelmäkutsu vaiheessa 4 paljastaa, mikä meni pieleen.

vihjeitä

Ennen kuin näytän sinulle esimerkin monimutkaisemmasta virheenkorjauksesta, näytän sinulle muutamia temppuja tehokkaaseen käyttöön ytsakki:

mies on ystäväsi

Monissa *nix-järjestelmissä täydellinen luettelo järjestelmäkutsuista ytimeen voidaan saada suorittamalla mies syscalls. Näet asioita, kuten brk(2), mikä tarkoittaa, että voit saada lisää tietoa suorittamalla mies 2 brk.

Pieni harava: mies 2 haarukka näyttää minulle kuoren sivun haarukka() в GNU libc, joka ilmeisesti toteutetaan soittamalla klooni(). Kutsu semantiikkaa haarukka pysyy samana, jos kirjoitat ohjelman käyttämällä haarukka()ja jäljittää - en löydä puheluita haarukka, niiden sijaan tulee olemaan klooni(). Tällaiset haravat hämmentävät sinua vain, jos alat vertailla lähdettä lähtöön ytsakki.

Tallenna tulos tiedostoon painamalla -o

ytsakki voi tuottaa laajoja tulosteita, joten on usein hyödyllistä tallentaa jäljitystulokset erillisiin tiedostoihin (kuten yllä olevassa esimerkissä). Tämä auttaa myös välttämään ohjelman tulosteen sekoittamista ulostuloon ytsakki konsolissa.

Käytä -s nähdäksesi lisää argumenttitietoja

Olet ehkä huomannut, että virheilmoituksen toista puoliskoa ei näy yllä olevassa esimerkkijäljessä. Se on koska ytsakki oletusarvo näyttää vain merkkijonoargumentin ensimmäiset 32 ​​tavua. Jos haluat nähdä lisää, lisää jotain vastaavaa -s 128 puheluun ytsakki.

-y helpottaa tiedostojen, pistokkeiden jne. seurantaa.

"Kaikki on tiedosto" tarkoittaa, että *nix-järjestelmät tekevät kaiken I/O:n tiedostokuvaajien avulla riippumatta siitä, koskeeko tämä tiedostoa, verkkoa tai prosessien välisiä putkia. Tämä on kätevä ohjelmoinnin kannalta, mutta tekee vaikeaksi seurata, mitä todella tapahtuu, kun näet yhteistä luettu и kirjoittaa järjestelmän puhelujäljityksen tuloksissa.

Lisäämällä operaattorin y, pakotat ytsakki merkitse jokainen tulosteen tiedostokuvaaja muistiinpanolla, mihin se viittaa.

Liitä jo käynnissä olevaan prosessiin komennolla -p**

Kuten alla olevasta esimerkistä näet, joskus sinun on jäljitettävä jo käynnissä oleva ohjelma. Jos tiedetään, että se toimii prosessina 1337 (esimerkiksi lähdöstä ps), voit jäljittää sen seuraavasti:

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

Saatat tarvita pääkäyttäjän oikeuksia.

Käytä -f:tä seurataksesi lapsiprosesseja

ytsakki Oletuksena se jäljittää vain yhden prosessin. Jos tämä prosessi synnyttää lapsiprosesseja, järjestelmäkutsu aliprosessin synnyttämiseksi voidaan nähdä, mutta aliprosessin järjestelmäkutsuja ei näytetä.

Jos uskot virheen olevan lapsiprosessissa, käytä lausetta -f, tämä mahdollistaa sen jäljittämisen. Huono puoli tässä on, että tulos hämmentää sinua entisestään. Kun ytsakki jäljittää yhden prosessin tai yhden säikeen, se näyttää yhden puhelutapahtumavirran. Kun se jäljittää useita prosesseja kerralla, saatat nähdä puhelun alkamisen keskeytettynä viestillä , sitten - joukko kutsuja muille suoritushaareille ja vasta sitten - ensimmäisen loppu <…foocal jatkettu>. Tai jaa kaikki jäljitystulokset eri tiedostoihin, myös operaattorin avulla -ff (tiedot sisään johto päälle ytsakki).

Suodata jäljet ​​käyttämällä -e

Kuten näette, jäljityksen tulos on todellinen kasa kaikkia mahdollisia järjestelmäkutsuja. Lippu -e Voit suodattaa jäljen (katso руководство päälle ytsakki). Suurin etu on, että suodatetun jäljityksen suorittaminen on nopeampaa kuin täyden jäljityksen tekeminen ja sitten grep`at. Rehellisesti sanottuna en melkein aina välitä.

Kaikki virheet eivät ole pahoja

Yksinkertainen ja yleinen esimerkki on ohjelma, joka etsii tiedostoa useista paikoista kerralla, kuten komentotulkki, joka etsii hakemistoa, joka sisältää suoritettavan tiedoston:

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

Heuristiikka, kuten "viimeinen epäonnistunut pyyntö ennen virheen ilmoittamista", on hyvä löytää asiaankuuluvia virheitä. Oli miten oli, on loogista aloittaa aivan lopusta.

C-ohjelmoinnin opetusohjelmat voivat auttaa sinua ymmärtämään järjestelmäkutsuja.

Vakiokutsut C-kirjastoihin eivät ole järjestelmäkutsuja, vaan vain ohutta pintakerrosta. Joten jos ymmärrät ainakin vähän kuinka ja mitä tehdä C:ssä, sinun on helpompi ymmärtää järjestelmäkutsujäljityksen tulokset. Esimerkiksi sinulla on ongelmia verkkojärjestelmien puheluiden virheenkorjauksessa, katso samaa klassikkoa Bijan verkko-ohjelmoinnin opas.

Monimutkaisempi virheenkorjausesimerkki

Sanoin jo, että esimerkki yksinkertaisesta virheenkorjauksesta on esimerkki siitä, mitä joudun useimmiten käsittelemään työskennellessäni ytsakki. Joskus tarvitaan kuitenkin todellista tutkimusta, joten tässä on tosielämän esimerkki edistyneemmästä virheenkorjauksesta.

bcron - Tehtävien käsittelyn ajoitus, toinen *nix-daemonin toteutus cron. Se on asennettu palvelimelle, mutta kun joku yrittää muokata aikataulua, tapahtuu seuraavaa:

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

Okei, se tarkoittaa bcron yritti kirjoittaa tietyn tiedoston, mutta se ei onnistunut, eikä hän myönnä miksi. Paljastumassa ytsakki:

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

Lähellä loppua tulee virheilmoitus kirjoittaa, mutta tällä kertaa jotain on toisin. Ensinnäkin ei ole asiaankuuluvaa järjestelmäkutsuvirhettä, joka tapahtuu yleensä ennen tätä. Toiseksi on selvää, että joku on jo lukenut virheilmoituksen. Näyttää siltä, ​​​​että todellinen ongelma on jossain muualla ja bcrontab yksinkertaisesti toistaa viestin.

Jos katsot mies 2 lukenut, näet, että ensimmäinen argumentti (3) on tiedostokuvaaja, jota *nix käyttää kaikkeen I/O-käsittelyyn. Kuinka saan selville, mitä tiedostokuvaaja 3 edustaa? Tässä nimenomaisessa tapauksessa voit juosta ytsakki operaattorin kanssa y (katso yllä) ja se kertoo sinulle automaattisesti, mutta tällaisten asioiden selvittämiseksi on hyödyllistä tietää, kuinka jäljitystuloksia voidaan lukea ja jäsentää.

Tiedostokuvaajan lähde voi olla yksi monista järjestelmäkutsuista (kaikki riippuu siitä, mihin kuvaaja on tarkoitettu - konsoliin, verkkopistorasiaan, itse tiedostoon tai johonkin muuhun), mutta olipa tilanne miten tahansa, etsimme kutsuja palauttamalla 3 (eli etsimme jäljitystuloksista "= 3"). Tässä tuloksessa niitä on 2: openat aivan huipulla ja pistorasia Keskellä. openat avaa tiedoston, mutta lähellä(3) näyttää sitten, että se sulkeutuu uudelleen. (Rake: tiedostokuvauksia voidaan käyttää uudelleen, kun ne avataan ja suljetaan). Puhelu pistorasia () sopii, koska se on viimeinen ennen lukea(), ja käy ilmi, että bcrontab toimii jonkin kanssa socketin kautta. Seuraava rivi näyttää, että tiedostokuvaaja liittyy unix-verkkotunnuksen liitäntä matkan varrella /var/run/bcron-spool.

Joten meidän on löydettävä siihen liittyvä prosessi unix-liitäntä toisella puolella. Tähän tarkoitukseen on olemassa pari siistiä temppua, jotka molemmat ovat hyödyllisiä palvelimen käyttöönottojen virheenkorjauksessa. Ensimmäinen on käyttää netstat tai uudempi ss (pistorasian tila). Molemmat komennot näyttävät järjestelmän aktiiviset verkkoyhteydet ja ottavat lausunnon -l kuvaamaan kuunteluliitäntöjä sekä operaattoria -p pistorasiaan kytkettyjen ohjelmien näyttämiseen asiakkaana. (On monia muita hyödyllisiä vaihtoehtoja, mutta nämä kaksi riittävät tähän tehtävää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))

Tämä viittaa siihen, että kuuntelija on komento inix-palvelin, joka toimii prosessitunnuksella 20629. (Ja sattumalta se käyttää tiedostokuvaajaa 3 kantajana.)

Toinen todella hyödyllinen työkalu saman tiedon löytämiseen on nimeltään lof. Se luettelee kaikki avoimet tiedostot (tai tiedostokuvaajat) järjestelmässä. Tai voit saada tietoja yhdestä tietystä tiedostosta:

# 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 on pitkäikäinen palvelin, joten voit liittää sen ytsakki käyttämällä jotain sellaista strace -o /tmp/trace -p 20629. Jos muokkaat cron-työtä toisessa päätteessä, saat jäljitystulosteen, jossa on virhe. Ja tässä tulos:

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

(Kestää hyväksyä() ei suoriteta loppuun jäljitettäessä.) Tämäkään tulos ei valitettavasti sisällä etsimäämme virhettä. Emme näe viestejä, jotka bcrontag lähettää pistorasiaan tai vastaanottaa sieltä. Sen sijaan täydellinen prosessinhallinta (klooni, odota 4, SIGCHLD jne.) Tämä prosessi synnyttää lapsiprosessin, joka, kuten arvata saattaa, tekee todellisen työn. Ja jos sinun on löydettävä hänen jälkensä, lisää puheluun viiva -f. Tämän löydämme, kun etsimme virheilmoitusta uudesta tuloksesta stracella -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 +++

No se nyt on jotain. Prosessi 21470 saa "pääsy estetty" -virheen yrittäessään luoda tiedostoa polulla tmp/spool.21470.1573692319.854640 (koskee nykyistä työhakemistoa). Jos tietäisimme vain nykyisen työhakemiston, tietäisimme myös koko polun ja pystyisimme selvittämään, miksi prosessi ei voi luoda väliaikaistiedostoaan siihen. Valitettavasti prosessi on jo päättynyt, joten et voi vain käyttää lsof -p 21470 löytääksesi nykyisen hakemiston, mutta voit työskennellä päinvastaiseen suuntaan - etsi PID 21470 -järjestelmäkutsuja, jotka muuttavat hakemistoa. (Jos niitä ei ole, PID 21470 on perinyt ne vanhemmalta, ja tämä on jo lsof -p ei löydy.) Tämä järjestelmäkutsu on chdir (joka on helppo selvittää nykyaikaisten online-hakukoneiden avulla). Ja tässä on tulos jäljitystuloksiin perustuvista käänteisistä hauista aina palvelimen PID:hen 20629 asti:

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

(Jos olet eksyksissä, kannattaa lukea edellinen viestini *nix-prosessinhallinnasta ja kuorista.) Joten palvelimen PID 20629 ei saanut lupaa luoda tiedostoa polulla /var/spool/cron/tmp/spool.21470.1573692319.854640. Todennäköisesti syynä tähän ovat klassiset tiedostojärjestelmän käyttöoikeusasetukset. Tarkistetaan:

# 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

Sinne koira on haudattu! Palvelin toimii käyttäjän cronina, mutta vain rootilla on oikeus kirjoittaa hakemistoon /var/spool/cron/tmp/. Yksinkertainen komento chown cron /var/spool/cron/tmp/ tahdonvoima bcron toimivat oikein. (Jos se ei ollut ongelma, niin seuraava todennäköisin epäilty on ytimen suojausmoduuli, kuten SELinux tai AppArmor, joten tarkistaisin ytimen viestilokin dmesg.)

Yhteensä

Järjestelmäkutsujen jäljitys voi olla ylivoimainen aloittelijalle, mutta toivon, että olen osoittanut, että ne ovat nopea tapa korjata monia yleisiä käyttöönottoongelmia. Kuvittele, että yrität virheenkorjata moniprosessia bcronkäyttämällä vaiheittaista debuggeria.

Jäljitystulosten jäsentäminen taaksepäin järjestelmän kutsuketjua pitkin vaatii taitoa, mutta kuten sanoin, lähes aina käyttämistä ytsakki, saan vain jäljitystuloksen ja etsin virheitä lopusta alkaen. Joka tapauksessa, ytsakki auttaa minua säästämään paljon aikaa virheenkorjauksessa. Toivottavasti siitä on hyötyä myös sinulle.

Lähde: will.com

Lisää kommentti