V dnešním článku se budeme zabývat problematikou programátorských nástrojů používaných pro ladění aplikací přeložených do nativního strojového kódu. Základním nástrojem vývojářů jsou v tomto případě takzvané debuggery. Pro Linux existuje větší množství debuggerů, ovšem nejpoužívanějším je stále GNU Debugger a jeho četné nadstavby, které svým uživatelům nabízí jednodušeji ovladatelné grafické uživatelské rozhraní. Rozhraní k GNU Debuggeru je taktéž součástí mnoha programátorských textových editorů a IDE.

Obsah

1. Debuggery a jejich nadstavby v Linuxu

2. Stručná historie vývoje debuggerů

3. Debuggery ovládané z příkazové řádky

4. Debuggery integrované do IDE a GUI nadstavby nad debuggery

5. GNU Debugger

6. Základní funkce nabízené GNU Debuggerem

7. Nástroj cgdb

8. Obsah druhé části článku

9. Odkazy na Internetu

1. Debuggery a jejich nadstavby v Linuxu

Mezi nejdůležitější nástroje používané dennodenně prakticky všemi
programátory při vývoji a taktéž při testování aplikací patří vedle překladačů
(popř. i interpretrů) a programátorských textových editorů či
integrovaných vývojových prostředí (IDE) taktéž ladicí nástroje, zejména pak
takzvané debuggery. Moderní debuggery nabízí programátorům hned několik
funkcí: spuštění programu s možností jeho zastavení na takzvaných
breakpointech popř. zastavení při splnění nějaké podmínky (počet
průchodů breakpointem), krokování programu po jednotlivých programových
řádcích, příkazech či dokonce po jednotlivých strojových instrukcích,
prohlížení obsahu globálních i lokálních proměnných, prohlížení obsahu paměti
(haldy) i zásobníku (resp. přesněji řečeno zásobníkových rámců), práci
s jednotlivými vlákny, prohlížení obsahu registrů mikroprocesoru a
v neposlední řadě pak analýzu tzv. core dumpu vygenerovaného při pádu
aplikace (pokud ovšem tato funkce není zakázána).

01_td_1

Obrázek 1: V minulosti velmi populární nástroj Turbo Debugger je
vybaven celoobrazovkovým textovým uživatelským rozhraním (TUI) připomínajícím
další produkty vytvořené společností Borland.

Mnohé současné debuggery se dokonce dokážou připojit k již běžící
aplikaci a provádět výše uvedené operace na „živém“ programu, a to
při správně provedené konfiguraci i vzdáleně. To znamená, že z uživatelské
stanice lze například ladit program běžící na serveru či naopak program běžící
na jednočipovém mikrořadiči připojeném přes sériovou linku, JTAG, rozhraní USB
apod. Samostatnou kapitolou jsou pak debuggery používané ve webových
prohlížečích pro ladění aplikací napsaných v JavaScriptu či debuggery,
které jsou součástí některých složitějších aplikací vybavených vlastním
programovacím jazykem. Dobrým příkladem může být AutoCAD. V dalším textu
se však zaměříme na debuggery používané především pro ladění nativních
aplikací, tj. aplikací přeložených do strojového kódu mikroprocesoru.
Zmíníme se i o některých nadstavbách nad existujícími debuggery, především pak
o nadstavbách nad GNU Debuggerem, které k tomuto užitečnému
nástroji doplňují grafické uživatelské rozhraní.

02_td_2

Obrázek 2: Jedno ze speciálních oken Turbo Debuggeru, v němž se
zobrazuje stav mikroprocesoru (obsahy pracovních registrů i příznakových bitů),
obsah vybrané části operační paměti a taktéž zdrojový kód proložený
disassemblovaným strojovým kódem (v této části okna je zvýrazněn právě
prováděný řádek).

2. Stručná historie vývoje debuggerů

