Produkty Novinky Články Návody Kontakty

Analýza výpisu paměti

Spustíme aplikaci a najednou bum! Aplikace byla ukončena a my obdržíme pouze stručné oznámení systému: ”Segmentation fault”. Co to? Nic, to nám jen Linux oznámil, že ukončil naši aplikaci, protože se pokoušela přistoupit k paměti, ke které nemá povolený přístup.
Ale co s tím? Jak zjistit, kde je chyba? Jedna z možností je, pustit si aplikaci znova s pomocí debuggeru a čekat až znovu spadne. Ale tím si moc nepomůžeme, protože sice budeme tušit, kde je místo pádu, ale nezjistíme hodnoty proměnných v daném okamžiku atd.
Existuje lepší jiná možnost - analyzovat výpis paměti (core dump) pořízený v okamžiku pádu aplikace. Výpis paměti je soubor, kam operační systém Linux v okamžiku abnormálního ukončení aplikace uloží obraz veškeré paměti, kterou aplikace v onen moment používala. Tento soubor má stejný formát jako spustitelný soubor, takže není problém jej analyzovat s využitím debuggeru gdb.
Situací, kdy jádro při pádu aplikace vypíše obsah paměti do souboru, je více. Nicméně nejčastější z nich je právě nepovolený přístup k paměti, který způsobí, že jádro pošle zlobivé aplikaci signál SIGSEGV (segmentation fault). Výchozí reakcí na tento signál je právě ukončení aplikace a výpis obsahu paměti.

Nastavení výpisů paměti

Výpis paměti je jádrem vygenerován, jen když jsou splněny tyto podmínky:
Výchozí velikost výpisu paměti je standardně nula, takže jádro výpis paměti normálně nevygeneruje. Změna nové hodnoty limitu je podle způsobu nastavení buď dočasná nebo trvalá.
Dočasné změny na nenulovou hodnotu docílíme pomocí shellového příkazu:
$ ulimit -c unlimited
nebo zavoláním funkce setrlimit() přímo z aplikace.
Trvalé změny limitu dosáhneme úpravou spouštěcích či přihlašovacích skriptů nebo změnou limitu v souboru /etc/security/limits.conf (dle distribuce). U distribucí na bázi Busyboxu se doporučuje využít přímo nastavení v konfiguraci Busyboxu, díky čemuž je všem procesům automaticky nastaven trvale nenulový limitInformationKompletní návod jak v linuxové distribuci zapnout podporu výpisů paměti je zde: http://www.rt-embedded.com/blog/archives/enabling-core-dumps-in-embedded-systems/.
Výpis paměti je ve výchozím nastavení ukládán vždy do aktuálního adresáře aplikace a jméno souboru s výpisem paměti je core.

Příklad analýzy výpisu paměti

Pojďme si nyní ukázat příklad pádu zlobivé aplikace hello, která se pokusí zapsat na neplatnou adresu NULL hodnotu 10.000.
obrázek vypis-pameti
Obrázek 1.1 Výpis paměti
Na obrázku 1.1 je vidět celý postup. Nejprve si nastavíme neomezenou velikost výpisu paměti a pak spustíme samotnou aplikaci hello. Operační systém aplikaci samozřejmě ”neočekávaně” ukončí, o čemž jsme informováni strohým ”Segmentation faul (core dumped)”. Ve výpisu seznamu adresářů můžeme vidět, že v adresáři aplikace skutečně přibyl soubor core.
Analýzu výpisu paměti zahájíme jednoduše voláním debuggeru gdb ve tvaru:
$ gdb hello core
obrázek analyza-vypisu-pameti
Obrázek 1.2 Analýza výpisu paměti
Na obrázku 1.2 vidíme, že gdb nejprve vypíše informace o načítání symbolů z dynamických knihoven a pak zobrazí název aplikace (hello), která způsobila vypsání paměti, spolu s důvodem ukončení aplikace - nepovolený přístup k paměti. Je vypsáno také číslo řádku ve zdrojovém souboru spolu s výpisem kódu, který byl v onen okamžik vykonáván.
Příkazem list si necháme vypsat kód v okolí inkriminovaného řádku, abychom mohli zjistit příčinu. Na výpisu kódu vidíme, že evidentní přičinou je výchozí hodnota ukazatele aIntPtr. Příkazem print aIntPtr si vypíšeme hodnotu ukazatele v okamžiku pádu aplikace. Hodnota 0x0 je samozřejmě neplatná, takže v otázce příčiny pádu aplikace máme jasno. Práci s gdb ukončíme příkazem quit.
Důležitou poznámkou je, že aplikace hello byla zkompilována s volbou kompilátoru -g, takže spustitelný soubor obsahoval ladicí informace. Bez nich by analýza výpisu paměti byla výrazně dobrodružnější detektivkou.