Làm thế nào để xem mã do JIT biên dịch trong JVM?


84

Có cách nào để xem mã gốc do JIT tạo ra trong JVM không?


Bạn có chắc chắn muốn xem mã do JIT biên dịch (gốc) hay chỉ mã byte? Tôi hỏi vì đặt câu hỏi này ở đây dẫn đến một số nghi ngờ nếu bạn thực sự muốn xem mã gốc ... Và, xin lỗi, tôi cũng không biết một công cụ như vậy.
gimpf

3
Tôi muốn xem mã gốc do JIT biên dịch chính xác. Tất nhiên đó không phải là thứ mà tôi cần phải hoàn thành công việc, mà là một loại thí nghiệm và điều tra mọi thứ.
alsor.net

Thách thức nhỏ về khung: một trình biên dịch động được sử dụng trong các JVM hiện đại không chỉ có một phiên bản mã đã biên dịch; nó có thể bắt đầu thông dịch, sau đó biên dịch một phương thức hoặc chỉ một phần của nó, sau đó có khả năng biên dịch lại nó nhiều lần khi các lớp được tải / không tải hoặc các mẫu sử dụng thay đổi hoặc dựa trên thống kê hiệu suất. (Tôi nghĩ rằng nó thậm chí có thể loại bỏ phiên bản đã biên dịch và quay lại phiên dịch nếu điều đó có vẻ có lợi.) Vì vậy, bạn có thể không chỉ nhận được mã khác nhau trên các máy khác nhau, thậm chí cho các lần chạy khác nhau trên cùng một máy mà còn ở các thời điểm khác nhau trong cùng một lần chạy .
gidds

Câu trả lời:


45

Giả sử bạn đang sử dụng Sun Hotspot JVM (tức là JVM được cung cấp trên java.com bởi Oracle), bạn có thể thêm cờ

-XX: + PrintOptoAssembly

khi chạy mã của bạn. Thao tác này sẽ in ra mã được tối ưu hóa do trình biên dịch JIT tạo ra và loại bỏ phần còn lại.

Nếu bạn muốn xem toàn bộ mã bytecode, bao gồm cả các phần chưa được tối ưu hóa, hãy thêm

-XX: CompileThreshold = #

khi bạn đang chạy mã của mình.

Bạn có thể đọc thêm về lệnh này và chức năng của JIT nói chung tại đây .


Tùy chọn này chỉ xuất hiện trong các bản dựng gỡ lỗi hay bất cứ thứ gì? Bởi vì JVM ("Java (TM) SE Runtime Environment (build 1.6.0_16-b01") của tôi không nhận ra nó mặc dù nguồn trên web cho biết tính năng này khả dụng trong Sun Java 6 và OpenJDK.
Joachim Sauer

2
Có, cần có mã nhị phân DEBUG. blogs.warwick.ac.uk/richardwarburton/entry/…
alsor.net

3
Đó không phải là (ngày nay) -XX: + PrintAssembly, ít nhất là ngày nay? Đã kiểm tra trên máy của tôi và khớp với những gì được nói ở đây: wikis.sun.com/display/HotSpotInternals/PrintAssembly Bạn cần -XX: + UnlockDiagnosticVMOptions trước tùy chọn này và một plugin trình tháo gỡ.
Blaisorblade

@Blaisorblade Tôi đang nhận được: Tùy chọn VM được chỉ định không đúng 'PrintAssembly' Lỗi: Không thể tạo Máy ảo Java. Lỗi: Đã xảy ra một ngoại lệ nghiêm trọng. Chương trình sẽ thoát.
Koray Tugay

@KorayTugay Xem các câu trả lời khác - liên kết được cập nhật là wikis.oracle.com/display/HotSpotInternals/PrintAssembly , như được cung cấp trong stackoverflow.com/a/15146962/53974 hoặc stackoverflow.com/a/4149878/53974 . Nếu hướng dẫn sau không hiệu quả, vui lòng hỏi chi tiết ở một số nơi thích hợp (không chắc liệu nó có nên câu hỏi khác cho trường hợp của bạn hay không, hãy tham khảo câu hỏi này).
Blaisorblade

76

Cách sử dụng chung

Như được giải thích bởi các câu trả lời khác, bạn có thể chạy với các tùy chọn JVM sau:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Lọc theo một phương pháp cụ thể

Bạn cũng có thể lọc theo một phương pháp cụ thể với cú pháp sau:

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

Ghi chú:

  • bạn có thể cần đặt đối số thứ hai trong dấu ngoặc kép tùy thuộc vào hệ điều hành, v.v.
  • nếu phương pháp được nội tuyến, bạn có thể bỏ lỡ một số tối ưu hóa

