C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Ladění chyb Tématicky zaměřený vývoj aplikací v jazyce C skupina Systémové programování – Linux Jiří Novosad Fakulta informatiky Masarykova univerzita novosad@fi.muni.cz 28. září 2014 J. Novosad 3 – Ladění chyb 28. září 2014 1 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr C Preprocesor Inspirováno přednáškou Rudy Čejky, FIT VUT J. Novosad 3 – Ladění chyb 28. září 2014 2 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Co umí preprocesor Vkládání zdrojových souborů Měly by mít syntax podobnou jazyku C J. Novosad 3 – Ladění chyb 28. září 2014 3 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Co umí preprocesor Vkládání zdrojových souborů Měly by mít syntax podobnou jazyku C Předdefinovaná makra __LINE__, __DATE__, __TIME__, __func__, ... J. Novosad 3 – Ladění chyb 28. září 2014 3 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Co umí preprocesor Vkládání zdrojových souborů Měly by mít syntax podobnou jazyku C Předdefinovaná makra __LINE__, __DATE__, __TIME__, __func__, ... Definice maker #define MA printf(“Dneska je úžasný den!\n”) #define MB(parametr) parametr #define MC(...) printf(__VA_ARGS__) J. Novosad 3 – Ladění chyb 28. září 2014 3 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Co umí preprocesor Vkládání zdrojových souborů Měly by mít syntax podobnou jazyku C Předdefinovaná makra __LINE__, __DATE__, __TIME__, __func__, ... Definice maker #define MA printf(“Dneska je úžasný den!\n”) #define MB(parametr) parametr #define MC(...) printf(__VA_ARGS__) Podmíněný překlad #if, #ifdef, #ifndef, #else, #elif, #endif J. Novosad 3 – Ladění chyb 28. září 2014 3 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Co umí preprocesor Vkládání zdrojových souborů Měly by mít syntax podobnou jazyku C Předdefinovaná makra __LINE__, __DATE__, __TIME__, __func__, ... Definice maker #define MA printf(“Dneska je úžasný den!\n”) #define MB(parametr) parametr #define MC(...) printf(__VA_ARGS__) Podmíněný překlad #if, #ifdef, #ifndef, #else, #elif, #endif Ostatní #, ##, #error, #pragma, . . . gcc.gnu.org/onlinedocs/cpp/ J. Novosad 3 – Ladění chyb 28. září 2014 3 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru I Obecně jen to, o čem víte jak to skutečně funguje a jste si vědomi důsledků. závorky, závorky, závorky Priorita oprátorů #define MULT(x,y) x*y int z = MULT(3 + 2, 4 + 3); J. Novosad 3 – Ladění chyb 28. září 2014 4 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru I Obecně jen to, o čem víte jak to skutečně funguje a jste si vědomi důsledků. závorky, závorky, závorky Priorita oprátorů #define MULT(x,y) x*y int z = MULT(3 + 2, 4 + 3); int z = 3 + 2*4 + 3; J. Novosad 3 – Ladění chyb 28. září 2014 4 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru I Obecně jen to, o čem víte jak to skutečně funguje a jste si vědomi důsledků. závorky, závorky, závorky Priorita oprátorů #define MULT(x,y) x*y int z = MULT(3 + 2, 4 + 3); int z = 3 + 2*4 + 3; #define ADD_TWO(x) x+2 int y = ADD_TWO(3) * 5; J. Novosad 3 – Ladění chyb 28. září 2014 4 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru I Obecně jen to, o čem víte jak to skutečně funguje a jste si vědomi důsledků. závorky, závorky, závorky Priorita oprátorů #define MULT(x,y) x*y int z = MULT(3 + 2, 4 + 3); int z = 3 + 2*4 + 3; #define ADD_TWO(x) x+2 int y = ADD_TWO(3) * 5; int y = 3+2 * 5; J. Novosad 3 – Ladění chyb 28. září 2014 4 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru I Obecně jen to, o čem víte jak to skutečně funguje a jste si vědomi důsledků. závorky, závorky, závorky Priorita oprátorů #define MULT(x,y) x*y int z = MULT(3 + 2, 4 + 3); int z = 3 + 2*4 + 3; #define ADD_TWO(x) x+2 int y = ADD_TWO(3) * 5; int y = 3+2 * 5; #define MULT(x,y) ((x) * (y)) #define ADD_TWO(x) ((x)+2) J. Novosad 3 – Ladění chyb 28. září 2014 4 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru II závorky, závorky, závorky Závorky kolem bloku kódu #define SWAP(x,y) a ^= b; b ^= a; a ^= b; if (x < 0) SWAP(x, y); J. Novosad 3 – Ladění chyb 28. září 2014 5 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru II závorky, závorky, závorky Závorky kolem bloku kódu #define SWAP(x,y) a ^= b; b ^= a; a ^= b; if (x < 0) SWAP(x, y); else SWAP(y, x); J. Novosad 3 – Ladění chyb 28. září 2014 5 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru II závorky, závorky, závorky Závorky kolem bloku kódu #define SWAP(x,y) a ^= b; b ^= a; a ^= b; if (x < 0) SWAP(x, y); else SWAP(y, x); #define SWAP(x,y) do { \ a ^= b; \ b ^= a; \ a ^= b; \ } while (0) J. Novosad 3 – Ladění chyb 28. září 2014 5 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru III Změna toku programu #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while(0) J. Novosad 3 – Ladění chyb 28. září 2014 6 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru III Změna toku programu #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while(0) Přístup k lokálním proměnným #define FOO(val) bar(index, val) J. Novosad 3 – Ladění chyb 28. září 2014 6 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru III Změna toku programu #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while(0) Přístup k lokálním proměnným #define FOO(val) bar(index, val) Makro jako l-hodnota FOO(x) = y; J. Novosad 3 – Ladění chyb 28. září 2014 6 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru III Změna toku programu #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while(0) Přístup k lokálním proměnným #define FOO(val) bar(index, val) Makro jako l-hodnota FOO(x) = y; Argumenty s vedlejšími efekty #define MIN(a,b) ((a)>(b)?(b):(a)) int z = MIN(x++, y--); J. Novosad 3 – Ladění chyb 28. září 2014 6 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru III Změna toku programu #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while(0) Přístup k lokálním proměnným #define FOO(val) bar(index, val) Makro jako l-hodnota FOO(x) = y; Argumenty s vedlejšími efekty #define MIN(a,b) ((a)>(b)?(b):(a)) int z = MIN(x++, y--); // x++ a y-- jsou vyhodnoceny 2 krát! J. Novosad 3 – Ladění chyb 28. září 2014 6 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru IV Středníky #define PRETTY_PRINT(msg) printf(msg); if (n < 10) PRETTY_PRINT("n is less than 10"); else PRETTY_PRINT("n is at least 10"); J. Novosad 3 – Ladění chyb 28. září 2014 7 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Chybné použití preprocesoru IV Středníky #define PRETTY_PRINT(msg) printf(msg); if (n < 10) PRETTY_PRINT("n is less than 10"); else PRETTY_PRINT("n is at least 10"); Pokud můžete téhož dosáhnout funkcí, nepoužívejte makra J. Novosad 3 – Ladění chyb 28. září 2014 7 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Logování událostí chybové a debuggovací výpisy, syslogd J. Novosad 3 – Ladění chyb 28. září 2014 8 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Formát zpráv Každá zpráva MUSÍ být SROZUMITELNÁ! Zvažte kdo bude zprávy číst – jinak bude vypadat debuggovací hláška a jinak chybová hláška, kterou uvidí uživatel kolik zpráv je potřeba (podrobnost výpisu) – je dobrý nápad mít několik úrovní vypisovaných hlášek jak/čím zprávu zobrazit – stderr, logovací soubory, email, . . . J. Novosad 3 – Ladění chyb 28. září 2014 9 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Zprávy pro debuggovací účely Zprávy jsou pro vás – vypisujte informace, které mají smysl a hlavně identifikují místo/zdroj problému Využívejte makra preprocesoru __FILE__, __LINE__, (__func__) __DATE__, __TIME__ J. Novosad 3 – Ladění chyb 28. září 2014 10 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Zprávy pro debuggovací účely Zprávy jsou pro vás – vypisujte informace, které mají smysl a hlavně identifikují místo/zdroj problému Využívejte makra preprocesoru __FILE__, __LINE__, (__func__) __DATE__, __TIME__ DEBUG_MSG extern int debug_level; #ifdef DEBUG # define DEBUG_MSG(level, ...) do { \ if (debug_level >= level) { \ fprintf(stderr, __VA_ARGS__); \ } \ } while (0) #else # define DEBUG_MSG(level, ...) ((void) 0) #endif J. Novosad 3 – Ladění chyb 28. září 2014 10 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Syslog Komplexní systém pro logování informací pro případnou pozdější analýzu. Zprávy lze dělit podle zdroje i podle důležitosti. Zprávy lze zapisovat na konkrétní zařízení (tiskárna, konsole), do lokálních souborů (/var/log/) nebo na vzdálený server. V prostředí GNU/Linuxu postupně několik generací logovacích nástrojů (syslogd(8), syslog-ng(8) a rsyslogd(8)) Konfigurace je např. v /etc/rsyslog.conf/, /etc/rsyslog.d/*1 Funkce ze syslog.h: openlog(3) syslog(3) closelog(3) 1 podle použitého daemona se může lišit J. Novosad 3 – Ladění chyb 28. září 2014 11 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr úkol použijte připravený debug.c a rozšiřte makro DEBUG_MSG o možnost zapisovat do syslogu J. Novosad 3 – Ladění chyb 28. září 2014 12 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Testování na co si dávat pozor a jak chyby řešit J. Novosad 3 – Ladění chyb 28. září 2014 13 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Motivace raketa Ariane 5 nastala výjimka jejíž ošetření bylo vypnuté z výkonnostních důvodů havárie sw → havárie rakety havárie Mars Polar Lander signál ze senzorů byl interpretován jako dosednutí na zem ačkoli zařízení bylo asi ve 40 m chyba byla v jediném řádku kódu J. Novosad 3 – Ladění chyb 28. září 2014 14 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Typologie chyb I Textové chyby typografie, gramatika a překlepy chyby v překladu, problemy s dokumentací obvykle snadná oprava, nízká priorita úloha pro spell-checker Pády aplikace Segmentation fault, buffer overflows, . . . kritický význam, často těžko reprodukovatelné úloha pro debugger Neočekávané chování program se chová jinak, než uživatel očekává nebo/a je popsáno v dokumentaci význam se může lišit, často přístup bug → feature J. Novosad 3 – Ladění chyb 28. září 2014 15 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Typologie chyb II Memory leaky program neuvolňuje alokovanou paměť význam se liší podle typu aplikace úloha pro valgrind a spol. Výkonostní problémy v určitých situacích program významně zpomalí a konzumuje nepřiměřené množství zdrojů význam obvykle vysoký úloha pro profilování Bezpečnostní problémy problémy s přístupovými právy, neoprávněnými zásahy do paměti, uživatelským vstupem, souběhem, . . . význam se liší podle účelu aplikace J. Novosad 3 – Ladění chyb 28. září 2014 16 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Postupy – co nefunguje Kompletní testování stavový prostor je příliš velký (až nekonečný) Statistické testování nefunguje u SW – výskyt chyby je ovlivnitelný příliš mnoha faktory (příliš mnoho náhodných veličin) Testování a hledání chyb je otázkou ceny, jakou chceme zaplatit. J. Novosad 3 – Ladění chyb 28. září 2014 17 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Postupy – co funguje unit testy testování malých, jasně definovaných částí. Následovat může testování subsystémů a celého systému. automatizace zvyšuje frekvenci testů a snižuje jejich cenu testování vstupů – náhodné/systematické/hraniční Testujte co nejdříve – rychle odhalená chyba je levnější J. Novosad 3 – Ladění chyb 28. září 2014 18 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr assert() #include void assert(scalar expression); testování podmínky a ukončení aplikace v případě její nesplnění pokud je definované makro NDEBUG, assert() nic neprovádí pozor na vedlejší efekty testovací podmínky pozor na testování vstupů J. Novosad 3 – Ladění chyb 28. září 2014 19 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Unit testy – framework check check.sourceforge.net jeden z mnoha Unit test frameworků hlavičkový soubor: #include knihovna: -lcheck J. Novosad 3 – Ladění chyb 28. září 2014 20 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Unit testy – framework check check.sourceforge.net jeden z mnoha Unit test frameworků hlavičkový soubor: #include knihovna: -lcheck úkol naimplementujte funkci kontrolující zletilost uživatele funkce vrací false (0) v případě, že od narození uživatele (born) do času kontroly (check_time) neuběhlo více než 18 let jinak vrací true pro tuto funkci si vytvořte Unit test ve frameworku check #include int is_mature(const struct tm *born, const struct tm *check_time); J. Novosad 3 – Ladění chyb 28. září 2014 20 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Nástroje pro debuggování a reverzní inženýrství debuggery, valgrind, ltrace, strace, ldd, file, object, nm, strip, size, objdump, perf, . . . J. Novosad 3 – Ladění chyb 28. září 2014 21 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Debuggery gdb(1) textový debugger základem pro grafické nadstavby ddd(1), kdbg, . . . J. Novosad 3 – Ladění chyb 28. září 2014 22 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Debuggery gdb(1) textový debugger základem pro grafické nadstavby ddd(1), kdbg, . . . DEMO J. Novosad 3 – Ladění chyb 28. září 2014 22 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Valgrind a spol. Sada nástrojů pro debugování a profilování. --tool= memcheck cachegrind callgrind . . . (http://valgrind.org/info/tools.html) J. Novosad 3 – Ladění chyb 28. září 2014 23 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Valgrind a spol. Sada nástrojů pro debugování a profilování. --tool= memcheck cachegrind callgrind . . . (http://valgrind.org/info/tools.html) DEMO J. Novosad 3 – Ladění chyb 28. září 2014 23 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Valgrind a spol. Sada nástrojů pro debugování a profilování. --tool= memcheck cachegrind callgrind . . . (http://valgrind.org/info/tools.html) DEMO Další užitečné přepínače --leak-check=yes --dump-instr=yes --cache-sim=yes J. Novosad 3 – Ladění chyb 28. září 2014 23 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Profilování Vyhledání míst vhodných k optimalizaci – řešte to, co má smysl! Postup 1 (překlad s nestandardními volbami překladače) 2 vygenerování profilovacích dat 3 analýza profilovacích dat Nástroje gprof (překlad programu s volbou -pg) callgrind, kcachegrind J. Novosad 3 – Ladění chyb 28. září 2014 24 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Profilování Vyhledání míst vhodných k optimalizaci – řešte to, co má smysl! Postup 1 (překlad s nestandardními volbami překladače) 2 vygenerování profilovacích dat 3 analýza profilovacích dat Nástroje gprof (překlad programu s volbou -pg) callgrind, kcachegrind DEMO J. Novosad 3 – Ladění chyb 28. září 2014 24 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Performance Counter Subsystem (perf) Abstrakce hardwarových čitačů moderních CPU Součást přímo kernelu od verze 2.6.31 https://perf.wiki.kernel.org/index.php/Main_Page Minimální režie, možnosti měření v kernel- i user-space Měřit lze i již běžící aplikace Použití perf record -- ./mujprog pref report J. Novosad 3 – Ladění chyb 28. září 2014 25 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Další nástroje (reverzního inženýrství) file – informace o souboru objdump – vypíše obsah objektového souboru ldd – použité dynamické knihovny strace – sledování systémových volání a signálů ltrace – sledování volání knihovních funkcí tcpdump/wireshark – monitorování síťové komunikace J. Novosad 3 – Ladění chyb 28. září 2014 26 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Souborový systém procfs V Linuxu je (téměř) všechno soubor – i procesy a parametry jádra jsou soubory dostupné v souborovém systému procfs (/proc). obsah souborů generuje jádro při každém open() největší část obsahuje informace o běžících procesech lze nejen číst, ale i zapisovat a měnit tak chování systému www.root.cz/serialy/co-pred-nami-taji-proc/ J. Novosad 3 – Ladění chyb 28. září 2014 27 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Souborový systém procfs V Linuxu je (téměř) všechno soubor – i procesy a parametry jádra jsou soubory dostupné v souborovém systému procfs (/proc). obsah souborů generuje jádro při každém open() největší část obsahuje informace o běžících procesech lze nejen číst, ale i zapisovat a měnit tak chování systému www.root.cz/serialy/co-pred-nami-taji-proc/ sysfs debugfs J. Novosad 3 – Ladění chyb 28. září 2014 27 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Závěr domácí úkoly a zdroje J. Novosad 3 – Ladění chyb 28. září 2014 28 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Domácí úkol Připravte si pro další úlohy vlastní knihovnu s debugovacími makry a funkcemi výpis chybových hlášek výpis debugovacích hlášek verze s výstupem na stderr i do syslogu (případně další výstupy) různé úrovně výpisů . . . Seznamte se s možnostmi různých ladících nástrojů a vyberte si vhodné nástroje pro vás. J. Novosad 3 – Ladění chyb 28. září 2014 29 / 30 C Preprocesor Logování událostí Testování Nástroje pro debuggování Závěr Zdroje syslog www.gnu.org/s/libc/manual/html_node/Submitting-Syslog-Messages.html debuging/profiling www.alexonlinux.com/how-debugger-works www.kdbg.org/ perf.wiki.kernel.org/index.php/Main_Page www.fit.vutbr.cz/∼martinek/clang/profiling.html.cs valgrind.org/docs/manual/QuickStart.html kcachegrind.sourceforge.net/html/Home.html J. Novosad 3 – Ladění chyb 28. září 2014 30 / 30