Ծրագրաշարի տեղադրման վրիպազերծում strace-ով

Ծրագրաշարի տեղադրման վրիպազերծում strace-ով

Իմ ամենօրյա աշխատանքը հիմնականում ծրագրային ապահովման տեղակայումն է, ինչը նշանակում է, որ ես շատ ժամանակ եմ ծախսում՝ փորձելով պատասխանել այնպիսի հարցերի, ինչպիսիք են.

  • Այս ծրագիրը աշխատում է մշակողի համար, բայց ոչ ինձ համար: Ինչո՞ւ։
  • Երեկ այս ծրագրաշարն աշխատում էր ինձ համար, բայց այսօր՝ ոչ: Ինչո՞ւ։

Սա վրիպազերծման մի տեսակ է, որը մի փոքր տարբերվում է սովորական ծրագրային ապահովման վրիպազերծումից: Կանոնավոր վրիպազերծումը վերաբերում է կոդի տրամաբանությանը, բայց տեղակայման վրիպազերծումը վերաբերում է կոդի և շրջակա միջավայրի փոխազդեցությանը: Նույնիսկ եթե խնդրի արմատը տրամաբանական սխալ է, այն փաստը, որ ամեն ինչ աշխատում է մի մեքենայի վրա, իսկ մյուսի վրա՝ ոչ, նշանակում է, որ խնդիրն ինչ-որ կերպ գտնվում է միջավայրում:

Այսպիսով, սովորական կարգաբերման գործիքների փոխարեն, ինչպիսիք են gdb Ես ունեմ տարբեր գործիքների շարք՝ տեղակայման վրիպազերծման համար: Եվ իմ սիրած գործիքը այնպիսի խնդրի հետ առնչվելու համար, ինչպիսին է «Ինչու՞ այս ծրագիրը չի աշխատում ինձ համար»: կանչեց ստրաս.

Ի՞նչ է ստրեյսը:

ստրաս «համակարգային զանգերի հետագծման» գործիք է։ Այն ի սկզբանե ստեղծվել է Linux-ի համար, բայց նույն կարգաբերման հնարքները կարելի է անել այլ համակարգերի գործիքների միջոցով (DTrace կամ ktrace).

Հիմնական դիմումը շատ պարզ է. Դուք պարզապես պետք է գործարկեք 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 +++

Որո՞նք են այս համակարգի կոչումները: Սա օպերացիոն համակարգի միջուկի API-ի պես մի բան է: Ժամանակին ծրագրակազմն ուղղակիորեն մուտք ուներ այն սարքաշարին, որի վրա աշխատում էր: Եթե, օրինակ, անհրաժեշտ էր ինչ-որ բան ցուցադրել էկրանին, այն խաղում էր պորտերի կամ վիդեո սարքերի հիշողության քարտեզագրված ռեգիստրների հետ: Երբ բազմաֆունկցիոնալ համակարգչային համակարգերը հայտնի դարձան, քաոսը տիրեց, քանի որ տարբեր հավելվածներ պայքարում էին ապարատային սարքավորման համար: Մեկ հավելվածի սխալները կարող են վնասել մյուսներին, եթե ոչ ամբողջ համակարգը: Այնուհետև պրոցեսորում հայտնվեցին արտոնությունների ռեժիմներ (կամ «մատանի պաշտպանություն»): Միջուկը դարձավ ամենաարտոնյալը. այն ստացավ ամբողջական մուտք դեպի ապարատ՝ առաջացնելով ավելի քիչ արտոնյալ հավելվածներ, որոնք արդեն ստիպված էին մուտք գործել միջուկից՝ համակարգային զանգերի միջոցով սարքաշարի հետ փոխազդելու համար:

Երկուական մակարդակում համակարգային զանգը մի փոքր տարբերվում է պարզ ֆունկցիայի կանչից, սակայն ծրագրերի մեծ մասը ստանդարտ գրադարանում օգտագործում է փաթաթան: Նրանք. POSIX C ստանդարտ գրադարանը պարունակում է ֆունկցիայի կանչ գրել (), որը պարունակում է համակարգային զանգի համար նախատեսված ճարտարապետությանը հատուկ ծածկագիրը գրել.