Historie vývoje a používání debuggerů je velmi dlouhá, protože se –
vedle assemblerů, makroassemblerů a překladačů – jedná o nejstarší
programátorské nástroje vůbec. Sálové počítače (mainframy) a později i
minipočítače většinou obsahovaly speciální hardwarovou konzoli určenou pro
ladění programů (představme si ovládací panel s tlačítky typu
Run, Step atd.). Debuggery implementované
jako běžné programy se pak nejvíce rozšířily na mikropočítačích. Mezi první
debugger v moderním slova smyslu, tedy program nabízející celoobrazovkové
uživatelské rozhraní a možnost interaktivní práce, patří zejména IBM
OLIVER (CICS interactive test/debug)
, jehož první verze vyšla již
v roce 1972. K rychlému vývoji debuggerů pak došlo v osmdesátých
a devadesátých letech, kdy se tyto nástroje staly nedílnou součástí
integrovaných vývojových prostředí.

03_qbasic_1

Obrázek 3: Velmi jednoduchý debugger byl použit i v QBasicu. Tento
debugger programátorům nabízel pouze základní funkce.

V našich zemích byla populární především integrovaná vývojová prostředí
prodávaná společností Borland (Turbo Pascal, Turbo C++, Borland Pascal,
částečně i Turbo Basic atd.), do kterých byl debugger postupně integrován.
Navíc tato společnost vytvořila i Turbo Debugger, který měl podobný
vzhled i ovládání jako další produkty Borlandu. Velmi jednoduchý
debugger byl součástí QBasicu, zde se ovšem jednalo o debugger doplněný do
interpretru a nikoli o „plnohodnotný“ debugger sloužící pro ladění
nativních aplikací. Zapomenout nesmíme ani na Watcom C++, který kromě
dalších nástrojů (včetně komerčního Vi) obsahoval i debugger. Až do relativně
nedávné doby byla součástí jak MS DOSu, tak i různých verzí MS Windows utilitka
nazvaná debug, což je velmi jednoduchý debugger ovládaný
z příkazové řádky. Ve skutečnosti tento debugger nepodporuje všechny
očekávané funkce, zejména pak propojení strojového kódu se zdrojovým kódem.
Náhradou je Windbg. Nyní se již začneme zabývat především těmi
debuggery a jejich nadstavbami, které lze využít na Linuxu.

04_blitz

Obrázek 4: Takto vypadá rozhraní debuggeru pro kdysi populární Blitz
Basic (screenshot byl pořízen na Amize).

3. Debuggery ovládané z příkazové řádky

Naprostou většinu v současnosti vyvíjených a používaných debuggerů je
možné rozdělit do dvou skupin. V první skupině nalezneme debuggery
ovládané z příkazové řádky. Někdy se tyto debuggery označují zkratkou
CLI neboli Command Line Interface. Příkladem debuggerů
spadajících do této kategorie je již výše zmíněný historický Debug,
novější (taktéž zmíněný) Windbg, dnes již pravděpodobně dále nevyvíjený
Intel Debugger či naopak nováček na tomto poli nazvaný LLDB.
Nejznámějším, nejpoužívanějším a současně i nejstarším ale stále využívaným
zástupcem této kategorie debuggerů je však GNU Debugger. Příkazy
ovládající debugger se v tomto případě zapisují na standardní vstup a
debugger vypisuje všechny zprávy na standardní výstup (mnohé debuggery si
dokonce vystačí i s takzvaným „hloupým terminálem“, který
dokáže rozpoznat pouze základní řídicí kódy). Tento způsob ovládání má své
přednosti, ale samozřejmě i zápory.

05_debug_1

Obrázek 5: Minimalistické rozhraní jednoduchého debuggeru nazvaného
debug. Na tomto screenshotu je zobrazena integrovaná
nápověda.

