Slide 1

Slide 1 text

Java Debugging Internals @yardenlaif

Slide 2

Slide 2 text

2 Who Am I? • Software Engineer at Rookout • Java, Go, Ruby • C#, Python, JavaScript, C++ • Amateur sewist

Slide 3

Slide 3 text

The Objective: Dynamic Snapshots ● Dynamically add/remove snapshots on lines of code ● Capture local variables ● Capture stack trace 3

Slide 4

Slide 4 text

The Objective: Dynamic Snapshots ● Dynamically add/remove snapshots on lines of code ● Capture local variables ● Capture stack trace 4 Breakpoints?

Slide 5

Slide 5 text

The Objective: Dynamic Snapshots ● Dynamically add/remove snapshots ● Capture local variables ● Capture stack trace 5 Breakpoints? breakpoints on lines of code

Slide 6

Slide 6 text

Production Grade Breakpoints ● Minimum performance impact ● Debug more than one instance at a time ● Multiple people debugging the same instance 6

Slide 7

Slide 7 text

jdb -attach 8000 java -agentlib:jdwp= transport=dt_socket,server=y, suspend=n,address=*:8000 myApp JDB - The Java Debugger 7 JVM MyApp Debugger CLI

Slide 8

Slide 8 text

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 8

Slide 9

Slide 9 text

JPDA - Java Platform Debugging Architecture ● JDI - Java Debug Interface ● JDWP - Java Debug Wire Protocol ● JVMTI - Java Virtual Machine Tool Interface 9

Slide 10

Slide 10 text

JDI - Java Debug Interface ● High level Java API ● Inspect & Control ● Connect remotely 10

Slide 11

Slide 11 text

JDI - Simple Debugger Flow ● Launch and connect ○ Get VirtualMachine instance ○ Request to get events (breakpoint, watchpoint, threadStop, threadStart…) ● When class is prepared, add a breakpoint ● When breakpoint is hit, print variables 11

Slide 12

Slide 12 text

Using JDI ● No availability during collection ● Zero control or visibility ● Easy to make mistakes ● Single instance debugging 12

Slide 13

Slide 13 text

JPDA - Java Platform Debugging Architecture ● JDI - Java Debug Interface ● JDWP - Java Debug Wire Protocol ● JVMTI - Java Virtual Machine Tool Interface 13

Slide 14

Slide 14 text

JDWP - Java Debug Wire Protocol ● A protocol ● Used for communication between a debugger and the JVM which it debugs 14 JVM MyApp Debugger CLI JDWP

Slide 15

Slide 15 text

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 15

Slide 16

Slide 16 text

JVMTI - Java Virtual Machine Tool Interface ● Native programming interface ● Interacts directly with the JVM ● Inspect & Control 16

Slide 17

Slide 17 text

JPDA - Java Platform Debugging Architecture ● JDI - Java Debug Interface ● JDWP - Java Debug Wire Protocol ● JVMTI - Java Virtual Machine Tool Interface 17

Slide 18

Slide 18 text

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); } } } } 18 *A lot of code*

Slide 19

Slide 19 text

Java - Get Stack Trace Thread.currentThread().getStackTrace() 19

Slide 20

Slide 20 text

Using JVMTI ● Incredibly Complex ● Native vulnerabilities ● Not portable ● Bugs are catastrophic 20

Slide 21

Slide 21 text

21 Newer? Simpler? Full control? In-process? Unbreakable?

Slide 22

Slide 22 text

Java Agents 22

Slide 23

Slide 23 text

Java Agents 23 JVM MyApp Java Agent java.lang.Instrument API

Slide 24

Slide 24 text

Sample Program 24 1| public class HelloWorld { 2| public static void main(String[] args) { 3| while(true) { 4| System.out.println("Hello, World!"); 5| } 6| } 7| }

Slide 25

Slide 25 text

Sample Program 25 ● Build HelloWorld to JAR ● Run: java -jar HelloWorld.jar ● Output: Hello, World! Hello, World! ...

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Java Agent - Premain 30 ● Build JavaAgent to JAR ● Run with java agent: java -javaagent:JavaAgent.jar -jar HelloWorld.jar ● Output: Hello, JavaSummit! Hello, World! ...

Slide 31

Slide 31 text

Java Agent - ClassFileTransformer 31 JVM Compiler Code Bytecode

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Java Agent - ClassFileTransformer 33 JVM 32 | … 33 | new Item() 34 | … Class Loader Get Item class bytecode Return Item class bytecode Class File Transformer Return transformed Item class bytecode

Slide 34

Slide 34 text

Java Agent - ClassFileTransformer 34 public class JavaAgent { public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { System.out.println("Hello, JavaSummit!"); } } ● void addTransformer(ClassFileTransformer transformer);

Slide 35

Slide 35 text

Java Agent - ClassFileTransformer 35 public class DebugTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {

Slide 36

Slide 36 text

Java Agent - ClassFileTransformer 36 public class DebugTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {

Slide 37

Slide 37 text

Java Agent - ClassFileTransformer 37 public class DebugTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {

Slide 38

Slide 38 text

Open Source Bytecode Manipulation Tools BCEL ASM 38

Slide 39

Slide 39 text

Bytecode Abstraction 39 ● AbstractInsnNode ○ JumpInsnNode ○ MethodInsnNode ○ VarInsnNode ● InsnList instructions

Slide 40

Slide 40 text

Bytecode Manipulation 40 byte[] addPrintCall(byte[] classfileBuffer) { // Add call to System.out.println("Transformed!") // Return transformed bytecode }

Slide 41

Slide 41 text

Java Agent - ClassFileTransformer 41 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; } }

Slide 42

Slide 42 text

Java Agent - ClassFileTransformer 42 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; } }

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Java Agent - Premain 45 ● Build JavaAgent to JAR ● Run with java agent: java -javaagent:JavaAgent.jar -jar HelloWorld.jar ● Output: Transformed! Hello, World! Hello, World! ...

Slide 46

Slide 46 text

Dynamic Snapshots!

Slide 47

Slide 47 text

The Method: Inject a Hook ● Inject a hook that will execute a specific function on the line of the snapshot 47 11: System.out.println(“Hello world!”); 12: return; Breakpoint()

Slide 48

Slide 48 text

Profiling Interpreter C2 C1 Profiling Profiling Interpreter C2 C1 Profiling JVM Execution Engine 48

Slide 49

Slide 49 text

The Method: Inject a Hook ● Pass all local variables to function 49 11: System.out.println(“Hello world!”); 12: return; Breakpoint(locals)

Slide 50

Slide 50 text

Add Breakpoint 50 public void Breakpoint(Object[] locals) { System.out.println("Breakpoint hit!"); for (Object local : locals) { System.out.println(local); } }

Slide 51

Slide 51 text

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 }

Slide 52

Slide 52 text

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 }

Slide 53

Slide 53 text

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 }

Slide 54

Slide 54 text

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 }

Slide 55

Slide 55 text

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 }

Slide 56

Slide 56 text

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| }

Slide 57

Slide 57 text

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; } }

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

59 What if the class has already been loaded?

Slide 60

Slide 60 text

Magic Instrumentation 60 void redefineClasses(ClassDefinition... definitions) void retransformClasses(Class... classes)

Slide 61

Slide 61 text

Putting it all together 61 ● Transform on demand ● Add call to breakpoint function ● Access locals as objects

Slide 62

Slide 62 text

62 Putting it all together

Slide 63

Slide 63 text

Thank You @yardenlaif rookout.com