+ All Categories
Transcript
Page 1: Living in the Matrix with Bytecode Manipulation (New Relic)

© 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Living in the Matrix with Bytecode Manipulation

Ashley Puls

Wednesday, September 10, 14

Page 2: Living in the Matrix with Bytecode Manipulation (New Relic)

2

Ashley PulsSenior Software Engineer

New Relic, Inc.

Wednesday, September 10, 14

Page 3: Living in the Matrix with Bytecode Manipulation (New Relic)

Outline

• What is bytecode• Why manipulate bytecode• In depth examination of 2 bytecode

manipulation frameworks• Applications• Lessons Learned

3

Wednesday, September 10, 14

Page 4: Living in the Matrix with Bytecode Manipulation (New Relic)

4Source: http://www.techlila.com/write-programs-linux/

What is Java bytecode?

Wednesday, September 10, 14

Page 5: Living in the Matrix with Bytecode Manipulation (New Relic)

5Source: http://www.techlila.com/write-programs-linux/

What is Java bytecode?

Wednesday, September 10, 14

Page 6: Living in the Matrix with Bytecode Manipulation (New Relic)

6

What is Java bytecode?

Wednesday, September 10, 14

Page 7: Living in the Matrix with Bytecode Manipulation (New Relic)

7Source: http://www.techlila.com/write-programs-linux/

What is Java bytecode?

Wednesday, September 10, 14

Page 8: Living in the Matrix with Bytecode Manipulation (New Relic)

8Source: http://www.techlila.com/write-programs-linux/

What is Java bytecode?

Wednesday, September 10, 14

Page 9: Living in the Matrix with Bytecode Manipulation (New Relic)

9

javac -verbose src/com/example/spring2gx/BankTransactions.java

What is Java bytecode?

Wednesday, September 10, 14

Page 10: Living in the Matrix with Bytecode Manipulation (New Relic)

10

javac -verbose src/com/example/spring2gx/BankTransactions.java

What is Java bytecode?

Wednesday, September 10, 14

Page 11: Living in the Matrix with Bytecode Manipulation (New Relic)

11

javac -verbose src/com/example/spring2gx/BankTransactions.java

What is Java bytecode?

Wednesday, September 10, 14

Page 12: Living in the Matrix with Bytecode Manipulation (New Relic)

12

What is Java bytecode?

javac -verbose src/com/example/spring2gx/BankTransactions.java

Wednesday, September 10, 14

Page 13: Living in the Matrix with Bytecode Manipulation (New Relic)

13

What is Java bytecode?

Wednesday, September 10, 14

Page 14: Living in the Matrix with Bytecode Manipulation (New Relic)

14

What is Java bytecode?

Wednesday, September 10, 14

Page 15: Living in the Matrix with Bytecode Manipulation (New Relic)

15

What is Java bytecode?

Source: http://www.techlila.com/write-programs-linux/Wednesday, September 10, 14

Page 16: Living in the Matrix with Bytecode Manipulation (New Relic)

16

What is Java bytecode?

Source: http://www.techlila.com/write-programs-linux/Wednesday, September 10, 14

Page 17: Living in the Matrix with Bytecode Manipulation (New Relic)

17

What is Java bytecode?

Source: http://www.techlila.com/write-programs-linux/

Instruction set of the Java Virtual Machine

Wednesday, September 10, 14

Page 18: Living in the Matrix with Bytecode Manipulation (New Relic)

18

What is Java bytecode?

Source: http://www.techlila.com/write-programs-linux/

Instruction set of the Java Virtual Machine

Wednesday, September 10, 14

Page 19: Living in the Matrix with Bytecode Manipulation (New Relic)

19

What is Java bytecode?

Source: http://www.techlila.com/write-programs-linux/

Instruction set of the Java Virtual Machine

Wednesday, September 10, 14

Page 20: Living in the Matrix with Bytecode Manipulation (New Relic)

Why learn about bytecode manipulation?

20

Wednesday, September 10, 14

Page 21: Living in the Matrix with Bytecode Manipulation (New Relic)

Why learn about bytecode manipulation?

21

• A bytecode manipulation framework is likely on your stack

• Spring• Hibernate

• Groovy• Clojure

• Eclipse• JRuby

Wednesday, September 10, 14

Page 22: Living in the Matrix with Bytecode Manipulation (New Relic)

• Spring• Hibernate

• A bytecode manipulation framework is likely on your stack

• It is really fun!

Why learn about bytecode manipulation?

22

• Groovy• Clojure

• Eclipse• JRuby

Wednesday, September 10, 14

Page 23: Living in the Matrix with Bytecode Manipulation (New Relic)

• Spring• Hibernate

• A bytecode manipulation framework is likely on your stack

• It is really fun!

• Many applications. Can become a valuable tool.

Why learn about bytecode manipulation?

23

• Groovy• Clojure

• Eclipse• JRuby

Wednesday, September 10, 14

Page 24: Living in the Matrix with Bytecode Manipulation (New Relic)

Why manipulate bytecode?

24

Wednesday, September 10, 14

Page 25: Living in the Matrix with Bytecode Manipulation (New Relic)

Why manipulate bytecode?

25

• Program analysis • find bugs in code• examine code complexity

Wednesday, September 10, 14

Page 26: Living in the Matrix with Bytecode Manipulation (New Relic)

Why manipulate bytecode?

26

• Program analysis • find bugs in code• examine code complexity

• Class generation• proxies• remove access to certain APIs• compiler for another language like Scala

Wednesday, September 10, 14

Page 27: Living in the Matrix with Bytecode Manipulation (New Relic)

Why manipulate bytecode?

27

• Program analysis • find bugs in code• examine code complexity

• Class generation• proxies• remove access to certain APIs• compiler for another language like Scala

• Transform classes without Java source code• profilers• optimization and obfuscation• additional logging

Wednesday, September 10, 14

Page 28: Living in the Matrix with Bytecode Manipulation (New Relic)

Why manipulate bytecode?

28

• Program analysis • find bugs in code• examine code complexity

• Class generation• proxies• remove access to certain APIs• compiler for another language like Scala

• Transform classes without Java source code• profilers• optimization and obfuscation• additional logging

Wednesday, September 10, 14

Page 29: Living in the Matrix with Bytecode Manipulation (New Relic)

Logging

29Source: http://www.vforteachers.com/About_NetSupport.htm

Wednesday, September 10, 14

Page 30: Living in the Matrix with Bytecode Manipulation (New Relic)

Logging

30Source: http://www.vforteachers.com/About_NetSupport.htm, http://inzolo.com/blog/tutorials/bank-accounts

Wednesday, September 10, 14

Page 31: Living in the Matrix with Bytecode Manipulation (New Relic)

Logging

31Source: http://www.vforteachers.com/About_NetSupport.htm, http://upload.wikimedia.org/wikipedia/commons/d/d3/49024-SOS-ATM.JPG

Wednesday, September 10, 14

Page 32: Living in the Matrix with Bytecode Manipulation (New Relic)

Logging

32Source: http://www.vforteachers.com/About_NetSupport.htm

A message should be logged every time an important action occurs

Wednesday, September 10, 14

Page 33: Living in the Matrix with Bytecode Manipulation (New Relic)

Logging

33Source: http://www.vforteachers.com/About_NetSupport.htm

A message should be logged every time an important action occurs

Log important values to identify the action

Wednesday, September 10, 14

Page 34: Living in the Matrix with Bytecode Manipulation (New Relic)

34

public class BankTransactions {

public static void main(String[] args) { BankTransactions bank = new BankTransactions(); // login and withdraw i dollars from account for (int i = 0; i < 100; i++) { String accountId = "account" + i; bank.login("password", accountId, "Ashley");

bank.unimportantProcessing(accountId); bank.withdraw(accountId, Double.valueOf(i)); } System.out.println(“Transactions completed”); }

}

Wednesday, September 10, 14

Page 35: Living in the Matrix with Bytecode Manipulation (New Relic)

35

public class BankTransactions {

public static void main(String[] args) { BankTransactions bank = new BankTransactions(); // login and withdraw i dollars from account for (int i = 0; i < 100; i++) { String accountId = "account" + i; bank.login("password", accountId, "Ashley");

bank.unimportantProcessing(accountId); bank.withdraw(accountId, Double.valueOf(i)); } System.out.println(“Transactions completed”); }

}

Wednesday, September 10, 14

Page 36: Living in the Matrix with Bytecode Manipulation (New Relic)

36

/** * A method annotation which should be used to indicate important methods whose * invocations should be logged. * */public @interface ImportantLog {

/** * The method parameter indexes whose values should be logged. For example, * if we have the method hello(int paramA, int paramB, int paramC), and we * wanted to log the values of paramA and paramC, then fields would be ["0", * "2"]. If we only want to log the value of paramB, then fields would be * ["1"]. */ String[] fields();}