Mezi přednosti aplikací ovládaných z příkazového řádku patří velká
portabilita takového debuggeru (stejný frontend totiž může být bez nutnosti
dalších úprav použit na různých platformách a na různých operačních systémech),
velmi malé nároky debuggeru na systémové prostředky (což je v mnoha
případech důležité, zejména při zásahu na „živém“ systému) a
v neposlední řadě i možnost ladit aplikaci po připojení debuggeru ze
vzdáleného počítače a to i ve chvíli, kdy ještě není inicializován celý systém.
Dokonce ještě ani nemusí být načteny všechny ovladače, v nouzi postačuje
mít jen základní driver pro práci se sériovým portem. Další předností je
podpora i dosti komplikovaných příkazů, které je možné jen poměrně složitě
realizovat v GUI. Mezi zápory tohoto způsobu ovládání debuggerů patří
především nutnost si postupně zapamatovat alespoň nejpoužívanější příkazy,
nemožnost efektivně rozdělit sledované informace podle jejich typu (část
obrazovky vyhrazená pro výpis informací o breakpointech, proměnných, obsahu
paměti, zdrojového kódu) atd.

06_debug_2

Obrázek 6: Minimalistické rozhraní jednoduchého debuggeru nazvaného
debug. Výpis obsahu operační paměti (horní část screenshotu) a
disassembling neboli nepřesně zpětný překlad programu (dolní část
screenshotu).

4. Debuggery integrované do IDE a GUI nadstavby nad debuggery

Tvůrci debuggerů ovládaných z příkazové řádky si jsou většinou dobře
vědomi některých problémů, které s sebou použití CLI přináší a proto
například dále popisovaný GNU Debugger obsahuje rozhraní, kterým lze
debugger ovládat z jiného procesu popř. dokonce i z jiného
počítače připojeného přes sériovou linku či přes protokol TCP/IP (realizovaný
přes Ethernet, paralelní port atd.). Tímto jiným procesem může být aplikace
vybavená grafickým uživatelským rozhraním, která možnosti debuggeru zpřístupní
jednodušším a mnohdy i mnohem názornějším způsobem. Takovýchto aplikací dnes
existuje značné množství, ať již se jedná o v podstatě jednoúčelové
nadstavby nad debuggerem (cgdb, DDD, Nemiver,
Xxgdb), tak i o textové editory (Vim+Clewn, Emacs,
Anjuta, Geany, Code::Blocks, JEdit) či o
integrovaná vývojová prostředí (jmenujme zejména Eclipse,
Netbeans, KDevelop, Qt Creator a v neposlední řadě
Lazarus).

07_turbo_1

Obrázek 7: Debugger integrovaný do slavného Turbo Pascalu. Zde je
konkrétně zobrazen dialog pro vytvoření breakpointu.

Nesmíme ovšem zapomenout ani na debuggery, které přímo obsahují textové či
grafické uživatelské rozhraní. I tyto debuggery se mnohdy integrují do nějakého
vývojového prostředí. Příkladem může být Gambas se svým debuggerem či
(relativně) samostatný Microsoft Visual Studio Debugger. Nicméně je
zajímavé, že i přes existenci mnohdy velmi kvalitních nadstaveb nad CLI
debuggery i nástrojů s plnohodnotným textovým či grafickým uživatelským
rozhraním stále můžeme najít poměrně velké množství vývojářů využívajících
klasický řádkový debugger přímo z konzole, což se dnes týká především
uživatelů GNU Debuggeru (možná typické je, že se jedná o starší
programátory s perfektní znalostí Unixu, mikroprocesorové techniky a
většinou i překladačů). Právě GNU Debuggerem a některými jeho
nadstavbami se budeme zabývat jak v následujících kapitolách, tak i
v navazujících částech tohoto článku.

08_turbo_2

Obrázek 8: Další snímek debuggeru integrovaného do Turbo Pascalu.
Zobrazení obsahů pracovních registrů a okno se sledovanými proměnnými
(povšimněte si, že přeložený kód je určen pro 16bitový reálný režim
mikroprocesoru).

5. GNU Debugger

