AtkļūdoŔanas programmatūras izvietoŔana ar strace

AtkļūdoŔanas programmatūras izvietoŔana ar strace

Mans ikdienas darbs galvenokārt ir programmatÅ«ras izvietoÅ”ana, kas nozÄ«mē, ka es pavadu daudz laika, mēģinot atbildēt uz tādiem jautājumiem kā:

  • Å Ä« programmatÅ«ra darbojas izstrādātājam, bet ne man. Kāpēc?
  • Vakar Ŕī programmatÅ«ra man darbojās, bet Å”odien tā nedarbojas. Kāpēc?

Å Ä« ir sava veida atkļūdoÅ”ana, kas nedaudz atŔķiras no parastās programmatÅ«ras atkļūdoÅ”anas. Regulāra atkļūdoÅ”ana ir saistÄ«ta ar koda loÄ£iku, bet izvietoÅ”anas atkļūdoÅ”ana ir saistÄ«ta ar mijiedarbÄ«bu starp kodu un vidi. Pat ja problēmas sakne ir loÄ£iska kļūda, tas, ka vienā maŔīnā viss darbojas, bet citā ne, nozÄ«mē, ka problēma ir kaut kādā veidā vidē.

Tātad parasto atkļūdoÅ”anas rÄ«ku vietā, piemēram, gdb Man ir cits rÄ«ku komplekts izvietoÅ”anas atkļūdoÅ”anai. Un mans iecienÄ«tākais rÄ«ks tādas problēmas risināŔanai kā ā€œKāpēc Ŕī programmatÅ«ra man nedarbojas?ā€ sauca strace.

Kas ir strace?

strace ir ā€œsistēmas zvanu izsekoÅ”anasā€ rÄ«ks. Sākotnēji tas tika izveidots operētājsistēmai Linux, taču tos paÅ”us atkļūdoÅ”anas trikus var veikt ar rÄ«kiem citām sistēmām (DTrace vai kttrace).

Pamata lietojumprogramma ir ļoti vienkārÅ”a. Jums vienkārÅ”i jāpalaiž strace ar jebkuru komandu un tas izmetÄ«s visus sistēmas izsaukumus (lai gan vispirms jums tas bÅ«s jāinstalē paÅ”am strace):

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

Kādi ir Å”ie sistēmas izsaukumi? Tas ir kaut kas lÄ«dzÄ«gs API operētājsistēmas kodolam. Kādreiz programmatÅ«rai bija tieÅ”a piekļuve aparatÅ«rai, kurā tā darbojās. Ja, piemēram, tai vajadzēja kaut ko parādÄ«t ekrānā, tas spēlēja ar video ierīču portiem vai atmiņas kartētiem reÄ£istriem. Kad daudzuzdevumu datorsistēmas kļuva populāras, valdÄ«ja haoss, kad dažādas lietojumprogrammas cÄ«nÄ«jās par aparatÅ«ru. Kļūdas vienā lietojumprogrammā var sabojāt citas, ja ne visu sistēmu. Tad CPU parādÄ«jās privilēģiju režīmi (vai ā€œzvana aizsardzÄ«baā€). Kodols kļuva par priviliģētāko: tas saņēma pilnu piekļuvi aparatÅ«rai, radot mazāk priviliģētas lietojumprogrammas, kurām jau bija jāpieprasa piekļuve no kodola, lai mijiedarbotos ar aparatÅ«ru, izmantojot sistēmas izsaukumus.

Binārajā lÄ«menÄ« sistēmas izsaukums nedaudz atŔķiras no vienkārÅ”as funkcijas izsaukuma, taču lielākā daļa programmu standarta bibliotēkā izmanto iesaiņojumu. Tie. POSIX C standarta bibliotēka satur funkcijas izsaukumu rakstÄ«t (), kurā ir viss sistēmas izsaukuma arhitektÅ«rai raksturÄ«gais kods rakstÄ«t.

AtkļūdoŔanas programmatūras izvietoŔana ar strace

