Ontfouting van sagteware-ontplooiing met strace

Ontfouting van sagteware-ontplooiing met strace

My dagtaak is meestal sagteware-ontplooiing, wat beteken dat ek baie tyd spandeer om vrae te beantwoord soos:

  • Hierdie sagteware werk vir die ontwikkelaar, maar nie vir my nie. Hoekom?
  • Gister het hierdie sagteware vir my gewerk, maar vandag nie. Hoekom?

Dit is 'n soort ontfouting wat effens verskil van gewone sagteware-ontfouting. Gereelde ontfouting gaan oor die logika van die kode, maar ontplooiing ontfouting gaan oor die interaksie tussen die kode en die omgewing. Selfs al is die wortel van die probleem 'n logiese fout, beteken die feit dat alles op een masjien werk en nie op 'n ander nie dat die probleem op een of ander manier in die omgewing is.

Dus in plaas van die gewone ontfoutingsinstrumente soos gdb Ek het 'n ander stel gereedskap vir ontfouting ontplooiing. En my gunsteling hulpmiddel vir die hantering van die probleem soos "Hoekom werk hierdie sagteware nie vir my nie?" geroep strewe.

Wat is strace?

strewe is 'n hulpmiddel vir "stelseloproepopsporing". Dit is oorspronklik vir Linux geskep, maar dieselfde ontfoutingstruuks kan gedoen word met gereedskap vir ander stelsels (DTrace of ktrasie).

Die basiese toepassing is baie eenvoudig. Jy hoef net strace met enige opdrag uit te voer en dit sal alle stelseloproepe dump (alhoewel jy dit eers self sal moet installeer strewe):

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

Wat is hierdie stelseloproepe? Dit is iets soos 'n API vir die bedryfstelsel kern. Eens op 'n tyd het sagteware direkte toegang gehad tot die hardeware waarop dit gehardloop het. As dit byvoorbeeld iets op die skerm moes vertoon, het dit met poorte of geheue-gekarteerde registers vir videotoestelle gespeel. Toe multitasking rekenaarstelsels gewild geword het, het chaos geheers terwyl verskeie toepassings oor die hardeware baklei het. Foute in een toepassing kan ander tot 'n val bring, indien nie die hele stelsel nie. Toe het voorregmodusse (of "ringbeskerming") in die SVE verskyn. Die kern het die mees bevoorregte geword: dit het volle toegang tot die hardeware gekry, wat minder bevoorregte toepassings voortgebring het wat reeds toegang van die kern moes versoek om deur stelseloproepe met die hardeware te kommunikeer.

Op die binêre vlak verskil 'n stelseloproep effens van 'n eenvoudige funksieoproep, maar die meeste programme gebruik 'n omhulsel in die standaardbiblioteek. Dié. die POSIX C standaard biblioteek bevat 'n funksie oproep skryf (), wat al die argitektuurspesifieke kode vir die stelseloproep bevat skryf.

Ontfouting van sagteware-ontplooiing met strace

Kortom, enige interaksie tussen 'n toepassing en sy omgewing (rekenaarstelsels) word deur stelseloproepe uitgevoer. Daarom, wanneer sagteware op een masjien werk, maar nie op 'n ander nie, sal dit goed wees om na die resultate van die stelseloproepopsporing te kyk. Meer spesifiek, hier is 'n lys van tipiese punte wat met 'n stelseloproepspoor ontleed kan word:

  • Konsole I/O
  • Netwerk I/O
  • Lêerstelseltoegang en lêer I/O
  • Bestuur die leeftyd van 'n prosesdraad
  • Laevlak geheuebestuur
  • Toegang tot spesifieke toestelbestuurders

Wanneer om strace te gebruik?

In teorie, strewe gebruik met enige program in gebruikersruimte, want enige program in gebruikersruimte moet stelseloproepe maak. Dit werk meer doeltreffend met saamgestelde, laevlak-programme, maar dit werk ook met hoëvlaktale soos Python as jy die bykomende geraas van die looptyd en tolk kan sny.

