Hlavní navigace

Pohled pod kapotu JVM – dokončení popisu rozhraní JDI: krokování

18. 6. 2013
Doba čtení: 30 minut

Sdílet

V dnešní části seriálu o programovacím jazyku Java i o virtuálním stroji Javy se již naposledy budeme zabývat popisem rozhraní JDI (Java Debugger Interface). Ukážeme si využití tohoto rozhraní při krokování, popř. trasování aplikace spuštěné ve sledovaném virtuálním stroji Javy.

Obsah

1. Pohled pod kapotu JVM – dokončení popisu rozhraní JDI: krokování

2. Povolení a nastavení krokování v metodě registerStepEvent()

3. Získání reference hlavního vlákna aplikace běžící v monitorovaném virtuálním stroji

4. Smyčka pro zpracování událostí

5. Výpis všech relevantních informací při každém kroku

6. Úplný zdrojový kód demonstračního příkladu JDIStepRequestDemo

7. Zdrojový kód testovací třídy Test8

8. Informace vypisované demonstračním příkladem po jeho spuštění: varianta se STEP_LINE

9. Informace vypisované demonstračním příkladem po jeho spuštění: varianta se STEP_MIN

10. Repositář se zdrojovými kódy demonstračních příkladů

11. Odkazy na Internetu

1. Pohled pod kapotu JVM – dokončení popisu rozhraní JDI: krokování

V předchozích dvanácti dílech seriálu o programovacím jazyku Java i o virtuálním stroji Javy jsme si popsali většinu funkcí nabízených rozhraním JDI (Java Debugger Interface). Dnes se popisem tohoto rozhraní budeme zabývat naposledy, protože další část tohoto seriálu bude věnována nástrojům určeným pro vytváření či modifikaci bajtkódu JVM. Minule jsme si na dvojici demonstračních příkladů ukázali způsob využití breakpointů, které je možné nastavit na konkrétní programové řádky. Ovšem naprostá většina debuggerů si nevystačí s běžnými breakpointy – debuggery totiž svým uživatelům nabízí i možnost aplikací krokovat, a to buď po jednotlivých programových řádcích, či po jednotlivých příkazech/výrazech. Možnost krokování programů je většinou spojena s nastavením breakpointů, ovšem to je pouze uživatelský pohled na celou problematiku – při implementaci debuggeru lze práci s breakpointy a s krokováním programů od sebe oddělit.

K oddělení práce s breakpointy a s krokováním programů došlo i při návrhu rozhraní JDI. Připomeňme si, že breakpoint se registruje s využitím metody:

EventRequestManager.createBreakpointRequest(com.sun.jdi.Location)

To mj. znamená, že breakpoint je zaregistrován na určitou lokaci v kódu sledované aplikace a současně se jedná o breakpoint platný pro všechna vlákna. Naproti tomu se podpora pro krokování nastavuje pro jedno vybrané vlákno a přitom se neuvádí žádná lokace:

EventRequestManager.createStepRequest(ThreadReference thread, int size, int depth)

Význam argumentů size a depth si vysvětlíme v navazující kapitole.

Pokud je krokování povoleno, je při každém kroku vytvořena událost typu StepEvent, která je zapsána do fronty událostí (event queue), stejně jako je tomu i u všech dalších typů událostí.

2. Povolení a nastavení krokování v metodě registerStepEvent()

Způsob krokování sledovanou (monitorovanou) aplikací si, podobně jako tomu bylo i v několika předchozích dílech tohoto seriálu, vysvětlíme na demonstračním debuggeru, který se bude jmenovat JDIStepRequestDemo. Tento debugger se bude ke sledovanému virtuálnímu stroji připojovat přes socket (port je stále nastaven na 6502) a po připojení se v uživatelské metodě acquireAndUseEventRequestManager() provede registrace dvou typů událostí – nám již známé události VMDeathEvent (na kterou musí debugger reagovat odpojením od sledované JVM) a taktéž události typu StepEvent. Zdrojový kód uživatelské metody acquireAndUseEventRequestManager() se tedy v mnoha ohledech podobá stejnojmenné metodě použité i v předchozích demonstračních příkladech:

    /**
     * Ukazka pouziti EventRequestManageru.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     */
    private static void acquireAndUseEventRequestManager(VirtualMachine virtualMachine) {
        EventRequestManager eventRequestManager = virtualMachine.eventRequestManager();
 
        // tuto udalost potrebujeme zpracovavat pro korektni ukonceni debuggeru
        VMDeathRequest vmDeathRequest = registerVMDeathEvent(eventRequestManager);
 
        // zadost o generovani udalosti vytvorenych pri pristupu na breakpoint
        StepRequest stepRequest = registerStepEvent(virtualMachine, eventRequestManager);
        printInfoAboutStepEvent(stepRequest);
 
        // klasicka smycka pro zpracovani udalosti
        eventLoop(virtualMachine, vmDeathRequest);
    }

