Trasujeme se strace

12. 3. 2001
Doba čtení: 6 minut

Sdílet

"Prožeň zlobivý program skrz strace, ať víš, v čem je problém", zní nezřídka odpověď z linuxové konference na dotaz, proč to či ono nefunguje. "Co je to ten strace a jak se to používá?" ptají se leckdy nejen začátečníci. Odpověď snad najdete v dnešním článku.

Jste-li postaveni před problém nefungujícího programu, máte v podstatě dvě možnosti: pokusit se zjistit, v čem je zakopaný pes, a nebo to vzdát. Protože vzdávat se je nedůstojné, nezbývá, než se ponořit do nitra vzdorující aplikace. Jednou z nejsnazších a zároveň velmi efektivních metod je právě použití utility strace.

Co to vlastně strace je?

Obvyklou metodou ladění je takzvané trasování, neboli sledování průběhu programu. Trasování může probíhat na různých úrovních, od řádků zdrojového kódu až po volání jednotlivých instrukcí procesoru. Strace pracuje na úrovni systémových volání, takže s jeho pomocí lze zjistit, co program požadoval po jádře. Jednotlivé zaznamenané operace mohou být například pokusy o otevření souboru, čtení či zápis do něj nebo třeba založení potomka. O každém volání se dozvíte, s jakými argumenty bylo voláno a také jak dopadlo. To už je dost na to, aby se strace stal velmi šikovným pomocníkem pro různé problémové situace.

Hlavní výhodou použití strace je, že pro ladění nemusíte mít k dispozici zdrojové kódy, program nemusí být kompilován s ladícími informacemi a může být staticky i dynamicky linkovaný. Jak vidno, strace lze bez problémů použít například i v případech, kdy jsou k dispozici jenom binárky zlobivého programu bez ladících informací. Subjektivně bych řekl, že použití je také pro neprogramátora o něco snazší, než například ladění pomocí debuggeru, protože systémových volání není zase až tak mnoho.

Použití v praxi

Jste-li běžný uživatel, možná vám běhá mráz po zádech při vyslovení slov, jako je „ladění“ či „trasování“. Není ale třeba mít přehnané obavy, strace je jednoduchý program, k jehož použití není nutné znát desítky přepínačů a voleb. Úplně nejjednodušším příkladem použití je tato syntaxe:

strace ls

V tomto případě strace spustí program ls a seznam systémových volání vypíše na standardní chybový výstup. Pokud program sám něco vypisuje na konzoli nebo chcete-li soupis volání studovat později, je vhodné přesměrovat výstup strace do souboru. To můžete udělat buď pomocí přesměrování v shellu (příklad pro bash)

strace ls 2>ls.strace

a nebo použít přímo přepínač -o

strace -o ls.strace ls

v obou případech je důsledkem vytvoření souboru ls.strace, který po ukončení obsahuje seznam systémových volání.

Dalším pro nás zajímavým přepínačem je -f respektive -ff. Oba slouží k zapnutí trasování potomků hlavního procesu. Rozdíl mezi nimi je ten, že -f způsobí logování všech volání do jediného souboru, zatímco -ff zajistí, že každý potomek bude mít vlastní logovací soubor odlišený připojením PID ke jménu specifikovaném parametrem -o. Bez volby -o se chová přepínač -ff jako -f a výstup je směřován na standardní chybový výstup.

Občas vyvstane nutnost trasovat již běžící proces. Pro tyto účely je strace vybaven přepínačem -p, za nímž následuje identifikátor procesu (PID). Zvláštností tohoto režimu je, že přerušení z klávesnice pomocí Ctrl-C způsobí ukončení trasování a odpojení strace od běžícího procesu.

Asi posledním přepínačem, který může být pro běžné použití důležitý, je -s. S jeho pomocí lze nastavit délku řetězce, která se bude používat pro vypsání obsahu argumentů systémových volání. Řetězce delší, než určuje -s budou ve výpisu oříznuty. Výchozí délka je 32 znaků, což nemusí vždy postačovat.

Výstup strace

Nyní již víme, jak se strace používá, takže se můžeme podívat na jeho výstup. Takto vypadá začátek výpisu strace při spuštění programu false:

execve("/bin/false", ["false"], [/* 35 vars */]) = 0
uname({sys="Linux", node="mike.4web", ...}) = 0
brk(0)                                  = 0x8049c98
open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=35620, ...}) = 0
old_mmap(NULL, 35620, PROT_READ, MAP_PRIVATE, 4, 0) = 0x40018000
close(4)                                = 0
open("/lib/libc.so.6", O_RDONLY)        = 4
...
...
_exit(1)                                = ?