Cách thực hiện: Cài đặt các thư viện cần thiết trên Windows

Nếu bạn đang chạy Windows, trang này có hướng dẫn về cách xây dựng và cài đặt hsdis-amd64.dllcũng hsdis-i386.dllnhư những hướng dẫn bắt buộc để làm cho nó hoạt động. Chúng tôi sao chép bên dưới và mở rộng nội dung của trang đó * để tham khảo:


Nơi lấy mã nhị phân dựng sẵn

Bạn có thể tải xuống tệp nhị phân dựng sẵn cho Windows từ dự án fcml

Cách xây dựng hsdis-amd64.dllhsdis-i386.dlltrên Windows

Phiên bản này của hướng dẫn được chuẩn bị trên Windows 8.1 64 bit sử dụng Cygwin 64 bit và tạo hsdis-amd64.dll

  1. Cài đặt Cygwin . Trên Select Packagesmàn hình, thêm các gói sau (bằng cách mở rộng Develdanh mục, sau đó nhấp một lần vào Skipnhãn bên cạnh mỗi tên gói):

    • make
    • mingw64-x86_64-gcc-core(chỉ cần thiết cho hsdis-amd64.dll)
    • mingw64-i686-gcc-core(chỉ cần thiết cho hsdis-i386.dll)
    • diffutils(trong Utilsdanh mục)
  2. Chạy Cygwin Terminal. Điều này có thể được thực hiện bằng cách sử dụng biểu tượng Desktop hoặc Start Menu được tạo bởi trình cài đặt và sẽ tạo thư mục chính Cygwin của bạn ( C:\cygwin\home\<username>\hoặc C:\cygwin64\home\<username>\theo mặc định).

  3. Tải xuống gói mã nguồn GNU binutils mới nhất và giải nén nội dung của nó vào thư mục chính Cygwin của bạn. Tại thời điểm viết bài, gói mới nhất là binutils-2.25.tar.bz2. Điều này sẽ dẫn đến một thư mục có tên binutils-2.25(hoặc bất kỳ phiên bản mới nhất nào) trong thư mục chính Cygwin của bạn.
  4. Tải xuống mã nguồn OpenJDK bằng cách đi tới kho lưu trữ Cập nhật JDK 8 , chọn thẻ tương ứng với phiên bản JRE đã cài đặt của bạn và nhấp vào bz2. Giải nén thư mục hsdis (tìm thấy trong src\share\tools) vào thư mục chính Cygwin của bạn.
  5. Trong Cygwin Terminal, nhập cd ~/hsdis.
  6. Để xây dựng hsdis-amd64.dll, hãy nhập

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

    Để xây dựng hsdis-i386.dll, hãy nhập

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

    Trong cả hai trường hợp, hãy thay thế 2.25bằng phiên bản binutils bạn đã tải xuống. OS=Linuxlà cần thiết bởi vì, mặc dù Cygwin là một môi trường giống như Linux, nhưng hsdis makefile không nhận ra nó như vậy.

  7. Việc xây dựng sẽ không thành công với các thông báo ./chew: No such file or directorygcc: command not found. Chỉnh sửa <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefiletrong trình soạn thảo văn bản như Wordpad hoặc Notepad ++ để thay đổi SUBDIRS = doc po(dòng 342, nếu sử dụng binutils 2.25) thành SUBDIRS = po. Chạy lại lệnh trước đó.

DLL hiện có thể được cài đặt bằng cách sao chép nó từ hsdis\build\Linux-amd64hoặc hsdis\build\Linux-i586vào thư mục bin\serverhoặc JRE của bạn bin\client. Bạn có thể tìm thấy tất cả các thư mục như vậy trên hệ thống của mình bằng cách tìm kiếm java.dll.

Mẹo bổ sung: nếu bạn thích cú pháp Intel ASM hơn AT&T, hãy chỉ định -XX:PrintAssemblyOptions=intelcùng với bất kỳ tùy chọn PrintAssembly nào khác mà bạn sử dụng.

* giấy phép trang là Creative Commons


1
Binaries Pre-xây dựng cho các nền tảng khác - kenai.com/projects/base-hsdis/downloads
Ashwin Jayaprakash

@AshwinJayaprakash Tôi phải đặt những tệp này ở đâu trong Mac OS?
Koray Tugay

@KorayTugay đưa chúng vào/usr/lib/
Jean-François Savard

Tôi đã cập nhật câu trả lời bằng cách sao chép từ phiên bản mới nhất của trang được liên kết đến, nhưng điều này làm nổi bật lý do chúng tôi thường liên kết đến các tài nguyên bên ngoài thay vì sao chép nguyên văn.
Aleksandr Dubinsky

