Vizualizare generală

Depanarea unei aplicații Java la distanță poate fi utilă în mai multe cazuri.

În acest tutorial, vom descoperi cum să facem acest lucru folosind instrumentele JDK.

Aplicația

Să începem prin a scrie o aplicație. O vom rula pe o locație la distanță și o vom depana local prin intermediul acestui articol:

public class OurApplication { private static String staticString = "Static String"; private String instanceString; public static void main(String args) { for (int i = 0; i < 1_000_000_000; i++) { OurApplication app = new OurApplication(i); System.out.println(app.instanceString); } } public OurApplication(int index) { this.instanceString = buildInstanceString(index); } public String buildInstanceString(int number) { return number + ". Instance String !"; }}

JDWP: The Java Debug Wire Protocol

Protocolul Java Debug Wire Protocol este un protocol utilizat în Java pentru comunicarea dintre un depanator și un depanator. Depanatorul este aplicația care este depanată, în timp ce depanatorul este o aplicație sau un proces care se conectează la aplicația care este depanată.

Ambele aplicații fie rulează pe aceeași mașină, fie pe mașini diferite. Ne vom concentra pe acestea din urmă.

3.1. Opțiunile lui JDWP

Vom folosi JDWP în argumentele liniei de comandă JVM la lansarea aplicației de depanare.

Invocarea acesteia necesită o listă de opțiuni:

  • transport este singura opțiune complet necesară. Ea definește mecanismul de transport care trebuie utilizat. dt_shmem funcționează numai pe Windows și dacă ambele procese rulează pe aceeași mașină, în timp ce dt_socket este compatibil cu toate platformele și permite ca procesele să ruleze pe mașini diferite
  • serverul nu este o opțiune obligatorie. Acest indicator, atunci când este activat, definește modul în care se atașează la depanator. Acesta fie expune procesul prin intermediul adresei definite în opțiunea address. În caz contrar, JDWP expune unul implicit
  • suspend definește dacă JVM trebuie să suspende și să aștepte sau nu atașarea unui depanator
  • address este opțiunea care conține adresa, în general un port, expusă de depanator. De asemenea, poate reprezenta o adresă tradusă ca un șir de caractere (cum ar fi javadebug dacă folosim server=y fără a furniza o adresă pe Windows)

3.2. Comanda de lansare

Să începem prin a lansa aplicația la distanță. Vom furniza toate opțiunile enumerate anterior:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 OurApplication

Până la Java 5, argumentul JVM runjdwwp trebuia utilizat împreună cu cealaltă opțiune debug:

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

Acest mod de utilizare a JDWP este încă suportat, dar va fi abandonat în versiunile viitoare. Vom prefera utilizarea celei mai noi notații atunci când este posibil.

3.3. De la Java 9

În sfârșit, una dintre opțiunile lui JDWP s-a schimbat odată cu lansarea versiunii 9 a Java. Este o schimbare destul de minoră, deoarece se referă doar la o singură opțiune, dar va face o diferență dacă încercăm să depanăm o aplicație de la distanță.

Această schimbare are un impact asupra modului în care se comportă adresa pentru aplicațiile de la distanță. Vechea notație address=8000 se aplică doar pentru localhost. Pentru a obține vechiul comportament, vom folosi un asterisc cu două puncte ca prefix pentru adresă (de exemplu, address=*:8000).

Potrivit documentației, acest lucru nu este sigur și se recomandă să se specifice adresa IP a depanatorului ori de câte ori este posibil:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:8000

JDB: The Java Debugger

JDB, depanatorul Java, este un instrument inclus în JDK conceput pentru a oferi un client de depanare convenabil din linia de comandă.

Pentru a lansa JDB, vom folosi modul attach. Acest mod atașează JDB la o JVM în execuție. Există și alte moduri de funcționare, cum ar fi listen sau run, dar sunt mai ales convenabile atunci când se depanează o aplicație care rulează local:

jdb -attach 127.0.0.1:8000> Initializing jdb ...

4.1. Puncte de întrerupere

Să continuăm prin plasarea unor puncte de întrerupere în aplicația prezentată în secțiunea 1.

Punem un punct de întrerupere în constructor:

> stop in OurApplication.<init>

Punem un altul în metoda statică main, folosind numele complet calificat al clasei String:

> stop in OurApplication.main(java.lang.String)

În fine, îl punem pe ultimul în metoda de instanță buildInstanceString:

> stop in OurApplication.buildInstanceString(int)

Ar trebui să observăm acum că aplicația server se oprește și că în consola noastră de depanare sunt tipărite următoarele:

> Breakpoint hit: "thread=main", OurApplication.<init>(), line=11 bci=0

Să adăugăm acum un punct de întrerupere pe o anumită linie, cea în care se află variabila app.instanceString este tipărită:

> stop at OurApplication:7

Noi observăm că at este folosit după stop în loc de in atunci când punctul de întrerupere este definit pe o linie specifică.

4.2. Navigare și evaluare

Acum că am stabilit punctele de întrerupere, să folosim cont pentru a continua execuția firului nostru până când ajungem la punctul de întrerupere de pe linia 7.

Ar trebui să vedem următoarele tipărite în consolă:

> Breakpoint hit: "thread=main", OurApplication.main(), line=7 bci=17

Ca un memento, ne-am oprit pe linia care conține următoarea bucată de cod:

System.out.println(app.instanceString);

Oprirea pe această linie ar fi putut fi făcută și prin oprirea pe metoda main și tastând step de două ori. step execută linia curentă de cod și oprește debuggerul direct pe următoarea linie.

Acum că ne-am oprit, debuggerul evaluează staticString-ul nostru, instanceString-ul aplicației, variabila locală i și, în final, aruncă o privire la modul de evaluare a altor expresii.

Să imprimăm staticField în consolă:

> eval OurApplication.staticStringOurApplication.staticString = "Static String"

Am pus în mod explicit numele clasei înaintea câmpului static.

Să tipărim acum câmpul de instanță al aplicației:

> eval app.instanceStringapp.instanceString = "68741. Instance String !"

În continuare, să vedem variabila i:

> print ii = 68741

În comparație cu celelalte variabile, variabilele locale nu necesită specificarea unei clase sau a unei instanțe. De asemenea, putem vedea că print are exact același comportament ca și eval: ambele evaluează o expresie sau o variabilă.

Vom evalua o nouă instanță a aplicației OurApplication pentru care am trecut un număr întreg ca parametru al constructorului:

> print new OurApplication(10).instanceStringnew OurApplication(10).instanceString = "10. Instance String !"

Acum că am evaluat toate variabilele de care aveam nevoie, vom dori să ștergem punctele de întrerupere setate anterior și să lăsăm firul de execuție să-și continue procesarea. Pentru a realiza acest lucru, vom folosi comanda clear urmată de identificatorul punctului de întrerupere.

Identificatorul este exact același cu cel folosit anterior cu comanda stop:

> clear OurApplication:7Removed: breakpoint OurApplication:7

Pentru a verifica dacă punctul de întrerupere a fost corect eliminat, vom folosi clear fără argumente. Aceasta va afișa lista punctelor de întrerupere existente, fără cel pe care tocmai l-am șters:

> clearBreakpoints set: breakpoint OurApplication.<init> breakpoint OurApplication.buildInstanceString(int) breakpoint OurApplication.main(java.lang.String)

Concluzie

I:n acest articol rapid, am descoperit cum să folosim JDWP împreună cu JDB, ambele instrumente JDK.

.