¿Cómo ver el código compilado JIT en JVM?

84

¿Hay alguna forma de ver el código nativo producido por el JIT en una JVM?

alsor.net
fuente
¿Está seguro de que desea ver el código compilado con JIT (nativo) o solo el código de bytes? Lo pregunto porque hacer esta pregunta aquí lleva a algunas dudas sobre si realmente quieres ver el código nativo ... Y, lo siento, tampoco conozco una herramienta así.
gimpf
3
Quiero ver un código nativo compilado exactamente con JIT. Por supuesto, no es algo que necesite para hacer un trabajo, más bien una especie de experimentos e investigación de cosas.
alsor.net
Desafío de marco menor: un compilador dinámico como se usa en las JVM modernas no tiene solo una versión de código compilado; puede comenzar interpretando, luego compilar un método o solo parte de él, luego potencialmente recompilarlo varias veces a medida que las clases se cargan / descargan o los patrones de uso cambian o se basan en estadísticas de rendimiento. (Creo que incluso puede descartar la versión compilada y volver a interpretar si eso parece beneficioso). Por lo tanto, es posible que no solo obtenga un código diferente en diferentes máquinas, ni siquiera para diferentes ejecuciones en la misma máquina, sino en diferentes momentos en la misma ejecución .
gidds

Respuestas:

45

Suponiendo que está utilizando Sun Hotspot JVM (es decir, la proporcionada en java.com por Oracle), puede agregar la bandera

-XX: + PrintOptoAssembly

al ejecutar su código. Esto imprimirá el código optimizado generado por el compilador JIT y omite el resto.

Si desea ver el código de bytes completo, incluidas las partes no optimizadas, agregue

-XX: CompileThreshold = #

cuando está ejecutando su código.

Puede leer más sobre este comando y la funcionalidad de JIT en general aquí .

Falaina
fuente
¿Esta opción solo está presente en compilaciones de depuración o algo así? Porque mi JVM ("Java (TM) SE Runtime Environment (build 1.6.0_16-b01") no lo reconoce aunque la fuente en la web indica que esta función está disponible en Sun Java 6 y OpenJDK.
Joachim Sauer
2
Sí, se necesitan binarios DEBUG. blogs.warwick.ac.uk/richardwarburton/entry/…
alsor.net
3
¿No debería ser (hoy en día) -XX: + PrintAssembly, al menos hoy en día? Probado en mi máquina, y coincide con lo que se dice aquí: wikis.sun.com/display/HotSpotInternals/PrintAssembly Necesita -XX: + UnlockDiagnosticVMOptions antes de esta opción y un complemento desensamblador.
Blaisorblade
@Blaisorblade Estoy obteniendo: Opción de VM incorrectamente especificada 'PrintAssembly' Error: No se pudo crear la máquina virtual Java. Error: se ha producido una excepción grave. Programa saldrá.
Koray Tugay
@KorayTugay Vea otras respuestas: el enlace actualizado es wikis.oracle.com/display/HotSpotInternals/PrintAssembly , como se indica en stackoverflow.com/a/15146962/53974 o stackoverflow.com/a/4149878/53974 . Si seguir las instrucciones no funciona, pregunte con los detalles en algún lugar apropiado (no estoy seguro de si debería hacer otra pregunta para su caso, haciendo referencia a esta).
Blaisorblade
76

Uso general

Como se explica en otras respuestas, puede ejecutar con las siguientes opciones de JVM:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Filtrar por un método específico

También puede filtrar por un método específico con la siguiente sintaxis:

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

Notas:

  • es posible que deba poner el segundo argumento entre comillas según el sistema operativo, etc.
  • si el método se inserta, podría perder algunas optimizaciones

Cómo: instalar las bibliotecas necesarias en Windows

Si está ejecutando Windows, esta página tiene instrucciones sobre cómo compilar e instalar hsdis-amd64.dlly hsdis-i386.dllcuáles son necesarios para que funcione. Copiamos a continuación y ampliamos el contenido de esa página * como referencia:


Dónde obtener binarios prediseñados

Puede descargar binarios precompilados para Windows desde el proyecto fcml

Cómo construir hsdis-amd64.dlly hsdis-i386.dllen Windows

Esta versión de la guía se preparó en Windows 8.1 de 64 bits utilizando Cygwin de 64 bits y produciendo hsdis-amd64.dll

  1. Instale Cygwin . En la Select Packagespantalla, agregue los siguientes paquetes (expandiendo la Develcategoría y luego haciendo clic una vez en la Skipetiqueta junto al nombre de cada paquete):

    • make
    • mingw64-x86_64-gcc-core(solo se necesita para hsdis-amd64.dll)
    • mingw64-i686-gcc-core(solo se necesita para hsdis-i386.dll)
    • diffutils(en Utilscategoría)
  2. Ejecute la Terminal Cygwin. Esto se puede hacer usando el icono del escritorio o del menú de inicio creado por el instalador, y creará su directorio de inicio de Cygwin ( C:\cygwin\home\<username>\o C:\cygwin64\home\<username>\por defecto).

  3. Descargue el último paquete fuente de GNU binutils y extraiga su contenido en su directorio de inicio de Cygwin. En el momento de escribir este artículo, el paquete más reciente es binutils-2.25.tar.bz2. Esto debería resultar en un directorio llamado binutils-2.25(o cualquiera que sea la última versión) en su directorio de inicio de Cygwin.
  4. Descargue la fuente OpenJDK yendo al repositorio de actualizaciones de JDK 8 , seleccionando la etiqueta correspondiente a su versión de JRE instalada y haciendo clic en bz2. Extraiga el directorio hsdis (que se encuentra en src\share\tools) a su directorio de inicio de Cygwin.
  5. En la Terminal Cygwin, ingrese cd ~/hsdis.
  6. Para construir hsdis-amd64.dll, ingrese

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

    Para construir hsdis-i386.dll, ingrese

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

    En cualquier caso, reemplácelo 2.25con la versión de binutils que descargó. OS=Linuxes necesario porque, aunque Cygwin es un entorno similar a Linux, el archivo MAKE hsdis no lo reconoce como tal.

  7. La compilación fallará con mensajes ./chew: No such file or directoryy gcc: command not found. Edite <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefileen un editor de texto como Wordpad o Notepad ++ para cambiar SUBDIRS = doc po(línea 342, si usa binutils 2.25) a SUBDIRS = po. Vuelva a ejecutar el comando anterior.

