$30 off During Our Annual Pro Sale. View Details »

CodeFest 2019. Андрей Паньгин (Одноклассники) — JVM TI: как сделать «плагин» для виртуальной машины

CodeFest 2019. Андрей Паньгин (Одноклассники) — JVM TI: как сделать «плагин» для виртуальной машины

JVM Tool Interface — стандартный API для разработки всевозможных инструментов: профайлеров, отладчиков и диагностических утилит. Фактически это единственный легальный способ обратиться к JVM, будь то HotSpot или другая виртуальная машина. Оказывается, JVM TI полезен не только при создании агентов. В докладе мы разберём, в каких случаях JVM TI может пригодиться разработчику, в том числе в обычных Java приложениях. Познакомимся с возможностями интерфейса, включая нововведения из Java 9 и 11, и напишем собственный инструмент. Несмотря на свою мощь, JVM TI не лишён недостатков. Прежде всего, он подразумевает написание кода на C/C++. Но, кроме того, с ним связана масса нетривиальных особенностей и даже JVM-багов. На реальных примерах из практики мы увидим, с какими проблемами JVM TI сталкиваются программисты, и как эти проблемы решить.

CodeFest

April 08, 2019
Tweet

More Decks by CodeFest

Other Decks in Programming

Transcript

  1. View Slide

  2. 2

    View Slide

  3. 3





    Java

    View Slide

  4. 4





    Java

    View Slide

  5. 5

    View Slide

  6. 6
    java.lang.NullPointerException
    at one.app.databus.DatabusHandler.addDetails(DatabusHandler.java:462)
    at one.app.databus.DatabusHandler.handleActivity(DatabusHandler.java:223)
    at one.app.activity.ActivityService.reportActivity(ActivityService.java:139)
    at one.app.activity.ActivityService$RunnableActivity.run(ActivityService.java:176)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.lang.Thread.run(Thread.java:745)

    View Slide

  7. 7
    public class DatabusHandler {
    private void addDetails(Activity activity, EventBuilder event) {
    ...
    case CHAT_ADD_USERS:
    for (long userID : activity.getData().getUsers()) {
    event.addUser(userID);
    }
    null?

    View Slide

  8. 8
    public class DatabusHandler {
    private void addDetails(Activity activity, EventBuilder event) {
    ...
    case CHAT_ADD_USERS:
    for (long userID : activity.getData().getUsers()) {
    event.addUser(userID);
    }
    null?

    View Slide

  9. 9
    public class DatabusHandler {
    private void addDetails(Activity activity, EventBuilder event) {
    ...
    case CHAT_ADD_USERS:
    for (long userID : activity.getData().getUsers()) {
    event.addUser(userID);
    }
    List null?

    View Slide

  10. 10
    java.lang.NullPointerException: called "getUsers()" method on null object
    at one.app.databus.DatabusHandler.addDetails(DatabusHandler.java:462)
    at one.app.databus.DatabusHandler.handleActivity(DatabusHandler.java:223)
    at one.app.activity.ActivityService.reportActivity(ActivityService.java:139)
    at one.app.activity.ActivityService$RunnableActivity.run(ActivityService.java:176)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.lang.Thread.run(Thread.java:745)

    View Slide

  11. 11

    View Slide

  12. 12

    View Slide

  13. 13

    View Slide

  14. 14

    View Slide

  15. 15

    View Slide

  16. 16


    View Slide

  17. 17


    View Slide

  18. 18



    java –agentpath:path/to/mytool.dll
    java –javaagent:myagent.jar

    View Slide

  19. View Slide

  20. 20

    View Slide

  21. 21

    View Slide

  22. 22

    View Slide





  23. 23

    View Slide

  24. 24

    View Slide

  25. 25
    #include "jmm.h"
    src/hotspot/share/include

    View Slide

  26. 26
    #include "jmm.h"
    JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
    void JNICALL ExceptionCallback(jvmtiEnv* jvmti, JNIEnv* env, ...) {
    JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);

    View Slide

  27. 27
    #include "jmm.h"
    JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
    void JNICALL ExceptionCallback(jvmtiEnv* jvmti, JNIEnv* env, ...) {
    JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
    jmm->DumpHeap0(env, env->NewStringUTF("dump.hprof"), JNI_FALSE);

    View Slide

  28. 28

    View Slide

  29. View Slide

  30. 30

    View Slide

  31. 31

    View Slide

  32. 32

    View Slide

  33. 33

    View Slide

  34. 34

    View Slide

  35. 35

    View Slide

  36. 36

    java –agentpath:first.dll –agentpath:second.dll

    View Slide

  37. 37


    java –agentpath:first.dll –agentpath:second.dll

    View Slide

  38. View Slide

  39. 39
    String getLocation() {
    StackTraceElement caller = Thread.currentThread().getStackTrace()[3];
    return caller.getFileName() + ':' + caller.getLineNumber();
    }
    "AsyncTask.java:14"

    View Slide

  40. 40
    String getLocation() {
    StackTraceElement caller = Thread.currentThread().getStackTrace()[3];
    return caller.getFileName() + ':' + caller.getLineNumber();
    }
    "AsyncTask.java:14"

    View Slide

  41. 41

    View Slide

  42. 42
    String getLocation() {
    return StackWalker.getInstance().walk(s -> {
    StackWalker.StackFrame frame = s.skip(3).findFirst().get();
    return frame.getFileName() + ':' + frame.getLineNumber();
    });
    }
    "AsyncTask.java:14"

    View Slide

  43. 43

    View Slide

  44. 44
    GetStackTrace(jthread thread,
    jint start_depth,
    jint max_frame_count,
    jvmtiFrameInfo* frame_buffer,
    jint* count_ptr)

    View Slide

  45. 45
    GetStackTrace(jthread thread,
    jint start_depth,
    jint max_frame_count,
    jvmtiFrameInfo* frame_buffer,
    jint* count_ptr)
    = 3
    = 1

    View Slide

  46. 46
    public class StackFrame {
    public static native String getLocation(int depth);
    static {
    System.loadLibrary("stackFrame");
    }
    }

    View Slide

  47. 47
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    vm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_0);
    ...
    }

    View Slide

  48. 48
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    vm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_0);
    ...
    }
    JNIEXPORT jstring JNICALL
    Java_StackFrame_getLocation(JNIEnv* env, jclass unused, jint depth) {
    jvmtiFrameInfo frame;
    jint count;
    jvmti->GetStackTrace(NULL, depth, 1, &frame, &count);

    View Slide

  49. 49

    View Slide


  50. 50

    View Slide

  51. 51



    View Slide

  52. 52



    View Slide

  53. 53



    jcmd JVMTI.agent_load /path/to/agent.dll [arguments]

    View Slide

  54. 54
    JNIEXPORT jint JNICALL
    Agent_OnLoad(JavaVM* vm, char* options, void* reserved)
    JNIEXPORT jint JNICALL
    Agent_OnAttach(JavaVM* vm, char* options, void* reserved)

    View Slide

  55. View Slide

  56. 56

    View Slide

  57. 57
    ByteBuffer buf = ByteBuffer.allocateDirect(1024);
    ((sun.nio.ch.DirectBuffer) buf).cleaner().clean();

    View Slide

  58. 58
    ByteBuffer buf = ByteBuffer.allocateDirect(1024);
    ((sun.nio.ch.DirectBuffer) buf).cleaner().clean();
    Exception in thread "main" java.lang.IllegalAccessError:
    class agent.demo6.PrivateApi (in unnamed module @0x3ac3fd8b)
    cannot access class jdk.internal.ref.Cleaner (in module java.base)

    View Slide

  59. 59
    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access by agent.demo6.Reflection
    to field java.lang.String.value
    WARNING: Please consider reporting this to the maintainers of
    agent.demo6.Reflection

    View Slide

  60. 60




    View Slide

  61. 61




    View Slide

  62. View Slide

  63. 63
    SampledObjectAlloc(jvmtiEnv* jvmti,
    JNIEnv* env,
    jthread thread,
    jobject object,
    jclass object_klass,
    jlong size)
    jvmti->SetHeapSamplingInterval(jint sampling_interval)

    View Slide

  64. 64

    View Slide

  65. 65

    View Slide

  66. 66

    View Slide

  67. 67




    View Slide

  68. View Slide