Vlastní registrace události typu StepEvent se provádí v další uživatelské metodě pojmenované registerStepEvent(). Tato metoda tvoří v určitém ohledu nejdůležitější část celého demonstračního debuggeru, takže si ji vysvětleme podrobněji. Při registraci podpory pro krokování se kromě reference na konkrétní vlákno ještě zadávají dva argumenty. První celočíselný argument může nabývat hodnoty StepRequest.STEP_LINE v případě, že se má krokování provádět po jednotlivých programových řádcích, popř. StepRequest.STEP_MIN pokud vyžadujeme krokování po co nejmenších celcích (například části výrazu – zde již záleží na vlastnostech konkrétní JVM i JITu, jak přesné krokování bude). Ve druhém celočíselném argumentu lze předat konstantu StepRequest.STEP_INTO, StepRequest.STEP_OVER či StepRequest.STEP_OUT v závislosti na tom, zda se má přeskočit volání metody jako jeden krok, zda se má provést krokování i volaných metod či zda se naopak má krokovat jen volající metoda. Navíc je možné omezit krokování pouze na určitou třídu přes metodu StepEvent.addClassFilter():

    /**
     * Registrace udalosti typu StepEvent.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @param eventRequestManager
     *            objekt zajistujici praci s registraci udalosti
     * @return objekt typu StepRequest
     */
    private static StepRequest registerStepEvent(VirtualMachine virtualMachine, EventRequestManager eventRequestManager) {
        // ziskat hlavni vlakno aplikace bezici ve sledovanem virtualnim stroji
        ThreadReference mainThread = getMainThread(virtualMachine);
        if (mainThread == null) {
            return null;
        }
 
        StepRequest stepRequest = eventRequestManager.createStepRequest(mainThread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
        // krokovane vlakno se ma zastavit po kazdem kroku
        stepRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        // chceme krokovat pouze testovaci tridou
        stepRequest.addClassFilter(TEST_CLASS_NAME);
        stepRequest.enable();
        return stepRequest;
    }

3. Získání reference hlavního vlákna aplikace běžící v monitorovaném virtuálním stroji

V uživatelské metodě registerStepEvent() popsané v předchozí kapitole se při volání EventRequestManager.createS­tepRequest() musela v prvním argumentu předat reference na vlákno, pro nějž se krokování povoluje (reference na vlákno je představována instancí třídy ThreadReference). V našem případě budeme chtít krokovat „hlavní“ vlákno celé aplikace, které nese název „main“. Jeden z nejjednodušších způsobů, jakým je možné referenci na toto vlákno získat, je implementován v uživatelské metodě nazvané příhodně getMainThread(). V této metodě se nejdříve přes volání:

List<ThreadReference> VirtualMachine.allThreads()

získají všechna vlákna existující v monitorovaném virtuálním stroji Javy a posléze se najde to vlákno, které má nastaveno jméno na „main“. Pokud by takové vlákno neexistovalo (což je u běžných typů aplikací nepravděpodobné), vrátí se namísto reference null:

    /**
     * Ziskani hlavniho vlakna aplikace bezici v monitorovanem virtualnim stroji.
     *
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @return
     */
    private static ThreadReference getMainThread(VirtualMachine virtualMachine) {
        List<ThreadReference> threads = virtualMachine.allThreads();
 
        // projit vsemi vlakny bezicimi ve sledovanem virtualnim stroji
        for (ThreadReference thread : threads) {
            // najit hlavni vlakno
            if (MAIN_THREAD_NAME.equals(thread.name())) {
                return thread;
            }
        }
 
        // hlavni vlakno nebylo nalezeno, podivne
        return null;
    }

4. Smyčka pro zpracování událostí

Smyčku událostí implementovanou v uživatelské metodě eventLoop() si nemusíme podrobněji popisovat, neboť ji již známe z mnoha předchozích demonstračních příkladů:

    /**
     * Klasicka smycka pro postupne zpracovani udalosti.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @param vmDeathRequest
     *            objekt pro rizeni udalosti typu VMDeathEvent
     */
    private static void eventLoop(VirtualMachine virtualMachine, VMDeathRequest vmDeathRequest) {
        EventQueue eventQueue = virtualMachine.eventQueue();
 
        // precist a zpracovat udalosti
        while (processEvents(eventQueue, vmDeathRequest)) {
            // jojo tady skutecne nic neni :)
        }
    }

Ke změně však došlo v další uživatelské metodě nazvané processEvents(), v níž se jednotlivé události postupně čtou z fronty událostí (event queue) a následně se zpracovávají. V dnešním demonstračním příkladu budeme reagovat na událost typu VMDeathEvent (to již známe) a taktéž na událost typu StepEvent. Za povšimnutí stojí i příkaz eventSet.resume(), který znovuspustí vlákno/vlákna sledované aplikace:

    /**
     * Precteni a zpracovani udalosti.
     * 
     * @param eventQueue
     *            fronta udalosti
     * @param vmDeathRequest
     *            objekt pro rizeni udalosti typu VMDeathEvent
     * 
     * @return true pokud se ma pokracovat ve zpracovavani udalosti false pokud
     *         se ma sledovana VM ukoncit
     */
    private static boolean processEvents(EventQueue eventQueue, VMDeathRequest vmDeathRequest) {
        EventSet eventSet;
        try {
            // precist udalosti z fronty
            eventSet = eventQueue.remove();
            int events = eventSet.size();
            System.out.println("Got " + events + " request" + (events > 1 ? "s:" : ":"));
 
            // projit vsemi udalostmi
            for (Event event : eventSet) {
                if (event instanceof VMStartEvent) {
                    System.out.println("    VMStartEvent");
                }
                else if (event instanceof VMDeathEvent) {
                    System.out.println("    VMDeathEvent");
                    // zakazat dalsi generovani udalosti
                    // (u VMDeathEvent je ve skutecnosti vzdy posledni udalost poslana) 
                    vmDeathRequest.disable();
                    // posleze se zavola shutdownVirtualMachine()
                    return false;
                }
                else if (event instanceof StepEvent) {
                    // nejake vlakno se zastavilo pro "krokovani"
                    printStepInfo((StepEvent)event);
                }
                else {
                    System.out.println("    other event");
                }
            }
            // znovu postit vsechna vlakna
            eventSet.resume();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }

5. Výpis všech relevantních informací při každém kroku

Ve smyčce událostí je při přečtení události typu StepEvent zavolána uživatelská metoda printStepInfo(), v níž dojde k výpisu základních informací o programovém řádku, v němž došlo k dalšímu kroku. Především se zjistí vlastní číslo programového řádku (pokud je k dispozici – je nutné překládat s parametrem -g), dále pak jméno třídy i jméno příslušné metody společně s informacemi o jménech a hodnotách lokálních proměnných i argumentů metody. Některé z těchto informací se zjišťují v dalších uživatelských metodách, které si však dnes již nebudeme popisovat, neboť jsme se s nimi seznámili v předchozích částech tohoto seriálu:

    /**
     * Vypis informaci o kazdem kroku.
     * 
     * @param event udalost
     */
    private static void printStepInfo(StepEvent event) {
        // ziskat objekt typu Location, ktery obsahuje informaci o tom,
        // ve kterem miste kodu se provedl krok
        Location location = event.location();
 
        // ziskat vsechny informace o pozici v pozastavenem vlaknu
        String className = getClassName(location);
        String methodName = getMethodName(location);
        String sourceName = getSourceName(location);
        String lineNumber = getLineNumber(location);
 
        // nyni jiz mame vsechny dulezite informace,
        // lze je tedy vypsat
        System.out.format("  at: %s.%s ", className, methodName);
 
        // po vypisu zakladnich informaci se jeste vypise jmeno zdrojoveho
        // souboru a cislo radku metody
        System.out.format("(%s:%s)\n", sourceName, lineNumber);
 
        // ziskat referenci na vlakno 
        ThreadReference threadRef = event.thread();
        try {
            // ziskat posledni zasobnikovy ramec vlakna
            StackFrame stackFrame = threadRef.frame(0);
            // a projit vsemi viditelnymi promennymi
            List<LocalVariable> visibleVars = stackFrame.visibleVariables();
            for (LocalVariable visibleVar : visibleVars) {
                Value val = stackFrame.getValue(visibleVar);
                System.out.println("    Local variable: " + visibleVar.name() + " = " + val);
            }
        }
        catch (IncompatibleThreadStateException e) {
            e.printStackTrace();
        }
        catch (AbsentInformationException e) {
            e.printStackTrace();
        }

    }

6. Úplný zdrojový kód demonstračního příkladu JDIStepRequestDemo

Nyní již máme dostatek informací k tomu, abychom si mohli ukázat celý zdrojový kód dnešního demonstračního debuggeru nazvaného JDIStepRequestDemo. Ten pracuje následovně: nejdříve se zaregistrují oba výše zmíněné typy zpracovávaných událostí (VMDeathEvent, StepEvent) a posléze se spustí smyčka událostí (event loop). Jakmile hlavní vlákno (vlákno se jménem „main“) dojde na další programový řádek popř. na další dále nedělitelnou část příkazu, vypíše se na standardní výstup informace o řádku (ve zdrojovém kódu), jménu třídy i jménu metody. Pod těmito základními informacemi se vypíšou i hodnoty parametrů metody a hodnoty jejích lokálních proměnných. Posléze se hlavní vlákno opět spustí:

import java.io.IOException;
import java.util.List;
import java.util.Map;
 
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Location;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.AttachingConnector;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMStartEvent;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.StepRequest;
import com.sun.jdi.request.VMDeathRequest;
 
 
 
/**
 * Pripojeni k bezicimu virtualnimu stroji Javy, ktery byl spusten s parametry:
 * java -agentlib:jdwp=transport=dt_socket,server=y,address=6502,suspend=y Trida
 *
 * Po pripojeni se zaregistruje udalost typu 'Step' a pri behu aplikace 
 * se budou postupne vypisovat trasovaci informace pro hlavni vlakno.
 *
 * @author Pavel Tisnovsky
 */
public class JDIStepRequestDemo {
 
    /**
     * Navratovy kod pouzity pri ukoncovani sledovane JVM.
     */
    private static final int EXIT_VALUE = 0;
 
    /**
     * Jmeno konektoru, ktery pro pripojeni pouziva sockety.
     */
    private static final String SOCKET_ATTACH_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
 
    /**
     * Jmeno testovane tridy.
     */
    private static final String TEST_CLASS_NAME = "Test8";
 
    /**
     * Jmeno hlavniho vlakna.
     */
    private static final String MAIN_THREAD_NAME = "main";
 
    /**
     * Vstupni metoda tohoto demonstracniho debuggeru.
     */
    public static void main(String[] args) {
        // ziskat (jedinou) instanci tridy VirtualMachineManager
        VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
 
        // ziskat vsechny konektory pouzite pro pripojeni k bezici JVM
        List<AttachingConnector> connectors = virtualMachineManager.attachingConnectors();
 
        // potrebujeme ziskat konektor pouzivajici pro pripojeni sockety
        AttachingConnector connector = getSocketAttachConnector(connectors);
 
        if (connector == null) {
            System.out.println("Socket connector is not available");
            return;
        }
 
        // jsme pripojeni ke sledovane JVM, takze lze provadet ladeni
        debugVirtualMachineUsingSocket(connector);
    }
 
    /**
     * Ziskat konektor pouzivajici pro pripojeni sockety
     * 
     * @param connectors
     *            seznam vsech dostupnych konektoru pro pripojeni
     * @return konektor pouzivajici pro pripojeni sockety
     */
    private static AttachingConnector getSocketAttachConnector(List<AttachingConnector> connectors) {
        for (AttachingConnector connector : connectors) {
            if (SOCKET_ATTACH_CONNECTOR_NAME.equals(connector.name())) {
                return connector;
            }
        }
        // nenasli jsme zadny vhodny konektor
        return null;
    }
 
    /**
     * Pripojeni k bezicimu virtualnimu stroji pres socket.
     * 
     * @param connector
     *            konektor pouzivajici pro pripojeni sockety
     * @throws InterruptedException
     */
    private static void debugVirtualMachineUsingSocket(AttachingConnector connector) {
        // nastaveni argumentu pouzivanych konektorem
        Map<String, Connector.Argument> arguments = prepareConnectorArguments(connector);
 
        try {
            // pripojeni ke vzdalenemu bezicimu virtualnimu stroji Javy
            VirtualMachine virtualMachine = connectToVirtualMachine(connector, arguments);
 
            // spustit sledovany virtualni stroj Javy
            runVirtualMachine(virtualMachine);
 
            // ukazka pouziti EventRequestManageru
            acquireAndUseEventRequestManager(virtualMachine);
 
            // ukonceni behu vzdaleneho virtualniho stroje
            shutdownVirtualMachine(virtualMachine);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        catch (IllegalConnectorArgumentsException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * Nastaveni portu na cilove JVM, jenz debugger pouzije pro navazani
     * spojeni.
     * 
     * @param connector
     *            konektor pouzity pro pripojeni
     * @return mapa obsahujici parametry konektoru
     */
    private static Map<String, Connector.Argument> prepareConnectorArguments(AttachingConnector connector) {
        Map<String, Connector.Argument> arguments = connector.defaultArguments();
        arguments.get("port").setValue("6502");
        return arguments;
    }
 
    /**
     * Pripojeni debuggeru ke sledovanemu virtualnimu stroji.
     * 
     * @param connector
     *            konektor vyuzivajici pro spojeni sockety
     * @param arguments
     *            mapa obsahujici parametry pripojeni
     * @return sledovany virtualni stroj, k nemuz je debugger vzdalene
     *         pripojen
     * 
     * @throws IOException
     *             vyvolane v pripade, ze se pripojeni k JVM nepodari
     * @throws IllegalConnectorArgumentsException
     *             vyvolane v pripade spatne zadanych parametru
     */
    private static VirtualMachine connectToVirtualMachine(
            AttachingConnector connector,
            Map<String, Connector.Argument> arguments) throws IOException,
            IllegalConnectorArgumentsException {
        System.out.println("Connecting to virtual machine");
        VirtualMachine virtualMachine = connector.attach(arguments);
        System.out.println("Connected");
        return virtualMachine;
    }
 
    /**
     * Spustit sledovany virtualni stroj.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     */
    private static void runVirtualMachine(VirtualMachine virtualMachine) {
        virtualMachine.resume();
    }
 
    /**
     * Ukonceni prace beziciho sledovaneho virtualniho stroje.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     */
    private static void shutdownVirtualMachine(VirtualMachine virtualMachine) {
        System.out.println("Calling exit");
        virtualMachine.exit(EXIT_VALUE);
    }
 
    /**
     * Ukazka pouziti EventRequestManageru.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     */
    private static void acquireAndUseEventRequestManager(VirtualMachine virtualMachine) {
        EventRequestManager eventRequestManager = virtualMachine.eventRequestManager();
 
        // tuto udalost potrebujeme zpracovavat pro korektni ukonceni debuggeru
        VMDeathRequest vmDeathRequest = registerVMDeathEvent(eventRequestManager);
 
        // zadost o generovani udalosti vytvorenych pri pristupu na breakpoint
        StepRequest stepRequest = registerStepEvent(virtualMachine, eventRequestManager);
        printInfoAboutStepEvent(stepRequest);
 
        // klasicka smycka pro zpracovani udalosti
        eventLoop(virtualMachine, vmDeathRequest);
    }
 
    /**
     * Vypis informace o registraci udalosti typu 'Step'
     * 
     * @param stepRequest
     *            instance tridy implementujici rozhrani StepRequest
     */
    private static void printInfoAboutStepEvent(StepRequest stepRequest) {
        if (stepRequest != null) {
            System.out.println("'Step event' has been registered: " + stepRequest.toString());
        }
        else {
            System.out.println("Unable to register 'Step event'");
        }
    }
 
    /**
     * Registrace udalosti typu VMDeathEvent.
     * 
     * @param eventRequestManager
     *            objekt zajistujici praci s registraci udalosti
     * @return objekt typu VMDeathRequest
     */
    private static VMDeathRequest registerVMDeathEvent(EventRequestManager eventRequestManager) {
        VMDeathRequest vmDeathRequest = eventRequestManager.createVMDeathRequest();
        // po registraci udalosti je jeste nutne tento typ udalosti povolit
        vmDeathRequest.enable();
        return vmDeathRequest;
    }
 
    /**
     * Registrace udalosti typu StepEvent.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @param eventRequestManager
     *            objekt zajistujici praci s registraci udalosti
     * @return objekt typu StepRequest
     */
    private static StepRequest registerStepEvent(VirtualMachine virtualMachine, EventRequestManager eventRequestManager) {
        // ziskat hlavni vlakno aplikace bezici ve sledovanem virtualnim stroji
        ThreadReference mainThread = getMainThread(virtualMachine);
        if (mainThread == null) {
            return null;
        }
 
        StepRequest stepRequest = eventRequestManager.createStepRequest(mainThread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
        // krokovane vlakno se ma zastavit po kazdem kroku
        stepRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        // chceme krokovat pouze testovaci tridou
        stepRequest.addClassFilter(TEST_CLASS_NAME);
        stepRequest.enable();
        return stepRequest;
    }
 
    /**
     * Ziskani hlavniho vlakna aplikace bezici v monitorovanem virtualnim stroji.
     *
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @return
     */
    private static ThreadReference getMainThread(VirtualMachine virtualMachine) {
        List<ThreadReference> threads = virtualMachine.allThreads();
 
        // projit vsemi vlakny bezicimi ve sledovanem virtualnim stroji
        for (ThreadReference thread : threads) {
            // najit hlavni vlakno
            if (MAIN_THREAD_NAME.equals(thread.name())) {
                return thread;
            }
        }
 
        // hlavni vlakno nebylo nalezeno, podivne
        return null;
    }
 
    /**
     * Klasicka smycka pro postupne zpracovani udalosti.
     * 
     * @param virtualMachine
     *            sledovany virtualni stroj, k nemuz je debugger vzdalene
     *            pripojen
     * @param vmDeathRequest
     *            objekt pro rizeni udalosti typu VMDeathEvent
     */
    private static void eventLoop(VirtualMachine virtualMachine, VMDeathRequest vmDeathRequest) {
        EventQueue eventQueue = virtualMachine.eventQueue();
 
        // precist a zpracovat udalosti
        while (processEvents(eventQueue, vmDeathRequest)) {
            // jojo tady skutecne nic neni :)
        }
    }
 
    /**
     * Precteni a zpracovani udalosti.
     * 
     * @param eventQueue
     *            fronta udalosti
     * @param vmDeathRequest
     *            objekt pro rizeni udalosti typu VMDeathEvent
     * 
     * @return true pokud se ma pokracovat ve zpracovavani udalosti false pokud
     *         se ma sledovana VM ukoncit
     */
    private static boolean processEvents(EventQueue eventQueue, VMDeathRequest vmDeathRequest) {
        EventSet eventSet;
        try {
            // precist udalosti z fronty
            eventSet = eventQueue.remove();
            int events = eventSet.size();
            System.out.println("Got " + events + " request" + (events > 1 ? "s:" : ":"));
 
            // projit vsemi udalostmi
            for (Event event : eventSet) {
                if (event instanceof VMStartEvent) {
                    System.out.println("    VMStartEvent");
                }
                else if (event instanceof VMDeathEvent) {
                    System.out.println("    VMDeathEvent");
                    // zakazat dalsi generovani udalosti
                    // (u VMDeathEvent je ve skutecnosti vzdy posledni udalost poslana) 
                    vmDeathRequest.disable();
                    // posleze se zavola shutdownVirtualMachine()
                    return false;
                }
                else if (event instanceof StepEvent) {
                    // nejake vlakno se zastavilo pro "krokovani"
                    printStepInfo((StepEvent)event);
                }
                else {
                    System.out.println("    other event");
                }
            }
            // znovu postit vsechna vlakna
            eventSet.resume();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }
 
    /**
     * Vypis informaci o kazdem kroku.
     * 
     * @param event udalost
     */
    private static void printStepInfo(StepEvent event) {
        // ziskat objekt typu Location, ktery obsahuje informaci o tom,
        // ve kterem miste kodu se provedl krok
        Location location = event.location();
 
        // ziskat vsechny informace o pozici v pozastavenem vlaknu
        String className = getClassName(location);
        String methodName = getMethodName(location);
        String sourceName = getSourceName(location);
        String lineNumber = getLineNumber(location);
 
        // nyni jiz mame vsechny dulezite informace,
        // lze je tedy vypsat
        System.out.format("  at: %s.%s ", className, methodName);
 
        // po vypisu zakladnich informaci se jeste vypise jmeno zdrojoveho
        // souboru a cislo radku metody
        System.out.format("(%s:%s)\n", sourceName, lineNumber);
 
        // ziskat referenci na vlakno 
        ThreadReference threadRef = event.thread();
        try {
            // ziskat posledni zasobnikovy ramec vlakna
            StackFrame stackFrame = threadRef.frame(0);
            // a projit vsemi viditelnymi promennymi
            List<LocalVariable> visibleVars = stackFrame.visibleVariables();
            for (LocalVariable visibleVar : visibleVars) {
                Value val = stackFrame.getValue(visibleVar);
                System.out.println("    Local variable: " + visibleVar.name() + " = " + val);
            }
        }
        catch (IncompatibleThreadStateException e) {
            e.printStackTrace();
        }
        catch (AbsentInformationException e) {
            e.printStackTrace();
        }

    }
 
    /**
     * Jmeno tridy, jejiz metoda byla zavolana.
     */
    private static String getClassName(Location location) {
        return location.method().declaringType().name();
    }
 
    /**
     * Jmeno volane metody.
     */
    private static String getMethodName(Location location) {
        return location.method().name();
    }
 
    /**
     * Ziskani informaci o jmene zdrojoveho souboru pro danou lokaci.
     */
    private static String getSourceName(Location location) {
        try {
            return location.sourceName();
        }
        catch (AbsentInformationException e) {
            return "unknown";
        }
    }
 
    /**
     * Prevod cisla radku na retezec, pokud je to mozne.
     */
    private static String getLineNumber(Location location) {
        int lineNumber = location.lineNumber();
        // u nativnich metod nelze zjistit cisla radku
        return lineNumber >= 0 ? "" + lineNumber : "<native method>";
    }
 
}

7. Zdrojový kód testovací třídy Test8

Dnešní demonstrační debugger JDIStepRequestDemo budeme testovat proti sledovanému virtuálnímu stroji Javy, v němž bude spuštěna velmi jednoduchá aplikace představovaná třídou Test8. Tuto třídu jsme ostatně využili i minule, takže si již bez dalšího popisu uvedeme její zdrojový kód. Za zmínku stojí především tělo metody run(), neboť se v něm nachází programová smyčka měnící dvojici lokálních proměnných – počitadla smyčky i a druhé lokální proměnné y:

public class Test8 {
 
    public void run(int x) {
        int y = 0;
        for (int i = 1; i < 10; i++) {
            y += x;
        }
    }
 
    public static void main(String[] args) {
        System.out.println("Press any key");
        try {
            System.in.read();
        }
        catch (java.io.IOException e) {
            // . //
        }
        new Test8().run(10);
    }
 
}

8. Informace vypisované demonstračním příkladem po jeho spuštění: varianta se STEP_LINE

Ve druhé kapitole jsme si řekli, že při požadavku na krokování určitého vlákna je možné při konstrukci objektu typu StepRequest určit, zda se má krokování provádět po jednotlivých programových řádcích či naopak po (obecně) menších krocích představujících z hlediska běhu vlákna již dále nedělitelnou entitu. V případě, že je testovací třída Test8 přeložena s použitím parametru -g a v demonstračním debuggeru použijeme v metodě registerStepEvent() konstantu StepRequest.STEP_LINE, měl by debugger při svém spuštění vypsat následující zprávy, z nichž lze například vyčíst chování programové smyčky na řádcích Test8.java:5 a Test8.java:6:

Connecting to virtual machine
Connected
'Step event' has been registered: step request instance of java.lang.Thread(name='main', id=1) (enabled)
Got 1 request:
  at: Test8.main (Test8.java:13)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:17)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.<init> (Test8.java:1)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.run (Test8.java:4)
    Local variable: x = 10
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:8)
    Local variable: x = 10
    Local variable: y = 90
Got 1 request:
  at: Test8.main (Test8.java:19)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 2 requests:
    VMDeathEvent
Calling exit

9. Informace vypisované demonstračním příkladem po jeho spuštění: varianta se STEP_MIN

V této kapitole si ukážeme, jak by mohly vypadat zprávy vypisované demonstračním debuggerem JDIStepRequestDemo tehdy, pokud je v uživatelské metodě createStepRequest() použita při konstrukci objektu typu StepRequest konstanta StepRequest.STEP_MIN. V tomto případě je totiž krokování hlavního vlákna prováděno po menších celcích, než jsou jednotlivé programové řádky. O jak velké (či možná přesněji řečeno malé) celky se jedná, je závislé na implementaci vlastního virtuálního stroje, popř. na parametrech JITu. Nicméně na testovací JVM je možné z následujícího výpisu vyčíst například zajímavé informace o již dvakrát zmiňované programové smyčce:

        for (int i = 1; i < 10; i++) {
            y += x;
        }

První řádek programové smyčky je v každé iteraci rozdělen do pěti kroků, zatímco řádek druhý (tělo smyčky) je rozdělen do kroků čtyř. Částečně to souvisí s instrukcemi bajtkódu implementujícího tuto smyčku:

root_podpora

   4:   iload_3          // radek 1 - ridici blok smycky
   5:   bipush  10       // radek 1 - ridici blok smycky
   7:   if_icmpge 20     // radek 1 - ridici blok smycky
   10:  iload_2              // radek 2 - telo smycky
   11:  iload_1              // radek 2 - telo smycky
   12:  iadd                 // radek 2 - telo smycky
   13:  istore_2             // radek 2 - telo smycky
   14:  iinc    3, 1     // radek 1 - ridici blok smycky
   17:  goto    4        // radek 1 - ridici blok smycky

Nyní již následuje slíbený výpis zpráv generovaný demonstračním příkladem:

Connecting to virtual machine
Connected
'Step event' has been registered: step request instance of java.lang.Thread(name='main', id=1) (enabled)
Got 1 request:
  at: Test8.main (Test8.java:13)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:17)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.<init> (Test8.java:1)
Got 1 request:
  at: Test8.<init> (Test8.java:1)
Got 1 request:
  at: Test8.<init> (Test8.java:1)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.main (Test8.java:18)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 1 request:
  at: Test8.run (Test8.java:4)
    Local variable: x = 10
Got 1 request:
  at: Test8.run (Test8.java:4)
    Local variable: x = 10
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 0
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 1
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 10
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 2
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 20
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 3
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 30
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 4
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 40
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 5
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 50
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 6
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 60
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 7
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 70
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 8
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:6)
    Local variable: x = 10
    Local variable: y = 80
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 9
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 10
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 10
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 10
Got 1 request:
  at: Test8.run (Test8.java:5)
    Local variable: x = 10
    Local variable: y = 90
    Local variable: i = 10
Got 1 request:
  at: Test8.run (Test8.java:8)
    Local variable: x = 10
    Local variable: y = 90
Got 1 request:
  at: Test8.main (Test8.java:19)
    Local variable: args = instance of java.lang.String[0] (id=8)
Got 2 requests:
    VMDeathEvent
Calling exit

10. Repositář se zdrojovými kódy demonstračních příkladů

Zdrojové kódy dnes popsaného demonstračního příkladu JDIStepRequestDemo, testovací třídy Test8 a pomocných skriptů jsou, jak se již v tomto seriálu stalo zvykem, uloženy do Mercurial repositáře dostupného na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/. V následující tabulce najdete odkazy na prozatím nejnovější verze těchto zdrojových kódů:

11. Odkazy na Internetu

  1. Class com.sun.jdi.Bootstrap
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­package-tree.html
  2. Interface com.sun.jdi.VirtualMachine
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­VirtualMachine.html
  3. Interface com.sun.jdi.Field
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­Field.html
  4. Interface com.sun.jdi.ReferenceType
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­ReferenceType.html
  5. Interface com.sun.jdi.TypeComponent
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­TypeComponent.html
  6. Interface com.sun.jdi.Accessible
    http://docs.oracle.com/ja­vase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/­Accessible.html
  7. Breakpoint (Wikipedia)
    http://cs.wikipedia.org/wi­ki/Breakpoint
  8. JVM Tool Interface Version 1.2 Documentation
    http://docs.oracle.com/ja­vase/7/docs/platform/jvmti/jvmti­.html
  9. JVM Tool Interface Version 1.2 Documentation – SetBreakpoint
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#SetBreakpoint
  10. JVM Tool Interface Version 1.2 Documentation – ClearBreakpoint
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#ClearBreakpoint
  11. JVM Tool Interface Version 1.2 Documentation – Breakpoint (callback funkce)
    http://docs.oracle.com/ja­vase/6/docs/platform/jvmti/jvmti­.html#Breakpoint
  12. The JVM Tool Interface (JVM TI): How VM Agents Work
    http://www.oracle.com/technet­work/articles/javase/jvm-ti-141370.html
  13. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  14. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  15. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  16. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  17. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  18. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  19. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  20. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  21. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  22. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  23. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  24. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  25. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html

Byl pro vás článek přínosný?