Jak wyświetlić kod skompilowany przez JIT w JVM?


84

Czy istnieje sposób, aby zobaczyć natywny kod generowany przez JIT w JVM?


Czy na pewno chcesz zobaczyć kod skompilowany przez JIT (natywny), czy tylko kod bajtowy? Pytam, ponieważ zadawanie tego pytania tutaj rodzi wątpliwości, czy naprawdę chcesz zobaczyć kod natywny ... I przepraszam, też nie znam takiego narzędzia.
gimpf

3
Chcę zobaczyć kod natywny skompilowany dokładnie w JIT. Oczywiście to nie jest coś, czego potrzebuję do wykonania pracy, raczej rodzaj eksperymentów i badań.
alsor.net

Drobne wyzwanie związane z ramkami: dynamiczny kompilator używany we współczesnych maszynach JVM nie ma tylko jednej wersji skompilowanego kodu; może rozpocząć interpretację, a następnie skompilować metodę lub tylko jej część, a następnie potencjalnie przekompilować ją wiele razy, gdy klasy są ładowane / usuwane lub zmieniają się wzorce użycia lub w oparciu o statystyki wydajności. (Myślę, że może nawet odrzucić skompilowaną wersję i powrócić do interpretacji, jeśli wydaje się to korzystne). Więc możesz nie tylko uzyskać inny kod na różnych maszynach, ani nawet dla różnych uruchomień na tej samej maszynie, ale w różnym czasie w tym samym przebiegu .
gidds

Odpowiedzi:


45

Zakładając, że używasz maszyny wirtualnej Sun Hotspot JVM (tj. Tej dostarczonej na java.com przez Oracle), możesz dodać flagę

-XX: + PrintOptoAssembly

podczas uruchamiania kodu. Spowoduje to wydrukowanie zoptymalizowanego kodu wygenerowanego przez kompilator JIT i pominie resztę.

Jeśli chcesz zobaczyć cały kod bajtowy, w tym niezoptymalizowane części, dodaj

-XX: CompileThreshold = #

kiedy uruchamiasz swój kod.

Możesz przeczytać więcej o tym poleceniu i ogólnie o funkcjonalności JIT tutaj .