In al sy glorie strewe manifesteer tydens ontfouting van sagteware wat goed op een masjien werk, maar skielik ophou werk op 'n ander, wat vae boodskappe oor lêers, toestemmings, of onsuksesvolle pogings om 'n paar opdragte of iets anders uit te voer ... Dit is jammer, maar dit doen nie kombineer so goed met hoëvlakprobleme soos sertifikaatverifikasiefoute. Gewoonlik vereis dit 'n kombinasie strewesoms spoor en hoër vlak gereedskap (soos die opdragreëlnutsding OpenSSL om die sertifikaat te ontfout).

Ons sal 'n selfstandige bediener as 'n voorbeeld gebruik, maar stelseloproepopsporing kan dikwels op meer komplekse ontplooiingsplatforms gedoen word. Jy hoef net die regte gereedskap te kies.

Eenvoudige ontfoutingsvoorbeeld

Kom ons sê jy wil die wonderlike bedienertoepassing foo laat loop, en dit is waarmee jy eindig:

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

Dit kon blykbaar nie die konfigurasielêer vind wat jy geskryf het nie. Dit gebeur omdat soms wanneer pakketbestuurders 'n toepassing saamstel, hulle die verwagte lêerliggings ignoreer. En as jy die installasiegids vir een verspreiding volg, vind jy in 'n ander lêers heeltemal anders as waar jy verwag het. Die probleem kan binne 'n paar sekondes opgelos word as die foutboodskap vertel waar om na die konfigurasielêer te soek, maar dit doen dit nie. So waar om te kyk?

As jy toegang tot die bronkode het, kan jy dit lees en alles uitvind. 'n Goeie rugsteunplan, maar nie die vinnigste oplossing nie. Jy kan gebruik maak van 'n stap-vir-stap ontfouter soos gdb en kyk wat die program doen, maar dit is baie meer effektief om 'n instrument te gebruik wat spesifiek ontwerp is om interaksie met die omgewing te toon: strewe.

Output strewe lyk dalk oorbodig, maar die goeie nuus is dat die meeste daarvan veilig geïgnoreer kan word. Dit is dikwels nuttig om die -o-operateur te gebruik om spoorresultate in 'n aparte lêer te stoor:

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

Ongeveer die hele eerste bladsy van afvoer strewe - Dit is gewoonlik 'n lae-vlak voorbereiding vir bekendstelling. (Baie oproepe MMAP, mbeskerm, veerpen vir dinge soos die opsporing van laevlakgeheue en die vertoon van dinamiese biblioteke.) Eintlik, tydens ontfouting van die uitvoer strewe Dit is beter om van die einde af te lees. Daar sal 'n uitdaging hieronder wees skryf, wat 'n foutboodskap vertoon. Ons kyk hierbo en sien die eerste foutiewe stelseloproep - die oproep oopmaak, wat 'n fout gooi ENOENT ("lêer of gids nie gevind nie") probeer oopmaak /etc/foo/config.json. Dit is waar die konfigurasielêer moet wees.

Dit was net 'n voorbeeld, maar ek sou sê 90% van die tyd wat ek gebruik strewe, daar is niks veel moeiliker om te doen as dit nie. Hieronder is 'n volledige stap-vir-stap ontfoutingsgids:

  • Word ontsteld as gevolg van 'n vae boodskap oor 'n stelsel-y-fout van 'n program
  • Herbegin die program met strewe
  • Vind die foutboodskap in die spoorresultate
  • Gaan hoër totdat jy die eerste mislukte stelseloproep tref

Dit is baie waarskynlik dat die stelseloproep in stap 4 sal onthul wat verkeerd geloop het.

Wenke

Voordat ek jou 'n voorbeeld van meer komplekse ontfouting wys, sal ek jou 'n paar truuks wys vir effektiewe gebruik strewe:

man is jou vriend

