Korrigjimi i vendosjes së softuerit me strace

Korrigjimi i vendosjes së softuerit me strace

Puna ime ditore është kryesisht vendosja e softuerit, që do të thotë se kaloj shumë kohë duke u përpjekur t'u përgjigjem pyetjeve si:

  • Ky softuer funksionon për zhvilluesin, por jo për mua. Pse?
  • Dje ky softuer funksionoi për mua, por sot nuk funksionon. Pse?

Ky është një lloj korrigjimi që është paksa i ndryshëm nga korrigjimi i rregullt i softuerit. Korrigjimi i rregullt ka të bëjë me logjikën e kodit, por korrigjimi i vendosjes ka të bëjë me ndërveprimin midis kodit dhe mjedisit. Edhe nëse rrënja e problemit është një gabim logjik, fakti që gjithçka funksionon në një makinë dhe jo në një tjetër do të thotë se problemi është disi në mjedis.

Pra, në vend të mjeteve të zakonshme të korrigjimit si gdb Unë kam një grup të ndryshëm mjetesh për vendosjen e korrigjimit. Dhe mjeti im i preferuar për t'u marrë me problemin si "Pse nuk funksionon ky softuer për mua?" thirrur shtrëngoj.

Çfarë është strace?

shtrëngoj është një mjet për "gjurmimin e thirrjeve të sistemit". Fillimisht u krijua për Linux, por të njëjtat truke korrigjimi mund të bëhen me mjete për sisteme të tjera (DGjurmë ose ktration).