Ծրագրաշարի տեղադրման վրիպազերծում strace-ով

Մի խոսքով, հավելվածի և դրա միջավայրի (համակարգչային համակարգերի) միջև ցանկացած փոխազդեցություն իրականացվում է համակարգային զանգերի միջոցով: Հետևաբար, երբ ծրագրակազմն աշխատում է մեկ մեքենայի վրա, բայց ոչ մյուսի վրա, լավ կլինի դիտարկել համակարգային զանգերի հետագծման արդյունքները: Ավելի կոնկրետ, ահա բնորոշ կետերի ցանկը, որոնք կարելի է վերլուծել՝ օգտագործելով համակարգային զանգի հետքը.

  • Վահանակով I/O
  • Ցանցի մուտք/ելք
  • Ֆայլային համակարգի մուտք և ֆայլ I/O
  • Գործընթացի շղթայի կյանքի ժամկետի կառավարում
  • Հիշողության ցածր մակարդակի կառավարում
  • Հատուկ սարքի վարորդների հասանելիություն

Ե՞րբ օգտագործել ստրեյսը:

Տեսականորեն, ստրաս օգտագործվում է օգտագործողի տարածքում գտնվող ցանկացած ծրագրի հետ, քանի որ օգտագործողի տարածքում գտնվող ցանկացած ծրագիր պետք է համակարգային զանգեր կատարի: Այն ավելի արդյունավետ է աշխատում կոմպիլացված, ցածր մակարդակի ծրագրերի հետ, բայց այն նաև աշխատում է բարձր մակարդակի լեզուներով, ինչպիսին է Python-ը, եթե կարողանաք կրճատել գործարկման ժամանակի և թարգմանչի լրացուցիչ աղմուկը:

Իր ողջ շքեղությամբ ստրաս դրսևորվում է ծրագրաշարի վրիպազերծման ժամանակ, որը լավ է աշխատում մի մեքենայի վրա, բայց հանկարծ դադարում է աշխատել մեկ այլ մեքենայի վրա՝ արտադրելով անորոշ հաղորդագրություններ ֆայլերի, թույլտվությունների կամ ինչ-որ հրամաններ կամ այլ բան կատարելու անհաջող փորձերի մասին... Ցավալի է, բայց դա այդպես չէ։ այնքան լավ համատեղել բարձր մակարդակի խնդիրների հետ, ինչպիսիք են վկայականի ստուգման սխալները: Սովորաբար դա պահանջում է համադրություն ստրասերբեմն հետք և ավելի բարձր մակարդակի գործիքներ (ինչպես հրամանի տող գործիքը openssl վկայականը վրիպազերծելու համար):

Որպես օրինակ մենք կօգտագործենք ինքնուրույն սերվեր, սակայն համակարգային զանգերի հետագծումը հաճախ կարող է իրականացվել ավելի բարդ տեղակայման հարթակներում: Պարզապես պետք է ընտրել ճիշտ գործիքները:

Վրիպազերծման պարզ օրինակ

Ենթադրենք, դուք ցանկանում եք գործարկել զարմանահրաշ սերվերային հավելված foo, և սա այն է, ինչի հետ դուք վերջանում եք.

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

Ըստ երևույթին, այն չկարողացավ գտնել ձեր գրած կազմաձևման ֆայլը: Դա տեղի է ունենում այն ​​պատճառով, որ երբեմն, երբ փաթեթի կառավարիչները դիմում են կազմում, նրանք անտեսում են ֆայլի ակնկալվող վայրերը: Եվ եթե դուք հետևում եք մի բաշխման տեղադրման ուղեցույցին, մյուսում դուք կգտնեք բոլորովին այլ ֆայլեր, քան դուք սպասում էիք: Խնդիրը կարող էր լուծվել մի քանի վայրկյանում, եթե սխալի հաղորդագրությունը ասեր, թե որտեղ փնտրել կազմաձևման ֆայլը, բայց դա չի անում: Այսպիսով, որտե՞ղ նայել:

Եթե ​​դուք մուտք ունեք աղբյուրի կոդը, կարող եք կարդալ այն և պարզել ամեն ինչ: Լավ պահուստային պլան, բայց ոչ ամենաարագ լուծումը: Դուք կարող եք դիմել քայլ առ քայլ վրիպազերծիչի նման gdb և տեսեք, թե ինչ է անում ծրագիրը, բայց շատ ավելի արդյունավետ է օգտագործել գործիք, որը հատուկ նախագծված է շրջակա միջավայրի հետ փոխգործակցություն ցույց տալու համար. ստրաս.

Արտադրողականություն ստրաս կարող է թվալ ավելորդ, բայց լավ նորությունն այն է, որ դրա մեծ մասը կարելի է ապահով կերպով անտեսել: Հաճախ օգտակար է օգտագործել -o օպերատորը՝ հետքի արդյունքները առանձին ֆայլում պահելու համար.

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

Արդյունքների մոտավորապես ամբողջ առաջին էջը ստրաս - Սա սովորաբար ցածր մակարդակի նախապատրաստություն է մեկնարկի համար: (Շատ զանգեր մմապ, mpprotec, բրկ այնպիսի բաների համար, ինչպիսիք են ցածր մակարդակի հիշողության հայտնաբերումը և դինամիկ գրադարանների ցուցադրումը:) Փաստորեն, ելքը վրիպազերծելու ժամանակ ստրաս Ավելի լավ է կարդալ ամենավերջից։ Ստորև կլինի մարտահրավեր գրել, որը ցուցադրում է սխալի հաղորդագրություն: Մենք նայում ենք վերևում և տեսնում ենք առաջին սխալ համակարգային զանգը՝ զանգը բացատ, որը սխալ է նետում ԷՆՈԵՆՏ («ֆայլը կամ գրացուցակը չի գտնվել») փորձում է բացել /etc/foo/config.json. Այստեղ պետք է լինի կազմաձևման ֆայլը:

Սա ընդամենը օրինակ էր, բայց ես կասեի, որ ես օգտագործում եմ ժամանակի 90%-ը ստրաս, սրանից շատ ավելի դժվար բան չկա անել։ Ստորև ներկայացված է վրիպազերծման ամբողջական քայլ առ քայլ ուղեցույց.

  • Վրդովվեք ծրագրի համակարգից սխալի մասին անորոշ հաղորդագրության պատճառով
  • Վերագործարկեք ծրագիրը ստրաս
  • Գտեք սխալի հաղորդագրությունը հետքի արդյունքներում
  • Գնացեք ավելի բարձր, մինչև չհասնեք առաջին ձախողված համակարգային զանգին

Շատ հավանական է, որ 4-րդ քայլի համակարգային զանգը կբացահայտի սխալը:

Խորհուրդներ

Նախքան ձեզ ավելի բարդ կարգաբերման օրինակ ցույց տալը, ես ձեզ ցույց կտամ մի քանի հնարքներ արդյունավետ օգտագործման համար ստրաս:

մարդը քո ընկերն է

Շատ *nix համակարգերում միջուկին համակարգային կանչերի ամբողջական ցանկը կարելի է ձեռք բերել գործարկման միջոցով մարդ syscalls. Դուք կտեսնեք նման բաներ brk (2), ինչը նշանակում է, որ ավելի շատ տեղեկություններ կարելի է ստանալ վազելով մարդ 2 բրկ.

Փոքր փոցխ. մարդ 2 պատառաքաղ ցույց է տալիս ինձ պատյանի էջը պատառաքաղ () в GNU libc, որը, պարզվում է, իրականացվում է զանգահարելով կլոն (). Զանգահարեք իմաստաբանություն պատառաքաղ մնում է նույնը, եթե դուք ծրագիր եք գրում օգտագործելով պատառաքաղ (), և կատարեք հետք - ես ոչ մի զանգ չեմ գտնի պատառաքաղ, նրանց փոխարեն կլինեն կլոն (). Նման փոցխները ձեզ միայն շփոթեցնում են, եթե սկսեք համեմատել աղբյուրը ելքի հետ ստրաս.