Op baie *nix-stelsels kan 'n volledige lys van stelseloproepe na die kern verkry word deur te hardloop man siscals. Jy sal dinge sien soos brk(2), wat beteken meer inligting kan verkry word deur te hardloop man 2 brk.

Klein hark: man 2 vurk wys my die bladsy vir die dop vurk () в GNU libc, wat, dit blyk, geïmplementeer word deur te bel kloon(). Noem semantiek vurk bly dieselfde as jy 'n program skryf deur vurk (), en voer 'n spoor - ek sal geen oproepe kry nie vurk, in plaas van hulle sal daar wees kloon(). Sulke harke verwar jou net as jy die bron met die uitset begin vergelyk strewe.

Gebruik -o om die uitvoer na 'n lêer te stoor

strewe kan uitgebreide uitvoer genereer, dus is dit dikwels nuttig om spoorresultate in aparte lêers te stoor (soos in die voorbeeld hierbo). Dit help ook om te verhoed dat programuitset met uitset verwar word strewe in die konsole.

Gebruik -s om meer argumentdata te sien

Jy het dalk opgemerk dat die tweede helfte van die foutboodskap nie in die voorbeeldspoor hierbo gewys word nie. Dis omdat strewe verstek wys slegs die eerste 32 grepe van die string argument. As jy meer wil sien, voeg iets by soos -s 128 na die oproep strewe.

-y maak dit makliker om lêers, voetstukke, ens.

"Alles is lêer" beteken dat *nix-stelsels alle I/O doen deur lêerbeskrywers te gebruik, of dit nou van toepassing is op 'n lêer of 'n netwerk of interprosespype. Dit is gerieflik vir programmering, maar maak dit moeilik om tred te hou met wat werklik aangaan wanneer jy algemeen sien lees и skryf in die stelsel oproep spoor resultate.

Deur 'n operateur by te voeg y, jy sal dwing strewe annoteer elke lêerbeskrywer in die uitvoer met 'n nota van waarna dit verwys.

Heg aan 'n proses wat reeds loop met -p**

Soos u uit die voorbeeld hieronder sal sien, moet u soms 'n program opspoor wat reeds aan die gang is. As dit bekend is dat dit as proses 1337 loop (sê, vanaf die afvoer ps), dan kan jy dit so naspeur:

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

Jy het dalk wortelregte nodig.

Gebruik -f om kinderprosesse te monitor

strewe By verstek spoor dit slegs een proses na. As hierdie proses kinderprosesse voortbring, dan kan die stelseloproep om die kindproses te laat ontstaan, gesien word, maar die kinderproses se stelseloproepe sal nie vertoon word nie.

As jy dink die fout is in 'n kinderproses, gebruik die stelling -f, sal dit die opsporing daarvan moontlik maak. Die nadeel hiervan is dat die uitset jou nog meer sal verwar. Wanneer strewe spoor een proses of een draad, dit wys 'n enkele stroom van oproep gebeure. Wanneer dit verskeie prosesse gelyktydig naspeur, kan jy die begin van 'n oproep sien wat deur 'n boodskap onderbreek word , dan - 'n klomp oproepe vir ander teregstelling takke, en eers dan - die einde van die eerste een <...fokroep hervat>. Of verdeel alle spoorresultate in verskillende lêers, ook deur die operateur te gebruik -ff (besonderhede in leierskap op strewe).

Filtreer spore met -e

Soos u kan sien, is die resultaat van die spoor 'n ware stapel van alle moontlike stelseloproepe. Vlag -e Jy kan die spoor filter (sien leierskap op strewe). Die grootste voordeel is dat dit vinniger is om 'n gefiltreerde spoor uit te voer as om 'n volledige spoor te doen en dan grep`by. Om eerlik te wees, gee ek amper altyd nie om nie.

Nie alle foute is sleg nie

'n Eenvoudige en algemene voorbeeld is 'n program wat op verskeie plekke gelyktydig 'n lêer soek, soos 'n dop wat 'n gids soek wat 'n uitvoerbare lêer bevat:

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