Wednesday, September 10, 14

Page 37: Living in the Matrix with Bytecode Manipulation (New Relic)

37

public void login(String password, String accountId, String userName) { // login logic }

public void withdraw(String accountId, Double moneyToRemove) { // transaction logic }

Wednesday, September 10, 14

Page 38: Living in the Matrix with Bytecode Manipulation (New Relic)

38

@ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic }

@ImportantLog(fields = { "0", "1" }) public void withdraw(String accountId, Double moneyToRemove) { // transaction logic }

Wednesday, September 10, 14

Page 39: Living in the Matrix with Bytecode Manipulation (New Relic)

39

@ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic }

@ImportantLog(fields = { "0", "1" }) public void withdraw(String accountId, Double moneyToRemove) { // transaction logic }

A call was made to "login" on "com/example/spring2gx/BankTransactions" Important params: Index 1 value: ${accountId} Index 2 value: ${userName}

Wednesday, September 10, 14

Page 40: Living in the Matrix with Bytecode Manipulation (New Relic)

40

@ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic }

@ImportantLog(fields = { "0", "1" }) public void withdraw(String accountId, Double moneyToRemove) { // transaction logic }

A call was made to "login" on "com/example/spring2gx/BankTransactions" Important params: Index 1 value: ${accountId} Index 2 value: ${userName}

A call was made to "withdraw" on "com/example/spring2gx/BankTransactions" Important params: Index 0 value: ${accountId} Index 1 value: ${moneyToAdd} Wednesday, September 10, 14

Page 41: Living in the Matrix with Bytecode Manipulation (New Relic)

Java Agent

41

• Ability to modify bytecode without modifying or accessing the application’s source code

• Feature added in Java 1.5• Docs: http://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html

Wednesday, September 10, 14

Page 42: Living in the Matrix with Bytecode Manipulation (New Relic)

Typical Java process

42

JVM

Wednesday, September 10, 14

Page 43: Living in the Matrix with Bytecode Manipulation (New Relic)

Typical Java process

43

java com/example/spring2gx/BankTransactions

JVM

Wednesday, September 10, 14

Page 44: Living in the Matrix with Bytecode Manipulation (New Relic)

Typical Java process

44

java com/example/spring2gx/BankTransactions

ClassloaderJVM

Wednesday, September 10, 14

Page 45: Living in the Matrix with Bytecode Manipulation (New Relic)

Typical Java process

45

java com/example/spring2gx/BankTransactions

BankTransactions.class

public static void main(String[] args)

ClassloaderJVM

Wednesday, September 10, 14

Page 46: Living in the Matrix with Bytecode Manipulation (New Relic)

Typical Java process

46

java com/example/spring2gx/BankTransactions

Transactions completed

BankTransactions.class

public static void main(String[] args)

ClassloaderJVM

Wednesday, September 10, 14

Page 47: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

47

JVM

Wednesday, September 10, 14

Page 48: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

48

JVM

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

JVM

Wednesday, September 10, 14

Page 49: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

49

JVM

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

JVM

Wednesday, September 10, 14

Page 50: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

50

JVM

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

JVM Agent

void premain(String args, Instrumentation inst)

Agent.class1. call Agent premain in manifest

Wednesday, September 10, 14

Page 51: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

51

JVM

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

JVM Agent

void premain(String args, Instrumentation inst)

Agent.class

MyTransformer.class

byte[] transform( . .. , byte[] bankTransBytes)

1. call Agent premain in manifest

2. JVM registers my transformer

Wednesday, September 10, 14

Page 52: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

52

JVM JVM Agent

void premain(String args, Instrumentation inst)

Agent.class

MyTransformer.class

byte[] transform( . .. , byte[] bankTransBytes)

1. call Agent premain in manifest

2. JVM registers my transformer

3. Give BankTransactions bytes to MyTransformer

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

Wednesday, September 10, 14

Page 53: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

53

JVM JVM Agent

void premain(String args, Instrumentation inst)

Agent.class

MyTransformer.class

byte[] transform( . .. , byte[] bankTransBytes)

1. call Agent premain in manifest

2. JVM registers my transformer

3. Give BankTransactions bytes to MyTransformer

4. MyTransformer provides bytes to load

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

Wednesday, September 10, 14

Page 54: Living in the Matrix with Bytecode Manipulation (New Relic)

Process with Java agent

54

JVM JVM Agent

void premain(String args, Instrumentation inst)

Agent.class

MyTransformer.class

byte[] transform( . .. , byte[] bankTransBytes)

Transactions completed

BankTransactions.classpublic static void main(String[] args)

1. call Agent premain in manifest

2. JVM registers my transformer

3. Give BankTransactions bytes to MyTransformer

4. MyTransformer provides bytes to load

5. BankTransactions loaded and main runs

java -javaagent:/to/agent.jar com/example/spring2gx/BankTransactions

Wednesday, September 10, 14

Page 55: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

55

JVM

Wednesday, September 10, 14

Page 56: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

56

JVM

Manifest: Premain-Class: com.example.spring2gx.agent.Agent

Wednesday, September 10, 14

Page 57: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

57

JVM

package com.example.spring2gx.agent;

public class Agent {

public static void premain(String args, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); }}

Manifest: Premain-Class: com.example.spring2gx.agent.Agent

Wednesday, September 10, 14

Page 58: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

58

JVM

package com.example.spring2gx.agent;

public class Agent {

public static void premain(String args, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); }}

Manifest: Premain-Class: com.example.spring2gx.agent.Agent

Wednesday, September 10, 14

Page 59: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

59

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; }}

Wednesday, September 10, 14

Page 60: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

60

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; }}

Wednesday, September 10, 14

Page 61: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

61

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; }}

Wednesday, September 10, 14

Page 62: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

62

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println(" Loading class: " + className); return null; }}

Wednesday, September 10, 14

Page 63: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

63

JVM

Starting the agent Loading class: java/lang/invoke/MethodHandleImpl Loading class: java/lang/invoke/MemberName$Factory Loading class: java/lang/invoke/LambdaForm$NamedFunction Loading class: java/lang/invoke/MethodType$ConcurrentWeakInternSet Loading class: java/lang/invoke/MethodHandleStatics Loading class: java/lang/invoke/MethodHandleStatics$1 Loading class: java/lang/invoke/MethodTypeForm Loading class: java/lang/invoke/Invokers Loading class: java/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry Loading class: java/lang/Void Loading class: java/lang/IllegalAccessException Loading class: sun/misc/PostVMInitHook Loading class: sun/launcher/LauncherHelper Loading class: java/util/concurrent/ConcurrentHashMap$ForwardingNode Loading class: sun/misc/URLClassPath$FileLoader$1 Loading class: com/example/spring2gx/mains/BankTransactions

Wednesday, September 10, 14

Page 64: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

64

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println(" Loading class: " + className); return null; }}

Wednesday, September 10, 14

Page 65: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

65

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println(" Loading class: " + className); return null; }}

Wednesday, September 10, 14

Page 66: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

66

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // manipulate the bytes here return null; }}

Wednesday, September 10, 14

Page 67: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

67

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // manipulate the bytes here return modified bytes; }}

Wednesday, September 10, 14

Page 68: Living in the Matrix with Bytecode Manipulation (New Relic)

Bytecode Manipulation Frameworks

68

• ASM: http://asm.ow2.org/

• BCEL: http://commons.apache.org/proper/commons-bcel/

• CGLib: https://github.com/cglib/cglib

• Javassist: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/

• Serp: http://serp.sourceforge.net/

• Cojen: https://github.com/cojen/Cojen/wiki

• AspectJ: http://www.eclipse.org/aspectj/

Wednesday, September 10, 14

Page 69: Living in the Matrix with Bytecode Manipulation (New Relic)

Bytecode Manipulation Frameworks• ASM: http://asm.ow2.org/

• BCEL: http://commons.apache.org/proper/commons-bcel/

• CGLib: https://github.com/cglib/cglib

• Javassist: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/

• Serp: http://serp.sourceforge.net/

• Cojen: https://github.com/cojen/Cojen/wiki

• AspectJ: http://www.eclipse.org/aspectj/69

Wednesday, September 10, 14

Page 70: Living in the Matrix with Bytecode Manipulation (New Relic)

• Java Programming Assistant

• Subproject of JBoss

• Object based API

• Actively updated

• Version 3.18.2 released May 2014

• Source code: https://github.com/jboss-javassist/javassist

• Docs: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/tutorial/tutorial.html

Javassist

70

Wednesday, September 10, 14

Page 71: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

71

CtClass:compile time classCtClass Name

BankTransactions