@AleksandrDubinsky Cảm ơn bạn đã cập nhật. Tôi đã cố ý sao chép nó: nếu trang web đó bị gỡ xuống, câu trả lời của tôi sẽ vẫn được giữ kín ...
assylias

29

Bạn cần một plugin hsdis để sử dụng PrintAssembly. Một lựa chọn thuận tiện là plugin hsdis dựa trên thư viện FCML.

Nó có thể được biên dịch cho các hệ thống giống UNIX và trên Windows, bạn có thể sử dụng các thư viện được tạo sẵn có sẵn trong phần tải xuống FCML trên Sourceforge:

Để cài đặt trong Windows:

  • Giải nén dll (nó có thể được tìm thấy trong hsdis-1.1.2-win32-i386.zip và hsdis-1.1.2-win32-amd64.zip).
  • Sao chép dll vào bất kỳ nơi nào tồn tại java.dll(sử dụng tìm kiếm của Windows). Trên hệ thống của mình, tôi tìm thấy nó ở hai vị trí:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Để cài đặt trong Linux:

  • Tải xuống mã nguồn, giải nén nó
  • 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
  • Trên hệ thống của tôi, JDK ở /usr/lib/jvm/java-8-oracle

Làm thế nào để chạy nó:

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

Các thông số cấu hình bổ sung:

In máy trước khi ghi nhớ.
intel Sử dụng cú pháp Intel.
gas Sử dụng cú pháp trình hợp dịch AT&T (tương thích trình hợp dịch GNU).
dec In IMM và dịch chuyển dưới dạng giá trị thập phân.
mpad = XX Phần đệm cho phần ghi nhớ của hướng dẫn.
cpad = XX Phần đệm cho mã máy.
seg Hiển thị các đăng ký phân đoạn mặc định.
số không Hiển thị các số không ở đầu trong trường hợp chữ HEX.

Cú pháp Intel là cú pháp mặc định trong trường hợp Windows, trong khi cú pháp AT&T là cú pháp mặc định cho GNU / Linux.

Để biết thêm chi tiết, hãy xem Sổ tay Tham khảo Thư viện FCML


Cảm ơn vì đã sửa lib. Nó cũng hoạt động tốt trên Linux. Tôi đang xóa các bình luận cũ của mình để đỡ lộn xộn.
Aleksandr Dubinsky

Trên Linux, sau khi tôi cài đặt libhsdis.so và tạo một liên kết mềm đến hsdis-amd64.so, tôi chạy lệnh java, prmpts không thể tìm thấy hsdis-amd64.so. Mình khởi động lại rồi chạy lại java thì ok. Làm thế nào để tránh khởi động lại để làm cho liên kết mềm hoạt động ngay lập tức? đăng xuất?
gfan

2
Chỉ cần bổ sung một chút: Trên một số bản phân phối linux, bạn chỉ có thể cài đặt một gói, ví dụ như trong Ubuntu: apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ). Việc tự xây dựng cái này có thể không cần thiết.
David Georg Reichelt


5

Tôi tin rằng WinDbg sẽ hữu ích nếu bạn đang chạy nó trên máy tính Windows. Tôi vừa chạy một lọ.

  • Sau đó, tôi đính kèm với quy trình java thông qua Windbg
  • Chủ đề được kiểm tra bằng lệnh ~ ; Có 11 luồng, 0 luồng là luồng công nhân chính
  • Đã chuyển sang 0 luồng - ~ 0 giây
  • Nhìn qua callstack không người lái theo kb, có:

    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

Các dòng được đánh dấu là mã JIT-ed đang chạy trực tiếp trên JVM.

  • Sau đó, chúng ta có thể tìm kiếm địa chỉ phương thức:
    java + 0x2f68 là 00402f68

  • Trên WinDBG:
    Nhấp vào View -> Disassembly.
    Nhấp vào Chỉnh sửa -> Chuyển đến Địa chỉ.
    Đặt 00402f68 ở đó
    và có

    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
    ... và v.v.

Để biết thêm thông tin, đây là Ví dụ về cách truy tìm lại mã JIT-ed từ các kết xuất bộ nhớ bằng cách sử dụng trình khám phá quy trình và WinDbg.


4

Một cách khác để xem mã máy và một số dữ liệu hiệu suất là sử dụng CodeAnalyst hoặc OProfile của AMD, có một plugin Java để trực quan hóa việc thực thi mã Java dưới dạng mã máy.


0

In tập hợp các điểm phát sóng của bạn bằng ( LinuxPerfAsmProfilerhoặc WinPerfAsmProfiler) các bộ định hình perfasm của JMH . JMH yêu cầu hsdisthư viện vì nó dựa vào PrintAssembly.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.