Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 2

  2. 5

  3. 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?
  4. 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?
  5. 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<Long> null?
  6. 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)
  7. 11

  8. 12

  9. 13

  10. 20

  11. 21

  12. 22

  13. 24

  14. 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);
  15. 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);
  16. 28

  17. 30

  18. 31

  19. 32

  20. 33

  21. 34

  22. 35

  23. 41

  24. 42 String getLocation() { return StackWalker.getInstance().walk(s -> { StackWalker.StackFrame frame

    = s.skip(3).findFirst().get(); return frame.getFileName() + ':' + frame.getLineNumber(); }); } "AsyncTask.java:14"
  25. 43

  26. 46 public class StackFrame { public static native String getLocation(int

    depth); static { System.loadLibrary("stackFrame"); } }
  27. 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);
  28. 49

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

    JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
  30. 56

  31. 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)
  32. 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
  33. 63 SampledObjectAlloc(jvmtiEnv* jvmti, JNIEnv* env, jthread thread, jobject object, jclass

    object_klass, jlong size) jvmti->SetHeapSamplingInterval(jint sampling_interval)
  34. 66