Wednesday, September 10, 14

Page 72: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

72

CtClass:compile time classCtClass Name

Bank TransactionsBankTransactions

Wednesday, September 10, 14

Page 73: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

73

CtClass:compile time class

ClassPool: container of CtClass objects

Bank Transactions

Wednesday, September 10, 14

Page 74: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

74

CtClass:compile time classClass Name

Bank Transactions

ClassPool: container of CtClass objects

Wednesday, September 10, 14

Page 75: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

75

CtClass:compile time classClass Name

Bank TransactionsBankTransactions

ClassPool: container of CtClass objects

Wednesday, September 10, 14

Page 76: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

76

Java Class: Bank Transactions

Wednesday, September 10, 14

Page 77: Living in the Matrix with Bytecode Manipulation (New Relic)

Java Class: Bank Transactions

Javassist

77

CtClass

Fields

Constructors

Methods

Wednesday, September 10, 14

Page 78: Living in the Matrix with Bytecode Manipulation (New Relic)

Java Class: Bank Transactions

Javassist

78

CtClass

Fields

Constructors

Methods

CtClass

Wednesday, September 10, 14

Page 79: Living in the Matrix with Bytecode Manipulation (New Relic)

Java Class: Bank Transactions

Javassist

79

CtClass

Fields

Constructors

Methods

CtClass

CtFields

CtConstructors

CtMethods

Wednesday, September 10, 14

Page 80: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

80

CtClass

CtField

ClassPool

CtClass

CtClass

CtClass

CtConst

CtMethod

CtMethod

Wednesday, September 10, 14

Page 81: Living in the Matrix with Bytecode Manipulation (New Relic)

81

Javassist

CtMethod

Wednesday, September 10, 14

Page 82: Living in the Matrix with Bytecode Manipulation (New Relic)

82

Javassist

line 1 of code

line 2 of code

line 3 of code

line 4 of code

line 5of code

CtMethod

Wednesday, September 10, 14

Page 83: Living in the Matrix with Bytecode Manipulation (New Relic)

83

Javassist

line 1 of code

line 2 of code

line 3 of code

line 4 of code

line 5 of code

putMethod.insertBefore(“System.out.println(\“before method\”);”);

System.out.println(“before method”);

CtMethod

Wednesday, September 10, 14

Page 84: Living in the Matrix with Bytecode Manipulation (New Relic)

84

Javassist

line 2 of code

line 3 of code

line 4 of code

line 5 of code

System.out.println(“after method”);

line 1 of code

putMethod.insertAfter(“System.out.println(\”after method\”);”);

CtMethod

Wednesday, September 10, 14

Page 85: Living in the Matrix with Bytecode Manipulation (New Relic)

85

Javassist

line 2 of code

line 3 of code

line 4 of code

catch (IOException e) { Sys.out.prln( . . .

line 5 of code

line 1 of code

CtClass exceptionType = classpool.get(“java.io.IOException”);putMethod.addCatch(“{System.out.println($e); throw $e}”, exceptionType);

CtMethod

Wednesday, September 10, 14

Page 86: Living in the Matrix with Bytecode Manipulation (New Relic)

86

Javassist

line 2 of code

System.out.println(“middle method”);

line 3 of code

line 4 of code

line 5 of code

line 1 of code

putMethod.insertAt(3, true, “System.out.println(\“middle method\”);”);

CtMethod

Wednesday, September 10, 14

Page 87: Living in the Matrix with Bytecode Manipulation (New Relic)

Javassist

87

CtClass

CtField

ClassPool

CtClass

CtClass

CtClass

CtConst

CtMethod

CtMethod

insertBefore

insertAfter

catchEx

insertAt

Wednesday, September 10, 14

Page 88: Living in the Matrix with Bytecode Manipulation (New Relic)

88

JVM

package com.example.spring2gx.agent;

public class Agent {

public static void premain(String args, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); }

}

Wednesday, September 10, 14

Page 89: Living in the Matrix with Bytecode Manipulation (New Relic)

Code for Java agent

89

JVM package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // manipulate the bytes here return modified bytes; }}

Wednesday, September 10, 14

Page 90: Living in the Matrix with Bytecode Manipulation (New Relic)

90

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { 1. convert byte array to a ct class object 2. check each method of ct class for annotation @ImportantLog 3. if @ImportantLog annotation is present on method, then a. get important method parameter indexes

b. add logging statement to beginning of the method return null;}Wednesday, September 10, 14

Page 91: Living in the Matrix with Bytecode Manipulation (New Relic)

91

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); 2. check each method of ct class for annotation @ImportantLog 3. if @ImportantLog annotation present on method, then a. get important method parameter indexes

b. add logging statement to beginning of the method return null;}Wednesday, September 10, 14

Page 92: Living in the Matrix with Bytecode Manipulation (New Relic)

92

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); 3. if @ImportantLog annotation present on method, then a. get important method parameter indexes

b. add logging statement to beginning of the method } } } return null;}Wednesday, September 10, 14

Page 93: Living in the Matrix with Bytecode Manipulation (New Relic)

93

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation);

b. add logging statement to beginning of the method } } } return null;}Wednesday, September 10, 14

Page 94: Living in the Matrix with Bytecode Manipulation (New Relic)

94

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 95: Living in the Matrix with Bytecode Manipulation (New Relic)

95

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 96: Living in the Matrix with Bytecode Manipulation (New Relic)

96