Aplikimi bazë është shumë i thjeshtë. Thjesht duhet të ekzekutoni strace me ndonjë komandë dhe ai do të hedhë të gjitha thirrjet e sistemit (edhe pse fillimisht ndoshta do t'ju duhet ta instaloni vetë shtrëngoj):

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

Cilat janë këto thirrje të sistemit? Kjo është diçka si një API për kernelin e sistemit operativ. Njëherë e një kohë, softueri kishte akses të drejtpërdrejtë në harduerin ku funksiononte. Nëse, për shembull, duhej të shfaqte diçka në ekran, ai luante me porte ose regjistra të hartuar me memorie për pajisjet video. Kur sistemet kompjuterike me shumë detyra u bënë të njohura, kaosi mbretëroi ndërsa aplikacione të ndryshme luftuan për harduerin. Gabimet në një aplikacion mund të rrëzojnë të tjerët, nëse jo të gjithë sistemin. Pastaj mënyrat e privilegjimit (ose "mbrojtja e unazës") u shfaqën në CPU. Kerneli u bë më i privilegjuari: ai mori akses të plotë në harduer, duke krijuar aplikacione më pak të privilegjuara që tashmë duhej të kërkonin akses nga kerneli për të ndërvepruar me harduerin përmes thirrjeve të sistemit.

Në nivelin binar, një thirrje sistemi është paksa e ndryshme nga një thirrje e thjeshtë funksioni, por shumica e programeve përdorin një mbështjellës në bibliotekën standarde. Ato. biblioteka standarde POSIX C përmban një thirrje funksioni shkruaj(), i cili përmban të gjithë kodin specifik të arkitekturës për thirrjen e sistemit shkruaj.

Korrigjimi i vendosjes së softuerit me strace

Me pak fjalë, çdo ndërveprim midis një aplikacioni dhe mjedisit të tij (sistemet kompjuterike) kryhet përmes thirrjeve të sistemit. Prandaj, kur softueri punon në një makinë, por jo në një tjetër, do të ishte mirë të shikoni rezultatet e gjurmimit të thirrjeve të sistemit. Më konkretisht, këtu është një listë e pikave tipike që mund të analizohen duke përdorur një gjurmim të thirrjeve të sistemit:

  • I/O i konsolës
  • I/O në rrjet
  • Qasja në sistemin e skedarëve dhe I/O e skedarit
  • Menaxhimi i jetëgjatësisë së një filli procesi
  • Menaxhimi i kujtesës në nivel të ulët
  • Qasja në drejtues të veçantë pajisjesh

Kur duhet përdorur strace?

Në teori, shtrëngoj përdoret me çdo program në hapësirën e përdoruesit, sepse çdo program në hapësirën e përdoruesit duhet të bëjë thirrje sistemore. Ai funksionon në mënyrë më efikase me programe të përpiluara, të nivelit të ulët, por gjithashtu funksionon me gjuhë të nivelit të lartë si Python nëse mund të shkurtoni zhurmën shtesë nga koha e ekzekutimit dhe interpretuesi.

Në tërë shkëlqimin e saj shtrëngoj manifestohet gjatë korrigjimit të softuerit që funksionon mirë në një makinë, por papritmas ndalon së punuari në një tjetër, duke prodhuar mesazhe të paqarta në lidhje me skedarët, lejet ose përpjekjet e pasuksesshme për të ekzekutuar disa komanda ose diçka tjetër... Është për të ardhur keq, por jo kombinohen aq mirë me probleme të nivelit të lartë si gabimet e verifikimit të certifikatave. Zakonisht kjo kërkon një kombinim shtrëngojndonjehere gjurmë dhe mjete të nivelit më të lartë (si mjeti i linjës së komandës openssl për të korrigjuar çertifikatën).

Ne do të përdorim një server të pavarur si shembull, por gjurmimi i thirrjeve të sistemit shpesh mund të bëhet në platforma më komplekse të vendosjes. Thjesht duhet të zgjidhni mjetet e duhura.

Shembull i thjeshtë korrigjimi

Le të themi se dëshironi të ekzekutoni aplikacionin e mrekullueshëm të serverit, dhe kjo është ajo me të cilën përfundoni:

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

Me sa duket nuk mund ta gjente skedarin e konfigurimit që ke shkruar. Kjo ndodh sepse ndonjëherë kur menaxherët e paketave përpilojnë një aplikacion, ata anashkalojnë vendndodhjet e pritshme të skedarëve. Dhe nëse ndiqni udhëzuesin e instalimit për një shpërndarje, në një tjetër do të gjeni skedarë krejtësisht të ndryshëm nga sa prisnit. Problemi mund të zgjidhet brenda disa sekondash nëse mesazhi i gabimit tregon se ku duhet kërkuar skedari i konfigurimit, por nuk e bën. Pra, ku të shikoni?

Nëse keni akses në kodin burimor, mund ta lexoni dhe të zbuloni gjithçka. Një plan i mirë rezervë, por jo zgjidhja më e shpejtë. Ju mund të përdorni një korrigjues hap pas hapi si gdb dhe shikoni se çfarë bën programi, por është shumë më efektive të përdorni një mjet që është krijuar posaçërisht për të treguar ndërveprimin me mjedisin: shtrëngoj.

Prodhim shtrëngoj mund të duket e tepërt, por lajmi i mirë është se shumica e tyre mund të injorohen në mënyrë të sigurtë. Shpesh është e dobishme të përdoret operatori -o për të ruajtur rezultatet e gjurmimit në një skedar të veçantë:

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

Përafërsisht e gjithë faqja e parë e prodhimit shtrëngoj - Kjo është zakonisht përgatitje e nivelit të ulët për nisje. (Shumë thirrje mmap, mprojtoj, gjilpërë për gjëra të tilla si zbulimi i memories së nivelit të ulët dhe shfaqja e bibliotekave dinamike.) Në fakt, gjatë korrigjimit të rezultatit shtrëngoj Është më mirë të lexosh nga fundi. Më poshtë do të ketë një sfidë shkruaj, i cili shfaq një mesazh gabimi. Ne shikojmë lart dhe shohim thirrjen e parë të gabuar të sistemit - thirrjen hapur, e cila hedh një gabim ENOENT ("skedari ose drejtoria nuk u gjet") duke u përpjekur të hapet /etc/foo/config.json. Këtu duhet të jetë skedari i konfigurimit.

Ky ishte vetëm një shembull, por unë do të thoja 90% të kohës që përdor shtrëngoj, nuk ka asgjë më të vështirë për të bërë se kjo. Më poshtë është një udhëzues i plotë i korrigjimit hap pas hapi:

  • Të mërzitur për shkak të një mesazhi të paqartë në lidhje me një gabim të sistemit nga një program
  • Rinisni programin me shtrëngoj
  • Gjeni mesazhin e gabimit në rezultatet e gjurmimit
  • Shkoni më lart derisa të arrini thirrjen e parë të dështuar të sistemit

Ka shumë të ngjarë që thirrja e sistemit në hapin 4 të zbulojë se çfarë shkoi keq.

Këshilla

Përpara se t'ju tregoj një shembull të korrigjimit më kompleks, unë do t'ju tregoj disa truke për përdorim efektiv shtrëngoj:

njeriu është miku juaj

Në shumë sisteme *nix, një listë e plotë e thirrjeve të sistemit në kernel mund të merret duke ekzekutuar njeri syscalls. Do të shihni gjëra të tilla si brk (2), që do të thotë se më shumë informacion mund të merret duke vrapuar burrë 2 brk.

Grabujë e vogël: njeri 2 pirun më tregon faqen për guaskën pirun () в GNU libc, e cila, rezulton, zbatohet duke thirrur klon (). Thirrje semantike pirun mbetet e njëjtë nëse shkruani një program duke përdorur pirun (), dhe ekzekutoni një gjurmë - nuk do të gjej asnjë telefonatë pirun, në vend të tyre do të ketë klon (). Të tilla raketa ju ngatërrojnë vetëm nëse filloni të krahasoni burimin me daljen shtrëngoj.

Përdorni -o për të ruajtur daljen në një skedar

shtrëngoj mund të gjenerojë rezultate të gjera, kështu që shpesh është e dobishme të ruhen rezultatet e gjurmëve në skedarë të veçantë (si në shembullin e mësipërm). Kjo gjithashtu ndihmon për të shmangur ngatërrimin e daljes së programit me daljen shtrëngoj në tastierë.

Përdorni -s për të parë më shumë të dhëna argumenti

Ju mund të keni vënë re se gjysma e dytë e mesazhit të gabimit nuk tregohet në shembullin e gjurmimit të mësipërm. Është sepse shtrëngoj default tregon vetëm 32 bajtët e parë të argumentit të vargut. Nëse dëshironi të shihni më shumë, shtoni diçka të tillë -s 128 në thirrje shtrëngoj.

-y e bën më të lehtë gjurmimin e skedarëve, prizave, etj.

"Gjithçka është skedar" do të thotë që sistemet *nix bëjnë të gjitha hyrjet/daljet duke përdorur përshkruesit e skedarëve, pavarësisht nëse kjo vlen për një skedar ose një rrjet ose tubacione ndërprocesore. Kjo është e përshtatshme për programim, por e bën të vështirë të mbash gjurmët e asaj që po ndodh realisht kur shikon të zakonshme lexoj и shkruaj në sistemin thirrni rezultatet e gjurmës.

Duke shtuar një operator y, do të detyrosh shtrëngoj shënoni çdo përshkrues skedari në dalje me një shënim se çfarë tregon.

Bashkangjitni një procesi tashmë të ekzekutuar me -p**

Siç do ta shihni nga shembulli më poshtë, ndonjëherë ju duhet të gjurmoni një program që tashmë po funksionon. Nëse dihet që po funksionon si proces 1337 (të themi, nga dalja ps), atëherë mund ta gjurmoni si kjo:

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

Ju mund të keni nevojë për të drejta rrënjësore.

Përdorni -f për të monitoruar proceset e fëmijëve

shtrëngoj Si parazgjedhje, ai gjurmon vetëm një proces. Nëse ky proces krijon procese fëmijësh, atëherë thirrja e sistemit për të krijuar procesin e fëmijës mund të shihet, por thirrjet e sistemit të procesit fëmijë nuk do të shfaqen.

Nëse mendoni se gabimi është në një proces fëmijësh, përdorni deklaratën -f, kjo do të mundësojë gjurmimin e tij. E keqja e kësaj është se rezultati do t'ju ngatërrojë edhe më shumë. Kur shtrëngoj gjurmon një proces ose një thread, ai tregon një rrjedhë të vetme të ngjarjeve të thirrjeve. Kur gjurmon shumë procese në të njëjtën kohë, mund të shihni fillimin e një telefonate të ndërprerë nga një mesazh , pastaj - një mori thirrjesh për degë të tjera ekzekutimi, dhe vetëm atëherë - fundi i së parës <…foocall rifilloi>. Ose ndani të gjitha rezultatet e gjurmëve në skedarë të ndryshëm, duke përdorur gjithashtu operatorin -ff (detajet në udhëheqja mbi shtrëngoj).

Filtro gjurmët duke përdorur -e

Siç mund ta shihni, rezultati i gjurmës është një grumbull i vërtetë i të gjitha thirrjeve të mundshme të sistemit. Flamuri -e Ju mund të filtroni gjurmën (shih udhëheqja mbi shtrëngoj). Avantazhi kryesor është se është më shpejt të ekzekutosh një gjurmë të filtruar sesa të bësh një gjurmë të plotë dhe më pas grep`në. Për të qenë i sinqertë, pothuajse gjithmonë nuk më intereson.

Jo të gjitha gabimet janë të këqija

Një shembull i thjeshtë dhe i zakonshëm është një program që kërkon një skedar në disa vende njëherësh, si një guaskë që kërkon një drejtori që përmban një skedar të ekzekutueshëm:

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

Heuristikat si "kërkesa e fundit e dështuar përpara raportimit të një gabimi" janë të mira në gjetjen e gabimeve përkatëse. Sido që të jetë, është logjike të fillohet nga fundi.

Udhëzimet e programimit C mund t'ju ndihmojnë të kuptoni thirrjet e sistemit.

Thirrjet standarde në bibliotekat C nuk janë thirrje sistemore, por vetëm një shtresë e hollë sipërfaqësore. Pra, nëse kuptoni të paktën pak se si dhe çfarë të bëni në C, do të jetë më e lehtë për ju të kuptoni rezultatet e gjurmimit të thirrjes së sistemit. Për shembull, keni probleme me korrigjimin e thirrjeve në sistemet e rrjetit, shikoni të njëjtin klasik Bija's Guide to Network Programming.

Një shembull më kompleks korrigjimi

Tashmë thashë se shembulli i korrigjimit të thjeshtë është një shembull i asaj që më së shumti duhet të merrem me të kur punoj shtrëngoj. Megjithatë, ndonjëherë kërkohet një hetim i vërtetë, kështu që këtu është një shembull i vërtetë i korrigjimit më të avancuar.

bcron - planifikuesi i përpunimit të detyrave, një zbatim tjetër i demonit *nix cron. Është i instaluar në server, por kur dikush përpiqet të modifikojë orarin, kjo është ajo që ndodh:

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

Mirë, kjo do të thotë bcron u përpoq të shkruante një skedar të caktuar, por nuk funksionoi dhe ai nuk do ta pranojë pse. Duke zbuluar shtrëngoj:

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

Ka një mesazh gabimi afër fundit shkruaj, por këtë herë diçka është ndryshe. Së pari, nuk ka asnjë gabim përkatës të thirrjes së sistemit, i cili zakonisht ndodh para kësaj. Së dyti, është e qartë se diku dikush tashmë e ka lexuar mesazhin e gabimit. Duket sikur problemi i vërtetë është diku tjetër, dhe bcrontab thjesht luan mesazhin.

Nëse shikoni njeriu 2 lexoi, mund të shihni se argumenti i parë (3) është një përshkrues skedari, të cilin *nix e përdor për të gjithë përpunimin I/O. Si mund të zbuloj se çfarë përfaqëson përshkruesi i skedarit 3? Në këtë rast të veçantë, ju mund të vraponi shtrëngoj me operator y (shih më lart) dhe do t'ju tregojë automatikisht, por për të kuptuar gjëra të tilla, është e dobishme të dini se si të lexoni dhe analizoni rezultatet e gjurmimit.

Burimi i një përshkruesi skedari mund të jetë një nga shumë thirrjet e sistemit (gjithçka varet nga ajo për çfarë shërben përshkruesi - një tastierë, një prizë rrjeti, vetë skedari ose diçka tjetër), por sido që të jetë, ne kërkojmë thërret duke kthyer 3 (d.m.th. ne kërkojmë "= 3" në rezultatet e gjurmimit). Në këtë rezultat janë 2 prej tyre: hapur në krye dhe fole Në mes. hapur hap skedarin por Mbyll(3) më pas do të tregojë se mbyllet përsëri. (Rake: përshkruesit e skedarëve mund të ripërdoren kur hapen dhe mbyllen). Thirrni fole () i pershtatshem sepse eshte i fundit me pare lexo (), dhe rezulton se bcrontab punon me diçka përmes një prize. Rreshti tjetër tregon se përshkruesi i skedarit është i lidhur me fole domeni unix Rrugës /var/run/bcron-spool.

Pra, ne duhet të gjejmë procesin që lidhet me fole unix ne anen tjeter. Ekzistojnë disa truke të rregullta për këtë qëllim, të dyja janë të dobishme për korrigjimin e vendosjeve të serverit. E para është të përdoret netstat ose më të reja ss (statusi i folesë). Të dy komandat tregojnë lidhjet aktive të rrjetit të sistemit dhe marrin deklaratën -l për të përshkruar prizat e dëgjimit, si dhe operatorin -p për të shfaqur programet e lidhura me prizën si klient. (Ka shumë opsione më të dobishme, por këto dy janë të mjaftueshme për këtë detyrë.)

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

Kjo sugjeron që dëgjuesi është komanda inixserver, që funksionon me ID-në e procesit 20629. (Dhe, rastësisht, ai përdor përshkruesin e skedarit 3 si fole.)

Mjeti i dytë me të vërtetë i dobishëm për gjetjen e të njëjtit informacion quhet lsof. Ai liston të gjithë skedarët e hapur (ose përshkruesit e skedarëve) në sistem. Ose mund të merrni informacion për një skedar specifik:

# 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 është një server jetëgjatë, kështu që ju mund ta bashkëngjitni atë shtrëngoj duke përdorur diçka të tillë strace -o /tmp/trace -p 20629. Nëse redaktoni një punë të cron në një terminal tjetër, do të merrni një dalje gjurmësh me një gabim. Dhe këtu është rezultati:

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

(E fundit pranoj () nuk do të plotësohet gjatë gjurmimit.) Përsëri, për fat të keq, ky rezultat nuk përmban gabimin që po kërkojmë. Ne nuk shohim asnjë mesazh që bcrontag dërgon ose merr nga priza. Në vend të kësaj, kontrolli i plotë i procesit (klon, prisni 4, SIGCHLD etj.) Ky proces krijon një proces fëmijësh, i cili, siç mund ta merrni me mend, bën punën e vërtetë. Dhe nëse keni nevojë të kapni gjurmët e saj, shtojeni thirrjen strace -f. Kjo është ajo që do të gjejmë kur të kërkojmë mesazhin e gabimit në rezultatin e ri me 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 +++

Tani, kjo është diçka. Procesi 21470 merr një gabim "qasja e mohuar" kur përpiqet të krijojë një skedar në shteg tmp/spool.21470.1573692319.854640 (në lidhje me drejtorinë aktuale të punës). Nëse thjesht do ta dinim direktoriumin aktual të punës, do të dinim gjithashtu rrugën e plotë dhe do të ishim në gjendje të kuptonim pse procesi nuk mund të krijojë skedarin e tij të përkohshëm në të. Fatkeqësisht, procesi tashmë ka dalë, kështu që nuk mund ta përdorni vetëm lsof -p 21470 në mënyrë që të gjeni direktoriumin aktual, por mund të punoni në drejtim të kundërt - kërkoni thirrjet e sistemit PID 21470 që ndryshojnë direktoriumin. (Nëse nuk ka asnjë, PID 21470 duhet t'i ketë trashëguar ato nga prindi i tij, dhe kjo tashmë ka përfunduar lsof -p nuk mund të zbulohet.) Kjo thirrje sistemi është çdir (që është e lehtë të zbulohet me ndihmën e motorëve modernë të kërkimit në internet). Dhe këtu është rezultati i kërkimeve të kundërta bazuar në rezultatet e gjurmës, deri në serverin 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 +++

(Nëse keni humbur, mund të dëshironi të lexoni postimin tim të mëparshëm rreth menaxhimit të *nix procesit dhe predhave.) Pra, serveri PID 20629 nuk mori leje për të krijuar një skedar në shteg /var/spool/cron/tmp/spool.21470.1573692319.854640. Me shumë mundësi, arsyeja për këtë janë cilësimet klasike të lejeve të sistemit të skedarëve. Le të kontrollojmë:

# 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

Ja ku është varrosur qeni! Serveri funksionon si një cron i përdoruesit, por vetëm root ka leje për të shkruar në drejtori /var/spool/cron/tmp/. Komandë e thjeshtë chown cron /var/spool/cron/tmp/ Do te detyroj bcron punoni si duhet. (Nëse nuk ishte ky problemi, atëherë i dyshuari tjetër më i mundshëm është një modul sigurie kernel si SELinux ose AppArmor, kështu që unë do të kontrolloja regjistrin e mesazheve të kernelit me dmesg.)

Në total

Gjurmët e thirrjeve të sistemit mund të jenë dërrmuese për një fillestar, por shpresoj se kam treguar se ato janë një mënyrë e shpejtë për të korrigjuar një klasë të tërë të problemeve të zakonshme të vendosjes. Imagjinoni të përpiqeni të korrigjoni një multiproces bcronduke përdorur një korrigjues hap pas hapi.

Analizimi i rezultateve të gjurmës mbrapsht përgjatë zinxhirit të thirrjes së sistemit kërkon aftësi, por siç thashë, pothuajse gjithmonë, duke përdorur shtrëngoj, Unë thjesht marr rezultatin e gjurmës dhe kërkoj gabime duke filluar nga fundi. Gjithsesi, shtrëngoj më ndihmon të kursej shumë kohë në korrigjimin e gabimeve. Shpresoj se do të jetë e dobishme edhe për ju.

Burimi: www.habr.com

Shto një koment