Օգտագործեք -o ելքը ֆայլում պահելու համար

ստրաս կարող է լայնածավալ արդյունք ստեղծել, ուստի հաճախ օգտակար է հետքի արդյունքները առանձին ֆայլերում պահելը (ինչպես վերը նշված օրինակում): Սա նաև օգնում է խուսափել ծրագրի արդյունքը ելքի հետ շփոթելուց ստրաս կոնսոլում։

Օգտագործեք -s՝ ավելի շատ փաստարկների տվյալներ դիտելու համար

Դուք կարող եք նկատել, որ սխալի հաղորդագրության երկրորդ կեսը ցուցադրված չէ վերը նշված օրինակի հետքում: Դա այն պատճառով է, որ ստրաս լռելյայն ցույց է տալիս լարային արգումենտի միայն առաջին 32 բայթը: Եթե ​​ցանկանում եք ավելին տեսնել, ավելացրեք նման բան -s 128 զանգին ստրաս.

-y-ն հեշտացնում է ֆայլերի, վարդակների և այլնի հետևելը:

«Ամեն ինչ ֆայլ է» նշանակում է, որ *nix համակարգերը կատարում են բոլոր մուտքերը/ելքը՝ օգտագործելով ֆայլերի նկարագրիչները, անկախ նրանից՝ դա վերաբերում է ֆայլին, ցանցին կամ միջգործընթացային խողովակներին: Սա հարմար է ծրագրավորման համար, բայց դժվարացնում է հետևել, թե իրականում ինչ է կատարվում, երբ տեսնում եք սովորական կարդալ и գրել համակարգում զանգահարել հետքի արդյունքներ:

Օպերատոր ավելացնելով այո, կպարտադրեք ստրաս նշեք յուրաքանչյուր ֆայլի նկարագրիչ ելքի մեջ՝ նշելով, թե ինչ է այն մատնանշում:

Կցեք արդեն գործող գործընթացին -p**-ով

Ինչպես կտեսնեք ստորև բերված օրինակից, երբեմն անհրաժեշտ է հետևել ծրագրին, որն արդեն աշխատում է: Եթե ​​հայտնի է, որ այն աշխատում է որպես պրոցես 1337 (ասենք՝ ելքից ps), ապա կարող եք հետևել դրան այսպես.

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

Ձեզ կարող է անհրաժեշտ լինել արմատային իրավունքներ:

Օգտագործեք -f երեխայի գործընթացները վերահսկելու համար

ստրաս Լռելյայնորեն, այն հետևում է միայն մեկ գործընթացին: Եթե ​​այս պրոցեսը առաջացնում է մանուկ գործընթացներ, ապա երեխայի պրոցեսը ստեղծելու համակարգային կանչը կարող է դիտվել, բայց երեխայի գործընթացի համակարգային զանգերը չեն ցուցադրվի:

Եթե ​​կարծում եք, որ սխալը երեխայի գործընթացում է, օգտագործեք հայտարարությունը -f, սա հնարավորություն կտա դրա հետագծումը։ Սրա բացասական կողմն այն է, որ արդյունքը ձեզ ավելի կշփոթեցնի: Երբ ստրաս հետևում է մեկ գործընթացին կամ մեկ շարանը, այն ցույց է տալիս զանգերի իրադարձությունների մեկ հոսք: Երբ այն միաժամանակ հետևում է մի քանի գործընթացների, դուք կարող եք տեսնել զանգի սկիզբը, որն ընդհատվում է հաղորդագրությամբ , այնուհետև՝ կատարման այլ ճյուղերի մի շարք զանգեր, և միայն դրանից հետո՝ առաջինի ավարտը . Կամ բոլոր հետքի արդյունքները բաժանեք տարբեր ֆայլերի՝ նաև օգտագործելով օպերատորը -ff (մանրամասները՝ ղեկավարությունը մասին ստրաս).