ÄŖsāk sakot, jebkura mijiedarbÄ«ba starp lietojumprogrammu un tās vidi (datorsistēmām) tiek veikta, izmantojot sistēmas izsaukumus. Tāpēc, ja programmatÅ«ra darbojas vienā maŔīnā, bet ne citā, bÅ«tu labi apskatÄ«t sistēmas zvanu izsekoÅ”anas rezultātus. Konkrētāk, Å”eit ir saraksts ar tipiskiem punktiem, kurus var analizēt, izmantojot sistēmas izsaukuma izsekoÅ”anu:

  • Konsoles I/O
  • TÄ«kla I/O
  • Piekļuve failu sistēmai un failu I/O
  • Procesa pavediena kalpoÅ”anas laika pārvaldÄ«ba
  • Zema lÄ«meņa atmiņas pārvaldÄ«ba
  • Piekļuve konkrētiem ierīču draiveriem

Kad lietot strace?

Teorētiski, strace izmanto ar jebkuru programmu lietotāja telpā, jo jebkurai programmai lietotāja telpā ir jāveic sistēmas izsaukumi. Tas darbojas efektīvāk ar kompilētām zema līmeņa programmām, taču tas darbojas arī ar augsta līmeņa valodām, piemēram, Python, ja varat samazināt izpildlaika un tulka papildu troksni.

Visā krāŔņumā strace izpaužas programmatÅ«ras atkļūdoÅ”anas laikā, kas labi darbojas vienā maŔīnā, bet pēkŔņi pārstāj darboties citā, producējot neskaidrus ziņojumus par failiem, atļaujām vai neveiksmÄ«giem mēģinājumiem izpildÄ«t kādas komandas vai ko citu... Žēl, bet tā nav tik labi apvienotas ar augsta lÄ«meņa problēmām, piemēram, sertifikātu pārbaudes kļūdām. Parasti tas prasa kombināciju stracedažreiz lttrace un augstāka lÄ«meņa rÄ«ki (piemēram, komandrindas rÄ«ks OpenSSL lai atkļūdotu sertifikātu).

Kā piemēru izmantosim atseviŔķu serveri, taču sistēmas zvanu izsekoÅ”anu bieži var veikt sarežģītākās izvietoÅ”anas platformās. Jums vienkārÅ”i jāizvēlas pareizie instrumenti.

VienkārÅ”s atkļūdoÅ”anas piemērs

Pieņemsim, ka vēlaties palaist apbrīnojamo servera lietojumprogrammu foo, un tas ir tas, ko jūs galu galā sasniedzat:

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

AcÄ«mredzot tas nevarēja atrast jÅ«su rakstÄ«to konfigurācijas failu. Tas notiek tāpēc, ka dažreiz, kad pakotņu pārvaldnieki apkopo lietojumprogrammu, viņi ignorē paredzētās failu atraÅ”anās vietas. Un, ja sekojat viena izplatÄ«Å”anas instalÄ“Å”anas rokasgrāmatai, citā jÅ«s atradÄ«sit failus, kas pilnÄ«gi atŔķiras no tā, kur jÅ«s gaidÄ«jāt. Problēmu varētu atrisināt dažu sekunžu laikā, ja kļūdas ziņojumā ir norādÄ«ts, kur meklēt konfigurācijas failu, bet tā nav. Tātad, kur meklēt?

Ja jums ir piekļuve avota kodam, varat to izlasÄ«t un uzzināt visu. Labs rezerves plāns, bet ne ātrākais risinājums. Varat izmantot soli pa solim atkļūdotāju, piemēram, gdb un redzēt, ko programma dara, taču daudz efektÄ«vāk ir izmantot rÄ«ku, kas ir Ä«paÅ”i izstrādāts, lai parādÄ«tu mijiedarbÄ«bu ar vidi: strace.

secinājums strace var Ŕķist lieki, taču labā ziņa ir tā, ka lielāko daļu no tā var droÅ”i ignorēt. Bieži vien ir lietderÄ«gi izmantot operatoru -o, lai saglabātu izsekoÅ”anas rezultātus atseviŔķā failā:

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

Apmēram visa izvades pirmā lapa strace - Tā parasti ir zema lÄ«meņa sagatavoÅ”anās palaiÅ”anai. (Daudz zvanu mmap, mprotect, spalviņa tādām lietām kā zema lÄ«meņa atmiņas noteikÅ”ana un dinamisko bibliotēku parādÄ«Å”ana.) Faktiski atkļūdoÅ”anas laikā izvade strace Labāk lasÄ«t no paŔām beigām. Zemāk bÅ«s izaicinājums rakstÄ«t, kas parāda kļūdas ziņojumu. Mēs skatāmies augŔā un redzam pirmo kļūdaino sistēmas zvanu - zvanu openat, kas rada kļūdu ENOENT (ā€œfails vai direktorijs nav atrastsā€), mēģinot atvērt /etc/foo/config.json. Å eit ir jāatrodas konfigurācijas failam.