Czy ta opcja jest obecna tylko w kompilacjach debugowania, czy cokolwiek? Ponieważ moja JVM („Java (TM) SE Runtime Environment (build 1.6.0_16-b01”) nie rozpoznaje go, mimo że źródła w sieci wskazują, że ta funkcja jest dostępna w Sun Java 6 i OpenJDK.
Joachim Sauer

2
Tak, potrzebne pliki binarne DEBUG. blogs.warwick.ac.uk/richardwarburton/entry/…
alsor.net

3
Czy nie powinno to być (obecnie) -XX: + PrintAssembly, przynajmniej obecnie? Przetestowane na moim komputerze i pasuje do tego, co zostało powiedziane tutaj: wikis.sun.com/display/HotSpotInternals/PrintAssembly Potrzebujesz -XX: + UnlockDiagnosticVMOptions przed tą opcją i wtyczką deasemblera.
Blaisorblade

@Blaisorblade Otrzymuję: Nieprawidłowo określona opcja maszyny wirtualnej „PrintAssembly” Błąd: nie można utworzyć wirtualnej maszyny języka Java. Błąd: wystąpił krytyczny wyjątek. Program zostanie zamknięty.
Koray Tugay

@KorayTugay Zobacz inne odpowiedzi - zaktualizowany link to wikis.oracle.com/display/HotSpotInternals/PrintAssembly , jak podano na stackoverflow.com/a/15146962/53974 lub stackoverflow.com/a/4149878/53974 . Jeśli poniższe instrukcje nie działają, zapytaj o szczegóły w odpowiednim miejscu (nie jesteś pewien, czy powinno to być inne pytanie w Twoim przypadku, odwołując się do tego).
Blaisorblade

76

Ogólne zastosowanie

Jak wyjaśniono w innych odpowiedziach, możesz uruchomić z następującymi opcjami JVM:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Filtruj według określonej metody

Możesz również filtrować według określonej metody za pomocą następującej składni:

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

Uwagi:

  • może być konieczne umieszczenie drugiego argumentu w cudzysłowie w zależności od systemu operacyjnego itp.
  • jeśli metoda zostanie wbudowana, możesz przegapić niektóre optymalizacje

Instrukcje: instalowanie wymaganych bibliotek w systemie Windows

Jeśli używasz systemu Windows, na tej stronie znajdują się instrukcje, jak zbudować i zainstalować hsdis-amd64.dlloraz hsdis-i386.dllco jest wymagane, aby działał. Kopiujemy poniżej i rozszerzamy zawartość tej strony * w celach informacyjnych:


Skąd wziąć gotowe pliki binarne

Możesz pobrać gotowe pliki binarne dla systemu Windows z projektu fcml

Jak budować hsdis-amd64.dlli hsdis-i386.dllna Windowsie

Ta wersja poradnika została przygotowana na Windows 8.1 64-bitowym przy użyciu 64-bitowego Cygwin i produkując hsdis-amd64.dll

  1. Zainstaluj Cygwin . Na Select Packagesekranie dodaj następujące pakiety (rozwijając Develkategorię, a następnie klikając raz Skipetykietę obok nazwy każdego pakietu):

    • make
    • mingw64-x86_64-gcc-core(potrzebne tylko do hsdis-amd64.dll)
    • mingw64-i686-gcc-core(potrzebne tylko do hsdis-i386.dll)
    • diffutils(w Utilskategorii)
  2. Uruchom terminal Cygwin. Można to zrobić za pomocą ikony Pulpit lub Menu Start utworzonej przez instalator i utworzy katalog domowy Cygwin ( C:\cygwin\home\<username>\lub C:\cygwin64\home\<username>\domyślnie).

  3. Pobierz najnowszy pakiet źródłowy GNU binutils i wypakuj jego zawartość do katalogu domowego Cygwin. W chwili pisania tego tekstu najnowszy pakiet to binutils-2.25.tar.bz2. Powinno to spowodować utworzenie katalogu o nazwie binutils-2.25(lub jakiejkolwiek najnowszej wersji) w katalogu domowym Cygwin.
  4. Pobierz źródło OpenJDK, przechodząc do repozytorium JDK 8 Updates , wybierając znacznik odpowiadający zainstalowanej wersji JRE i klikając bz2. Rozpakuj katalog hsdis (znajdujący się w src\share\tools) do katalogu domowego Cygwin.
  5. W terminalu Cygwin wprowadź cd ~/hsdis.
  6. Aby zbudować hsdis-amd64.dll, wejdź

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

    Aby zbudować hsdis-i386.dll, wejdź

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

    W obu przypadkach zastąp 2.25pobraną wersją binutils. OS=Linuxjest konieczne, ponieważ chociaż Cygwin jest środowiskiem podobnym do Linuksa, plik makefile hsdis nie rozpoznaje go jako takiego.

  7. Kompilacja zakończy się niepowodzeniem z komunikatami ./chew: No such file or directoryi gcc: command not found. Edytuj <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefilew edytorze tekstu, takim jak Wordpad lub Notepad ++, aby zmienić SUBDIRS = doc po(wiersz 342, jeśli używasz binutils 2.25) na SUBDIRS = po. Ponownie uruchom poprzednie polecenie.

Bibliotekę DLL można teraz zainstalować, kopiując ją z hsdis\build\Linux-amd64lub hsdis\build\Linux-i586do środowiska JRE bin\serverlub bin\clientkatalogu. Możesz znaleźć wszystkie takie katalogi w swoim systemie, wyszukując java.dll.

Dodatkowa wskazówka: jeśli wolisz składnię Intel ASM od AT&T, określ -XX:PrintAssemblyOptions=intelobok innych używanych opcji PrintAssembly.

* Licencja strony to Creative Commons


1
Gotowe pliki binarne dla innych platform - kenai.com/projects/base-hsdis/downloads
Ashwin Jayaprakash

@AshwinJayaprakash Gdzie mam umieścić te pliki w systemie Mac OS?
Koray Tugay

@KorayTugay umieścił je/usr/lib/
Jean-François Savard

Zaktualizowałem odpowiedź, kopiując z najnowszej wersji strony, do której prowadzą linki, ale to podkreśla powód, dla którego generalnie łączymy się z zasobami zewnętrznymi, zamiast kopiować je dosłownie.
Aleksandr Dubinsky

@AleksandrDubinsky Dzięki za aktualizację. Skopiowałem to celowo: jeśli ta strona zostanie wyłączona, moja odpowiedź nadal będzie niezależna ...
asylias

29

Potrzebujesz wtyczki hsdis do użycia PrintAssembly. Wygodnym wyborem jest wtyczka hsdis oparta na bibliotece FCML.

Można go skompilować dla systemów typu UNIX, aw systemie Windows można użyć gotowych bibliotek dostępnych w sekcji pobierania FCML na Sourceforge:

Aby zainstalować w systemie Windows:

  • Wyodrębnij bibliotekę dll (można ją znaleźć w plikach hsdis-1.1.2-win32-i386.zip i hsdis-1.1.2-win32-amd64.zip).
  • Skopiuj bibliotekę dll do dowolnego miejsca java.dll(użyj wyszukiwania systemu Windows). W moim systemie znalazłem go w dwóch lokalizacjach:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Aby zainstalować w systemie Linux:

  • Pobierz kod źródłowy, wypakuj go
  • 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
  • W moim systemie JDK jest włączony /usr/lib/jvm/java-8-oracle

Jak to uruchomić:

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

Dodatkowe parametry konfiguracyjne:

kod Wydrukuj kod maszynowy przed mnemonikiem.
intel Użyj składni Intela.
gas Używa składni asemblera AT&T (kompatybilna z GNU assembler).
dec Drukuje IMM i przemieszczenie jako wartości dziesiętne.
mpad = XX Wypełnienie mnemonicznej części instrukcji.
cpad = XX Wypełnienie kodu maszynowego.
seg Pokazuje domyślne rejestry segmentów.
zera W przypadku literałów szesnastkowych wyświetla zera wiodące.

Składnia Intela jest domyślna w przypadku Windows, podczas gdy AT&T jest domyślna dla GNU / Linux.

Aby uzyskać więcej informacji, zobacz Podręcznik referencyjny biblioteki FCML


Dzięki za naprawienie lib. Działa również świetnie na Linuksie. Usuwam moje stare komentarze, aby zachować porządek.
Aleksandr Dubinsky

W Linuksie, po zainstalowaniu libhsdis.so i utworzeniu miękkiego linku do hsdis-amd64.so, uruchamiam polecenie java, prmpts nie może znaleźć hsdis-amd64.so. Ponownie uruchamiam i uruchamiam java, wszystko w porządku. Jak uniknąć ponownego uruchomienia, aby miękkie łącze działało natychmiast? Wyloguj?
gfan

2
Tylko mały dodatek: w niektórych dystrybucjach Linuksa możesz po prostu zainstalować pakiet, na przykład w Ubuntu: apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ). Samodzielne budowanie może nie być konieczne.
David Georg Reichelt