Pro operační systém Linux existuje větší množství debuggerů, včetně
komerčních nástrojů Affinic a TotalView, ovšem nejpoužívanějším
debuggerem je stále GNU Debugger, jehož první verze vznikla již
v roce 1986, takže se po 29 letech kontinuálního vývoje jedná o velmi
vyzrálý produkt. GNU Debugger byl v průběhu svého vývoje portován
jak na mnoho operačních systémů, tak i na nepřeberné množství procesorových a
mikroprocesorových architektur, z nichž jmenujme především řadu x86,
x86_64, ARM, Motorola 68HC11, MIPS či PowerPC. Tento debugger podporuje všechny
překladače z rodiny GNU, což mj. znamená, že dokáže zobrazit a
pracovat se zdrojovými kódy v jazycích Ada, C, C++, Go, Objective-C, D,
Fortran, Modula-2, Pascal a Java (ovšem jen v případě překladu Javy do
nativního kódu). Na základě jazyka, v němž je laděný program napsán, se
upravují i zprávy GNU Debuggeru, takže se například používá správný
formát hexadecimálních čísel, struktur záznamů atd. Taktéž assemblery používané
na Linuxu GNU Debugger přímo podporují (jedná se jak o
as, tak i o NASM).

09_gdb_1

Obrázek 9: Na tomto screenshotu můžeme vidět průběh typického
„sezení“ programátora využívajícího debugger gdb
při ladění jednoduché aplikace naprogramované v céčku. Nejprve je program
přelože s vygenerováním ladicích informací (-g) a
následné je spuštěn debugger.

Ladicí nástroj GNU Debugger, který je taktéž někdy po své spustitelné
(binární) části pojmenováván gdb, primárně používá ke
komunikaci s uživatelem příkazový řádek, alternativně lze použít i již
zmíněný protokol pro nepřímé ovládání debuggeru a v případě potřeby je
možné k laděné aplikaci přidat relativně krátký „stub“
sloužící pro přímé ladění takové aplikace (touto nepochybně zajímavou
problematikou se však dnes nebudeme zabývat). Většina často používaných příkazů
má i svoji zkrácenou podobu (bt=backtrace, c=continue,
f=frame) a navíc je možné používat klávesu [Tab] pro automatické
doplnění celého jména příkazu. Pokud je správně nastavený terminál, bude
fungovat i historie příkazového řádku, a to stejným způsobem, jaký známe ze
shellu. Alternativně je možné využít gdbtui
s celoobrazovkovým výstupem a přiblížit se tak možnostem debuggerů
s plnohodnotným grafickým uživatelským rozhraním.

10_gdb_2

Obrázek 10: Další screenshot představující typické „sezení“
programátora využívajícího debugger gdb. Nejprve je provedeno
nastavení breakpointu příkazem b main, následně spuštění
laděného programu příkazem r a posléze krokování příkazem
n (klávesa Enter zopakuje poslední zadaný příkaz, proto je
n vlastně v tomto příkladu používán nadbytečně).

Instalace GNU Debuggeru na Fedoře je velmi jednoduchá:

yum install gdb
Loaded plugins: langpacks
fedora/21/x86_64/metalink                                   |  27 kB  00:00     
updates/21/x86_64/metalink                                  |  24 kB  00:00     
updates                                                     | 4.9 kB  00:00     
(1/2): updates/21/x86_64/group_gz                           | 230 kB  00:00     
(2/2): updates/21/x86_64/primary_db                         | 7.8 MB  00:00     
(1/2): updates/21/x86_64/updateinfo                         | 1.2 MB  00:00     
(2/2): updates/21/x86_64/pkgtags                            | 1.5 MB  00:00     
Resolving Dependencies
--> Running transaction check
---> Package gdb.x86_64 0:7.8.1-30.fc21 will be updated
---> Package gdb.x86_64 0:7.8.2-38.fc21 will be an update
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package       Arch             Version                 Repository         Size
================================================================================
Updating:
 gdb           x86_64           7.8.2-38.fc21           updates           2.7 M
 
Transaction Summary
================================================================================
Upgrade  1 Package
 
Total download size: 2.7 M
Is this ok [y/d/N]: y
Downloading packages:
updates/21/x86_64/prestodelta                             | 1.8 MB  00:00:00
gdb-7.8.2-38.fc21.x86_64.rpm                              | 2.7 MB  00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Updating   : gdb-7.8.2-38.fc21.x86_64                                      1/2
  Cleanup    : gdb-7.8.1-30.fc21.x86_64                                      2/2
  Verifying  : gdb-7.8.2-38.fc21.x86_64                                      1/2
  Verifying  : gdb-7.8.1-30.fc21.x86_64                                      2/2
 