Úplně na začátku vidíme, že nově vzniklý proces bude nahrazen programem false. Volání execve přebírá tři parametry: plnou cestu k programu, pole jeho argumentů a pole proměnných prostředí. Dále se dozvíme, že volání dopadlo dobře, protože zrovna toto volání vrací při úspěchu nulu.
Tady bych rád malinko odbočil a objasnil některé zákonitosti systémových volání, bez nichž se při ladění neobejdete. Návratové hodnoty volání mohou být různé, ale obvykle platí, že chyba je signalizována hodnotou –1 a nula nebo kladné číslo značí úspěch. K přesnějšímu určení chyby pak slouží proměnná errno, kterou strace do výpisu rovněž zahrne, a to dokonce i s textem chybového hlášení, takže orientace je poměrně snadná. Budete-li se chtít o některém volání dozvědět více, například co který argument znamená, můžete si prohlédnout jeho manuálovou stránku (respektive manuálovou stránku céčkové funkce, která jej zastupuje). Věřím, že máme jenom samé inteligentní čtenáře :), ale pro jistotu připomenu, že názvy některých volání se kryjí se jmény povelů nebo interními funkcemi shellu, takže může být zapotřebí sdělit manu, ve které sekci se povel nachází (např. man 2 open).

Takové jsou tedy obecná pravidla a nyní si v rychlosti projdeme zbytek ukázkového výpisu.

Po vlastním execve následuje volání uname. Za rovnítkem je nula, což značí úspěch. Volání uname umožňuje získat stejné informace, jaké zobrazuje povel uname – verzi jádra, hardwarovou platformu atd.

Systémové volání brk nastavuje velikost datového segmentu – v každém výpisu strace jej obvykle najdeme hned několikrát. Nutno podotknouti, že zde asi zrovna chybu neodhalíte.

Následují dva pokusy o otevření souboru. Oba dva má na starosti dynamický linker. První open se snaží otevřít soubor /etc/ld.so.preload, kam lze uvádět knihovny načítané takříkajíc z donucení. Jelikož na mém počítači tento soubor neexistuje, je pěkně vidět, že volání vrátilo –1 (neúspěch) s chybou ENOENT a popisem, z nějž je jasné, že soubor nebyl nalezen. Protože to pro program není kritická chyba, pokračuje dál. Linker si zde otevírá soubor /etc/ld.so.cache, kde jsou uloženy informace o dynamicky linkovatelných knihovnách v prohledávaných adresářích. Linker tak zjistí, kde jsou umístěny knihovny nutné k běhu programu. Vrácené kladné číslo je identifikátor otevřeného souboru, který je použit v dalším volání fstat64 (první argument). Toto volání vrací informace o souboru a jeho popis opět najdete v manuálové stránce (pokud by neexistovala pro fstat64, poslouží stejně dobře fstat).

Další volání namapuje obsah souboru do paměti.

Volání close zavře otevřený soubor určený identifikátorem, který někdy dříve vrátilo volání open. Pozor, při otevírání souboru se používá nejnižší volný identifikátor, takže není vyloučeno, že opakované volání open může vrátit pokaždé stejný identifikátor, pokud je před dalším voláním soubor opět uzavřen.

Dále opět následuje otevření souboru, kterým je tentokrát základní knihovna libc. Jak vidno, program samotný se ještě vůbec nedostal, jak se říká, k lizu. nejdříve se „vyřádí“ dynamický linker, po něm většinou následuje inicializace libc (nastavení locales, pokusy o otevření katalogů zpráv, atd.) a až na konec přijde na řadu program. V našem případě s programem false jde zhruba o 80 systémových volání, z nichž pouze jediné a poslední plní vlastní funkce programu, kterou je ukončení programu s návratovým kódem odlišným od nuly – _exit(1).

bitcoin_skoleni

Co tedy vlastně s pomocí strace zjistím?

Předem je třeba podotknout, že čím větší jsou vaše znalosti, tím více vám strace prozradit. Jen mírně zkušený uživatel však může poměrně snadno zjistit chyby typu neexistující soubor nebo adresář či nevhodně nastavená přístupová práva, což jsou konec konců chyby převažující. Koketujete-li s programováním, můžete se samozřejmě dozvědět i více. No, a pokud si na zkoumání výstupu programu vůbec netroufáte, můžete jej například ukázat zkušenějšímu kolegovi či kamarádovi a nebo jej přiložit k chybovému reportu. Leckdy je to výstižnější, než sáhodlouhé pokusy o vysvětlení chyby lidskou řečí :)

Rozhodnete-li se se strace experimentovat, pak nemusíte ani příliš hledat a pátrat, neboť bývá součástí snad všech linuxových distribucí. Pokud by tomu tak nebylo, jeho domovskou stránku najdete zde.

Autor článku