7 tipů pro práci v Debug režimu v IntelliJ Idea

Před lety jsem psal článek o debugování aplikací v Javě. K mému překvapení jsem se totiž setkal s programátory, kteří v Javě k debugování kódu používali System.out(...) místo debug režimu. Po letech otvírám stejné téma z jiného pohledu. Jak efektivně používáme nástroje debug režimu, které nám naše IDE nabízí? Je totiž plno situací, kdy se můžeme s debugováním dost nadřít, nebo ... vědět co a jak v daném okamžiku nastavit tak, abychom se k výslednému pochopení problému dostali zkratkou. IntelliJ Idea těchto nástrojů nabízí celou řadu a v tomto článku bych rád rozebral několik z nich, které sám rád používám.

1. HotSwapping

Výměnu tříd v JVM za běhu v debug režimu zná snad každý. Několikrát jsem však musel radit lidem, kterým to "nefungovalo" nebo nevěděli hned jak v IntelliJ Idee, jak na to. HotSwaping i IntelliJ Idee zafunguje tehdy, jste li v debug režimu a zkompilujete třídu (Ctrl + Shift + F9) nebo celý projekt (Ctrl + F9). Následně by měl vyběhnout popup, který vás informuje o počtu vyměněných tříd, popř. o chybě. Chyba nastává v případě, že v třídě provedete strukturální změnu. Viz. následující obrázek:

hotswap

Pokud se vám zdá, že vám IntelliJ Idea nereaguje - prověřte jednak, že jste skutečně v debug režimu a koukněte také do nastavení debuggeru (Ctrl + Alt + S, Debug, HotSwap), že máte vybranou možnost Always nebo Ask u položky Reload classes after compilation - viz. předchozí obrázek.

2. Frames - živý stacktrace

Často využívám okna s názvem frames, kterému říkám živý stacktrace. Pokud v něm kliknete na některý z nižších řádků, automaticky se vám v editoru otevře odpovídající zdrojový soubor a řádek, který řádku v stacku odpovídá. Spolu s tím se vám i obnoví obsah okna variables, ve kterém se vám zobrazí lokální proměnné platné pro dané místo. Pokud na tento řádek kliknete pravým tlačíkem myši a v popupu vyberete volbu Drop frames, vrátíte se jakoby v kódu zpět do daného místa a můžete z něj znovu tracovat až k vašemu breakpointu. Drop frames totiž vyresetuje všechny lokální proměnné framů mezi aktuálním breakpointem a vámi zvoleným framem a umožní vám vrátit execution point zpět. Změny globálních proměnných (tj. těch, které neleží na stacku jsou nevratné, to musíte vzít v úvahu). Tuto vlastnost jsem se snažil znázornit na následujícím obrázku:

stacktrace

Tyto funkce jsou skvělé v případě, že informace v místě breakpointu nedostačují k odhalení příčin hledaného problému. Příčiny často leží totiž ve výš v místech, kde je konkrétní kód volán. Tam je tedy obvykle velmi zajímavé analyzovat lokální proměnné a případně použít Expression Evaluation (Alt + F8, Ctrl + Alt + F8 nebo Alt + levé tlačítko myši pro okamžité zobrazení) pro vyhodnocení nějakého konkrétního Java výrazu v lokálním kontextu.

3. Stepping

Pokud používáte dynamické AOP, narazíte při debugování na ten problém, že se při procházení kódem často dostáváte do infrastrukturních tříd Cglib / JavaAssist / Spring Frameworku a dalších, což vám pochopení vlastního problému spíš ztěžuje. Brouzdání kódem infrastrukturních knihoven je dobré v případě, že chcete pochopit, jak to uvnitř nich funguje. Pokud vás to ale nezajímá nebo to už víte, neustálé procházení jejich kódu zdržuje a zabírá mentální kapacitu, kterou byste mohli lépe využít na rozlousknutí daného problému. Obvykle totiž chyba neleží v těchto knihovnách, protože ty jsou důkladně otestovány, ale spíš ve vašem kódu.

IntelliJ Idea pro tento případ nabízí skvělou možnost při kráčení v debug režimu vynechávat konkrétní třídy, nebo třídy odpovídající zvolenému patternu (Ctrl + Alt + S, Debug, Stepping) - viz. následující obrázek:

stepping

Při šikovném nastavení tedy při vstupu do metody, která je reálně implementovaná na úrovni dynamické proxy vstoupíte přímo do vlastního kódu - tedy nějaké advice apod. Debugování dynamických tříd se tím krásně zjednoduší.

4. Vlastní renderery dat

Tuto funkcionalitu opět využijete jen ve speciálních případech. V našem případě se jednalo o nevypovídající náhled proměnných, které byly představovány Spring dynamickými proxy. V takovém případě se vám totiž zobrazí fieldy dané proxy, ve kterých jsou teprve ukryty vaše objekty, které jsou pro vás zajímavé. Schválně si porovnejte přehlednost následujících dvou zobrazení proměnné:

data-viewer

Myšlenka je veskrze jednoduchá - Idea vám nabízí možnost sami si definovat výrazy (Ctrl + Alt + S, Debug, Data Type Renderers), které budou použity při zobrazení proměnných konkrétního typu v okně variables. Můžete si sami přesně určit, co se má vypisovat na úrovni daného uzlu i co se má zobrazit po rozkliknutí nebo dokonce definovat, jestli uzel vůbec bude možné rozkliknout. Na první pohled se to zdá možná jako dost práce, ale v případě často používaných typů s netriviální strukturou se pár minut práce brzy vrátí. Navíc definice expression s code completion, kterou IntelliJ Idea nabízí všude, kde se pracuje s kódem, je směšně jednoduché.

data-viewer-settings

Více si o sestavování vlastních rendererů můžete přečíst přímo na blogu JetBrains.

5. Značkování

Čas od času potřebujete zjistit, zda instance, se kterou se pracuje je skutečně ta stejná instance, se kterou pracujete někde jinde. V podstatě byste si potřebovali udělat něco jako assertSame, jenže ve chvíli, kdy stojíte na breakpointu se nemůžete jednoznačně dobrat té druhé instance, nebo si nejste jisti, že byste dostali tu správnou. První nápad je tedy obvykle ten, poznamenat si někam id reference dané instance a na druhém místě jej porovnat - idčkem myslím kombinaci nazevTridy@cislo, které Idea v zobrazení proměnných uvádí. Tím se ale okrádáte o cenné flow - navíc se časem rychle ztratíte v řadě id poznamenaných na papíře. A přitom stačí jen Idee říct, aby vám konkrétní instanci při výpisech pojmenovala a zvýraznila:

marking

6. Code coverage

Tento bod s debugováním souvisí pouze okrajově. Pomáhá nám psát testy, které dokáží odhalit co nejvíce chyb - respektive pomáhá nám najít oblasti, které nemáme testy pokryté a které tedy představují potenciální hrozbu.

Integrace pokrytí kódu testy v rámci IDE je obrovská výhoda - okamžitě přímo v editoru ihned po ručním spuštění testu vidíte, které řádky produkčního kódu vám test pokrývá. Obvykle se toto tato funkcionalita nastavuje jako součást Ant nebo Maven buildu a často není úplně triviální. Navíc výsledky potom máte v externím reportu, takže musíte přepínat mezi ním a IDE, nehledě na prodlevu, která je nezanedbatelná. Idea nám naštěstí nabízí nativní integraci code coverage - stačí zvolit Edit configurations v toolbaru a pro konkrétní Debug konfiguraci vybrat záložku Code coverage:

code-coverage-edit-config


code-coverage

Pokud byste toto nastavení chtěli propagovat do všech nově vytvořených běhových konfigurací, nakonfigurujte toto nastavení po kliknutí na tlačítko Edit defaults v levé dolní části dialogu.

Detaily o novinkách v IntelliJ Idea 9 při práci s Run/Debug konfiguracemi si přečtěte přímo na blogu JetBrains.

Výsledky code coverage po spuštění testů jsou velmi přehledné a můžete z nich rychle usoudit, kde nutně potřebujete rozšířit svou sadu automatizovaných testů:

code-coverage-view

7. Speciální typy breakpointů

Používání breakpointů je standard - stačí kliknout na nalevo od požadovaného řádku zastavení. Mezi běžné znalosti, řekl bych, patří i nastavení podmínky pro zastavení na daném breakpointu (Ctrl + Shift + F8, Condition) - častokrát byste se totiž uklikali, než byste zastavili na breakpointu v požadované situaci a dynamická podmínka vám takto práci výrazně usnadní.

Co už ale programátoři míň používají, jsou breakpointy definované na polích třídy. Ty jsou velmi efektivní v případě, že potřebujete zjistit, kdo a kdy modifikuje hodnotu fieldu ať už přes setter metodu nebo prostým přiřazením v rámci jiné metody v třídě.

breakpoint-field

Velmi zajímavé jsou také breakpointy nad exception. V případě, že se vám zalogovaný stacktrace ztrácí v tuně dalších logovaných informací a nemůžete se dobrat příčiny konkrétní exception, jsou tyto breakpointy nejlepším způsobem jak se velmi rychle dobrat výsledku. Osobně mám vždy nastavený breakpoint na NullPointerException, protože to je vyjímka, která by se ve vyladěném kódu nikdy neměla vyskytnout. Při takto nastaveném breakpointu jsem okamžitě Ideou přesměrován na problémové místo.

breakpoint-exception

Odkazy

Při tvorbě tohoto článku jsem, kromě svých zkušeností, čerpal detaily z těchto článků:

Stejně jako v minulém článku vám budu vděčný za další postřehy v komentářích. Příjemných překvapení v Idee je totiž tolik, že bych se velmi divil, kdybych věděl o všech :-) .