Updated:
  gdb.x86_64 0:7.8.2-38.fc21
 
Complete!

6. Základní funkce nabízené GNU Debuggerem

V seznamu pod tímto odstavcem jsou zmíněny vybrané základní operace,
které je možné v GNU Debuggeru provádět:

  1. Ladění přeloženého programu (spuštění procesu přímo z debuggeru),
    připojení debuggeru k běžícímu procesu, analýza core dumpu.
  2. Spuštění (run), pozastavení a znovuspuštění laděného programu
    (continue). Prakticky tytéž operace je možné provádět
    s jednotlivými vlákny.
  3. Krokování programu, přičemž se vývojář může rozhodnout, jestli se mají
    volané funkce provést v jednom kroku (step over) či zda se naopak
    má přejít i dovnitř těchto funkcí (step into).
  4. Nastavení breakpointů i breakpointů s podmínkou,
    tj. breakpointů, které začnou být aktivní až ve chvíli, kdy dojde ke
    splnění zadané podmínky (vhodné například při sledování chování programových
    smyček či rekurzivních algoritmů).
  5. Nastavení takzvaných watchpointů. Jedná se o speciální případ
    breakpointů; program se zastaví ve chvíli, kdy dojde ke změně zadaného výrazu,
    v nejjednodušším případě ke změně hodnoty nějaké proměnné
    (popř. místa v paměti). Alternativně lze watchpoint nastavit pro
    detekci čtení z proměnné. To se samozřejmě týká i položek v záznamech
    (record) či prvků polí.
  6. Podpora tracepointů. Zjednodušeně řečeno je možné říci, že
    tracepointy slouží pro zjištění stavu programu v nějakém specifikovaném
    bodu, ovšem bez (po)zastavení programu. Samotné pozastavení programu totiž může
    v některých případech způsobit jeho chybnou činnost či naopak zastínit
    některé chyby vyplývající ze špatně implementované synchronizace vláken či při
    přístupu ke sdíleným prostředkům.
  7. Výpis obsahu zásobníkových rámců (backtrace) a tím pádem i zjištění
    historie volaných funkcí (včetně informací o předaných parametrech).
  8. Prohlížení obsahu paměti, k čemuž slouží výkonný příkaz print,
    jemuž je možné zadat mnohdy i velmi komplikovaný výraz, jehož výsledek se
    vypíše na standardní výstup. Lze kombinovat s krokováním.

11_gdb_3

Obrázek 11: GNU Debugger samozřejmě taktéž podporuje disassembling ze
strojového kódu popř. z bajtkódu do čitelné podoby (jazyka
symbolických instrukcí). Z tohoto screenshotu je patrné, že program byl
přeložen do strojového kódu mikroprocesorů z rodiny x86_64.

O tom, jak se ladění aplikací provádí v praxi, si řekneme
v navazující části článku.

12_gdb_4

Obrázek 12: GNU Debugger je možné použít i v prostředí Cygwinu, zde
konkrétně můžeme vidět nastavení a použití výše zmíněných watchpointů.

7. Nástroj cgdb

Vzhledem k tomu, že práce s příkazovým řádkem GNU Debuggeru
nemusí vyhovovat všem uživatelům, vznikla pro tento debugger poměrně
jednoúčelová nadstavba pojmenovaná cgdb, která taktéž pracuje
v terminálu a lze ji tedy využít v prakticky všech případech, kdy by
musel být spuštěn gdb. Ve svém základním nastavení nástroj cgdb
rozděluje okno terminálu (konzole) na dvě části. V horní části je zobrazen
zdrojový kód laděné aplikace a v části dolní pak rozhraní samotného GNU
Debuggeru
. Mezi oběma částmi je možné se s využitím několika
klávesových zkratek přepínat, přičemž je nutné poznamenat, že většinu
složitějších příkazů je možné zadávat jen v rozhraní GNU Debuggeru.
Horní část slouží zejména pro dobrou orientaci v laděném programu, pro
zobrazení nastavených breakpointů (v základním nastavení je použita
červená barva) a taktéž pro zobrazení místa, v němž se právě nachází
laděný program (v základním nastavení je tento řádek zobrazen zeleně).

