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:
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:
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:
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é:
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é.
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:
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:
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ů:
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ě.
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.
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
.















S těmi breakpointy na fieldy pozor – nemusí (netestoval jsem všechna JDK) zareagovat při nastavení pomocí reflection nebo deserializace
No vsechno tohle sem automaticky v Eclipse pouzival az n to prejmenovani intanci a stapping. To by mohlo byt pomerne uzitecne (zkusim to najit).
No ale vedlo mne to k jine myslence. Prilis neholduju buildovani z IDE, obzvlast kdyz nad kodem mate navesene dalsi tooly, ktere ho predpripravi nez dojde k samotne kompilaci. Pro JavaME je to dost potreba, protoze jinak je vsechno desne praccne. No a pokud nema dany tool intergraci pro vase IDE dost se zapotite. No takze by to chtelo aby HotSwap sel spustit mimo IDE a pokud mozno primo do rozjeteho emulatoru JavaME. Mate s necim takovym zkusenosti? Ted sem nasel jenom maven2-hotswap-plugin, ale nevim jestli to tim vyresim (budu zkouset), ale jestli mate nekdo s necim takovym zkusenosti, dejte mi prosim vedet.
Ja jeste hodne casto pri krokovani pouzivam Shift + F7 (Smart Step Into).
Zobrazi se pop menu se vsemi volanymi metodami na radku. Vetsinou chci skocit do te posledni, ktera je volana
@benzin poslední idea (možná to umí i osmička) má na compiler navěšený Annotation processing, nevím, jestli by se to k tomu nemohlo využít. Praktické zkušenosti ale nemám – navíc pro mobily jsem zatím nikdy neprogramoval, takže tady si nedovolím žádný komentář. Pro naše účely Idea dokáže plně nahradit maven build, s tím rozdílem, že je výrazně rychlejší.
@Petr Prochazka: díky – tohle jsem postřehl při brouzdání novinkami v Idee, ale neobjevil jsem nikde zkratku. Díky vyzkouším.
Diky za clanek, ne vsechno jsem znal (pouzivam sice Eclipse, ale bylo prijemne to v nem objevit
.
super clanok…uz mi nebude vrtat hlavou na co tam to “Mark Object” vlastne je
, ten breakpoint na NullPointerException si tiez vyskusam
Super clanek. O tom hotswapu jsem nevedel, jak na to.
Nyni resim ale tento problem. Pouzivam debugger na vzdalene debugovani web aplikace. Vse funguje ok, tak ale zmenim strukturu classy, udelam redeploy, ale dalsi hotswap uz nefunguje, jakoby si porad nekde drzela starou nezmenenou classu.. Pomuze jen restart idei
Nevite nekdo, jak na to?
Build a (re)deploy resim pres ANT, mimo ideu..
To úplně nechápu … redeploy znamená, stopnutí a nastartování app serveru? Nebo jen stopnutí kontextu, ale app server stále žije? Zkuste trošku blíž popsat, co se tam vlastně děje …
Redeploy je asi jen stopnuti kontextu, ostatni aplikace na domene jedou dal i v prubehu redeploy. Pouzivame Sun portal na Glassfish 8.1.
Pouzivame Ant org.apache.tools.ant.taskdefs.optional.sun.appserv.DeployTask, nenina tom nic specialniho.
V tom případě bych si tipnul, že po redeploy zmizí tebou původně hotswapovaná verze třídy (tříd) a načte se verze z daného WARu. Nicméně novým hotswapem by se měl dát normálně zase vyměnit obsah metod. Pokud se tak neděje, tak je to opravdu možná nějaká issue Idey.
Abych pravdu řekl, pro vývoj používám kompilační výsledek Idey (při synchronizaci s Mavenem je při standardních buildech identicky s buildem spuštěným přes Maven) a restart Tomcata. Tohle je asi nejrychlejší turnaround, ke kterému jsem dospěl. Idea má totiž narozdíl od Antu/Mavenu přehled o tom, které třídy a jejich závislosti je nutné překompilovat a udělá jen ty. Ant/Maven musím slepě překompilovat všechno, protože ty vazby neznají.
Tím pádem používám úplně jiný use-case a díky tomu jsem asi na tebou popisovaný problém nenarazil.
Ja bych spis rekl, ze se ta nova verze nenacte a to je ten problem. Idea si porad mysli, ze byla provedena zmena struktury.. Jde o to vymazat nejak tu z idey tu starou strukturu a prekvapive rebuild aplikace nepomuze, ale restart idei ano. Zrejme se pouziva nejaka cache..
Diky za tvuj cas.
Ještě mě napadá, jestli by to nemohlo souviset s classloader leakem. Co když se při restartu kontextu neuvolnil ten starý classloader a stále je v paměti? Bohužel netuším jak funguje při JVM hotswapu nalezení třídy u které se má vyměňovat její obsah. Třeba je někde pořád živá reference na tu původní “už hotswapovanou” classu v neuvolněném classloaderu? Jenže tady už úplně vařím z vody … tohle by chtělo nějaký test.
Odpojení debug režimu a nové nastartování taky nepomůže?
Co restart serveru … ten asi pomůže stejně jako restart Idey že?
Ahoj Honzo, díky za tipy, určitě se mi hodí ten drop frames, věděl jsem, že se v tom bodě dá dívat na proměnné, ale ne že se dá odtud znovu debugovat.
Taky díky za Smart Step Into (z komentáře od Petra Prochazky).