Å is bija tikai piemērs, bet es teiktu, ka 90% laika izmantoju strace, nav nekā daudz grÅ«tāk izdarāma par Å”o. Tālāk ir sniegts pilnÄ«gs soli pa solim atkļūdoÅ”anas ceļvedis:

  • Esiet satraukts, jo programmā parādās neskaidrs ziņojums par sistēmas y kļūdu
  • Restartējiet programmu ar strace
  • IzsekoÅ”anas rezultātos atrodiet kļūdas ziņojumu
  • Dodieties augstāk, lÄ«dz sasniedzat pirmo neveiksmÄ«go sistēmas zvanu

Ļoti iespējams, ka sistēmas izsaukums 4. darbībā atklās, kas nogāja greizi.

Padomi

Pirms parādÄ«Å”u sarežģītākas atkļūdoÅ”anas piemēru, es parādÄ«Å”u dažus trikus efektÄ«vai lietoÅ”anai strace:

vīrietis ir tavs draugs

Daudzās *nix sistēmās, palaižot, var iegūt pilnu sistēmas izsaukumu sarakstu kodolam vīrietis syscalls. Jūs redzēsit tādas lietas kā brk (2), kas nozīmē, ka vairāk informācijas var iegūt, palaižot vīrietis 2 brk.

Mazs grābeklis: cilvēks 2 dakÅ”a parāda man čaulas lapu dakÅ”a () Š² GNU libc, kas, izrādās, tiek Ä«stenots zvanot klons (). Zvanu semantika dakÅ”a paliek nemainÄ«gs, ja rakstāt programmu, izmantojot dakÅ”a (), un izsekot ā€” es neatradÄ«Å”u nevienu zvanu dakÅ”a, viņu vietā bÅ«s klons (). Šādi grābekļi jÅ«s mulsina tikai tad, ja sākat salÄ«dzināt avotu ar izvadi strace.

Izmantojiet -o, lai saglabātu izvadi failā

strace var radÄ«t plaÅ”u izvadi, tāpēc bieži vien ir lietderÄ«gi izsekoÅ”anas rezultātus saglabāt atseviŔķos failos (kā iepriekÅ” minētajā piemērā). Tas arÄ« palÄ«dz nesajaukt programmas izvadi ar izvadi strace konsolē.

Izmantojiet -s, lai skatītu vairāk argumentu datu

Iespējams, pamanÄ«jāt, ka kļūdas ziņojuma otrā puse nav parādÄ«ta iepriekÅ” minētajā piemērā. Tas ir tāpēc strace pēc noklusējuma tiek rādÄ«ti tikai pirmie 32 virknes argumenta baiti. Ja vēlaties redzēt vairāk, pievienojiet kaut ko lÄ«dzÄ«gu -s 128 uz zvanu strace.

-y atvieglo failu, ligzdu utt. izsekoŔanu.

ā€œViss ir failsā€ nozÄ«mē, ka *nix sistēmas veic visus I/O, izmantojot failu deskriptorus, neatkarÄ«gi no tā, vai tas attiecas uz failu vai tÄ«klu vai starpprocesu caurulēm. Tas ir ērti programmÄ“Å”anai, taču apgrÅ«tina izsekot tam, kas patiesÄ«bā notiek, kad redzat kopÄ«go lasÄ«t Šø rakstÄ«t sistēmas zvanu izsekoÅ”anas rezultātos.

Pievienojot operatoru -у, tu piespiedÄ«si strace anotēt katru faila deskriptoru izvadē ar piezÄ«mi, uz ko tas norāda.

Pievienojiet jau esoŔam procesam ar -p**

Kā redzēsit tālāk sniegtajā piemērā, dažreiz jums ir jāizseko programmai, kas jau darbojas. Ja ir zināms, ka tas darbojas kā process 1337 (teiksim, no izejas ps), varat to izsekot Ŕādi:

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