Heuristieke soos "laaste mislukte versoek voordat 'n fout aangemeld word" is goed om relevante foute te vind. Hoe dit ook al sy, dit is logies om van die einde af te begin.

C-programmeringstutoriale kan jou help om stelseloproepe te verstaan.

Standaardoproepe na C-biblioteke is nie stelseloproepe nie, maar slegs 'n dun oppervlaklaag. Dus, as jy ten minste 'n bietjie verstaan ​​hoe en wat om in C te doen, sal dit vir jou makliker wees om die resultate van die stelseloproepspoor te verstaan. Byvoorbeeld, jy sukkel om oproepe na netwerkstelsels te ontfout, kyk na dieselfde klassieke Bija se gids tot netwerkprogrammering.

'n Meer komplekse ontfoutingsvoorbeeld

Ek het reeds gesê dat die voorbeeld van eenvoudige ontfouting 'n voorbeeld is van waarmee ek meestal te doen het wanneer ek werk strewe. Soms word 'n werklike ondersoek egter vereis, so hier is 'n werklike voorbeeld van meer gevorderde ontfouting.

bcron - taakverwerkingskeduleerder, nog 'n implementering van die *nix-demon cron. Dit is op die bediener geïnstalleer, maar wanneer iemand probeer om die skedule te wysig, is dit wat gebeur:

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

Goed, dit beteken bcron het probeer om 'n sekere lêer te skryf, maar dit het nie uitgewerk nie, en hy sal nie erken hoekom nie. Ontbloot strewe:

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

Daar is 'n foutboodskap heel aan die einde skryf, maar hierdie keer is iets anders. Eerstens is daar geen relevante stelseloproepfout nie, wat gewoonlik voor dit voorkom. Tweedens is dit duidelik dat iemand iewers reeds die foutboodskap gelees het. Dit lyk of die werklike probleem iewers anders is, en bcrontab speel eenvoudig die boodskap terug.

As jy kyk na man 2 lees, kan jy sien dat die eerste argument (3) 'n lêerbeskrywer is, wat *nix vir alle I/O-verwerking gebruik. Hoe vind ek uit wat lêerbeskrywing 3 verteenwoordig? In hierdie spesifieke geval kan jy hardloop strewe met operateur y (sien hierbo) en dit sal jou outomaties vertel, maar om dinge soos hierdie uit te vind, is dit nuttig om te weet hoe om spoorresultate te lees en te ontleed.

Die bron van 'n lêerbeskrywing kan een van baie stelseloproepe wees (dit hang alles af van waarvoor die beskrywer is - 'n konsole, 'n netwerksok, die lêer self, of iets anders), maar hoe dit ook al sy, ons soek oproepe deur 3 terug te gee (d.w.s. ons soek "= 3" in die naspoorresultate). In hierdie resultaat is daar 2 van hulle: oopmaak heel bo en socket In die middel. oopmaak maak die lêer oop, maar naby(3) sal dan wys dat dit weer toemaak. (Rake: lêerbeskrywers kan hergebruik word wanneer hulle oop- en toegemaak word). Bel sok () geskik want dit is die laaste een vantevore lees (), en dit blyk dat bcrontab met iets werk deur 'n sok. Die volgende reël wys dat die lêerbeskrywer geassosieer word met unix-domein-sok oppad /var/run/bcron-spoel.

So, ons moet die proses vind wat verband hou met unix-sok aan die ander kant. Daar is 'n paar netjiese truuks vir hierdie doel, wat albei nuttig is om bedienerontplooiings te ontfout. Die eerste is om te gebruik netstat of nuwer ss (sok status). Beide opdragte wys die stelsel se aktiewe netwerkverbindings en neem die stelling -l luistervoete te beskryf, sowel as die operateur -p om programme wat aan die sok gekoppel is as 'n kliënt te vertoon. (Daar is baie meer nuttige opsies, maar hierdie twee is voldoende vir hierdie taak.)

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

Dit dui daarop dat die luisteraar die opdrag is inixbediener, loop met proses ID 20629. (En toevallig gebruik dit lêerbeskrywer 3 as die sok.)

