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

Java Debugging Internals

Java Debugging Internals

Yarden Laifenfeld

November 22, 2022
Tweet

More Decks by Yarden Laifenfeld

Other Decks in Programming

Transcript

  1. 2 Who Am I? • Software Engineer at Rookout •

    Background in low level C programming in linux IOT environments • Java, Go, Ruby • C#, Python, JavaScript, C++
  2. JPDA - Java Platform Debugging Architecture The Java Platform Debugger

    Architecture (JPDA) consists of three interfaces designed for use by debuggers in development environments for desktop systems. - Oracle Documentation 6
  3. JPDA - Java Platform Debugging Architecture • JDI - Java

    Debug Interface • JDWP - Java Debug Wire Protocol • JVMTI - Java Virtual Machine Tool Interface 7
  4. JDI - Java Debug Interface • High level Java API

    • Inspect & Control • Connect remotely 8
  5. JDI - Java Debug Interface • Add breakpoint ◦ createBreakpointRequest

    • Collect data ◦ visibleVariables ◦ getArgumentValues ◦ frames • Continue program ◦ resume 9
  6. Using JDI • No availability during collection • Zero control

    or visibility • Easy to make mistakes • Single instance debugging 10
  7. JPDA - Java Platform Debugging Architecture • JDI - Java

    Debug Interface • JDWP - Java Debug Wire Protocol • JVMTI - Java Virtual Machine Tool Interface 11
  8. JDWP - Java Debug Wire Protocol • A protocol •

    Used for communication between a debugger and the JVM which it debugs 12 JVM MyApp Debugger CLI JDWP
  9. Using JDWP • No availability during collection • Zero control

    or visibility • Easy to make mistakes • Single instance debugging • Managing multiple clients and connecting to multiple servers 13
  10. JPDA - Java Platform Debugging Architecture • JDI - Java

    Debug Interface • JDWP - Java Debug Wire Protocol • JVMTI - Java Virtual Machine Tool Interface 14
  11. JVMTI - Java Virtual Machine Tool Interface • Native programming

    interface • Interacts directly with the JVM • Inspect & Control 15
  12. JVMTI - Java Virtual Machine Tool Interface • SetBreakpoint •

    SetEventNotificationMode ◦ Breakpoint • GetLocalObject ◦ jobject 16
  13. JVMTI - Get Stack Trace /* Get Stack Trace */

    err = (*jvmti)->GetStackTrace(jvmti, thr, 0, 5, &frames, &count); if (err != JVMTI_ERROR_NONE) { printf("(GetThreadInfo) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, err); describe(err); printf("\n"); } printf("Number of records filled: %d\n", count); if (err == JVMTI_ERROR_NONE && count >=1) { char *methodName; methodName = "yet_to_call()"; char *declaringClassName; jclass declaring_class; int i=0; printf("Exception Stack Trace\n"); printf("=====================\n"); printf("Stack Trace Depth: %d\n", count); for ( i=0; i < count; i++) { err = (*jvmti)->GetMethodName(jvmti, frames[i].method, &methodName, NULL, NULL); if (err == JVMTI_ERROR_NONE) { err = (*jvmti)->GetMethodDeclaringClass(jvmti, frames[i].method, &declaring_class); err = (*jvmti)->GetClassSignature(jvmti, declaring_class, &declaringClassName, NULL); if (err == JVMTI_ERROR_NONE) { printf("at method %s() in class %s\n", methodName, declaringClassName); } } } } 17 *A lot of code*
  14. Requirements 21 • Don’t break • Predictability • Availability •

    Performance • Security and compliance • Multi-instance debugging
  15. Thread 2: Client Connection public Response Process(Request req) { String

    id = req.GetID(); if (isExistingUser(id)) { return new Response("User exists!"); } // Create new user... } Requirements 22 • Don’t break • Availability Thread 1: Server Client Connection Thread 3: Client Connection public Response Process(Request req) { String id = req.GetID(); if (isExistingUser(id)) { return new Response("User exists!"); } // Create new user... } Thread 2: Client Connection public Response Process(Request req) { String id = req.GetID(); if (isExistingUser(id)) { return new Response("User exists!"); } // Create new user... }
  16. Sample Program 26 1| public class HelloWorld { 2| public

    static void main(String[] args) { 3| while(true) { 4| System.out.println("Hello, World!"); 5| } 6| } 7| }
  17. Sample Program 27 • Build HelloWorld to JAR • Run:

    java -jar HelloWorld.jar • Output: Hello, World! Hello, World! ...
  18. Java Agent - Premain 28 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } }
  19. Java Agent - Premain 29 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } }
  20. Java Agent - Premain 30 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } }
  21. Java Agent - Premain 31 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } }
  22. Java Agent - Premain 32 • Build JavaAgent to JAR

    • Run with java agent: java -javaagent:JavaAgent.jar -jar HelloWorld.jar • Output: Hello, JavaSummit! Hello, World! ...
  23. Java Agent - ClassFileTransformer 34 JVM 32 | … 33

    | new Item() 34 | … Class Loader Get Item class bytecode Return Item class bytecode
  24. Java Agent - ClassFileTransformer 35 JVM 32 | … 33

    | new Item() 34 | … Class Loader Get Item class bytecode Return Item class bytecode Class File Transformer Return transformed Item class bytecode
  25. Java Agent - ClassFileTransformer 36 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } } • void addTransformer(ClassFileTransformer transformer);
  26. Java Agent - ClassFileTransformer 37 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
  27. Java Agent - ClassFileTransformer 38 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
  28. Java Agent - ClassFileTransformer 39 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
  29. Bytecode Manipulation 42 byte[] addPrintCall(byte[] classfileBuffer) { // Add call

    to System.out.println("Transformed!") // Return transformed bytecode }
  30. Java Agent - ClassFileTransformer 43 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (className.contains("HelloWorld")) { return addPrintCall(classfileBuffer); } return classfileBuffer; } }
  31. Java Agent - ClassFileTransformer 44 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (className.contains("HelloWorld")) { return addPrintCall(classfileBuffer); } return classfileBuffer; } }
  32. Java Agent - Premain 45 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { DebugTransformer transformer = new DebugTransformer(); instrumentation.addTransformer(transformer); } }
  33. Java Agent - Premain 46 public class JavaAgent { public

    static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { DebugTransformer transformer = new DebugTransformer(); instrumentation.addTransformer(transformer); } }
  34. Java Agent - Premain 47 • Build JavaAgent to JAR

    • Run with java agent: java -javaagent:JavaAgent.jar -jar HelloWorld.jar • Output: Transformed! Hello, World! Hello, World! ...
  35. Add Breakpoint 51 byte[] addBreakpoint(byte[] classfileBuffer, int lineno) { //

    Find where to insert Breakpoint call // Push local variables onto stack // Insert call to Breakpoint // Return transformed bytecode }
  36. Add Breakpoint 52 byte[] addBreakpoint(byte[] classfileBuffer, int lineno) { //

    Find where to insert Breakpoint call // Push local variables onto stack // Insert call to Breakpoint // Return transformed bytecode }
  37. Add Breakpoint 53 byte[] addBreakpoint(byte[] classfileBuffer, int lineno) { //

    Find where to insert Breakpoint call // Push local variables onto stack // Insert call to Breakpoint // Return transformed bytecode }
  38. Add Breakpoint 54 byte[] addBreakpoint(byte[] classfileBuffer, int lineno) { //

    Find where to insert Breakpoint call // Push local variables onto stack // Insert call to Breakpoint // Return transformed bytecode }
  39. Add Breakpoint 55 byte[] addBreakpoint(byte[] classfileBuffer, int lineno) { //

    Find where to insert Breakpoint call // Push local variables onto stack // Insert call to Breakpoint // Return transformed bytecode }
  40. Sample Program 56 1| public class HelloWorld { 2| public

    static void main(String[] args) { 3| while(true) { 4| System.out.println("Hello, World!"); 5| } 6| } 7| }
  41. Java Agent - ClassFileTransformer 57 public class DebugTransformer implements ClassFileTransformer

    { @Override public byte[] transform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (className.contains("HelloWorld")) { return addBreakpoint(classfileBuffer, 4); } return classfileBuffer; } }
  42. Java Agent - Premain 58 • Build JavaAgent to JAR

    • Run with java agent: java -javaagent:JavaAgent.jar -jar HelloWorld.jar • Output: Breakpoint hit! [Ljava.lang.Object;@3d075dc0 Hello, World! ...
  43. Putting it all together 61 • Transform on demand •

    Add call to breakpoint function • Access locals as objects