Զտել հետքերը՝ օգտագործելով -e

Ինչպես տեսնում եք, հետքի արդյունքը բոլոր հնարավոր համակարգային զանգերի իրական կույտն է: Դրոշ -e Դուք կարող եք զտել հետքը (տես ղեկավարությունը մասին ստրաս) Հիմնական առավելությունն այն է, որ ֆիլտրացված հետքը գործարկելն ավելի արագ է, քան ամբողջական հետք կատարելը և հետո grep՝ ժամը. Անկեղծ ասած, ինձ գրեթե միշտ չի հետաքրքրում:

Ոչ բոլոր սխալներն են վատ

Պարզ և սովորական օրինակ է մի ծրագիր, որը ֆայլ է փնտրում միանգամից մի քանի վայրերում, ինչպես կեղևը, որը փնտրում է գործարկվող ֆայլ պարունակող գրացուցակ.

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

Էվրիստիկա, ինչպիսին է «վերջին ձախողված հարցումը, նախքան սխալի մասին հայտնելը», լավ են համապատասխան սխալներ գտնելու համար: Ինչքան էլ որ լինի, տրամաբանական է սկսել հենց վերջից։

C ծրագրավորման ձեռնարկները կարող են օգնել ձեզ հասկանալ համակարգային զանգերը:

Ստանդարտ զանգերը դեպի C գրադարաններ համակարգային զանգեր չեն, այլ միայն բարակ մակերեսային շերտ: Այսպիսով, եթե դուք գոնե մի փոքր հասկանաք, թե ինչպես և ինչ անել C-ում, ձեզ համար ավելի հեշտ կլինի հասկանալ համակարգի կանչի հետքի արդյունքները: Օրինակ, դուք խնդիրներ ունեք ցանցային համակարգերի զանգերը կարգաբերելու համար, նայեք նույն դասականին Բիջայի Ցանցային ծրագրավորման ուղեցույց.

Վրիպազերծման ավելի բարդ օրինակ

Ես արդեն ասացի, որ պարզ վրիպազերծման օրինակը այն օրինակն է, թե ինչի հետ ես հիմնականում գործ ունեմ աշխատելիս ստրաս. Այնուամենայնիվ, երբեմն իրական հետաքննություն է պահանջվում, ուստի ահա ավելի առաջադեմ վրիպազերծման իրական օրինակ:

բկրոն - առաջադրանքների մշակման ժամանակացույց, *nix daemon-ի մեկ այլ իրականացում cron. Այն տեղադրված է սերվերի վրա, բայց երբ ինչ-որ մեկը փորձում է խմբագրել ժամանակացույցը, սա տեղի է ունենում.

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

Լավ, դա նշանակում է բկրոն փորձեց գրել որոշակի ֆայլ, բայց չստացվեց, և նա չի խոստովանի, թե ինչու: Բացահայտում ստրաս:

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

Վերջում կա սխալի հաղորդագրություն գրել, բայց այս անգամ մի բան այլ է. Նախ, չկա համապատասխան համակարգային զանգի սխալ, որը սովորաբար տեղի է ունենում մինչ այդ: Երկրորդ, պարզ է, որ ինչ-որ տեղ ինչ-որ մեկը արդեն կարդացել է սխալի հաղորդագրությունը: Կարծես թե իրական խնդիրն այլ տեղ է, և bcrontab պարզապես նվագարկում է հաղորդագրությունը:

Եթե ​​նայեք մարդ 2 կարդաց, դուք կարող եք տեսնել, որ առաջին արգումենտը (3) ֆայլի նկարագրիչ է, որը *nix-ն օգտագործում է I/O ամբողջ մշակման համար։ Ինչպե՞ս պարզել, թե ինչ է ներկայացնում ֆայլի նկարագրիչը 3: Կոնկրետ այս դեպքում կարող եք վազել ստրաս օպերատորի հետ այո (տե՛ս վերևում) և այն ավտոմատ կերպով ձեզ կասի, բայց նման բաները պարզելու համար օգտակար է իմանալ, թե ինչպես կարդալ և վերլուծել հետքի արդյունքները:

Ֆայլի նկարագրիչի աղբյուրը կարող է լինել բազմաթիվ համակարգային զանգերից մեկը (ամեն ինչ կախված է նրանից, թե ինչի համար է նկարագրիչը՝ կոնսոլ, ցանցային վարդակ, բուն ֆայլ կամ մեկ այլ բան), բայց ինչպես դա կարող է լինել, մենք փնտրում ենք. զանգեր՝ վերադարձնելով 3 (այսինքն՝ մենք փնտրում ենք «= 3» հետագծման արդյունքներում): Այս արդյունքում կան դրանցից 2-ը. բացատ ամենավերևում և վարդակից Մեջտեղում. բացատ բացում է ֆայլը, բայց սերտ(3) այնուհետև ցույց կտա, որ այն կրկին փակվում է: (Rake. ֆայլի նկարագրիչները կարող են կրկին օգտագործվել, երբ դրանք բացվում և փակվում են): Զանգահարեք վարդակից () հարմար է, քանի որ դա վերջինն է նախկինում կարդալ (), և պարզվում է, որ bcrontab-ը աշխատում է ինչ-որ բանի հետ վարդակից: Հաջորդ տողը ցույց է տալիս, որ ֆայլի նկարագրիչը կապված է unix տիրույթի վարդակից ճանապարհին /var/run/bcron-spool.

Այսպիսով, մենք պետք է գտնենք դրա հետ կապված գործընթացը unix վարդակից մյուս կողմից. Այս նպատակով կան մի քանի կոկիկ հնարքներ, որոնք երկուսն էլ օգտակար են սերվերի տեղակայման վրիպազերծման համար: Առաջինը օգտագործելն է netstat կամ ավելի նոր ss (վարդակից կարգավիճակ): Երկու հրամաններն էլ ցույց են տալիս համակարգի ակտիվ ցանցային կապերը և ընդունում են հայտարարությունը -l նկարագրելու լսողական վարդակներ, ինչպես նաև օպերատորը -p վարդակից միացված ծրագրերը որպես հաճախորդ ցուցադրելու համար: (Կան շատ ավելի օգտակար տարբերակներ, բայց այս երկուսը բավարար են այս առաջադրանքի համար):

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

Սա հուշում է, որ ունկնդիրն է հրամանը inixserver, որն աշխատում է գործընթացի ID 20629-ով: (Եվ, պատահաբար, այն օգտագործում է ֆայլի նկարագրիչ 3 որպես վարդակ):

Նույն տեղեկատվությունը գտնելու երկրորդ իսկապես օգտակար գործիքը կոչվում է lsof. Այն թվարկում է համակարգի բոլոր բաց ֆայլերը (կամ ֆայլերի նկարագրիչները): Կամ կարող եք տեղեկություններ ստանալ մեկ կոնկրետ ֆայլի մասին.

# 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-ը երկարակյաց սերվեր է, այնպես որ կարող եք կցել այն ստրաս օգտագործելով նման բան strace -o /tmp/trace -p 20629. Եթե ​​դուք խմբագրում եք cron աշխատանքը մեկ այլ տերմինալում, դուք կստանաք հետքի ելք՝ սխալմամբ: Եվ ահա արդյունքը.

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

(Վերջին ընդունել() Հետագծման ժամանակ չի ավարտվի:) Կրկին, ցավոք, այս արդյունքը չի պարունակում այն ​​սխալը, որը մենք փնտրում ենք: Մենք չենք տեսնում որևէ հաղորդագրություն, որը bcrontag-ը ուղարկում կամ ստանում է վարդակից: Փոխարենը, ամբողջական գործընթացի վերահսկում (clone, սպասիր 4 թ, ՍԻԳՉԼԴ և այլն) Այս գործընթացը ծնում է երեխայի գործընթաց, որը, ինչպես կարող եք կռահել, կատարում է իրական աշխատանքը: Եվ եթե դուք պետք է բռնեք նրա հետքը, ավելացրեք զանգին strace -f. Սա այն է, ինչ մենք կգտնենք, երբ որոնենք սխալի հաղորդագրությունը նոր արդյունքում 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 +++