5

Wierzę, że WinDbg byłby pomocny, jeśli uruchamiasz go na komputerze z systemem Windows. Właśnie wypuściłem jeden słoik.

  • Następnie dołączyłem do procesu java za pośrednictwem Windbg
  • Sprawdzone wątki poleceniem ~ ; Było 11 wątków, 0 był głównym wątkiem roboczym
  • Przełączono na wątek 0 - ~ 0 s
  • Przejrzałem niezarządzany stos wywołań przez kb :

    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

Wyróżnione wiersze to bezpośrednio uruchamiany kod JIT w JVM.

  • Następnie możemy poszukać adresu metody:
    java + 0x2f68 to 00402f68

  • W WinDBG:
    Kliknij Widok -> Demontaż.
    Kliknij Edytuj -> Idź do adresu.
    Umieść 00402f68 tam
    i otrzymaj

    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
    ... i tak dalej

Aby uzyskać dodatkowe informacje, tutaj znajduje się przykład śledzenia wstecznego kodu w formacie JIT ze zrzutów pamięci za pomocą eksploratora procesów i WinDbg.


4

Innym sposobem zobaczenia kodu maszynowego i niektórych danych dotyczących wydajności jest użycie CodeAnalyst lub OProfile firmy AMD, które mają wtyczkę Java do wizualizacji wykonywania kodu Java jako kodu maszynowego.


0

Wydrukuj zespół swoich punktów aktywnych za pomocą profilerów perfasm JMH ( LinuxPerfAsmProfilerlub WinPerfAsmProfiler). JMH wymaga hsdisbiblioteki, ponieważ opiera się na PrintAssembly.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.