13_cgdb1

Obrázek 13: Takto vypadá textové uživatelské rozhraní nástroje cgbd po
spuštění. V horním okně můžeme vidět zdrojový kód se zeleně zvýrazněným
aktivním řádkem, v dolní polovině terminálu se pak nachází rozhraní GNU
Debuggeru.

V nástroji cgbd se využívají klávesové zkratky známé především
z textových editorů Vi a Vim, ovšem i ti uživatelé, kteří tyto editory
nepoužívají (a tudíž dané zkratky neznají), nebudou ztraceni, protože se
například ve zdrojovém textu mohou pro přesun kurzoru používat i kurzorové
klávesy atd. cgdb obsahuje i vestavěnou nápovědu dostupnou po stisku
klávesy F1.

Instalace cgdb je stejně snadná jako instalace GNU
Debuggeru
:

yum install cgdb
Loaded plugins: langpacks
Resolving Dependencies
--> Running transaction check
---> Package cgdb.x86_64 0:0.6.8-1.fc21 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
=================================================================================
 Package        Arch             Version                 Repository         Size
=================================================================================
Installing:
 cgdb           x86_64           0.6.8-1.fc21            updates           152 k
 
Transaction Summary
=================================================================================
Install  1 Package
 
Total download size: 152 k
Installed size: 356 k
Is this ok [y/d/N]: y

14_cgdb2

Obrázek 14: Ladění programu v cgdb. Na řádku 21 je nastaven
breakpoint, proto je tento řádek zvýrazněn červeně. Řízení programu přešlo na
řádek číslo 23 (zvýrazněno zeleně) a v dolní části si vývojář
s využitím příkazu print vypsal obsah dvou lokálních
proměnných.

8. Obsah druhé části článku

Ve druhé části tohoto článku se budeme zabývat popisem dalších nadstaveb
vytvořených nad GNU Debuggerem. Podrobně si popíšeme zejména nástroje
DDD a Nemiver, které svým uživatelům nabízí většinu funkcionality
GNU Debuggeru, ovšem se zachováním přehlednosti a taktéž snadnosti
použití svého grafického uživatelského rozhraní (podle mého názoru se
v případě Nemiveru jedná o jeden z nejzajímavějších podpůrných
nástrojů vůbec, zejména pro začátečníky). Taktéž si popíšeme propojení mezi
GNU Debuggerem a populárními textovými editory Geany a
Atom – viz též screenshoty zobrazené pod tímto odstavcem.

15_ddd1

Obrázek 15: Takto vypadá textové uživatelské rozhraní nástroje DDD po
spuštění. V horním okně můžeme vidět zdrojový kód aplikace, v dolní polovině terminálu se pak nachází rozhraní GNU Debuggeru.

16_atom_gdb

Obrázek 16: GNU Debugger je možné po instalaci správného pluginu
používat i z populárního textového editoru Atom.

17_anjuta

Obrázek 17: Nastavení ladění v programátorském textovém editoru (a
současně i jednoduchém integrovaném vývojovém prostředí) Geany. Tento editor
při ladění volá GNU Debugger.

9. Odkazy na Internetu

  1. GDB – Dokumentacehttp://sourceware.org/gdb/current/onlinedocs/gdb/
  2. Supported Languageshttp://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages
  3. GNU Debugger (Wikipedia)https://en.wikipedia.org/wiki/GNU_Debugger
  4. The LLDB Debuggerhttp://lldb.llvm.org/
  5. Debugger (Wikipedia)https://en.wikipedia.org/wiki/Debugger
  6. 13 Linux Debuggers for C++ Reviewedhttp://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  7. Clewnhttp://clewn.sourceforge.net/
  8. Clewn installationhttp://clewn.sourceforge.net/install.html
  9. Clewn – soubory ke staženíhttp://sourceforge.net/projects/clewn/files/OldFiles/
  10. Pyclewn installation noteshttp://pyclewn.sourceforge.net/install.html