private Annotation getAnnotation(CtMethod method) { MethodInfo mInfo = method.getMethodInfo(); // the attribute we are looking for is a runtime invisible attribute // use Retention(RetentionPolicy.RUNTIME) on the annotation to make it // visible at runtime AnnotationsAttribute attInfo = (AnnotationsAttribute) mInfo .getAttribute(AnnotationsAttribute.invisibleTag); if (attInfo != null) { // this is the type name meaning use dots instead of slashes return attInfo.getAnnotation("com.example.spring.mains.ImportantLog"); } return null; }

Wednesday, September 10, 14

Page 97: Living in the Matrix with Bytecode Manipulation (New Relic)

97

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 98: Living in the Matrix with Bytecode Manipulation (New Relic)

98

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 99: Living in the Matrix with Bytecode Manipulation (New Relic)

99

private List<String> getParamIndexes(Annotation annotation) { ArrayMemberValue fields = (ArrayMemberValue) annotation .getMemberValue(“fields”); if (fields != null) { MemberValue[] values = (MemberValue[]) fields.getValue(); List<String> parameterIndexes = new ArrayList<String>(); for (MemberValue val : values) { parameterIndexes.add(((StringMemberValue) val).getValue()); } return parameterIndexes; } return Collections.emptyList(); }

Wednesday, September 10, 14

Page 100: Living in the Matrix with Bytecode Manipulation (New Relic)

100

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 101: Living in the Matrix with Bytecode Manipulation (New Relic)

101

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 102: Living in the Matrix with Bytecode Manipulation (New Relic)

102

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 103: Living in the Matrix with Bytecode Manipulation (New Relic)

103

private String createJavaString(CtMethod currentMethod, String className, List<String> indexParameters) { StringBuilder sb = new StringBuilder(); sb.append("{StringBuilder sb = new StringBuilder"); sb.append("(\"A call was made to method '\");"); sb.append("sb.append(\""); sb.append(currentMethod.getName()); sb.append("\");sb.append(\"' on class '\");"); sb.append("sb.append(\""); sb.append(className); sb.append("\");sb.append(\"'.\");"); sb.append("sb.append(\"\\n Important params:\");"); . . .

Wednesday, September 10, 14

Page 104: Living in the Matrix with Bytecode Manipulation (New Relic)

104

private String createJavaString(CtMethod currentMethod, String className, List<String> indexParameters) { StringBuilder sb = new StringBuilder(); sb.append("{StringBuilder sb = new StringBuilder"); sb.append("(\"A call was made to method '\");"); sb.append("sb.append(\""); sb.append(currentMethod.getName()); sb.append("\");sb.append(\"' on class '\");"); sb.append("sb.append(\""); sb.append(className); sb.append("\");sb.append(\"'.\");"); sb.append("sb.append(\"\\n Important params:\");"); . . .

Put multiple statements inside brackets {}

Wednesday, September 10, 14

Page 105: Living in the Matrix with Bytecode Manipulation (New Relic)

105

private String createJavaString(CtMethod currentMethod, String className, List<String> indexParameters) { . . . for (String index : indexParameters) { try { int localVar = Integer.parseInt(index) + 1; sb.append("sb.append(\"\\n Index: \");"); sb.append("sb.append(\""); sb.append(index); sb.append("\");sb.append(\" value: \");"); sb.append("sb.append($" + localVar + ");"); } catch (NumberFormatException e) { e.printStackTrace(); } } sb.append("System.out.println(sb.toString());}"); return sb.toString();}

Wednesday, September 10, 14

Page 106: Living in the Matrix with Bytecode Manipulation (New Relic)

106

private String createJavaString(CtMethod currentMethod, String className, List<String> indexParameters) { . . . for (String index : indexParameters) { try { int localVar = Integer.parseInt(index) + 1; sb.append("sb.append(\"\\n Index: \");"); sb.append("sb.append(\""); sb.append(index); sb.append("\");sb.append(\" value: \");"); sb.append("sb.append($" + localVar + ");"); } catch (NumberFormatException e) { e.printStackTrace(); } } sb.append("System.out.println(sb.toString());}"); return sb.toString();} $0, $1, $2, ... used to access “this” and method parametersWednesday, September 10, 14

Page 107: Living in the Matrix with Bytecode Manipulation (New Relic)

107

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] cfbuffer) throws IllegalClassFormatException { pool.insertClassPath(new ByteArrayClassPath(className,classfileBuffer)); CtClass cclass = pool.get(className.replaceAll("/", ".")); if (!cclass.isFrozen()) { for (CtMethod currentMethod : cclass.getDeclaredMethods()) { Annotation annotation = getAnnotation(currentMethod); if (annotation != null) { List<String> parameterIndexes = getParamIndexes(annotation); currentMethod.insertBefore(createJavaString( currentMethod, className, parameterIndexes)); } } return cclass.toBytecode(); } return null;}Wednesday, September 10, 14

Page 108: Living in the Matrix with Bytecode Manipulation (New Relic)

• Positives

• Simplicity

• Documentation

• Negatives

• Limited functionality

• Slow

108

Javassist

Wednesday, September 10, 14

Page 109: Living in the Matrix with Bytecode Manipulation (New Relic)

ASM

109

• Released in Open Source in 2002

• Actively updated

• Version 5.0.3 released May 2014

• Two ASM libraries

• Event based (SAX like)

• Object based (DOM like)

• Documentation: http://download.forge.objectweb.org/asm/asm4-guide.pdf

Wednesday, September 10, 14

Page 110: Living in the Matrix with Bytecode Manipulation (New Relic)

110

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 111: Living in the Matrix with Bytecode Manipulation (New Relic)

111

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 112: Living in the Matrix with Bytecode Manipulation (New Relic)

112

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 113: Living in the Matrix with Bytecode Manipulation (New Relic)

113

ASM

ClassReader: event producer

ClassWriter: event consumer

ClassVisitor: event filter

BankTrans

Wednesday, September 10, 14

Page 114: Living in the Matrix with Bytecode Manipulation (New Relic)

ClassReader: given a byte[], parses a compiled class

114

ASM

ClassReader: event producer

ClassWriter: event consumer

ClassVisitor: event filter

BankTrans

Wednesday, September 10, 14

Page 115: Living in the Matrix with Bytecode Manipulation (New Relic)

ClassWriter: produces output byte[]

115

ASM

ClassReader: event producer

ClassWriter: event consumer

ClassVisitor: event filter

ClassWriter: event consumer

BankTrans

Wednesday, September 10, 14

Page 116: Living in the Matrix with Bytecode Manipulation (New Relic)

116

ASM

ClassReader: event producer

ClassWriter: event consumer

ClassVisitor: event filter

BankTrans

ClassWriter: event consumer

BankTrans

Wednesday, September 10, 14

Page 117: Living in the Matrix with Bytecode Manipulation (New Relic)

ClassVisitor: delegates class events, event filter

117

ASM

ClassReader: event producer

ClassWriter: event consumer

BankTrans

ClassVisitor: event filter

BankTrans

Wednesday, September 10, 14

Page 118: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

118

ASM

ClassReader: event producer

ClassWriter: event consumer

BankTrans

ClassVisitor: event filter

visitField

visitMethod

BankTrans

ClassVisitor: delegates class events, event filter

Wednesday, September 10, 14

Page 119: Living in the Matrix with Bytecode Manipulation (New Relic)

119

ASM

package com.example.spring2gx.agent;

import java.lang.instrument.Instrumentation;

public class Agent {

public static void premain(String args, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); }

}

Wednesday, September 10, 14

Page 120: Living in the Matrix with Bytecode Manipulation (New Relic)

120

package com.example.spring2gx.agent;

public class ImportantLogClassTransformer implements ClassFileTransformer {

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // manipulate the bytes here return modified bytes; }}

ASM

Wednesday, September 10, 14

Page 121: Living in the Matrix with Bytecode Manipulation (New Relic)

121

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);

cr.accept(cw, 0); return cw.toByteArray();

}

ASM

Wednesday, September 10, 14

Page 122: Living in the Matrix with Bytecode Manipulation (New Relic)

122

public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); ClassVisitor cv = new LogMethodClassVisitor(cw, className); cr.accept(cv, 0); return cw.toByteArray();

}

ASM

Wednesday, September 10, 14

Page 123: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

123

ASM

ClassReader: event producer

ClassWriter: event consumer

BankTrans

ClassVisitor: event filter

visitField

visitMethod

BankTrans

Wednesday, September 10, 14

Page 124: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

124

ASM

ClassReader: event producer

ClassWriter: event consumer

BankTransvisitField

visitMethod

BankTrans

LogMethodClassVisitor:event filter

Wednesday, September 10, 14

Page 125: Living in the Matrix with Bytecode Manipulation (New Relic)

125

ASM

ClassReader: event producer

BankTrans

ClassWriter: event consumer

BankTrans

LogMethodClassVisitor:event filter

visitMethod

Wednesday, September 10, 14

Page 126: Living in the Matrix with Bytecode Manipulation (New Relic)

126

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 127: Living in the Matrix with Bytecode Manipulation (New Relic)

127

ASMpublic class LogMethodClassVisitor extends ClassVisitor {

private String className;

public LogMethodIfAnnotationVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; }

@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // put our logic in here }}

Wednesday, September 10, 14

Page 128: Living in the Matrix with Bytecode Manipulation (New Relic)

128

ASMpublic class LogMethodClassVisitor extends ClassVisitor {

private String className;

public LogMethodIfAnnotationVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; }

@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // put our logic in here }}

Wednesday, September 10, 14

Page 129: Living in the Matrix with Bytecode Manipulation (New Relic)

129

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 130: Living in the Matrix with Bytecode Manipulation (New Relic)

130

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 131: Living in the Matrix with Bytecode Manipulation (New Relic)

131

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 132: Living in the Matrix with Bytecode Manipulation (New Relic)

132

ASM

Method Visitor

visitAnnotation

Method Visitor

visitCode

visitEnd

Login Method Login Method

Wednesday, September 10, 14

Page 133: Living in the Matrix with Bytecode Manipulation (New Relic)

133

ASMpublic class LogMethodClassVisitor extends ClassVisitor { private String className;

public LogMethodIfAnnotationVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; }

@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // put our logic in here }}Wednesday, September 10, 14

Page 134: Living in the Matrix with Bytecode Manipulation (New Relic)

134

ASMpublic class LogMethodClassVisitor extends ClassVisitor { private String className;

public LogMethodIfAnnotationVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; }

@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new PrintMessageMethodVisitor(mv, name, className); }}Wednesday, September 10, 14

Page 135: Living in the Matrix with Bytecode Manipulation (New Relic)

135

ASM

Source: http://download.forge.objectweb.org/asm/asm4-guide.pdfWednesday, September 10, 14

Page 136: Living in the Matrix with Bytecode Manipulation (New Relic)

136

ASMpublic class PrintMessageMethodVisitor extends MethodVisitor {

@Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { }

@Override public void visitCode() { }

}

Wednesday, September 10, 14

Page 137: Living in the Matrix with Bytecode Manipulation (New Relic)

137

ASMpublic class PrintMessageMethodVisitor extends MethodVisitor {

@Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 1. check method for annotation @ImportantLog 2. if annotation present, then get important method param indexes }

@Override public void visitCode() { 3. if annotation present, add logging to beginning of the method }

}

Wednesday, September 10, 14

Page 138: Living in the Matrix with Bytecode Manipulation (New Relic)

138

ASM public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if ("Lcom/example/spring2gx/mains/ImportantLog;".equals(desc)) { isAnnotationPresent = true; return new AnnotationVisitor(Opcodes.ASM5, super.visitAnnotation(desc, visible)) { public AnnotationVisitor visitArray(String name) { if (“fields”.equals(name)) { return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { public void visit(String name, Object value) { parameterIndexes.add((String) value); super.visit(name, value); } }; } else { return super.visitArray(name); } } }; } return super.visitAnnotation(desc, visible); }

Wednesday, September 10, 14

