Come vedere il codice compilato JIT in JVM?


86

C'è un modo per vedere il codice nativo prodotto da JIT in una JVM?


Sei sicuro di voler vedere il codice (nativo) compilato con JIT o solo il codice byte? Lo chiedo perché fare questa domanda qui porta ad alcuni dubbi se vuoi davvero vedere il codice nativo ... E, scusa, nemmeno io conosco uno strumento del genere.
gimpf

3
Voglio vedere il codice nativo compilato con JIT esatto. Ovviamente non è qualcosa di cui ho bisogno per portare a termine il lavoro, piuttosto una specie di esperimenti e indagini.
alsor.net

Minor frame challenge: un compilatore dinamico come quello usato nelle moderne JVM non ha solo una versione del codice compilato; può iniziare a interpretare, quindi compilare un metodo o solo una parte di esso, quindi potenzialmente ricompilarlo più volte man mano che le classi vengono caricate / scaricate o i modelli di utilizzo cambiano o in base alle statistiche delle prestazioni. (Penso che possa persino scartare la versione compilata e tornare all'interpretazione se ciò sembra vantaggioso.) Quindi potresti non solo ottenere codice diverso su macchine diverse, né anche per esecuzioni diverse sulla stessa macchina, ma in momenti diversi nella stessa esecuzione .
gidds

Risposte:


45

Supponendo che tu stia utilizzando Sun Hotspot JVM (ovvero quello fornito su java.com da Oracle), puoi aggiungere il flag

-XX: + PrintOptoAssembly

durante l'esecuzione del codice. Questo stamperà il codice ottimizzato generato dal compilatore JIT e tralascia il resto.

Se vuoi vedere l'intero bytecode, comprese le parti non ottimizzate, aggiungi

-XX: CompileThreshold = #

quando esegui il codice.

Puoi leggere di più su questo comando e sulla funzionalità di JIT in generale qui .


Questa opzione è presente solo nelle build di debug o altro? Perché il mio JVM ("Java (TM) SE Runtime Environment (build 1.6.0_16-b01") non lo riconosce anche se la fonte sul Web indica che questa funzione è disponibile in Sun Java 6 e OpenJDK.
Joachim Sauer

2
Sì, sono necessari i binari DEBUG. blogs.warwick.ac.uk/richardwarburton/entry/…
alsor.net

3
Non dovrebbe essere (al giorno d'oggi) -XX: + PrintAssembly, almeno oggi? Testato sulla mia macchina e corrisponde a quanto detto qui: wikis.sun.com/display/HotSpotInternals/PrintAssembly Hai bisogno di -XX: + UnlockDiagnosticVMOptions prima di questa opzione e di un plug-in disassemblatore.
Blaisorblade

@ Blaisorblade Sto ottenendo: Errore "PrintAssembly" opzione VM specificata in modo errato: Impossibile creare la Java Virtual Machine. Errore: si è verificata un'eccezione irreversibile. Il programma uscirà.
Koray Tugay

@KorayTugay Vedi altre risposte: il link aggiornato è wikis.oracle.com/display/HotSpotInternals/PrintAssembly , come indicato in stackoverflow.com/a/15146962/53974 o stackoverflow.com/a/4149878/53974 . Se le seguenti istruzioni non funzionano, chiedi con i dettagli in un posto appropriato (non sono sicuro se dovrebbe un'altra domanda per il tuo caso, facendo riferimento a questo).
Blaisorblade

77

Utilizzo generale

Come spiegato da altre risposte, puoi eseguire con le seguenti opzioni JVM:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Filtra in base a un metodo specifico

Puoi anche filtrare in base a un metodo specifico con la seguente sintassi:

-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod

Appunti:

  • potresti dover inserire il secondo argomento tra virgolette a seconda del sistema operativo, ecc.
  • se il metodo viene integrato, potresti perdere alcune ottimizzazioni

Procedura: installare le librerie richieste su Windows

Se utilizzi Windows, questa pagina contiene le istruzioni su come compilare e installare hsdis-amd64.dlle hsdis-i386.dllquali sono necessarie per farlo funzionare. Copiamo di seguito ed estendiamo il contenuto di quella pagina * per riferimento:


Dove ottenere i binari predefiniti

È possibile scaricare i binari predefiniti per Windows dal progetto fcml

Come costruire hsdis-amd64.dlle hsdis-i386.dllsu Windows

Questa versione della guida è stata preparata su Windows 8.1 64bit utilizzando Cygwin a 64 bit e producendo hsdis-amd64.dll

  1. Installa Cygwin . Nella Select Packagesschermata, aggiungi i seguenti pacchetti (espandendo la Develcategoria, quindi facendo clic una volta Skipsull'etichetta accanto al nome di ciascun pacchetto):

    • make
    • mingw64-x86_64-gcc-core(necessario solo per hsdis-amd64.dll)
    • mingw64-i686-gcc-core(necessario solo per hsdis-i386.dll)
    • diffutils(nella Utilscategoria)
  2. Esegui il terminale Cygwin. Questo può essere fatto utilizzando l'icona del desktop o del menu Start creata dall'installatore e creerà la directory home di Cygwin ( C:\cygwin\home\<username>\o C:\cygwin64\home\<username>\per impostazione predefinita).

  3. Scarica l'ultimo pacchetto sorgente binutils GNU ed estrai il suo contenuto nella directory home di Cygwin. Al momento della scrittura, l'ultimo pacchetto è binutils-2.25.tar.bz2. Ciò dovrebbe risultare in una directory denominata binutils-2.25(o qualunque sia l'ultima versione) nella directory home di Cygwin.
  4. Scarica l'origine di OpenJDK accedendo al repository degli aggiornamenti di JDK 8 , selezionando il tag corrispondente alla versione JRE installata e facendo clic su bz2. Estrai la directory hsdis (che si trova in src\share\tools) nella tua home directory di Cygwin.
  5. Nel terminale Cygwin, inserisci cd ~/hsdis.
  6. Per costruire hsdis-amd64.dll, entra

    make OS=Linux MINGW=x86_64-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    Per costruire hsdis-i386.dll, entra

    make OS=Linux MINGW=i686-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    In entrambi i casi, sostituisci 2.25con la versione binutils che hai scaricato. OS=Linuxè necessario perché, sebbene Cygwin sia un ambiente simile a Linux, il makefile hsdis non riesce a riconoscerlo come tale.

  7. La build fallirà con messaggi ./chew: No such file or directorye gcc: command not found. Modifica <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefilein un editor di testo come Wordpad o Notepad ++ per cambiare SUBDIRS = doc po(riga 342, se usi binutils 2.25) in SUBDIRS = po. Riesegui il comando precedente.

La DLL può ora essere installata copiandola da hsdis\build\Linux-amd64o hsdis\build\Linux-i586nella directory bin\servero JRE bin\client. Puoi trovare tutte queste directory sul tuo sistema cercando java.dll.

-XX:PrintAssemblyOptions=intelSuggerimento bonus: se preferisci la sintassi Intel ASM ad AT&T, specifica insieme a qualsiasi altra opzione PrintAssembly che utilizzi.

* la licenza della pagina è Creative Commons



@AshwinJayaprakash Dove dovrei mettere questi file in Mac OS?
Koray Tugay

@KorayTugay li ha inseriti/usr/lib/
Jean-François Savard

Ho aggiornato la risposta copiando dall'ultima versione della pagina collegata, ma questo evidenzia il motivo per cui generalmente ci colleghiamo a risorse esterne piuttosto che copiarle alla lettera.
Aleksandr Dubinsky

@AleksandrDubinsky Grazie per l'aggiornamento. L'ho copiato apposta: se quel sito viene disattivato la mia risposta sarà comunque autosufficiente ...
assylias

29

Hai bisogno di un plugin hsdis da usare PrintAssembly. Una scelta conveniente è il plugin hsdis basato sulla libreria FCML.

Può essere compilato per sistemi tipo UNIX e su Windows è possibile utilizzare librerie predefinite disponibili nella sezione download FCML su Sourceforge:

Per installare in Windows:

  • Estrai la dll (può essere trovata in hsdis-1.1.2-win32-i386.zip e hsdis-1.1.2-win32-amd64.zip).
  • Copia la dll ovunque esista java.dll(usa la ricerca di Windows). Sul mio sistema, l'ho trovato in due posizioni:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Per installare in Linux:

  • Scarica il codice sorgente, estrailo
  • cd <source code dir>
  • ./configure && make && sudo make install
  • cd example/hsdis && make && sudo make install
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/lib/amd64/hsdis-amd64.so
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/jre/lib/amd64/hsdis-amd64.so
  • Sul mio sistema, il JDK è in /usr/lib/jvm/java-8-oracle

Come eseguirlo:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly 
-XX:+LogCompilation -XX:PrintAssemblyOptions=intel,mpad=10,cpad=10,code 
-jar fcml-test.jar

Parametri di configurazione aggiuntivi:

codice Stampa il codice macchina prima dello mnemonico.
intel Usa la sintassi Intel.
gas Usa la sintassi dell'assembler AT&T (compatibile con l'assembler GNU).
dec Stampa IMM e displacement come valori decimali.
mpad = XX Padding per la parte mnemonica dell'istruzione.
cpad = XX Imbottitura per il codice macchina.
seg Mostra i registri di segmento predefiniti.
zeros Mostra gli zeri iniziali in caso di letterali HEX.

La sintassi Intel è quella predefinita nel caso di Windows, mentre quella AT&T è quella predefinita per GNU / Linux.

Per maggiori dettagli vedere il Manuale di riferimento della libreria FCML


Grazie per aver corretto il file lib. Funziona benissimo anche su Linux. Sto cancellando i miei vecchi commenti per mantenere il disordine.
Aleksandr Dubinsky

Su Linux, dopo aver installato libhsdis.so e creato un collegamento software a hsdis-amd64.so, eseguo il comando java, prmpts non riesce a trovare hsdis-amd64.so. Riavvio e poi rieseguo java, va bene. Come evitare il riavvio per far funzionare immediatamente il collegamento software? disconnettersi?
gfan

2
Solo una piccola aggiunta: su alcune distribuzioni Linux, puoi semplicemente installare un pacchetto, ad esempio in Ubuntu: apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ). Costruirlo da soli potrebbe non essere necessario.
David Georg Reichelt


5

Credo che WinDbg sarebbe utile se lo stai eseguendo su una macchina Windows. Ho appena eseguito un barattolo.

  • Quindi mi sono collegato al processo java tramite Windbg
  • Thread esaminati da ~ comando; C'erano 11 thread, 0 thread era il thread di lavoro principale
  • Passato a 0 thread - ~ 0 s
  • Guardato attraverso lo stack di chiamate non gestito da kb c'era:

    0008fba8 7c90e9c0 ntdll! KiFastSystemCallRet
    0008fbac 7c8025cb ntdll! ZwWaitForSingleObject + 0xc
    0008fc10 7c802532 kernel32! WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 kernel32! WaitForSingleObject + 0x12
    0008fc40 00402f68 java + 0x3a13
    0008fee4 004087b8 java + 0x2f68
    0008ffc0 7c816fd7 java + 0x87b8

    0008fff0 00000000 kernel32! BaseProcessStart + 0x23

Le righe evidenziate eseguono direttamente codice JIT su JVM.

  • Quindi possiamo cercare l'indirizzo del metodo:
    java + 0x2f68 è 00402f68

  • In WinDBG: fare
    clic su Visualizza -> Smontaggio.
    Fare clic su Modifica -> Vai a indirizzo.
    Metti 00402f68
    e ottenuto

    00402f68 55 push ebp
    00402f69 8bec mov ebp, esp
    00402f6b 81ec80020000 sub esp, 280h
    00402f71 53 push ebx
    00402f72 56 push esi
    00402f73 57 push edi
    ... e così via

Per ulteriori informazioni, ecco l' esempio su come tracciare il codice JIT dai dump della memoria utilizzando Process Explorer e WinDbg.


4

Un altro modo per vedere il codice macchina e alcuni dati sulle prestazioni è utilizzare CodeAnalyst o OProfile di AMD, che hanno un plugin Java per visualizzare l'esecuzione del codice Java come codice macchina.


1

Stampa l'assemblaggio dei tuoi hotspot con i profilatori perfasm di JMH ( LinuxPerfAsmProfilero WinPerfAsmProfiler). JMH richiede la hsdislibreria poiché si basa su PrintAssembly.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.