Jums var būt nepiecieŔamas root tiesības.

Izmantojiet -f, lai uzraudzītu bērnu procesus

strace Pēc noklusējuma tas izseko tikai vienu procesu. Ja Å”is process rada bērnprocesus, var redzēt sistēmas izsaukumu, lai radÄ«tu bērnprocesu, bet pakārtotā procesa sistēmas izsaukumi netiks parādÄ«ti.

Ja domājat, ka kļūda ir pakārtotā procesā, izmantojiet paziņojumu -f, tas ļaus to izsekot. NegatÄ«vā puse ir tāda, ka izvade jÅ«s mulsinās vēl vairāk. Kad strace izseko vienu procesu vai vienu pavedienu, tas parāda vienu zvanu notikumu straumi. Ja tas vienlaikus izseko vairākus procesus, iespējams, redzēsit zvana sākumu, ko pārtrauc ziņojums , tad - kaudze izsaukumu uz citiem izpildes zariem, un tikai tad - beigas pirmajam <ā€¦foocal resumed>. Vai arÄ« sadaliet visus izsekoÅ”anas rezultātus dažādos failos, izmantojot arÄ« operatoru -ff (sÄ«kāka informācija vadÄ«ba par strace).

Filtrējiet pēdas, izmantojot -e

Kā redzat, izsekoÅ”anas rezultāts ir reāla visu iespējamo sistēmas izsaukumu kaudze. Karogs -e Varat filtrēt pēdas (sk vadÄ«ba par strace). Galvenā priekÅ”rocÄ«ba ir tā, ka ir ātrāk palaist filtrētu trasÄ“Å”anu, nekā veikt pilnu izsekoÅ”anu un pēc tam grep`at. GodÄ«gi sakot, man gandrÄ«z vienmēr ir vienalga.

Ne visas kļūdas ir sliktas

VienkārÅ”s un izplatÄ«ts piemērs ir programma, kas meklē failu vairākās vietās vienlaikus, piemēram, apvalks, kas meklē direktoriju, kurā ir izpildāms fails:

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

Heiristika, piemēram, ā€œpēdējais neveiksmÄ«gais pieprasÄ«jums pirms ziņoÅ”anas par kļūduā€, labi palÄ«dz atrast attiecÄ«gās kļūdas. Lai kā arÄ« bÅ«tu, loÄ£iski ir sākt no paŔām beigām.

C programmÄ“Å”anas apmācÄ«bas var palÄ«dzēt izprast sistēmas zvanus.

Standarta izsaukumi uz C bibliotēkām nav sistēmas izsaukumi, bet tikai plāns virsmas slānis. Tātad, ja jÅ«s vismaz nedaudz saprotat, kā un ko darÄ«t C, jums bÅ«s vieglāk saprast sistēmas izsaukuma izsekoÅ”anas rezultātus. Piemēram, jums ir problēmas ar tÄ«kla sistēmu zvanu atkļūdoÅ”anu, skatiet to paÅ”u klasiku Bija ceļvedis tÄ«kla programmÄ“Å”anai.

Sarežģītāks atkļūdoÅ”anas piemērs

Es jau teicu, ka vienkārÅ”as atkļūdoÅ”anas piemērs ir piemērs tam, ar ko man galvenokārt jāsaskaras, strādājot strace. Tomēr dažreiz ir nepiecieÅ”ama reāla izmeklÄ“Å”ana, tāpēc Å”eit ir reāls piemērs uzlabotai atkļūdoÅ”anai.

bcron - uzdevumu apstrādes plānotājs, cita *nix dēmona ievieÅ”ana cron. Tas ir instalēts serverÄ«, bet, kad kāds mēģina rediģēt grafiku, notiek Ŕādi:

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

Labi, tas nozÄ«mē bcron mēģināja uzrakstÄ«t noteiktu failu, bet tas neizdevās, un viņŔ neatzÄ«s, kāpēc. Atklājot strace:

# strace -o /tmp/trace crontab -e -u logs
bcrontab: Fatal: Could not create temporary file
# cat /tmp/trace
...
openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
read(3, "#Ansible: logsaggn20 14 * * * lo"..., 8192) = 150
read(3, "", 8192)                       = 0
munmap(0x7f82049b4000, 8192)            = 0
close(3)                                = 0
socket(AF_UNIX, SOCK_STREAM, 0)         = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
write(3, "156:Slogs #Ansible: logsaggn20 1"..., 161) = 161
read(3, "32:ZCould not create temporary f"..., 8192) = 36
munmap(0x7f82049b4000, 8192)            = 0
close(3)                                = 0
write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49
unlink("bcrontab.14779.1573691864.847933") = 0
exit_group(111)                         = ?
+++ exited with 111 +++

GandrÄ«z paŔās beigās ir kļūdas ziņojums rakstÄ«t, bet Å”oreiz kaut kas ir savādāk. Pirmkārt, nav atbilstoÅ”as ā€‹ā€‹sistēmas izsaukuma kļūdas, kas parasti notiek pirms Ŕīs. Otrkārt, ir skaidrs, ka kaut kur kāds jau ir izlasÄ«jis kļūdas ziņojumu. Å Ä·iet, ka patiesā problēma ir kaut kur citur, un bcrontab vienkārÅ”i atskaņo ziņojumu.

Ja paskatās man 2 lasÄ«t, varat redzēt, ka pirmais arguments (3) ir faila deskriptors, ko *nix izmanto visai I/O apstrādei. Kā uzzināt, ko apzÄ«mē faila deskriptors 3? Å ajā konkrētajā gadÄ«jumā jÅ«s varat palaist strace ar operatoru -у (skatiet iepriekÅ”), un tas jums automātiski pateiks, taču, lai noskaidrotu Ŕādas lietas, ir noderÄ«gi zināt, kā lasÄ«t un parsēt izsekoÅ”anas rezultātus.

Faila deskriptora avots var bÅ«t viens no daudziem sistēmas izsaukumiem (tas viss ir atkarÄ«gs no tā, kam deskriptors ir paredzēts - konsolei, tÄ«kla ligzdai, paÅ”am failam vai kaut kam citam), taču, lai kā arÄ« bÅ«tu, mēs meklējam zvani, atgriežot 3 (t.i., izsekoÅ”anas rezultātos mēs meklējam ā€œ= 3ā€). Å ajā rezultātā ir 2 no tiem: openat paŔā augŔā un ligzda VidÅ«. openat atver failu, bet aizvērt(3) parādÄ«s, ka tas atkal aizveras. (Rake: failu deskriptorus var izmantot atkārtoti, kad tie tiek atvērti un aizvērti). Zvaniet ligzda () piemērots, jo tas ir pēdējais iepriekÅ” lasÄ«t (), un izrādās, ka bcrontab ar kaut ko strādā caur ligzdu. Nākamā rinda parāda, ka faila deskriptors ir saistÄ«ts ar unix domēna ligzda paceļam /var/run/bcron-spool.

Tātad, mums ir jāatrod process, kas saistÄ«ts ar unix ligzda citā pusē. Å im nolÅ«kam ir daži glÄ«ti triki, kas abi ir noderÄ«gi servera izvietoÅ”anas atkļūdoÅ”anai. Pirmais ir izmantot netstat vai jaunāka ss (ligzdas statuss). Abas komandas parāda sistēmas aktÄ«vos tÄ«kla savienojumus un ņem paziņojumu -l lai aprakstÄ«tu klausÄ«Å”anās ligzdas, kā arÄ« operatoru -p lai parādÄ«tu programmas, kas pievienotas kontaktligzdai kā klientam. (Ir daudz vairāk noderÄ«gu iespēju, taču Å”im uzdevumam pietiek ar Ŕīm divām.)

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

Tas liek domāt, ka klausÄ«tājs ir komanda inixserveris, kas darbojas ar procesa ID 20629. (Un nejauÅ”i tas izmanto faila deskriptoru 3 kā ligzdu.)

Tiek saukts otrs patieŔām noderÄ«gais rÄ«ks vienas un tās paÅ”as informācijas atraÅ”anai lsof. Tajā ir uzskaitÄ«ti visi sistēmā atvērtie faili (vai failu deskriptori). Vai arÄ« varat iegÅ«t informāciju par vienu konkrētu failu:

# 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 ir ilgmūžīgs serveris, tāpēc varat to pievienot strace izmantojot kaut ko lÄ«dzÄ«gu strace -o /tmp/trace -p 20629. Ja rediģējat cron darbu citā terminālā, jÅ«s saņemsit izsekoÅ”anas izvadi ar kļūdu. Un Å”eit ir rezultāts:

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

(Pēdējais pieņemt () izsekoÅ”anas laikā netiks pabeigts.) Diemžēl Å”is rezultāts nesatur kļūdu, kuru mēs meklējam. Mēs neredzam ziņojumus, ko bcrontag sÅ«ta uz ligzdu vai saņem no tās. Tā vietā pilnÄ«ga procesa kontrole (klons, gaidÄ«t 4, SIGCHLD utt.) Å is process rada bērnu procesu, kas, kā jÅ«s varētu nojaust, veic patieso darbu. Un, ja jums ir jānoÄ·er viņas pēdas, pievienojiet zvanam strace -f. To mēs atradÄ«sim, meklējot kļūdas ziņojumu jaunajā rezultātā ar 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 +++

Tagad tas ir kaut kas. Process 21470 saņem kļūdu "piekļuve liegta", mēģinot izveidot failu ceļā tmp/spool.21470.1573692319.854640 (attiecas uz paÅ”reizējo darba direktoriju). Ja mēs tikai zinātu paÅ”reizējo darba direktoriju, mēs zinātu arÄ« pilnu ceļu un varētu izdomāt, kāpēc process nevar tajā izveidot savu pagaidu failu. Diemžēl process jau ir beidzies, tāpēc jÅ«s nevarat vienkārÅ”i izmantot lsof -p 21470 lai atrastu paÅ”reizējo direktoriju, bet varat strādāt arÄ« pretējā virzienā - meklējiet PID 21470 sistēmas zvanus, kas maina direktoriju. (Ja tādu nav, PID 21470 tos noteikti ir mantojis no sava vecāka, un tas jau ir pabeigts lsof -p nevar noskaidrot.) Å is sistēmas izsaukums ir chdir (ko ir viegli noskaidrot, izmantojot mÅ«sdienu tieÅ”saistes meklētājprogrammas). Un Å”eit ir apgrieztās meklÄ“Å”anas rezultāts, pamatojoties uz izsekoÅ”anas rezultātiem, lÄ«dz pat servera 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 +++

(Ja esat apmaldÄ«jies, iespējams, vēlēsities izlasÄ«t manu iepriekŔējo ziņu par *nix procesu pārvaldÄ«bu un čaulām.) Tātad servera PID 20629 nesaņēma atļauju izveidot failu ceļā /var/spool/cron/tmp/spool.21470.1573692319.854640. Visticamāk, iemesls tam ir klasiskie failu sistēmas atļauju iestatÄ«jumi. PārbaudÄ«sim:

# 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

Tur suns ir aprakts! Serveris darbojas kā lietotāja cron, bet tikai root ir atļauja rakstÄ«t direktorijā /var/spool/cron/tmp/. VienkārÅ”a komanda chown cron /var/spool/cron/tmp/ piespiedÄ«s bcron strādāt pareizi. (Ja tā nebija problēma, tad nākamais visticamākais aizdomÄ«gais ir kodola droŔības modulis, piemēram, SELinux vai AppArmor, tāpēc es pārbaudÄ«tu kodola ziņojumu žurnālu ar dmesg.)

Kopā

Sistēmas izsaukuma izsekoÅ”ana iesācējiem var bÅ«t nepārvarama, taču es ceru, ka esmu parādÄ«jis, ka tie ir ātrs veids, kā atkļūdot visas izplatÄ«tākās izvietoÅ”anas problēmas. Iedomājieties, ka mēģināt atkļūdot vairāku procesu bcronizmantojot soli pa solim atkļūdotāju.

IzsekoÅ”anas rezultātu parsÄ“Å”ana atpakaļ pa sistēmas izsaukuma ķēdi prasa prasmes, taču, kā jau teicu, gandrÄ«z vienmēr, izmantojot strace, es vienkārÅ”i saņemu izsekoÅ”anas rezultātu un meklēju kļūdas, sākot no beigām. Jebkurā gadÄ«jumā strace palÄ«dz man ietaupÄ«t daudz laika atkļūdoÅ”anai. Ceru, ka tas noderēs arÄ« jums.

Avots: www.habr.com

Pievieno komentāru