En introduktion til Java Debug Interface (JDI)

1. Oversigt

Vi kan undre os over, hvordan bredt anerkendte IDE'er som IntelliJ IDEA og Eclipse implementerer fejlfindingsfunktioner. Disse værktøjer er stærkt afhængige af Java Platform Debugger Architecture (JPDA).

I denne indledende artikel diskuterer vi Java Debug Interface API (JDI), der er tilgængelig under JPDA.

På samme tid, vi skriver et brugerdefineret fejlfindingsprogram trin for trin, der gør os fortrolige med praktiske JDI-grænseflader.

2. Introduktion til JPDA

Java Platform Debugger Architecture (JPDA) er et sæt veldesignede grænseflader og protokoller, der bruges til at debugge Java.

Det giver tre specielt designede grænseflader til implementering af brugerdefinerede debuggere til et udviklingsmiljø i desktop-systemer.

Til at begynde med hjælper Java Virtual Machine Tool Interface (JVMTI) os med at interagere og kontrollere udførelsen af ​​applikationer, der kører i JVM.

Derefter er der Java Debug Wire Protocol (JDWP), der definerer den protokol, der bruges mellem applikationen under test (debuggee) og debugger.

Endelig bruges Java Debug Interface (JDI) til at implementere debugger-applikationen.

3. Hvad er? JDI?

Java Debug Interface API er et sæt grænseflader, der leveres af Java, til implementering af frontend af debugger. JDI er det højeste lag i JPDA.

En debugger bygget med JDI kan debugge applikationer, der kører i enhver JVM, der understøtter JPDA. På samme tid kan vi tilslutte det til ethvert lag af fejlretning.

Det giver mulighed for at få adgang til VM og dens tilstand sammen med adgang til variabler i debuggee. Samtidig giver det mulighed for at indstille breakpoints, step, watchpoints og håndtere tråde.

4. Opsætning

Vi kræver to separate programmer - en debuggee og en debugger - for at forstå JDI's implementeringer.

Først skriver vi et eksempelprogram som debuggee.

Lad os oprette en JDIEeksempelDebuggee klasse med et par Snor variabler og println udsagn:

offentlig klasse JDIExampleDebuggee {public static void main (String [] args) {String jpda = "Java Platform Debugger Architecture"; System.out.println ("Hej alle, velkommen til" + jpda); // tilføj et brudpunkt her String jdi = "Java Debug Interface"; // tilføj et brudpunkt her og træder også ind her String text = "I dag dykker vi ned i" + jdi; System.out.println (tekst); }}

Derefter skriver vi et fejlfindingsprogram.

Lad os oprette en JDIEeksempelDebugger klasse med egenskaber til at holde fejlretningsprogrammet (debugClass) og linjenumre for breakpoints (breakPointLines):

offentlig klasse JDIExampleDebugger {privat klasse debugClass; private int [] breakPointLines; // getters og setters}

4.1. LaunchingConnector

Først kræver en fejlfinding et stik for at oprette en forbindelse til den virtuelle målmaskine (VM).

Derefter bliver vi nødt til at indstille debuggee som connectorens vigtigste argument. Endelig skal stikket starte VM til fejlfinding.

For at gøre dette leverer JDI en Bootstrap klasse, der giver en instans af LaunchingConnector. Det LaunchingConnector giver et kort over standardargumenterne, hvor vi kan indstille vigtigste argument.

Lad os derfor tilføje connectAndLaunchVM metode til JDIDebuggerExample klasse:

offentlig VirtualMachine connectAndLaunchVM () kaster undtagelse {LaunchingConnector launchingConnector = Bootstrap.virtualMachineManager (). defaultConnector (); Kortargumenter = launchingConnector.defaultArguments (); argumenter.get ("main"). setValue (debugClass.getName ()); return launchingConnector.launch (argumenter); }

Nu tilføjer vi vigtigste metode til JDIDebuggerExample klasse for at debugge JDIEeksempelDebuggee:

offentlig statisk ugyldig hoved (String [] args) kaster Undtagelse {JDIExampleDebugger debuggerInstance = ny JDIExampleDebugger (); debuggerInstance.setDebugClass (JDIExampleDebuggee.class); int [] breakPoints = {6, 9}; debuggerInstance.setBreakPointLines (breakPoints); VirtualMachine vm = null; prøv {vm = debuggerInstance.connectAndLaunchVM (); vm.resume (); } fange (Undtagelse e) {e.printStackTrace (); }}

Lad os sammensætte begge vores klasser, JDIEeksempelDebuggee (debuggee) og JDIEeksempelDebugger (debugger):

javac -g -cp "/Bibliotek/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar" com / baeldung / jdi / *. java

Lad os diskutere javac kommando, der bruges her, i detaljer.

Det -g option genererer alle fejlretningsoplysninger uden hvilken, kan vi se AbsentInformationException.

Og -cp vil tilføje tools.jar i klassestien for at kompilere klasserne.

Alle JDI-biblioteker er tilgængelige under tools.jar af JDK. Sørg derfor for at tilføje tools.jar i klassestien ved både kompilering og udførelse.

Det er det, nu er vi klar til at udføre vores brugerdefinerede debugger JDIEeksempelDebugger:

java -cp "/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar :." JDIEeksempelDebugger

Bemærk “:.” med tools.jar. Dette tilføjes tools.jar til klassestien for den aktuelle kørselstid (brug “;.” på windows).

4.2. Bootstrap og ClassPrepareRequest

At udføre fejlfindingsprogrammet her giver ingen resultater, da vi ikke har forberedt klassen til fejlretning og indstillet breakpoints.

Det VirtualMachine klasse har eventRequestManager metode til at oprette forskellige anmodninger som ClassPrepareRequest, BreakpointRequestog StepEventRequest.

Så lad os tilføje enableClassPrepareRequest metode til JDIEeksempelDebugger klasse.

Dette filtrerer JDIEeksempelDebuggee klasse og muliggør ClassPrepareRequest:

public void enableClassPrepareRequest (VirtualMachine vm) {ClassPrepareRequest classPrepareRequest = vm.eventRequestManager (). createClassPrepareRequest (); classPrepareRequest.addClassFilter (debugClass.getName ()); classPrepareRequest.enable (); }

4.3. ClassPrepareEvent og BreakpointRequest

Enkelt gang, ClassPrepareRequest til JDIEeksempelDebuggee klasse er aktiveret, vil begivenhedskøen på den virtuelle computer begynde at have forekomster af ClassPrepareEvent.

Ved brug af ClassPrepareEvent, vi kan få placeringen til at indstille et brudpunkt og skaber et BreakPointRequest.

Lad os tilføje setBreakPoints metode til JDIEeksempelDebugger klasse:

public void setBreakPoints (VirtualMachine vm, ClassPrepareEvent event) kaster AbsentInformationException {ClassType classType = (ClassType) event.referenceType (); for (int lineNumber: breakPointLines) {Location location = classType.locationsOfLine (lineNumber) .get (0); BreakpointRequest bpReq = vm.eventRequestManager (). CreateBreakpointRequest (placering); bpReq.enable (); }}

4.4. BreakPointEvent og StackFrame

Indtil videre har vi forberedt klassen til debugging og indstillet breakpoints. Nu skal vi fange BreakPointEvent og vise variablerne.

JDI leverer StackFrame klasse for at få listen over alle synlige variabler i debuggee.

Lad os derfor tilføje displayVariables metode til JDIEeksempelDebugger klasse:

public void displayVariables (LocatableEvent event) kaster IncompatibleThreadStateException, AbsentInformationException {StackFrame stackFrame = event.thread (). frame (0); hvis (stackFrame.location (). toString (). indeholder (debugClass.getName ())) {Map visibleVariables = stackFrame .getValues ​​(stackFrame.visibleVariables ()); System.out.println ("Variabler ved" + stackFrame.location (). ToString () + ">"); for (Map.Entry entry: visibleVariables.entrySet ()) {System.out.println (entry.getKey (). name () + "=" + entry.getValue ()); }}}

5. Fejlfindingsmål

På dette trin er alt, hvad vi har brug for, at opdatere vigtigste metode til JDIEeksempelDebugger for at starte fejlretning.

Derfor bruger vi de allerede diskuterede metoder som enableClassPrepareRequest, setBreakPointsog displayVariables:

prøv {vm = debuggerInstance.connectAndLaunchVM (); debuggerInstance.enableClassPrepareRequest (vm); EventSet eventSet = null; while ((eventSet = vm.eventQueue (). remove ())! = null) {for (Event event: eventSet) {if (event instance of ClassPrepareEvent) {debuggerInstance.setBreakPoints (vm, (ClassPrepareEvent) event); } if (event instance of BreakpointEvent) {debuggerInstance.displayVariables ((BreakpointEvent) event); } vm.resume (); }}} fangst (VMDisconnectedException e) {System.out.println ("Virtuel maskine er afbrudt."); } fange (Undtagelse e) {e.printStackTrace (); }

Lad os først sammensætte JDIDebuggerExample klasse igen med det allerede diskuterede javac kommando.

Og sidst udfører vi debuggerprogrammet sammen med alle ændringer for at se output:

Variabler ved com.baeldung.jdi.JDIExampleDebuggee: 6> args = instans af java.lang.String [0] (id = 93) Variabler ved com.baeldung.jdi.JDIExampleDebuggee: 9> jpda = "Java Platform Debugger Architecture" args = forekomst af java.lang.String [0] (id = 93) Den virtuelle maskine er afbrudt.

Hurra! Vi har fejlagtigt fejlagtigt JDIEeksempelDebuggee klasse. På samme tid har vi vist værdierne for variablerne ved brudpunktsplaceringerne (linje nummer 6 og 9).

Derfor er vores brugerdefinerede debugger klar.

5.1. StepRequest

Fejlfinding kræver også at gå gennem koden og kontrollere status for variablerne i efterfølgende trin. Derfor opretter vi en trinanmodning ved brudpunktet.

Mens du opretter forekomsten af StepRequest, vi skal give trinets størrelse og dybde. Vi definerer STEP_LINE og TRÆD OVER henholdsvis.

Lad os skrive en metode til at aktivere trinanmodningen.

For at gøre det nemmere begynder vi at træde til det sidste brydepunkt (linje nummer 9):

public void enableStepRequest (VirtualMachine vm, BreakpointEvent event) {// aktiver trinforespørgsel for sidste pausepunkt, hvis (event.location (). toString (). indeholder (debugClass.getName () + ":" + breakPointLines [breakPointLines.length- 1])) {StepRequest stepRequest = vm.eventRequestManager () .createStepRequest (event.thread (), StepRequest.STEP_LINE, StepRequest.STEP_OVER); stepRequest.enable (); }}

Nu kan vi opdatere vigtigste metode til JDIEeksempelDebugger, for at aktivere trinanmodningen, når den er en BreakPointEvent:

if (event instanceof BreakpointEvent) {debuggerInstance.enableStepRequest (vm, (BreakpointEvent) event); }

5.2. StepEvent

Svarende til BreakPointEvent, kan vi også vise variablerne på StepEvent.

Lad os opdatere vigtigste metode i overensstemmelse hermed:

if (event instanceof StepEvent) {debuggerInstance.displayVariables ((StepEvent) event); }

Endelig udfører vi fejlfindingsprogrammet for at se variablenes tilstand, mens vi går gennem koden:

Variabler ved com.baeldung.jdi.JDIEeksempelDebuggee: 6> args = forekomst af java.lang.String [0] (id = 93) Variabler ved com.baeldung.jdi.JDIExampleDebuggee: 9> args = forekomst af java.lang.String [0] (id = 93) jpda = "Java Platform Debugger Architecture" Variabler ved com.baeldung.jdi.JDIExampleDebuggee: 10> args = forekomst af java.lang.String [0] (id = 93) jpda = "Java Platform Fejlfindingsarkitektur "jdi =" Java Debug Interface "Variabler på com.baeldung.jdi.JDIExampleDebuggee: 11> args = forekomst af java.lang.String [0] (id = 93) jpda =" Java Platform Debugger Architecture "jdi =" Java Debug Interface "text =" I dag dykker vi ned i Java Debug Interface "Variabler på com.baeldung.jdi.JDIExampleDebuggee: 12> args = instans af java.lang.String [0] (id = 93) jpda =" Java Platform Debugger Architecture "jdi =" Java Debug Interface "text =" I dag dykker vi ned i Java Debug Interface "Den virtuelle maskine er afbrudt.

Hvis vi sammenligner output, vil vi indse, at debugger trådte ind fra linje nummer 9 og viser variablerne i alle efterfølgende trin.

6. Læs udførelsesoutput

Det bemærker vi måske println erklæringer fra JDIEeksempelDebuggee klasse har ikke været en del af debugger-output.

I henhold til JDI-dokumentationen, hvis vi starter VM gennem LaunchingConnector, dens output og fejlstrømme skal læses af Behandle objekt.

Lad os derfor føje det til langt om længe klausul om vores vigtigste metode:

endelig {InputStreamReader reader = ny InputStreamReader (vm.process (). getInputStream ()); OutputStreamWriter-forfatter = ny OutputStreamWriter (System.out); char [] buf = ny char [512]; reader.read (buf); writer.write (buf); writer.flush (); }

Nu vil udførelse af fejlfindingsprogrammet også tilføje println udsagn fra JDIEeksempelDebuggee klasse til debugging output:

Hej alle, velkommen til Java Platform Debugger Architecture I dag dykker vi ned i Java Debug Interface

7. Konklusion

I denne artikel har vi udforsket Java Debug Interface (JDI) API, der er tilgængelig under Java Platform Debugger Architecture (JPDA).

Undervejs har vi bygget en brugerdefineret debugger ved hjælp af de praktiske grænseflader, der leveres af JDI. På samme tid har vi også tilføjet afbrydelsesfunktion til at trænge ind.

Da dette kun var en introduktion til JDI, anbefales det at se på implementeringerne af andre grænseflader, der er tilgængelige under JDI API.

Som normalt er alle kodeimplementeringer tilgængelige på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found