Obsah
1. Trasování a ladění nativních aplikací v Linuxu: jazyk používaný SystemTapem
2. Globální a lokální proměnné
3. Automatické vytištění obsahu globálních proměnných
4. Demonstrační příklad – automatické vytištění počtu přečtených a zapsaných bajtů
6. Demonstrační příklad: použití uživatelských funkcí
7. Operátory a funkce pro práci s histogramem a statistickými veličinami
8. Demonstrační příklad: vytvoření histogramu čtení a zápisu do souborů
9. Výsledky demonstračního příkladu
10. Základní funkce pro získání statistických informací
11. Demonstrační příklad: získání statistických informací o čtení a zápisech do souborů
1. Trasování a ladění nativních aplikací v Linuxu: jazyk používaný SystemTapem
V páté části (mini)seriálu o trasování a ladění nativních aplikací v Linuxu se podrobněji seznámíme s možnostmi programovacího jazyka, v němž se popisuje chování takzvaných „sond“ (probes) používaných nástrojem SystemTap. Tento programovací jazyk je sice odvozen od klasického céčka (podobně jako jazyk D používaný v konkurenčním nástroji DTrace), ovšem některé jeho vlastnosti se od známého a široce používaného jazyka C odlišují. Jedná se především o rozdílný způsob práce s proměnnými (statické versus dynamické určování typů, …), dále o podporu asociativních polí a taktéž podporu pro tvorbu histogramů a zjišťování dalších statistických veličin. Právě práce s histogramem a výpočty sumy, průměru, maximální či minimální hodnoty atd. jsou při ladění aplikací či při sledování chování systému velmi často používány. V případě potřeby je dokonce možné zkombinovat programovací jazyk SystemTapu s funkcemi zapsanými v céčku. V tomto případě ovšem přijdeme o některé kontroly zdrojového kódu, které se jinak provádějí automaticky.
Kombinace obou jazyků může vypadat takto (podrobnosti si řekneme příště):
%{ #include <linux/in.h> #include <linux/ip.h> %} /* <-- top level */ /* Reads the char value stored at a given address: */ function __read_char:long(addr:long) %{ /* pure */ STAP_RETURN(kderef(sizeof(char), STAP_ARG_addr)); CATCH_DEREF_FAULT (); %} /* <-- function body */ /* Determines whether an IP packet is TCP, based on the iphdr: */ function is_tcp_packet:long(iphdr) { protocol = @cast(iphdr, "iphdr")->protocol return (protocol == %{ IPPROTO_TCP %}) /* <-- expression */ }
2. Globální a lokální proměnné
V jazyce využívaném nástrojem SystemTap pro popis chování jednotlivých sond je možné používat jak globální, tak i lokální proměnné. To pravděpodobně není pro většinu čtenářů příliš překvapivé, ovšem zajímavé jsou vlastnosti těchto proměnných. Globální proměnné musí být deklarovány klíčovým slovem global, za nímž se uvede jméno proměnné a popř. i výraz, jehož výsledek je použit pro inicializaci proměnné. Nikde se však neuvádí typ proměnné, neboť ten je v případě potřeby zjišťován dynamicky. Zatímco globální proměnné je nutné deklarovat, u lokálních proměnných je tomu přesně naopak – žádná deklarace se neuvádí, protože při prvním použití nové proměnné se tato proměnná vytvoří automaticky, samozřejmě jako proměnná lokální (už z tohoto popisu je zřejmé, že ani u lokálních proměnných se explicitně neuvádí jejich typ). Podívejme se na velmi jednoduchý příklad, v němž se používají jak globální proměnné, tak i proměnné lokální:
global read_count=0 global write_count=0 probe begin(1) { cnt=0 for (i = 0; i < 50; i++) if (isprime (i)) { cnt++ printf("Prime number #%-2d is %2d\n", cnt, i) } }
Ve skriptu jsou deklarovány a současně i inicializovány dvě globální proměnné nazvané read_count a write_count. Používají se zde i dvě proměnné lokální. První lokální proměnná pojmenovaná cnt je platná pro celou sondu, zatímco druhá proměnná nazvaná i je platná pouze uvnitř své smyčky typu for (platnost proměnných odpovídá jejich viditelnosti).
Poznámka: i přesto, že se u deklarací proměnných neuvádí jejich typ, se jedná o silně typovaný jazyk. V případě potřeby lze typ uvést, což platí i pro argumenty funkcí či návratové hodnoty funkcí.
Poznámka: výjimka platí pro pole, která mohou být pouze globální, nikoli lokální. Toto omezení pravděpodobně souvisí se způsobem správy paměti v nástroji SystemTap. Pro adresaci prvků v polích se používají jak celočíselné indexy, tak i jiné hodnoty (podporovány jsou tedy asociativní pole).
3. Automatické vytištění obsahu globálních proměnných
Globální proměnné mají ještě dvě zajímavé a mnohdy velmi užitečné vlastnosti. Pokud se globální proměnná nikde ve skriptu nevyužívá (pouze se deklaruje na jeho začátku), je o tomto potenciálním problému uživatel informován ihned při spuštění nástroje SystemTap v průběhu analýzy a překladu skriptu. V tomto případě se ovšem jedná pouze o varování (Warning), nikoli o zásadní chybu, která by ihned ukončila práci skriptu.
Zajímavější je však fakt, že hodnoty těch globálních proměnných, do nichž se pouze provádí zápis, jsou automaticky vypsány na standardní výstup při ukončování skriptu. To je velmi užitečná vlastnost, protože je tímto způsobem snadné do skriptu přidat další sledovanou veličinu, aniž by bylo nutné jakýmkoli způsobem upravovat sondu typu end, v níž se typicky provádí závěrečné vyhodnocení chování sledované aplikace či sledovaného systému. Úprava je samozřejmě nutná tehdy, pokud nám styl výchozího formátování nevyhovuje (hexadecimální hodnoty atd.)
Jednoduchá sonda, která po svém ukončení vypíše počet volání systémové funkce sys_sync, tedy může vypadat následovně:
global count=0 probe kernel.function("sys_sync") { count++ }
Nástroj sám rozpozná, že se globální proměnná používá pouze pro zápis nových hodnot a tudíž její výsledek vypíše při ukončování skriptu na standardní výstup.
Poznámka: z proměnné se ve skutečnosti čte, protože count++ lze napsat jako count = count + 1, ovšem žádné další operace se s hodnotou proměnné neprovádí.
4. Demonstrační příklad – automatické vytištění počtu přečtených a zapsaných bajtů
Podívejme se nyní na jednoduchý demonstrační příklad, v němž jsou použity globální proměnné, do nichž se ve skriptu pouze zapisuje (nejsou například vypisovány příkazem printf/println atd.). Ve skriptu se do proměnných read_count a write_count průběžně zapisuje počet operací čtení či zápisu do souboru (přesněji řečeno do file deskriptoru), zatímco v proměnných read_bytes a write_bytes se počítá objem čtených a zapisovaných dat (pro celý systém!). Pátá globální proměnná není nikde využita, takže se při spuštění skriptu vypíše varování o potenciálním problému, který SystemTap nalezl:
global read_count=0 global write_count=0 global read_bytes=0 global write_bytes=0 global unused_variable=0 probe begin { println("STAP prepared"); } probe process("ls").begin { printf("ls with PID=%d started\n", pid()); } probe process("ls").end { printf("ls with PID=%d finished\n", pid()); } probe syscall.open { filename = user_string($filename); printf("ls opened file %s\n", filename); } probe syscall.read { bytes=$count into=$fd read_bytes += bytes printf("read %d bytes from file descriptor %d\n", bytes, into); read_count++ if (read_count>10000) exit() } probe syscall.write { bytes=$count into=$fd write_bytes += bytes printf("write %d bytes to file descriptor %d\n", bytes, into); write_count++ if (write_count>10000) exit() }
Poznámky:
- Ve skutečnosti může při déletrvajícím běhu skriptu dojít k přetečení hodnot globálních proměnných read_bytes a write_bytes, podobně jako v programovacím jazyku C.
- Povšimněte si použití „céčkových“ operátorů typu ++ a +=. Ty jsou v nástroji SystemTap samozřejmě podporovány, stejně jako ostatní podobné operátory (--, -=, *= atd.).
- I přesto, že se volá funkce exit() po provedení 10000 zápisů či čtení, je před ukončením SystemTapu zavolána sonda end, pokud samozřejmě existuje. Exit tedy v tomto ohledu není destruktivní a ihned provedenou operací.
5. Zápis uživatelských funkcí
Kromě sond se programové skripty používané nástrojem SystemTap mohou skládat z uživatelských funkcí. Deklarace těchto funkcí začíná klíčovým slovem function, za nímž následuje jméno funkce a v kulatých závorkách pak jména parametrů (pokud funkce nějaké parametry akceptuje). Případné návratové hodnoty se vrací příkazem return, stejně jako v céčku:
function count_add(arg1, arg2) { return arg1 + arg2 }
V případě potřeby lze explicitně uvést i typ návratové hodnoty, popř. typy parametrů, a to tímto stylem:
function concatenate:string(arg1:long, arg2) { return sprintf("%d%s", arg1, arg2) }
Jak jsme si již řekli v předchozích kapitolách, každé jméno proměnné, které neodpovídá proměnné globální, povede k automatickému vzniku lokální proměnné. Podívejme se na příklad s lokální proměnnou pojmenovanou i platnou pouze uvnitř počítané programové smyčky for (v kódu je vidět i použití příkazu break pro okamžité ukončení smyčky):
function isprime (x) { if (x < 2) return 0 for (i = 2; i < x; i++) { if (x % i == 0) return 0 if (i * i > x) break } return 1 }
Použít je možné i rekurzi, samozřejmě se všemi důsledky, které to přináší (problémy s kapacitou zásobníku v případě hluboké či dokonce nekonečné rekurze atd.):
function fibonacci(i) { if (i < 1) error("bad number") if (i == 1) return 1 if (i == 2) return 2 return fibonacci (i-1) + fibonacci (i-2) }
6. Demonstrační příklad: použití uživatelských funkcí
Dvojici uživatelských funkcí nazvaných isprime a fibonacci, s nimiž jsme se seznámili v předchozí kapitole, použijeme v dalším demonstračním příkladu, v němž se nachází dvojice sond begin (o pořadových číslech jsme si řekli základní informace minule). Po spuštění tohoto skriptu se pouze vypíšou všechna prvočísla od 2 do 50 a následně prvních deset prvků Fibonacciho řady. Posléze je skript ukončen zavoláním funkce exit():
function isprime (x) { if (x < 2) return 0 for (i = 2; i < x; i++) { if (x % i == 0) return 0 if (i * i > x) break } return 1 } probe begin(1) { cnt=0 for (i = 0; i < 50; i++) if (isprime (i)) { cnt++ printf("Prime number #%-2d is %2d\n", cnt, i) } } function fibonacci(i) { if (i < 1) error ("bad number") if (i == 1) return 1 if (i == 2) return 2 return fibonacci (i-1) + fibonacci (i-2) } probe begin(2) { for (i = 1; i <= 10; i++) printf ("Fibonacci number #%-2d is %2d\n", i, fibonacci(i)) exit () }
Po spuštění příkazem stap by se na standardní výstup měly vypsat následující řádky s výsledky výpočtů:
Prime number #1 is 2 Prime number #2 is 3 Prime number #3 is 5 Prime number #4 is 7 Prime number #5 is 11 Prime number #6 is 13 Prime number #7 is 17 Prime number #8 is 19 Prime number #9 is 23 Prime number #10 is 29 Prime number #11 is 31 Prime number #12 is 37 Prime number #13 is 41 Prime number #14 is 43 Prime number #15 is 47 Fibonacci number #1 is 1 Fibonacci number #2 is 2 Fibonacci number #3 is 3 Fibonacci number #4 is 5 Fibonacci number #5 is 8 Fibonacci number #6 is 13 Fibonacci number #7 is 21 Fibonacci number #8 is 34 Fibonacci number #9 is 55 Fibonacci number #10 is 89
7. Operátory a funkce pro práci s histogramem a statistickými veličinami
Programovací jazyk využívaný nástrojem SystemTap obsahuje užitečný operátor zapisovaný následovně: <<<. Jedná se o binární operátor (s dvojicí operandů), přičemž levým operandem musí být pole, což je – jak již víme z předchozího textu – globální proměnná (pole totiž nemohou být lokálními proměnnými) a pravým operandem je libovolný výraz, jehož výsledkem je číselná hodnota (nikoli řetězec či nějaké pole). Pomocí tohoto operátoru se do pole na levé straně operátoru přidává další hodnota, což má ten význam, že se následně může celé pole k tomu připravenými funkcemi zpracovat a získat z něho histogram či různé statistické veličiny typu suma, průměr, maximální hodnota a minimální hodnota. Podívejme se na jednoduchý příklad (používaný mj. i v dokumentaci), v němž se zjišťuje objem dat tekoucích přes síťové rozhraní:
global histogram probe begin { printf("Capturing...\n") } probe netdev.transmit { histogram <<< length } probe netdev.receive { histogram <<< length } probe end { printf( "\n" ) print( @hist_log(histogram) ) }
Při ukončení SystemTapu se zavolá sonda end, která z pole hodnot získá histogram s logaritmickým měřítkem.
Další příklad je již složitější a zaslouží si stručný popis:
global receive_stats global transmit_stats probe begin { printf("Starting network capture (Ctl-C to end)\n") } probe netdev.receive { receive_stats[dev_name, pid(), execname()] <<< length } probe netdev.transmit { transmit_stats[dev_name, pid(), execname()] <<< length } probe end { printf("\nEnd Capture\n\n") printf("Iface Process........ PID.. RcvPktCnt XmtPktCnt\n") foreach ([dev, pid, name] in receive_stats) { recvcount = @count(receive_stats[dev, pid, name]) xmitcount = @count(transmit_stats[dev, pid, name]) printf( "%5s %-15s %-5d %9d %9d\n", dev, name, pid, recvcount, xmitcount ) } delete receive_stats delete transmit_stats }
Opět zde nalezneme dvojici globálních proměnných, tentokrát se ovšem jedná o asociativní pole, v němž jsou prvky „indexovány“ jménem síťového rozhraní, PID aktivního procesu a současně jménem procesu. Do těchto polí se ukládají délky bloků dat přenášených přes dané rozhraní a na konci běhu skriptu se v sondě end vypíše celkový počet přenosů, a to jednotlivě pro zařízení, PID procesu a jména procesu.
8. Demonstrační příklad: vytvoření histogramu čtení a zápisu do souborů
V dnešním třetím demonstračním příkladu rozšíříme možnosti skriptu, s nímž jsme se již seznámili minule. Budeme sledovat čtení a zápisy do souborů. Počet zapsaných či přečtených bajtů se ukládá do globálních polí pojmenovaných reads a writes. Z těchto dat se posléze vytvoří histogram funkcí @hist_linear. Této funkci je nutné předat čtyři hodnoty: vlastní pole, minimální hodnotu na ose, maximální hodnotu na ose a šířku intervalu (čím je šířka intervalu menší, tím více hodnot se v histogramu zobrazí):
global reads global writes global read_write_count=0 probe begin { println("STAP prepared"); } probe process("ls").begin { printf("ls with PID=%d started\n", pid()); } probe process("ls").end { printf("ls with PID=%d finished\n", pid()); } probe syscall.open { filename = user_string($filename); printf("ls opened file %s\n", filename); } probe syscall.read { bytes=$count into=$fd reads <<< bytes printf("read %d bytes from file descriptor %d\n", bytes, into); read_write_count++ if (read_write_count>10000) exit() } probe syscall.write { bytes=$count into=$fd writes <<< bytes printf("write %d bytes to file descriptor %d\n", bytes, into); read_write_count++ if (read_write_count>10000) exit() } probe end { println("Reads:") print(@hist_linear(reads, 0, 1000, 100)) println() println("Writes:") print(@hist_linear(writes, 0, 1000, 100)) }
9. Výsledky demonstračního příkladu
Pokud výše uvedený skript spustíme na středně vytíženém systému, můžeme získat například následující dva histogramy. Ty jsou samozřejmě vytištěny na standardní výstup, vystačíme si tedy s pouhým terminálem. Znaky ~ jsou použity pro vynechání „nezajímavých“ hodnot, znak > na začátku řádku zase říká, že maximální hodnota je příliš nízká:
Reads: value |-------------------------------------------------- count 0 | 0 100 | 2 200 | 0 300 |@ 10 ~ 800 | 0 900 | 0 1000 | 1 >1000 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 35621
Histogram zápisů vypadá zcela jinak a naznačuje, že některé procesy zapisují po jednotlivých znacích (možná na standardní výstup) či mají malý buffer:
Writes: value |-------------------------------------------------- count 0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 17940 100 |@@@@@@@@@@@@@@@@@@@@@@@@@@ 9492 200 |@ 468 300 |@@ 873 400 |@@ 1070 500 | 211 600 | 91 700 | 26 800 | 12 900 | 1 1000 | 5 >1000 | 147
10. Základní funkce pro získání statistických informací
Na prvky (globálního) pole použitého pro tvorbu histogramu je možné aplikovat i některé další funkce vracející mnohdy užitečné statistické informace:
# | Funkce | Stručný popis |
---|---|---|
1 | @count() | počet prvků (vzorků) |
2 | @sum | součet (suma) hodnot prvků |
3 | @min | nalezení vzorku s minimální hodnotou |
4 | @max | nalezení vzorku s maximální hodnotou |
5 | @avg | výpočet průměrné hodnoty |
6 | @hist_linear | normální histogram s lineární osou |
7 | @hist_log | histogram s logaritmickou osou |
Histogram s logaritmickou osou vypadá takto:
value |-------------------------------------------------- count 1 | 0 2 | 0 4 | 0 8 | 0 16 | 0 32 | 0 64 | 0 128 | 0 256 | 0 512 | 0 1024 | 0 2048 | 0 4096 | 0
11. Demonstrační příklad: získání statistických informací o čtení a zápisech do souborů
Zmíněné funkce pro výpočet základních statistických veličin byly přidány do dalšího demonstračního příkladu, který opět sleduje čtení a zápisy do souborů. Při ukončení skriptu (stisk Ctrl+C popř. po dosažení 10000 čtení/zápisů) se v sondě end jednotlivé hodnoty vypočtou a následně zobrazí. Mimochodem, povšimněte si, že se funkce printf chová prakticky stejně jako v céčku (až na ten rozdíl, že v SystemTapu se lépe kontrolují typy parametrů):
global reads global writes global read_write_count=0 probe begin { println("STAP prepared"); } probe process("ls").begin { printf("ls with PID=%d started\n", pid()); } probe process("ls").end { printf("ls with PID=%d finished\n", pid()); } probe syscall.open { filename = user_string($filename); printf("ls opened file %s\n", filename); } probe syscall.read { bytes=$count into=$fd reads <<< bytes printf("read %d bytes from file descriptor %d\n", bytes, into); read_write_count++ if (read_write_count>10000) exit() } probe syscall.write { bytes=$count into=$fd writes <<< bytes printf("write %d bytes to file descriptor %d\n", bytes, into); read_write_count++ if (read_write_count>10000) exit() } probe end { println("Reads:") printf("Count: %d operations\n", @count(reads)) printf("Total: %d bytes\n", @sum(reads)) printf("Min: %d bytes\n", @min(reads)) printf("Max: %d bytes\n", @max(reads)) printf("Avg: %d bytes/operation\n", @avg(reads)) println() println("Writes:") printf("Count: %d operations\n", @count(writes)) printf("Total: %d bytes\n", @sum(writes)) printf("Min: %d bytes\n", @min(writes)) printf("Max: %d bytes\n", @max(writes)) printf("Avg: %d bytes/operation\n", @avg(writes)) }
Výsledky demonstračního příkladu mohou vypadat následovně (čistě náhodou se počet čtení prakticky rovná počtu zápisů, vy však na svém systému můžete naměřit odlišné hodnoty):
Reads: Count: 5003 operations Total: 538640392 bytes Min: 8196 bytes Max: 131072 bytes Avg: 107663 bytes/operation Writes: Count: 4999 operations Total: 828740 bytes Min: 40 bytes Max: 2312 bytes Avg: 165 bytes/operation
12. Odkazy na Internetu
- SystemTap Reference: Context Functions
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/SystemTap_Tapset_Reference/context_stp.html - SystemTap Beginners Guide (RHEL 6)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/SystemTap_Beginners_Guide/index.html - SystemTap Tapset Reference (RHEL 6)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html-single/SystemTap_Tapset_Reference/index.html - SystemTap Beginners Guide (RHEL 7)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SystemTap_Beginners_Guide/index.html - SystemTap Tapset Reference (RHEL 6)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SystemTap_Tapset_Reference/index.html - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Tracing (software)
https://en.wikipedia.org/wiki/Tracing_%28software%29 - ltrace(1) – Linux man page
http://linux.die.net/man/1/ltrace - ltrace (Wikipedia)
https://en.wikipedia.org/wiki/Ltrace - strace(1) – Linux man page
http://linux.die.net/man/1/strace - strace (stránka projektu na SourceForge)
https://sourceforge.net/projects/strace/ - strace (Wikipedia)
https://en.wikipedia.org/wiki/Strace - SystemTap (stránka projektu)
https://sourceware.org/systemtap/ - SystemTap (Wiki projektu)
https://sourceware.org/systemtap/wiki - SystemTap (Wikipedia)
https://en.wikipedia.org/wiki/SystemTap - Dynamic Tracing with DTrace & SystemTap
http://myaut.github.io/dtrace-stap-book/ - DTrace (Wikipedia)
https://en.wikipedia.org/wiki/DTrace - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Getting started with ltrace: how does it do that?
https://www.ellexus.com/getting-started-with-ltrace-how-does-it-do-that/ - Reverse Engineering Tools in Linux – strings, nm, ltrace, strace, LD_PRELOAD
http://www.thegeekstuff.com/2012/03/reverse-engineering-tools/ - 7 Strace Examples to Debug the Execution of a Program in Linux
http://www.thegeekstuff.com/2011/11/strace-examples/ - Oracle® Solaris 11.3 DTrace (Dynamic Tracing) Guide
http://docs.oracle.com/cd/E53394_01/html/E53395/gkwpo.html#scrolltoc - An Introduction To Using GDB Under Emacs
http://tedlab.mit.edu/~dr/gdbintro.html - GNU Emacs
https://www.gnu.org/software/emacs/emacs.html - The Emacs Editor
https://www.gnu.org/software/emacs/manual/html_node/emacs/index.html - Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html - An Introduction to Programming in Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html - 27.6 Running Debuggers Under Emacs
https://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html - GdbMode
http://www.emacswiki.org/emacs/GdbMode - Emacs (Wikipedia)
https://en.wikipedia.org/wiki/Emacs - Emacs Lisp (Wikipedia)
https://en.wikipedia.org/wiki/Emacs_Lisp - Pyclewn installation notes
http://pyclewn.sourceforge.net/install.html - pip Installation
https://pip.pypa.io/en/latest/installing.html - Clewn
http://clewn.sourceforge.net/ - Clewn installation
http://clewn.sourceforge.net/install.html - Clewn – soubory
http://sourceforge.net/projects/clewn/files/OldFiles/ - KDbg: úvodní stránka
http://www.kdbg.org/ - Nemiver (stránky projektu)
https://wiki.gnome.org/Apps/Nemiver - Basic Assembler Debugging with GDB
http://dbp-consulting.com/tutorials/debugging/basicAsmDebuggingGDB.html - Nemiver FAQ
https://wiki.gnome.org/Apps/Nemiver/FAQ - Nemiver (Wikipedia)
https://en.wikipedia.org/wiki/Nemiver - Data Display Debugger
https://www.gnu.org/software/ddd/ - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - BASH Debugger
http://bashdb.sourceforge.net/ - The Perl Debugger(s)
http://debugger.perl.org/ - Visual Debugging with DDD
http://www.drdobbs.com/tools/visual-debugging-with-ddd/184404519 - Pydb – Extended Python Debugger
http://bashdb.sourceforge.net/pydb/ - Insight
http://www.sourceware.org/insight/ - Supported Languages (GNU Debugger)
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Clewn
http://clewn.sourceforge.net/ - Clewn installation
http://clewn.sourceforge.net/install.html - Clewn – soubory ke stažení
http://sourceforge.net/projects/clewn/files/OldFiles/ - Pyclewn installation notes
http://pyclewn.sourceforge.net/install.html - Debugging
http://janus.uclan.ac.uk/pagray/labs/debug.htm