Page 139: Living in the Matrix with Bytecode Manipulation (New Relic)

139

ASMpublic class PrintMessageMethodVisitor extends MethodVisitor {

@Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 1. check method for annotation @ImportantLog 2. if annotation present, then get important method param indexes }

@Override public void visitCode() { 3. if annotation present, add logging to beginning of the method }

}

Wednesday, September 10, 14

Page 140: Living in the Matrix with Bytecode Manipulation (New Relic)

140

ASM public void visitCode() { if (isAnnotationPresent) { // create string builder mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); mv.visitInsn(Opcodes.DUP); // add everything to the string builder mv.visitLdcInsn("A call was made to method \""); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false); mv.visitLdcInsn(methodName); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

. . .Wednesday, September 10, 14

Page 141: Living in the Matrix with Bytecode Manipulation (New Relic)

141

Javap

Wednesday, September 10, 14

Page 142: Living in the Matrix with Bytecode Manipulation (New Relic)

142

Javappublic class PrintMessage { private String methodName; private String className; public PrintMessage(String mName, String cName) { methodName = mName; className = cName; } public void print() { StringBuilder sb = new StringBuilder(); sb.append("A call was made to method \""); sb.append(methodName); sb.append("\" on class \""); sb.append(className); sb.append("\"."); System.out.println(sb.toString()); }

Wednesday, September 10, 14

Page 143: Living in the Matrix with Bytecode Manipulation (New Relic)

143

Javap

javap -c com/example/spring2gx/mains/PrintMessage

Wednesday, September 10, 14

Page 144: Living in the Matrix with Bytecode Manipulation (New Relic)

public void print(); Code: 0: new #38 // class java/lang/StringBuilder 3: dup 4: invokespecial #40 // Method java/lang/StringBuilder."<init>":()V 7: astore_1 8: aload_1 9: ldc #41 // String A call was made to method \" 11: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: pop 15: aload_1 16: aload_0 17: getfield #14 // Field methodName:Ljava/lang/String; 20: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;. . .

144

Javap

Wednesday, September 10, 14

Page 145: Living in the Matrix with Bytecode Manipulation (New Relic)

public void print(); Code: 0: new #38 // class java/lang/StringBuilder 3: dup 4: invokespecial #40 // Method java/lang/StringBuilder."<init>":()V 7: astore_1 8: aload_1 9: ldc #41 // String A call was made to method \" 11: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: pop 15: aload_1 16: aload_0 17: getfield #14 // Field methodName:Ljava/lang/String; 20: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;. . .

145

Javap

Wednesday, September 10, 14

Page 146: Living in the Matrix with Bytecode Manipulation (New Relic)

146

Wednesday, September 10, 14

Page 147: Living in the Matrix with Bytecode Manipulation (New Relic)

147

Wednesday, September 10, 14

Page 148: Living in the Matrix with Bytecode Manipulation (New Relic)

148

Javap

Wednesday, September 10, 14

Page 149: Living in the Matrix with Bytecode Manipulation (New Relic)

149

Javap

Wednesday, September 10, 14

Page 150: Living in the Matrix with Bytecode Manipulation (New Relic)

150

Javap

Wednesday, September 10, 14

Page 151: Living in the Matrix with Bytecode Manipulation (New Relic)

151

ASM public void visitCode() { if (isAnnotationPresent) { // create string builder mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); mv.visitInsn(Opcodes.DUP); // add everything to the string builder mv.visitLdcInsn("A call was made to method \""); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false); mv.visitLdcInsn(methodName); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

. . .Wednesday, September 10, 14

Page 152: Living in the Matrix with Bytecode Manipulation (New Relic)

152

ASM• Positives

• Speed and Size

• Documentation

• Depth of functionality

• Number of people using the framework

• Negatives

• Developer ramp-up

• Writing byte code

Wednesday, September 10, 14

Page 153: Living in the Matrix with Bytecode Manipulation (New Relic)

153

Applications

Wednesday, September 10, 14

Page 154: Living in the Matrix with Bytecode Manipulation (New Relic)

154

Hibernate

Wednesday, September 10, 14

Page 155: Living in the Matrix with Bytecode Manipulation (New Relic)

155

Hibernate

LazyInitializationException

Wednesday, September 10, 14

Page 156: Living in the Matrix with Bytecode Manipulation (New Relic)

156

Hibernate

LazyInitializationException

Wednesday, September 10, 14

Page 157: Living in the Matrix with Bytecode Manipulation (New Relic)

157

Hibernate

9 instances of “throw new LazyInitializationException”

LazyInitializationException

Wednesday, September 10, 14

Page 158: Living in the Matrix with Bytecode Manipulation (New Relic)

158

Hibernate

Name Location Team

Joe Smith Dallas, TX Team 1

Jane Doe Portland, OR

Triathlon Club

Tim Doe San Fran, CA

Running Club

Sue Smith Austin, TX Red Lizards

Name Location Description

Team 1 Dallas, TX Casual runners

Triathlon Club

Beaverton, OR

Swim, bike, run

Running Club

Santa Clara, CA

For fun

Red Lizards Austin, TX Fast

Runner Team

Wednesday, September 10, 14

Page 159: Living in the Matrix with Bytecode Manipulation (New Relic)

159

Hibernate

Conference AttendeeRunner

Wednesday, September 10, 14

Page 160: Living in the Matrix with Bytecode Manipulation (New Relic)

160

Hibernate

Name: Ashley Puls

Runner

Wednesday, September 10, 14

Page 161: Living in the Matrix with Bytecode Manipulation (New Relic)

161

Hibernate

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 162: Living in the Matrix with Bytecode Manipulation (New Relic)

162

Hibernate

Name: Ashley Puls

Team: Hibernate Proxy

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 163: Living in the Matrix with Bytecode Manipulation (New Relic)

163

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Team

Hibernate Proxy

Name: Triathlon Club

Location: null

Description: null

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 164: Living in the Matrix with Bytecode Manipulation (New Relic)

164

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Hibernate Proxy

Name: Triathlon Club

Description:

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 165: Living in the Matrix with Bytecode Manipulation (New Relic)

165

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Hibernate Proxy

Name: Triathlon Club

Description:

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 166: Living in the Matrix with Bytecode Manipulation (New Relic)

166

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Team

Hibernate Proxy

call a getter on the hibernate proxy:runner.getTeam().getLocation();Name: Ashley Puls

Location: Portland, OR

Runner

Name: Triathlon Club

Location: null

Description: null

Wednesday, September 10, 14

Page 167: Living in the Matrix with Bytecode Manipulation (New Relic)

167

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Team

Hibernate Proxy Team

Name: Ashley Puls

Location: Portland, OR

Runner

Name: Triathlon Club

Location: null

Description: null

Name: Triathlon Club

Location: Beaverton, OR

Description: swim, bike, run

call a getter on the hibernate proxy:runner.getTeam().getLocation();

Wednesday, September 10, 14

Page 168: Living in the Matrix with Bytecode Manipulation (New Relic)

168

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Hibernate Proxy

Name: Triathlon Club

Description:

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 169: Living in the Matrix with Bytecode Manipulation (New Relic)

169

Hibernate

Name: Ashley Puls

Company:

164

Name: Ashley Puls

Team

Hibernate Proxy Team

Name: Triathlon Club

Location: Beaverton, OR

Description: swim, bike, run

Trying to access uninitialized field outside a session

LazyInitializationException

Name: Ashley Puls

Location: Portland, OR

Runner

Name: Triathlon Club

Location: null

Description: null

Wednesday, September 10, 14

Page 170: Living in the Matrix with Bytecode Manipulation (New Relic)

170

Hibernate

package org.hibernate.proxy.pojo.javassist;

/** * A Javassist-based lazy initializer proxy. * * @author Muga Nishizawa */public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler {

Wednesday, September 10, 14

Page 171: Living in the Matrix with Bytecode Manipulation (New Relic)

171

Hibernate

public static HibernateProxy getProxy( . . . ) { . . . ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(interfaces.length == 1 ? persistentClass : null); factory.setInterfaces(interfaces); factory.setFilter(FINALIZE_FILTER); Class cl = factory.createClass(); final HibernateProxy proxy = (HibernateProxy) cl.newInstance(); ((Proxy) proxy).setHandler(instance); instance.constructed = true; return proxy; . . . }

Wednesday, September 10, 14

Page 172: Living in the Matrix with Bytecode Manipulation (New Relic)

172

Hibernatepublic Object invoke(final Object proxy, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable { if (this.constructed) { Object result = this.invoke(thisMethod, args, proxy); if (result == INVOKE_IMPLEMENTATION) { Object target = getImplementation(); final Object returnValue = thisMethod.invoke(target, args); if (returnValue == target) { if (returnValue.getClass().isInstance(proxy)) { return proxy; } return returnValue; } else { return result; } } else { if (thisMethod.getName().equals("getHibernateLazyInitializer")) { return this; } else { return proceed.invoke(proxy, args); } }}

Wednesday, September 10, 14

Page 173: Living in the Matrix with Bytecode Manipulation (New Relic)

173

Hibernate

Hibernate Proxy

Why do we need a proxy?

Name: Ashley Puls

Team: Hibernate Proxy

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 174: Living in the Matrix with Bytecode Manipulation (New Relic)

174

Hibernate

Name: Ashley Puls

Title: Software Engineer

Company: New Relic

Conference Attendee

1. Is the entity dirty? Have we changed any of the data?

2. What fields on the entity have been loaded?

Name: Ashley Puls

Team: Hibernate Proxy

Name: Ashley Puls

Location: Portland, OR

Runner

Wednesday, September 10, 14

Page 175: Living in the Matrix with Bytecode Manipulation (New Relic)

Hibernate - Build Time Tasks

• Schema Tasks• Schema Export• Schema Update• Schema Validator

175

Wednesday, September 10, 14

Page 176: Living in the Matrix with Bytecode Manipulation (New Relic)

Hibernate - Build Time Tasks

• Schema Tasks• Schema Export• Schema Update• Schema Validator

• ByteCode Tasks• EnhancementTask• InstrumentTask

176

Wednesday, September 10, 14

Page 177: Living in the Matrix with Bytecode Manipulation (New Relic)

Hibernate - Build Time Tasks

• Schema Tasks• Schema Export• Schema Update• Schema Validator

• ByteCode Tasks• EnhancementTask• InstrumentTask

177

Wednesday, September 10, 14

Page 178: Living in the Matrix with Bytecode Manipulation (New Relic)

Hibernate - Ant

178

<taskdef name="enhance" classname="org.hibernate.tool.enhance.EnhancementTask" classpathref="enhancement.classpath" />

<enhance> <fileset dir="${ejb-classes}/org/hibernate/auction/model" includes="**/*.class"/></enhance>

Wednesday, September 10, 14

Page 179: Living in the Matrix with Bytecode Manipulation (New Relic)

Hibernate - Gradle

179

apply plugin: 'java'apply plugin: 'maven'apply plugin: 'enhance'buildscript { repositories { mavenCentral() } dependencies { classpath 'org.hibernate:hibernate-gradle-plugin:VERSION' }}dependencies { compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-[SPEC-VERSION]-api', version: '[IMPL-VERSION]' compile group: 'org.hibernate', name: 'hibernate-gradle-plugin', version: 'VERSION'}

Wednesday, September 10, 14

Page 180: Living in the Matrix with Bytecode Manipulation (New Relic)

180

Hibernatepublic class EnhancementTask extends Task implements EnhancementContext { private List<FileSet> filesets = new ArrayList<FileSet>(); private final ClassPool classPool = new ClassPool( false ); private final Enhancer enhancer = new Enhancer( this ); @Override public void execute() throws BuildException { for (FileSet fileSet : filesets) { final File fileSetBaseDir = fileSet.getDir(project);

for (String relativeIncludedFileName :scanner.getIncludedFiles()) { final File javaClassFile = new File( fileSetBaseDir, relativeIncludedFileName ); processClassFile( javaClassFile); } } }Wednesday, September 10, 14

Page 181: Living in the Matrix with Bytecode Manipulation (New Relic)

181

Hibernatepublic class EnhancementTask extends Task implements EnhancementContext { private List<FileSet> filesets = new ArrayList<FileSet>(); private final ClassPool classPool = new ClassPool( false ); private final Enhancer enhancer = new Enhancer( this ); @Override public void execute() throws BuildException { for (FileSet fileSet : filesets) { final File fileSetBaseDir = fileSet.getDir(project);

for (String relativeIncludedFileName :scanner.getIncludedFiles()) { final File javaClassFile = new File( fileSetBaseDir, relativeIncludedFileName ); processClassFile( javaClassFile); } } }Wednesday, September 10, 14

Page 182: Living in the Matrix with Bytecode Manipulation (New Relic)

182

Hibernatepublic class EnhancementTask extends Task implements EnhancementContext { private List<FileSet> filesets = new ArrayList<FileSet>(); private final ClassPool classPool = new ClassPool( false ); private final Enhancer enhancer = new Enhancer( this ); @Override public void execute() throws BuildException { for (FileSet fileSet : filesets) { final File fileSetBaseDir = fileSet.getDir(project);

for (String relativeIncludedFileName :scanner.getIncludedFiles()) { final File javaClassFile = new File( fileSetBaseDir, relativeIncludedFileName ); processClassFile( javaClassFile); } } }Wednesday, September 10, 14

Page 183: Living in the Matrix with Bytecode Manipulation (New Relic)

183

Hibernatepublic class EnhancementTask extends Task implements EnhancementContext { private List<FileSet> filesets = new ArrayList<FileSet>(); private final ClassPool classPool = new ClassPool( false ); private final Enhancer enhancer = new Enhancer( this ); @Override public void execute() throws BuildException { for (FileSet fileSet : filesets) { final File fileSetBaseDir = fileSet.getDir(project);

for (String relativeIncludedFileName :scanner.getIncludedFiles()) { final File javaClassFile = new File( fileSetBaseDir, relativeIncludedFileName ); processClassFile( javaClassFile); } } }Wednesday, September 10, 14

Page 184: Living in the Matrix with Bytecode Manipulation (New Relic)

184

Hibernate

/** * Atm only process files annotated with either @Entity or @Embeddable */ private void processClassFile(File javaClassFile) {

final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );

processEntityClassFile(javaClassFile, ctClass); }

Wednesday, September 10, 14

Page 185: Living in the Matrix with Bytecode Manipulation (New Relic)

/** * Atm only process files annotated with either @Entity or @Embeddable */ private void processClassFile(File javaClassFile) {

final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );

processEntityClassFile(javaClassFile, ctClass); }

185

Hibernate

Wednesday, September 10, 14

Page 186: Living in the Matrix with Bytecode Manipulation (New Relic)

/** * Atm only process files annotated with either @Entity or @Embeddable */ private void processClassFile(File javaClassFile) {

final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );

processEntityClassFile(javaClassFile, ctClass); }

186

Hibernate

Wednesday, September 10, 14

Page 187: Living in the Matrix with Bytecode Manipulation (New Relic)

187

Hibernate

private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {

byte[] result = enhancer.enhance(ctClass.getName(),ctClass.toBytecode()); if(result != null) writeEnhancedClass(javaClassFile, result); }

}

Wednesday, September 10, 14

Page 188: Living in the Matrix with Bytecode Manipulation (New Relic)

188

Hibernate

private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {

byte[] result = enhancer.enhance(ctClass.getName(),ctClass.toBytecode()); if(result != null) writeEnhancedClass(javaClassFile, result); }

}

Wednesday, September 10, 14

Page 189: Living in the Matrix with Bytecode Manipulation (New Relic)

private void enhance(CtClass managedCtClass, boolean isComposite) {

// 1. stop if managedCtClass is an interface// 2. stop if managedCtClass is already enhanced

// 3. stop if managedCtClass is not an entity }

189

Hibernate

Wednesday, September 10, 14

Page 190: Living in the Matrix with Bytecode Manipulation (New Relic)

private void enhance(CtClass managedCtClass, boolean isComposite) {

// 1. stop if managedCtClass is an interface// 2. stop if managedCtClass is already enhanced

// 3. stop if managedCtClass is not an entity // 4. enhance managedCtClass.addInterface( managedEntityCtClass ); enhancePersistentAttributes( managedCtClass ); addEntityInstanceHandling( managedCtClass ); addEntityEntryHandling( managedCtClass ); addLinkedPreviousHandling( managedCtClass ); addLinkedNextHandling( managedCtClass );}

190

Hibernate

Wednesday, September 10, 14

Page 191: Living in the Matrix with Bytecode Manipulation (New Relic)

private void enhance(CtClass managedCtClass, boolean isComposite) {

// 1. stop if managedCtClass is an interface// 2. stop if managedCtClass is already enhanced

// 3. stop if managedCtClass is not an entity // 4. enhance managedCtClass.addInterface( managedEntityCtClass ); enhancePersistentAttributes( managedCtClass ); addEntityInstanceHandling( managedCtClass ); addEntityEntryHandling( managedCtClass ); addLinkedPreviousHandling( managedCtClass ); addLinkedNextHandling( managedCtClass );}

191

Hibernate

Wednesday, September 10, 14

Page 192: Living in the Matrix with Bytecode Manipulation (New Relic)

private void enhance(CtClass managedCtClass, boolean isComposite) {

// 1. stop if managedCtClass is an interface// 2. stop if managedCtClass is already enhanced

// 3. stop if managedCtClass is not an entity // 4. enhance managedCtClass.addInterface( managedEntityCtClass ); enhancePersistentAttributes( managedCtClass ); addEntityInstanceHandling( managedCtClass ); addEntityEntryHandling( managedCtClass ); addLinkedPreviousHandling( managedCtClass ); addLinkedNextHandling( managedCtClass );}

192

Hibernate

Wednesday, September 10, 14

Page 193: Living in the Matrix with Bytecode Manipulation (New Relic)

193

Hibernateprivate CtField addField(CtClass targetClass, CtClass fieldType, String fieldName, boolean makeTransient) { final ConstPool constPool = targetClass.getClassFile().getConstPool(); final CtField theField = new CtField( fieldType, fieldName, targetClass ); targetClass.addField( theField ); // make that new field (1) private, (2) transient and (3) @Transient if ( makeTransient ) { theField.setModifiers( theField.getModifiers() | Modifier.TRANSIENT ); } theField.setModifiers( Modifier.setPrivate( theField.getModifiers() ) );

final AnnotationsAttribute annotationsAttribute = getVisibleAnnotations(theField.getFieldInfo()); annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) ); return theField;}Wednesday, September 10, 14

Page 194: Living in the Matrix with Bytecode Manipulation (New Relic)

194

Hibernateprivate CtField addField(CtClass targetClass, CtClass fieldType, String fieldName, boolean makeTransient) { final ConstPool constPool = targetClass.getClassFile().getConstPool(); final CtField theField = new CtField( fieldType, fieldName, targetClass ); targetClass.addField( theField ); // make that new field (1) private, (2) transient and (3) @Transient if ( makeTransient ) { theField.setModifiers( theField.getModifiers() | Modifier.TRANSIENT ); } theField.setModifiers( Modifier.setPrivate( theField.getModifiers() ) );

final AnnotationsAttribute annotationsAttribute = getVisibleAnnotations(theField.getFieldInfo()); annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) ); return theField;}Wednesday, September 10, 14

Page 195: Living in the Matrix with Bytecode Manipulation (New Relic)

195

Hibernateprivate CtField addField(CtClass targetClass, CtClass fieldType, String fieldName, boolean makeTransient) { final ConstPool constPool = targetClass.getClassFile().getConstPool(); final CtField theField = new CtField( fieldType, fieldName, targetClass ); targetClass.addField( theField ); // make that new field (1) private, (2) transient and (3) @Transient if ( makeTransient ) { theField.setModifiers( theField.getModifiers() | Modifier.TRANSIENT ); } theField.setModifiers( Modifier.setPrivate( theField.getModifiers() ) );

final AnnotationsAttribute annotationsAttribute = getVisibleAnnotations(theField.getFieldInfo()); annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) ); return theField;}Wednesday, September 10, 14

Page 196: Living in the Matrix with Bytecode Manipulation (New Relic)

196

Hibernateprivate CtField addField(CtClass targetClass, CtClass fieldType, String fieldName, boolean makeTransient) { final ConstPool constPool = targetClass.getClassFile().getConstPool(); final CtField theField = new CtField( fieldType, fieldName, targetClass ); targetClass.addField( theField ); // make that new field (1) private, (2) transient and (3) @Transient if ( makeTransient ) { theField.setModifiers( theField.getModifiers() | Modifier.TRANSIENT ); } theField.setModifiers( Modifier.setPrivate( theField.getModifiers() ) );

final AnnotationsAttribute annotationsAttribute = getVisibleAnnotations(theField.getFieldInfo()); annotationsAttribute.addAnnotation( new Annotation( Transient.class.getName(), constPool ) ); return theField;}Wednesday, September 10, 14

Page 197: Living in the Matrix with Bytecode Manipulation (New Relic)

197

Hibernate

private void addGetter(CtClass targetClass, CtField theField, String getterName) { targetClass.addMethod( CtNewMethod.getter( getterName, theField ) );}

private void addSetter(CtClass targetClass, CtField theField, String setterName) { targetClass.addMethod( CtNewMethod.setter( setterName, theField ) );}

Wednesday, September 10, 14

Page 198: Living in the Matrix with Bytecode Manipulation (New Relic)

198

Hibernate

private void addGetter(CtClass targetClass, CtField theField, String getterName) { targetClass.addMethod( CtNewMethod.getter( getterName, theField ) );}

private void addSetter(CtClass targetClass, CtField theField, String setterName) { targetClass.addMethod( CtNewMethod.setter( setterName, theField ) );}

Wednesday, September 10, 14

Page 199: Living in the Matrix with Bytecode Manipulation (New Relic)

199

Hibernate

private byte[] getByteCode(CtClass managedCtClass) { final ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); final DataOutputStream out = new DataOutputStream( byteStream ); try { managedCtClass.toBytecode( out ); return byteStream.toByteArray(); } finally { out.close(); }}

Wednesday, September 10, 14

Page 200: Living in the Matrix with Bytecode Manipulation (New Relic)

200

Hibernate

private byte[] getByteCode(CtClass managedCtClass) { final ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); final DataOutputStream out = new DataOutputStream( byteStream ); try { managedCtClass.toBytecode( out ); return byteStream.toByteArray(); } finally { out.close(); }}

Wednesday, September 10, 14

Page 201: Living in the Matrix with Bytecode Manipulation (New Relic)

201

Hibernate

private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {

byte[] result = enhancer.enhance(ctClass.getName(),ctClass.toBytecode()); if(result != null) writeEnhancedClass(javaClassFile, result); }

}

Wednesday, September 10, 14

Page 202: Living in the Matrix with Bytecode Manipulation (New Relic)

202

Hibernate

private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {

byte[] result = enhancer.enhance(ctClass.getName(),ctClass.toBytecode()); if(result != null) writeEnhancedClass(javaClassFile, result); }

}

Wednesday, September 10, 14

Page 203: Living in the Matrix with Bytecode Manipulation (New Relic)

203

Hibernate private void writeEnhancedClass(File javaClassFile, byte[] result) {

if ( javaClassFile.delete() ) { if ( ! javaClassFile.createNewFile() ) { log( "Unable to recreate class file [" + javaClassFile.getName() + "]", Project.MSG_INFO ); } }

FileOutputStream outputStream = new FileOutputStream( javaClassFile, false ); try { outputStream.write( result); outputStream.flush(); } finally { outputStream.close(); } }

Wednesday, September 10, 14

Page 204: Living in the Matrix with Bytecode Manipulation (New Relic)

204

Hibernate private void writeEnhancedClass(File javaClassFile, byte[] result) {

if ( javaClassFile.delete() ) { if ( ! javaClassFile.createNewFile() ) { log( "Unable to recreate class file [" + javaClassFile.getName() + "]", Project.MSG_INFO ); } }

FileOutputStream outputStream = new FileOutputStream( javaClassFile, false ); try { outputStream.write( result); outputStream.flush(); } finally { outputStream.close(); } }

Wednesday, September 10, 14

Page 205: Living in the Matrix with Bytecode Manipulation (New Relic)

205

Hibernate private void writeEnhancedClass(File javaClassFile, byte[] result) {

if ( javaClassFile.delete() ) { if ( ! javaClassFile.createNewFile() ) { log( "Unable to recreate class file [" + javaClassFile.getName() + "]", Project.MSG_INFO ); } }

FileOutputStream outputStream = new FileOutputStream( javaClassFile, false ); try { outputStream.write( result); outputStream.flush(); } finally { outputStream.close(); } }

Wednesday, September 10, 14

Page 206: Living in the Matrix with Bytecode Manipulation (New Relic)

206

Spring

Wednesday, September 10, 14

Page 207: Living in the Matrix with Bytecode Manipulation (New Relic)

207

Spring

Wednesday, September 10, 14

Page 208: Living in the Matrix with Bytecode Manipulation (New Relic)

208

Spring

https://github.com/scottfrederick/spring-music

Wednesday, September 10, 14

Page 209: Living in the Matrix with Bytecode Manipulation (New Relic)

209

Springpackage org.cloudfoundry.samples.music.config.web;

@Configuration@ComponentScan(basePackageClasses = { AlbumController.class })public class WebMvcConfig extends WebMvcConfigurerAdapter {

@Bean public InternalResourceViewResolver internalResourceViewResolver() { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setPrefix("/WEB-INF/"); . . . Wednesday, September 10, 14

Page 210: Living in the Matrix with Bytecode Manipulation (New Relic)

210

Springpackage org.cloudfoundry.samples.music.config.web;

@Configuration@ComponentScan(basePackageClasses = { AlbumController.class })public class WebMvcConfig extends WebMvcConfigurerAdapter {

@Bean public InternalResourceViewResolver internalResourceViewResolver() { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setPrefix("/WEB-INF/"); . . . Wednesday, September 10, 14

Page 211: Living in the Matrix with Bytecode Manipulation (New Relic)

211

Springpackage org.cloudfoundry.samples.music.config.web;

@Configuration@ComponentScan(basePackageClasses = { AlbumController.class })public class WebMvcConfig extends WebMvcConfigurerAdapter {

@Bean public InternalResourceViewResolver internalResourceViewResolver() { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setPrefix("/WEB-INF/"); . . . Wednesday, September 10, 14

Page 212: Living in the Matrix with Bytecode Manipulation (New Relic)

212

Spring

@Controller@RequestMapping(value = "/albums")public class AlbumController { private static final Logger logger = LoggerFactory.getLogger(AlbumController.class);

private final AlbumRepository repository; private SongsService songs;

Wednesday, September 10, 14

Page 213: Living in the Matrix with Bytecode Manipulation (New Relic)

213

Spring

@Controller@RequestMapping(value = "/albums")public class AlbumController { private static final Logger logger = LoggerFactory.getLogger(AlbumController.class);

private final AlbumRepository repository; private SongsService songs;

Wednesday, September 10, 14

Page 214: Living in the Matrix with Bytecode Manipulation (New Relic)

214

Spring

@Component: A generic Spring component

@Repository: Class at the persistence layer

@Controller: Class which receives HTTP servlet requests and responses

@Service: Class at the service level

AlbumRepositoryPopulator

MongoAlbumRepository, JpaAlbumRepository

ErrorController, InfoController, AlbumController

Wednesday, September 10, 14

Page 215: Living in the Matrix with Bytecode Manipulation (New Relic)

215

Springpackage org.springframework.context.annotation;

/** * A component provider that scans the classpath from a base package. * It then applies exclude and include filters to the resulting * classes to find candidates. * * <p>This implementation is based on Spring's * {@link org.springframework.core.type.classreading.MetadataReader * MetadataReader} * facility, backed by an ASM * {@linkorg.springframework.asm.ClassReader ClassReader}. */public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

Wednesday, September 10, 14

Page 216: Living in the Matrix with Bytecode Manipulation (New Relic)

216

Spring

public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + resourcePattern; Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } return candidates;}

Wednesday, September 10, 14

Page 217: Living in the Matrix with Bytecode Manipulation (New Relic)

217

Spring

public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + resourcePattern; Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } return candidates;}

Wednesday, September 10, 14

Page 218: Living in the Matrix with Bytecode Manipulation (New Relic)

218

SpringSimpleMetadataReader

Resource

Wednesday, September 10, 14

Page 219: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

219

SpringSimpleMetadataReader

Resource

Wednesday, September 10, 14

Page 220: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

220

SpringSimpleMetadataReader

metadataResource

Wednesday, September 10, 14

Page 221: Living in the Matrix with Bytecode Manipulation (New Relic)

221

Spring

package org.springframework.core.type.classreading;

final class SimpleMetadataReader implements MetadataReader {

SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader = new ClassReader(is); AnnotationMetadataReadingVisitor visitor = new

AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }

Wednesday, September 10, 14

Page 222: Living in the Matrix with Bytecode Manipulation (New Relic)

222

Spring

package org.springframework.core.type.classreading;

final class SimpleMetadataReader implements MetadataReader {

SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader = new ClassReader(is); AnnotationMetadataReadingVisitor visitor = new

AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }

Wednesday, September 10, 14

Page 223: Living in the Matrix with Bytecode Manipulation (New Relic)

223

Spring

package org.springframework.core.type.classreading;

final class SimpleMetadataReader implements MetadataReader {

SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader = new ClassReader(is); AnnotationMetadataReadingVisitor visitor = new

AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }

Wednesday, September 10, 14

Page 224: Living in the Matrix with Bytecode Manipulation (New Relic)

224

Spring

package org.springframework.core.type.classreading;

final class SimpleMetadataReader implements MetadataReader {

SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader = new ClassReader(is); AnnotationMetadataReadingVisitor visitor = new

AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }

Wednesday, September 10, 14

Page 225: Living in the Matrix with Bytecode Manipulation (New Relic)

225

Spring

package org.springframework.core.type.classreading;

final class SimpleMetadataReader implements MetadataReader {

SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader = new ClassReader(is); AnnotationMetadataReadingVisitor visitor = new

AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }

Wednesday, September 10, 14

Page 226: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

226

Spring

ClassReader

SimpleMetadataReader

metadata

AnnotationMetadataReadingVisitor

Resource

Wednesday, September 10, 14

Page 227: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

227

ASM

ClassReader: event producer

ClassWriter: event consumer

BankTrans

ClassVisitor: event filter

visitField

visitMethod

BankTrans

ClassVisitor: delegates class events, event filter

Wednesday, September 10, 14

Page 228: Living in the Matrix with Bytecode Manipulation (New Relic)

228

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 229: Living in the Matrix with Bytecode Manipulation (New Relic)

229

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 230: Living in the Matrix with Bytecode Manipulation (New Relic)

230

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 231: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

231

Spring

ClassReader

SimpleMetadataReader

metadata

AnnotationMetadataReadingVisitor

Resource

visitMethod

Wednesday, September 10, 14

Page 232: Living in the Matrix with Bytecode Manipulation (New Relic)

232

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 233: Living in the Matrix with Bytecode Manipulation (New Relic)

233

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 234: Living in the Matrix with Bytecode Manipulation (New Relic)

234

Springpublic class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } return new MethodMetadataReadingVisitor(name, access, getClassName(), classLoader, methodMetadataSet); }

public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, attributesMap,

metaAnnotationMap, classLoader); }Wednesday, September 10, 14

Page 235: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

235

Spring

ClassReader

SimpleMetadataReader

metadata

AnnotationMetadataReadingVisitor

Resource

visitMethod

visitAnnotation

Wednesday, September 10, 14

Page 236: Living in the Matrix with Bytecode Manipulation (New Relic)

R:B:

236

Spring

ClassReader

SimpleMetadataReader

metadata

AnnotationMetadataReadingVisitor

Resource

visitMethod

visitAnnotation

visitAnnotation

visitvisitAnnotation

visitArrayvisitEnum

Wednesday, September 10, 14

Page 237: Living in the Matrix with Bytecode Manipulation (New Relic)

237

Springrepackaging ASM in spring-core

Source: http://www.clker.com/clipart-3085.html

Wednesday, September 10, 14

Page 238: Living in the Matrix with Bytecode Manipulation (New Relic)

238

Springrepackaging ASM in spring-core

Source: http://www.clker.com/clipart-3085.html

https://jira.spring.io/browse/SPR-10292

Wednesday, September 10, 14

Page 239: Living in the Matrix with Bytecode Manipulation (New Relic)

239

Lessons Learned

Wednesday, September 10, 14

Page 240: Living in the Matrix with Bytecode Manipulation (New Relic)

240

Lessons Learned

Source: http://www.mindbodygreen.com/0-4965/How-Baby-Steps-Lead-to-Big-Results.html

Wednesday, September 10, 14

Page 241: Living in the Matrix with Bytecode Manipulation (New Relic)

241

Lessons Learned

Source: http://www.dishesandsocks.com/2011/06/16/teacher-classroom-organization-part-1/

Wednesday, September 10, 14

Page 242: Living in the Matrix with Bytecode Manipulation (New Relic)

242

Lessons Learned

Source: http://4hdwallpapers.com/transformer-isolated-on-white-background-3d-render.html

Wednesday, September 10, 14

Page 243: Living in the Matrix with Bytecode Manipulation (New Relic)

243

Applications

Obfuscate so users can not view your source code

Find bugs

Write your own compiler for a language that runs on the JVM

Add logging to an application

Perform preprocessing of code at build time

Restrict API methods for specific users

Determine if a collection is accessed by multiple threads

Lazy load data from a databaseFind differences between jars

Wednesday, September 10, 14

Page 244: Living in the Matrix with Bytecode Manipulation (New Relic)

244

Make bytecode manipulation frameworks your friend. They might save your job one day.

Source: http://www.kulfoto.com/dog-pictures/15799/mans-best-friend

Wednesday, September 10, 14

Page 245: Living in the Matrix with Bytecode Manipulation (New Relic)

245

Wednesday, September 10, 14

Page 246: Living in the Matrix with Bytecode Manipulation (New Relic)

Source: http://alangregerman.typepad.com/.a/6a00d83516c0ad53ef017c3329aba8970b-800wi

Questions?

246

Wednesday, September 10, 14


Top Related