La DLL ahora se puede instalar copiándola desde hsdis\build\Linux-amd64o hsdis\build\Linux-i586hacia su directorio bin\servero JRE bin\client. Puede encontrar todos estos directorios en su sistema buscando java.dll.

-XX:PrintAssemblyOptions=intelConsejo adicional : si prefiere la sintaxis Intel ASM a AT&T, especifique junto con cualquier otra opción de PrintAssembly que utilice.

* la licencia de la página es Creative Commons

Assylias
fuente
1
Binarios prediseñados
Ashwin Jayaprakash
@AshwinJayaprakash ¿Dónde se supone que debo colocar estos archivos en Mac OS?
Koray Tugay
@KorayTugay los puso/usr/lib/
Jean-François Savard
Actualicé la respuesta copiando de la última versión de la página vinculada, pero esto destaca la razón por la que generalmente vinculamos recursos externos en lugar de copiarlos textualmente.
Aleksandr Dubinsky
@AleksandrDubinsky Gracias por la actualización. He copiado a propósito: si ese sitio se baja mi respuesta seguirá siendo autónomo ...
assylias
29

Necesita un complemento hsdis para usar PrintAssembly. Una opción conveniente es el complemento hsdis basado en la biblioteca FCML.

Se puede compilar para sistemas similares a UNIX y en Windows puede usar bibliotecas preconstruidas disponibles en la sección de descarga de FCML en Sourceforge:

Para instalar en Windows:

  • Extraiga el dll (se puede encontrar en hsdis-1.1.2-win32-i386.zip y hsdis-1.1.2-win32-amd64.zip).
  • Copie el dll donde sea que exista java.dll(use la búsqueda de Windows). En mi sistema, lo encontré en dos ubicaciones:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Para instalar en Linux:

  • Descarga el código fuente, extráelo
  • 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
  • En mi sistema, el JDK está en /usr/lib/jvm/java-8-oracle

Cómo ejecutarlo:

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

Parámetros de configuración adicionales:

code Imprime el código de máquina antes del mnemónico.
intel Utilice la sintaxis de Intel.
gas Usa la sintaxis del ensamblador de AT&T (compatible con ensamblador GNU).
dec Imprime IMM y desplazamiento como valores decimales.
mpad = XX Relleno para la parte nemotécnica de la instrucción.
cpad = XX Relleno para el código máquina.
seg Muestra los registros de segmento predeterminados.
ceros Muestra ceros a la izquierda en caso de literales HEX.

La sintaxis de Intel es predeterminada en el caso de Windows, mientras que la de AT&T es predeterminada para GNU / Linux.

Para obtener más detalles, consulte el Manual de referencia de la biblioteca FCML

swojtasiak
fuente
Gracias por arreglar lib. También funciona muy bien en Linux. Estoy borrando mis comentarios anteriores para mantener el desorden bajo control.
Aleksandr Dubinsky
En Linux, después de instalar libhsdis.so y hacer un enlace suave a hsdis-amd64.so, ejecuto el comando java, prmpts no puede encontrar hsdis-amd64.so. Reinicio y luego vuelvo a ejecutar Java, está bien. ¿Cómo evitar el reinicio para que el enlace suave funcione instantáneamente? ¿cerrar sesión?
gfan
2
Solo una pequeña adición: en algunas distribuciones de Linux, puede simplemente instalar un paquete, por ejemplo en Ubuntu: apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ). Puede que no sea necesario construirlo usted mismo.
David Georg Reichelt
5

Creo que WinDbg sería útil si lo está ejecutando en una máquina con Windows. Acabo de ejecutar un frasco.

  • Luego lo adjunté al proceso de Java a través de Windbg
  • Hilos examinados por ~ comando; Había 11 hilos, 0 hilo era el hilo principal del trabajador
  • Cambiado a hilo 0 - ~ 0 s
  • Miró a través de la pila de llamadas no manipulada por kb había:

    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

Las líneas resaltadas son código JIT-ed de ejecución directa en JVM.

  • Luego podemos buscar la dirección del método:
    java + 0x2f68 es 00402f68

  • En WinDBG:
    haga clic en Ver -> Desmontaje.
    Haga clic en Editar -> Ir a dirección.
    Ponga 00402f68 allí
    y obtuve

    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
    ... y así sucesivamente

Para obtener información adicional, aquí está el ejemplo de cómo rastrear el código JIT-ed de los volcados de memoria utilizando el explorador de procesos y WinDbg.

Andriy Tkach
fuente
4

Otra forma de ver el código de máquina y algunos datos de rendimiento es utilizar CodeAnalyst u OProfile de AMD, que tienen un complemento de Java para visualizar la ejecución de código Java como código de máquina.

Ian
fuente
0

Imprima el ensamblaje de sus hotspots con los perfiladores perfasm de JMH ( LinuxPerfAsmProfilero WinPerfAsmProfiler). JMH requiere la hsdisbiblioteca, ya que se basa en PrintAssembly.

Aleksandr Dubinsky
fuente