Հիմա, դա ինչ-որ բան է: Գործընթացը 21470-ը ստանում է «մուտքի արգելված» սխալ, երբ փորձում է ֆայլ ստեղծել ճանապարհին tmp/spool.21470.1573692319.854640 (կապված ընթացիկ աշխատանքային գրացուցակի հետ): Եթե ​​մենք պարզապես իմանայինք ընթացիկ աշխատանքային գրացուցակը, մենք նաև կիմանայինք ամբողջ ուղին և կկարողանայինք պարզել, թե ինչու գործընթացը չի կարող դրանում ստեղծել իր ժամանակավոր ֆայլը: Ցավոք, գործընթացն արդեն դուրս է եկել, այնպես որ դուք չեք կարող պարզապես օգտագործել lsof -p 21470 ընթացիկ գրացուցակը գտնելու համար, բայց դուք կարող եք աշխատել հակառակ ուղղությամբ. փնտրեք PID 21470 համակարգի զանգեր, որոնք փոխում են գրացուցակը: (Եթե դրանք չկան, PID 21470-ը պետք է ժառանգած լինի դրանք իր ծնողից, և սա արդեն ավարտված է lsof -p հնարավոր չէ պարզել:) Այս համակարգային զանգը չդիր (ինչը հեշտ է պարզել ժամանակակից առցանց որոնման համակարգերի օգնությամբ): Եվ ահա հակադարձ որոնումների արդյունքը, որը հիմնված է հետքի արդյունքների վրա, մինչև սերվեր 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 +++

(Եթե կորցրել եք, կարող եք կարդալ իմ նախորդ գրառումը * nix գործընթացի կառավարման և կեղևների մասին.) Այսպիսով, PID 20629 սերվերը ուղու վրա ֆայլ ստեղծելու թույլտվություն չի ստացել /var/spool/cron/tmp/spool.21470.1573692319.854640. Ամենայն հավանականությամբ, դրա պատճառը դասական ֆայլային համակարգի թույլտվության կարգավորումներն են: Եկեք ստուգենք.

# 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

Ահա թե որտեղ է թաղված շունը։ Սերվերն աշխատում է որպես օգտագործողի cron, բայց միայն root-ն ունի գրացուցակում գրելու թույլտվություն /var/spool/cron/tmp/. Պարզ հրաման chown cron /var/spool/cron/tmp/ կստիպի բկրոն ճիշտ աշխատել. (Եթե դա չէր խնդիրը, ապա հաջորդ ամենահավանական կասկածյալը միջուկի անվտանգության մոդուլն է, ինչպիսին է SELinux-ը կամ AppArmor-ը, ուստի ես կստուգեի միջուկի հաղորդագրությունների գրանցամատյանը: dmesg.)

Ընդհանուր

Համակարգային զանգերի հետքերը կարող են ճնշող լինել սկսնակների համար, բայց հուսով եմ, որ ես ցույց եմ տվել, որ դրանք արագ միջոց են տեղակայման ընդհանուր խնդիրների մի ամբողջ դասի վրիպազերծման համար: Պատկերացրեք, փորձում եք վրիպազերծել բազմապրոցեսը բկրոնօգտագործելով քայլ առ քայլ վրիպազերծիչ:

Համակարգի զանգերի շղթայի երկայնքով հետքի արդյունքների վերլուծությունը հմտություն է պահանջում, բայց ինչպես ասացի, գրեթե միշտ, օգտագործելով ստրասԵս պարզապես ստանում եմ հետքի արդյունքը և փնտրում եմ սխալներ՝ սկսած վերջից: Ինչևէ, ստրաս օգնում է ինձ խնայել շատ ժամանակ վրիպազերծման վրա: Հուսով եմ, որ դա ձեզ նույնպես օգտակար կլինի։

Source: www.habr.com

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