Die tweede baie nuttige hulpmiddel om dieselfde inligting te vind, word genoem lsof. Dit lys alle oop lêers (of lêerbeskrywings) op die stelsel. Of jy kan inligting oor een spesifieke lêer kry:

# 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

Proses 20629 is 'n langlewende bediener, so jy kan dit aanheg strewe gebruik van iets soos strace -o /tmp/trace -p 20629. As jy 'n cron-taak in 'n ander terminaal wysig, sal jy 'n spooruitset met 'n fout ontvang. En hier is die resultaat:

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

(Laaste aanvaar () sal nie voltooi word wanneer dit opgespoor word nie.) Weereens, ongelukkig bevat hierdie resultaat nie die fout waarna ons soek nie. Ons sien geen boodskappe wat bcrontag na of ontvang vanaf die sok nie. Voltooi eerder prosesbeheer (kloon, wag 4, SIGCHLD ens.) Hierdie proses veroorsaak 'n kinderproses, wat, soos jy dalk kan raai, die werklike werk doen. En as jy haar spoor moet vang, voeg by die oproep spoor -f. Dit is wat ons sal vind wanneer ons soek na die foutboodskap in die nuwe resultaat met 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 +++

Nou, dis iets. Proses 21470 ontvang 'n "toegang geweier"-fout wanneer 'n lêer by pad probeer skep word tmp/spoel.21470.1573692319.854640 (wat verband hou met die huidige werkgids). As ons net die huidige werkgids geken het, sou ons ook die volle pad ken en in staat wees om uit te vind hoekom die proses nie sy tydelike lêer daarin kan skep nie. Ongelukkig het die proses reeds afgesluit, so jy kan nie net gebruik nie lsof -p 21470 om die huidige gids te vind, maar jy kan in die teenoorgestelde rigting werk - kyk vir PID 21470-stelseloproepe wat die gids verander. (As daar geen is nie, moes PID 21470 hulle van sy ouer geërf het, en dit is reeds deur lsof -bl kan nie uitgevind word nie.) Hierdie stelseloproep is chdir (wat maklik is om uit te vind met behulp van moderne aanlyn soekenjins). En hier is die resultaat van omgekeerde soektogte gebaseer op die spoorresultate, tot by bediener 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 +++

(As jy verlore is, wil jy dalk my vorige plasing lees oor *nix prosesbestuur en skulpe.) Dus, die bediener PID 20629 het nie toestemming ontvang om 'n lêer by die pad te skep nie /var/spool/cron/tmp/spool.21470.1573692319.854640. Waarskynlik is die rede hiervoor die klassieke lêerstelseltoestemminginstellings. Kom ons kyk:

# 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

Dis waar die hond begrawe is! Die bediener loop as 'n gebruiker cron, maar slegs root het toestemming om na die gids te skryf /var/spool/cron/tmp/. Eenvoudige opdrag chown cron /var/spool/cron/tmp/ sal dwing bcron reg werk. (As dit nie die probleem was nie, dan is die volgende mees waarskynlike verdagte 'n kernsekuriteitsmodule soos SELinux of AppArmor, so ek sal die kernboodskaplog met dmesg.)

In totaal

Stelseloproepspore kan oorweldigend wees vir 'n beginner, maar ek hoop ek het gewys dat dit 'n vinnige manier is om 'n hele klas algemene ontplooiingsprobleme te ontfout. Stel jou voor dat jy probeer om 'n multiproses te ontfout bcronmet behulp van 'n stap-vir-stap ontfouter.

Om spoorresultate agteruit langs die stelseloproepketting te ontleed vereis vaardigheid, maar soos ek gesê het, byna altyd, gebruik strewe, Ek kry net die spoorresultaat en soek foute wat van die einde af begin. In elk geval, strewe help my om baie tyd op ontfouting te bespaar. Ek hoop dit sal vir jou ook nuttig wees.

Bron: will.com

Voeg 'n opmerking