SlideShare une entreprise Scribd logo
1  sur  138
QCon NY 2014
Living in the Matrix with
Bytecode Manipulation
Wednesday, June 11, 14
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/asm-cglib-javassist
Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
Ashley Puls
Senior Software Engineer
New Relic, Inc.
Wednesday, June 11, 14
Follow Along
http://slidesha.re/1kZwCXr
Wednesday, June 11, 14
Outline
• What is bytecode?
• Why manipulate bytecode?
• What frameworks can be used to
manipulate bytecode?
• Examine two frameworks in depth
Wednesday, June 11, 14
What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Wednesday, June 11, 14
What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Wednesday, June 11, 14
What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Instruction set of the Java Virtual Machine
Wednesday, June 11, 14
Why learn about Java bytecode
manipulation frameworks ?
Wednesday, June 11, 14
Why learn about Java bytecode
manipulation frameworks ?
• A bytecode manipulation framework is likely already
on your stack
• Spring
• Hibernate
• Groovy
• Clojure
• Eclipse
• JRuby
Wednesday, June 11, 14
• A bytecode manipulation framework is likely already
on your stack
• Spring
• Hibernate
• Groovy
• Clojure
• Eclipse
• JRuby
• Its really fun!!!
Why learn about Java bytecode
manipulation frameworks ?
Wednesday, June 11, 14
Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• 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, June 11, 14
Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• 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, June 11, 14
Logging
Source: http://www.vforteachers.com/About_NetSupport.htm
Wednesday, June 11, 14
Logging
Source: http://www.vforteachers.com/About_NetSupport.htm, http://inzolo.com/blog/tutorials/bank-accounts
Wednesday, June 11, 14
Logging
Source: http://www.vforteachers.com/About_NetSupport.htm, http://upload.wikimedia.org/wikipedia/commons/d/d3/49024-SOS-ATM.JPG
Wednesday, June 11, 14
Logging
Audit Log: a message should be
logged every time an important
method is called
Source: http://www.vforteachers.com/About_NetSupport.htm
Wednesday, June 11, 14
Logging
Source: http://www.vforteachers.com/About_NetSupport.htm
log important input parameters
to the method
Audit Log: a message should be
logged every time an important
method is called
Wednesday, June 11, 14
Logging
public class BankTransactions {
	 public static void main(String[] args) {
	 	 BankTransactions bank = new BankTransactions();
	 	 // login and add i dollars to account
	 	 for (int i = 0; i < 100; i++) {
	 	 	 String accountId = "account" + i;
	 	 	 bank.login("password", accountId, "Ashley");
bank.unimportantProcessing(accountId);
	 	 	 bank.finalizeTransaction(accountId, Double.valueOf(i));
	 	 }
System.out.println(“Transactions completed”);
	 }
}
Wednesday, June 11, 14
Logging
public class BankTransactions {
	 public static void main(String[] args) {
	 	 BankTransactions bank = new BankTransactions();
	 	 // login and add i dollars to each account
	 	 for (int i = 0; i < 100; i++) {
	 	 	 String accountId = "account" + i;
	 	 	 bank.login("password", accountId, "Ashley");
bank.unimportantProcessing(accountId);
	 	 	 bank.finalizeTransaction(accountId, Double.valueOf(i));
	 	 }
System.out.println(“Transactions completed”);
	 }
}
Wednesday, June 11, 14
Logging
/**
* 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, June 11, 14
Logging
	 public void login(String password, String accountId, String userName) {
	 	 // login logic
	 }
	 public void finalizeTransaction(String accountId, Double moneyToAdd) {
	 	 // transaction logic
	 }
Wednesday, June 11, 14
Logging
	 @ImportantLog(fields = { "1", "2" })
	 public void login(String password, String accountId, String userName) {
	 	 // login logic
	 }
	 @ImportantLog(fields = { "0", "1" })
	 public void finalizeTransaction(String accountId, Double moneyToAdd) {
	 	 // transaction logic
	 }
Wednesday, June 11, 14
Logging
	 @ImportantLog(fields = { "1", "2" })
	 public void login(String password, String accountId, String userName) {
	 	 // login logic
	 }
	 @ImportantLog(fields = { "0", "1" })
	 public void finalizeTransaction(String accountId, Double moneyToAdd) {
	 	 // transaction logic
	 }
A call was made to method "login" on class "com/example/qcon/mains/BankTransactions".
Important params:
Index 1 value: ${accountId}
Index 2 value: ${userName}
Wednesday, June 11, 14
Logging
	 @ImportantLog(fields = { "1", "2" })
	 public void login(String password, String accountId, String userName) {
	 	 // login logic
	 }
	 @ImportantLog(fields = { "0", "1" })
	 public void finalizeTransaction(String accountId, Double moneyToAdd) {
	 	 // transaction logic
	 }
A call was made to method "finalizeTransaction" on class "com/example/qcon/
mains/BankTransactions".
Important params:
Index 0 value: ${accountId}
Index 1 value: ${moneyToAdd}
A call was made to method "login" on class "com/example/qcon/mains/BankTransactions".
Important params:
Index 1 value: ${accountId}
Index 2 value: ${userName}
Wednesday, June 11, 14
Java Agent
• Ability to modify the bytecode without modifying the
application source code
• Added in Java 1.5
• Docs: http://docs.oracle.com/javase/8/docs/api/
java/lang/instrument/package-summary.html
Wednesday, June 11, 14
Typical Java Process
JVM
public static void main(String[] args)
BankTransactions.class
Classloader
java com/example/qcon/mains/BankTransactions
Transactions completed
Wednesday, June 11, 14
Java Agent
JVMAgent
java -javaagent:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Wednesday, June 11, 14
Java Agent
JVM
java -javaagent:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Wednesday, June 11, 14
Java Agent
JVMAgent
java -javaagent:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Wednesday, June 11, 14
Java Agent
JVM
void premain(String agentArgs, Instrumentation inst)
Agent.class
MyTransformer.class
byte[] transform( . . . , byte[] bankTransBytes)
Agent
1. call Agent premain in manifest
2. JVM registers my transformer
java -javaagent:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Wednesday, June 11, 14
Java Agent
JVM
void premain(String agentArgs, Instrumentation inst)
Agent.class
MyTransformer.class
byte[] transform( . . . , byte[] bankTransBytes)
Agent
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:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Wednesday, June 11, 14
Java Agent
JVM
void main(String[] args)
BankTransactions.class
void premain(String agentArgs, Instrumentation inst)
Agent.class
MyTransformer.class
byte[] transform( . . . , byte[] bankTransBytes)
Agent
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:/path/to/agent.jar com/example/qcon/mains/
BankTransactions
Transactions completed
Wednesday, June 11, 14
lets code
Manifest:
Premain-Class: com.example.qcon.agent.Agent
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
public class Agent {
	 public static void premain(String agentArgs, Instrumentation inst) {
	 	 System.out.println("Starting the agent");
	 	 inst.addTransformer(new ImportantLogClassTransformer());
	 }
}
Manifest:
Premain-Class: com.example.qcon.agent.Agent
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
public class Agent {
	 public static void premain(String agentArgs, Instrumentation inst) {
	 	 System.out.println("Starting the agent");
	 	 inst.addTransformer(new ImportantLogClassTransformer());
	 }
}
Manifest:
Premain-Class: com.example.qcon.agent.Agent
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements
ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements
ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
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, June 11, 14
lets code
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/qcon/mains/BankTransactions
Loading class: sun/launcher/LauncherHelper$FXHelper
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
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, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements
ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
// manipulate the bytes here and then return them
	 	 return null;
	 }
}
Wednesday, June 11, 14
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
• Soot: http://www.sable.mcgill.ca/soot/
Wednesday, June 11, 14
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
• Soot: http://www.sable.mcgill.ca/soot/
Wednesday, June 11, 14
Javassist
• Java Programming Assistant
• Subproject of JBoss
• Actively updated
• Version 3.18.2 released May 2014
• Source code: https://github.com/jboss-javassist/
javassist
• Documentation: http://www.csg.ci.i.u-tokyo.ac.jp/
~chiba/javassist/tutorial/tutorial.html
Wednesday, June 11, 14
Javassist
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
CtClass: represents a compile time class
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
ByteArrayClassPath: converts a byte[] to a CtClass
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
CtClass bank = Classpool.getDefault().get(“BankTrans”);
Wednesday, June 11, 14
Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
CtClass bank = Classpool.getDefault().get(“BankTrans”);
Wednesday, June 11, 14
Javassist
BankTrans
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
CtConstructor: represents a compile time constructor
init
BankTrans
CtClass: represents a compile time class
Wednesday, June 11, 14
Javassist
CtConstructor: represents a compile time constructor
init
BankTrans
CtClass: represents a compile time class
CtMethod: represents a compile time class
login
process
Wednesday, June 11, 14
Javassist
CtConstructor: represents a compile time constructor
init
CtClass: represents a compile time class
CtMethod: represents a compile time method
CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”);
BankTrans
login
process
Wednesday, June 11, 14
Javassist
CtConstructor: represents a compile time constructor
init
CtClass: represents a compile time class
CtMethod: represents a compile time method
CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”);
login
BankTrans
login
process
Wednesday, June 11, 14
Javassist
login
CtMethod: represents a compile time method
Wednesday, June 11, 14
Javassist
Modifications:
login
CtMethod: represents a compile time method
Wednesday, June 11, 14
Javassist
Modifications:
login
CtMethod: represents a compile time method
putMethod.insertAfter(“System.out.println(”after method”);”);
putMethod.insertBefore(“System.out.println(“before method”);”);
Wednesday, June 11, 14
Javassist
Modifications:
System.out.println(“before method”);
CtMethod: represents a compile time method
putMethod.insertAfter(“System.out.println(”after method”);”);
putMethod.insertBefore(“System.out.println(“before method”);”);
System.out.println(“after method”);
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
CtClass exceptionType = classpool.get(“java.io.IOException”);
putMethod.addCatch(“{System.out.println($e); throw $e}”, exceptionType);
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
CtClass exceptionType = classpool.get(“java.io.IOException”);
System.out.println(“exception”);
throw exception;
putMethod.addCatch(“{System.out.println($e); throw $e}”, exceptionType);
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
putMethod.insertAt(5, true, “System.out.println(“hello”);”);
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
putMethod.insertAt(5, true, “System.out.println(“hello”);”);
System.out.println(“hello”);
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
insertBefore insertAfter insertAt addCatch
System.out.println(“before method”);
System.out.println(“after method”);
System.out.println(“hello”);
System.out.println(“exception”);
throw exception;
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
insertBefore insertAfter insertAt addCatch
Freeze Class:
System.out.println(“before method”);
System.out.println(“after method”);
System.out.println(“hello”);
System.out.println(“exception”);
throw exception;
login
Wednesday, June 11, 14
Javassist
Modifications:
CtMethod: represents a compile time method
insertBefore insertAfter insertAt addCatch
Freeze Class:
System.out.println(“before method”);
System.out.println(“after method”);
System.out.println(“hello”);
System.out.println(“exception”);
throw exception;
writeFile() toClass() toBytecode()
login
Wednesday, June 11, 14
lets code
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
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 present on method, then
a. get important method parameter indexes
b. add logging statement to beginning of the method
	 return null;
}
Wednesday, June 11, 14
lets code
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, June 11, 14
lets code
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, June 11, 14
lets code
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, June 11, 14
lets code
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, June 11, 14
lets code
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, June 11, 14
lets code
	 	 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.qcon.mains.ImportantLog");
	 	 }
	 	 return null;
	 }
Wednesday, June 11, 14
lets code
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, June 11, 14
lets code
	 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, June 11, 14
lets code
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, June 11, 14
lets code
	 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, June 11, 14
lets code
	 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, June 11, 14
lets code
	 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, June 11, 14
lets code
	 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, ... can be used to access
“this” and the actual method parameters
Wednesday, June 11, 14
lets code
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, June 11, 14
Javassist
• Positives
• Simplicity
• Do not have to write actual bytecode
• Decent documentation
• Negatives
• Generally slower than ASM
• Less functionality
Wednesday, June 11, 14
ASM
• Released in Open Source in 2002
• Actively updated
• Version 5.0.3 released May 2014
• Two ASM libraries
• Event based (SAX like)
• Visitor design pattern
• Object based (DOM like)
• Documentation: http://download.forge.objectweb.org/
asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
ClassVisitor:
event filter
Wednesday, June 11, 14
ASM
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassWriter: produces output byte[]
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
BankTrans
ClassVisitor:
event filter
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.Instrumentation;
public class Agent {
	 public static void premain(String agentArgs, Instrumentation inst) {
	 	 System.out.println("Starting the agent");
	 	 inst.addTransformer(new ImportantLogClassTransformer());
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.Instrumentation;
public class Agent {
	 public static void premain(String agentArgs, Instrumentation inst) {
	 	 System.out.println("Starting the agent");
	 	 inst.addTransformer(new ImportantLogClassTransformer());
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
	 public byte[] transform(ClassLoader loader, String className,
	 	 	 Class classBeingRedefined, ProtectionDomain protectionDomain,
	 	 	 byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
	 	 return null;
	 }
}
Wednesday, June 11, 14
lets code
	 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();
	 }
Wednesday, June 11, 14
lets code
	 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();
	 }
Wednesday, June 11, 14
ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassWriter: produces output byte[]
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitMethod
BankTrans
LogMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
lets code
public class LogMethodClassVisitor extends ClassVisitor {
	 private String className;
	 public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) {
	 	 super(Opcodes.ASM5, cv);
	 	 className = pClassName;
	 }
	 @Override
	 public MethodVisitor visitMethod(int access, String name, String desc,
	 	 	 String signature, String[] exceptions) {
// put our logic in here
	 }
}
Wednesday, June 11, 14
lets code
public class LogMethodClassVisitor extends ClassVisitor {
	 private String className;
	 public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) {
	 	 super(Opcodes.ASM5, cv);
	 	 className = pClassName;
	 }
	 @Override
	 public MethodVisitor visitMethod(int access, String name, String desc,
	 	 	 String signature, String[] exceptions) {
// put our logic in here
	 }
}
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
ASM
MethodVisitor: method event filter
MethodVisitor MethodVisitor
visitCode
login method
visitor
login method
visitor
visitAnnotation visitEnd visitCode visitTryCatchBlock
visitEnd
visitAttribute
Wednesday, June 11, 14
ASM
Source: http://download.forge.objectweb.org/asm/asm4-guide.pdf
Wednesday, June 11, 14
lets code
public class LogMethodClassVisitor extends ClassVisitor {
	 private String className;
	 public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) {
	 	 super(Opcodes.ASM5, cv);
	 	 className = pClassName;
	 }
	 @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, June 11, 14
lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
	 	 private String methodName;
	 	 private String className;
private boolean isAnnotationPresent;
	 	 private List<String> parameterIndexes;
	 	 public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
	 	 	 	 String className) {
	 	 }
	 	 @Override
	 	 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
	
	 	 }
	 	 @Override
	 	 public void visitCode() {
}
}
Wednesday, June 11, 14
lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
	 	 private String methodName;
	 	 private String className;
private boolean isAnnotationPresent;
	 	 private List<String> parameterIndexes;
	 	 public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
	 	 	 	 String className) {
// initialize instance variables
	 	 }
	 	 @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, June 11, 14
lets code
	
	 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
	 	 if ("Lcom/example/qcon/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, June 11, 14
lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
	 	 private String methodName;
	 	 private String className;
private boolean isAnnotationPresent;
	 	 private List<String> parameterIndexes;
	 	 public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
	 	 	 	 String className) {
// initialize instance variables
	 	 }
	 	 @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 statement to beginning of the method
}
}
Wednesday, June 11, 14
lets code
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, June 11, 14
What is Java bytecode?
use javap to examine the bytecode
Wednesday, June 11, 14
What is Java bytecode?
public 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, June 11, 14
What is Java bytecode?
javap -c bin/com/example/qcon/mains/PrintMessage
Wednesday, June 11, 14
What is Java bytecode?
javap -c bin/com/example/qcon/mains/PrintMessage
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;
. . .
Wednesday, June 11, 14
What is Java bytecode?
each line represents one JVM instruction
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;
. . .
Wednesday, June 11, 14
What is Java bytecode?
JVM instruction = 1 opcode with 0 or more operands
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;
. . .
Wednesday, June 11, 14
What is Java bytecode?
Opcode Description
load
load a reference onto the stack from a local variable
iload, lload, fload, dload, aload
store
stores a value from the stack into a local variable
istore, lstore, fstore, dstore, astore
lcd push a constant from a constant pool onto the stack
return
method return instruction
ireturn, lreturn, freturn, dreturn, areturn, return
invokevirtual invoke an instance method on an object
invokespecial
invokes an instance method requiring special handling
such as an initialization method
See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf
Wednesday, June 11, 14
What is Java bytecode?
Opcode Description
invokestatic invoke a class (static) method on a named class
athrow throws an exception
new create a new class
newarray create a new array
if<cond>
branch if int comparison with 0 succeeds
ifeq, ifne, iflt, ifge, ifgt, ifle
dup duplicate the top operand stack value
See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf
Wednesday, June 11, 14
What is Java bytecode?
Wednesday, June 11, 14
What is Java bytecode?
Wednesday, June 11, 14
What is Java bytecode?
Wednesday, June 11, 14
lets code
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, June 11, 14
ASM
• Positives
• Speed and Size
• Decent documentation
• Depth of Functionality
• Number of people using framework
• Negatives
• Need to write actual byte code
• longer developer ramp-up
Wednesday, June 11, 14
Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• 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, June 11, 14
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
• Soot: http://www.sable.mcgill.ca/soot/
Wednesday, June 11, 14
Source: http://www.kulfoto.com/dog-pictures/15799/mans-best-friend
Make bytecode manipulation frameworks your friend.
They might save your job one day.
Wednesday, June 11, 14
Wednesday, June 11, 14
Source: http://alangregerman.typepad.com/.a/6a00d83516c0ad53ef017c3329aba8970b-800wi
Questions?
Wednesday, June 11, 14
Watch the video with slide synchronization on
InfoQ.com!
http://www.infoq.com/presentations/asm-cglib-
javassist

Contenu connexe

En vedette

Prinya acis slide for swpark - it & information security human resource deve...
Prinya acis slide for swpark  - it & information security human resource deve...Prinya acis slide for swpark  - it & information security human resource deve...
Prinya acis slide for swpark - it & information security human resource deve...
TISA
 
What Good is this Tool? A Guide to Choosing the Right Application Security Te...
What Good is this Tool? A Guide to Choosing the Right Application Security Te...What Good is this Tool? A Guide to Choosing the Right Application Security Te...
What Good is this Tool? A Guide to Choosing the Right Application Security Te...
Kevin Fealey
 
Continuous Application Security at Scale with IAST and RASP -- Transforming D...
Continuous Application Security at Scale with IAST and RASP -- Transforming D...Continuous Application Security at Scale with IAST and RASP -- Transforming D...
Continuous Application Security at Scale with IAST and RASP -- Transforming D...
Jeff Williams
 
The differing ways to monitor and instrument
The differing ways to monitor and instrumentThe differing ways to monitor and instrument
The differing ways to monitor and instrument
Jonah Kowall
 
Injecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at RuntimeInjecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at Runtime
Ajin Abraham
 

En vedette (7)

Application Security Workshop
Application Security Workshop Application Security Workshop
Application Security Workshop
 
Prinya acis slide for swpark - it & information security human resource deve...
Prinya acis slide for swpark  - it & information security human resource deve...Prinya acis slide for swpark  - it & information security human resource deve...
Prinya acis slide for swpark - it & information security human resource deve...
 
HackFest 2015 - Rasp vs waf
HackFest 2015 - Rasp vs wafHackFest 2015 - Rasp vs waf
HackFest 2015 - Rasp vs waf
 
What Good is this Tool? A Guide to Choosing the Right Application Security Te...
What Good is this Tool? A Guide to Choosing the Right Application Security Te...What Good is this Tool? A Guide to Choosing the Right Application Security Te...
What Good is this Tool? A Guide to Choosing the Right Application Security Te...
 
Continuous Application Security at Scale with IAST and RASP -- Transforming D...
Continuous Application Security at Scale with IAST and RASP -- Transforming D...Continuous Application Security at Scale with IAST and RASP -- Transforming D...
Continuous Application Security at Scale with IAST and RASP -- Transforming D...
 
The differing ways to monitor and instrument
The differing ways to monitor and instrumentThe differing ways to monitor and instrument
The differing ways to monitor and instrument
 
Injecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at RuntimeInjecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at Runtime
 

Similaire à Living in the Matrix with Bytecode Manipulation

Intro to PHP Testing
Intro to PHP TestingIntro to PHP Testing
Intro to PHP Testing
Ran Mizrahi
 
Practices and tools for building better APIs
Practices and tools for building better APIsPractices and tools for building better APIs
Practices and tools for building better APIs
NLJUG
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
Sanjaykumar Kakaso Mane_MAY2016
Sanjaykumar Kakaso Mane_MAY2016Sanjaykumar Kakaso Mane_MAY2016
Sanjaykumar Kakaso Mane_MAY2016
Sanjay Mane
 
Webdevcon Keynote hh-2012-09-18
Webdevcon Keynote hh-2012-09-18Webdevcon Keynote hh-2012-09-18
Webdevcon Keynote hh-2012-09-18
Pierre Joye
 

Similaire à Living in the Matrix with Bytecode Manipulation (20)

Bytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASMBytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASM
 
Practical Internationalization Improvement for Sakai CLE
Practical Internationalization Improvement for Sakai CLEPractical Internationalization Improvement for Sakai CLE
Practical Internationalization Improvement for Sakai CLE
 
Intro to PHP Testing
Intro to PHP TestingIntro to PHP Testing
Intro to PHP Testing
 
Microservices and the Art of Taming the Dependency Hell Monster
Microservices and the Art of Taming the Dependency Hell MonsterMicroservices and the Art of Taming the Dependency Hell Monster
Microservices and the Art of Taming the Dependency Hell Monster
 
Practices and Tools for Building Better APIs
Practices and Tools for Building Better APIsPractices and Tools for Building Better APIs
Practices and Tools for Building Better APIs
 
Practices and tools for building better APIs
Practices and tools for building better APIsPractices and tools for building better APIs
Practices and tools for building better APIs
 
Practices and tools for building better API (JFall 2013)
Practices and tools for building better API (JFall 2013)Practices and tools for building better API (JFall 2013)
Practices and tools for building better API (JFall 2013)
 
Life beyond Java 8
Life beyond Java 8Life beyond Java 8
Life beyond Java 8
 
ELUNA2014: Developing and Testing an open source web application
ELUNA2014: Developing and Testing an open source web applicationELUNA2014: Developing and Testing an open source web application
ELUNA2014: Developing and Testing an open source web application
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
Generating Unified APIs with Protocol Buffers and gRPC
Generating Unified APIs with Protocol Buffers and gRPCGenerating Unified APIs with Protocol Buffers and gRPC
Generating Unified APIs with Protocol Buffers and gRPC
 
SPTechCon 2014 How to develop and debug client side code in SharePoint
SPTechCon 2014 How to develop and debug client side code in SharePointSPTechCon 2014 How to develop and debug client side code in SharePoint
SPTechCon 2014 How to develop and debug client side code in SharePoint
 
Example Of Import Java
Example Of Import JavaExample Of Import Java
Example Of Import Java
 
Donald Ferguson - Old Programmers Can Learn New Tricks
Donald Ferguson - Old Programmers Can Learn New TricksDonald Ferguson - Old Programmers Can Learn New Tricks
Donald Ferguson - Old Programmers Can Learn New Tricks
 
REST API Best Practices & Implementing in Codeigniter
REST API Best Practices & Implementing in CodeigniterREST API Best Practices & Implementing in Codeigniter
REST API Best Practices & Implementing in Codeigniter
 
Sanjaykumar Kakaso Mane_MAY2016
Sanjaykumar Kakaso Mane_MAY2016Sanjaykumar Kakaso Mane_MAY2016
Sanjaykumar Kakaso Mane_MAY2016
 
Webdevcon Keynote hh-2012-09-18
Webdevcon Keynote hh-2012-09-18Webdevcon Keynote hh-2012-09-18
Webdevcon Keynote hh-2012-09-18
 
Migrating to Jakarta EE 10
Migrating to Jakarta EE 10Migrating to Jakarta EE 10
Migrating to Jakarta EE 10
 
Introduction to node.js by Ran Mizrahi @ Reversim Summit
Introduction to node.js by Ran Mizrahi @ Reversim SummitIntroduction to node.js by Ran Mizrahi @ Reversim Summit
Introduction to node.js by Ran Mizrahi @ Reversim Summit
 
API workshop: Introduction to APIs (TC Camp)
API workshop: Introduction to APIs (TC Camp)API workshop: Introduction to APIs (TC Camp)
API workshop: Introduction to APIs (TC Camp)
 

Plus de C4Media

Plus de C4Media (20)

Streaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live VideoStreaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
 
Next Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy MobileNext Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy Mobile
 
Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020
 
Understand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java ApplicationsUnderstand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java Applications
 
Kafka Needs No Keeper
Kafka Needs No KeeperKafka Needs No Keeper
Kafka Needs No Keeper
 
High Performing Teams Act Like Owners
High Performing Teams Act Like OwnersHigh Performing Teams Act Like Owners
High Performing Teams Act Like Owners
 
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to JavaDoes Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
 
Service Meshes- The Ultimate Guide
Service Meshes- The Ultimate GuideService Meshes- The Ultimate Guide
Service Meshes- The Ultimate Guide
 
Shifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CDShifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CD
 
CI/CD for Machine Learning
CI/CD for Machine LearningCI/CD for Machine Learning
CI/CD for Machine Learning
 
Fault Tolerance at Speed
Fault Tolerance at SpeedFault Tolerance at Speed
Fault Tolerance at Speed
 
Architectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep SystemsArchitectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep Systems
 
ML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.jsML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.js
 
Build Your Own WebAssembly Compiler
Build Your Own WebAssembly CompilerBuild Your Own WebAssembly Compiler
Build Your Own WebAssembly Compiler
 
User & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix ScaleUser & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix Scale
 
Scaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's EdgeScaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's Edge
 
Make Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home EverywhereMake Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home Everywhere
 
The Talk You've Been Await-ing For
The Talk You've Been Await-ing ForThe Talk You've Been Await-ing For
The Talk You've Been Await-ing For
 
Future of Data Engineering
Future of Data EngineeringFuture of Data Engineering
Future of Data Engineering
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreAutomated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
 

Dernier

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Dernier (20)

Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 

Living in the Matrix with Bytecode Manipulation

  • 1. QCon NY 2014 Living in the Matrix with Bytecode Manipulation Wednesday, June 11, 14
  • 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /asm-cglib-javassist
  • 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  • 4. Ashley Puls Senior Software Engineer New Relic, Inc. Wednesday, June 11, 14
  • 6. Outline • What is bytecode? • Why manipulate bytecode? • What frameworks can be used to manipulate bytecode? • Examine two frameworks in depth Wednesday, June 11, 14
  • 7. What is Java bytecode? .java file .class file Java compiler (javac) Class Loader Bytecode Verifier Java Runtime System Native OS Java Virtual Machine Source: http://www.techlila.com/write-programs-linux/ Wednesday, June 11, 14
  • 8. What is Java bytecode? .java file .class file Java compiler (javac) Class Loader Bytecode Verifier Java Runtime System Native OS Java Virtual Machine Source: http://www.techlila.com/write-programs-linux/ Wednesday, June 11, 14
  • 9. What is Java bytecode? .java file .class file Java compiler (javac) Class Loader Bytecode Verifier Java Runtime System Native OS Java Virtual Machine Source: http://www.techlila.com/write-programs-linux/ Instruction set of the Java Virtual Machine Wednesday, June 11, 14
  • 10. Why learn about Java bytecode manipulation frameworks ? Wednesday, June 11, 14
  • 11. Why learn about Java bytecode manipulation frameworks ? • A bytecode manipulation framework is likely already on your stack • Spring • Hibernate • Groovy • Clojure • Eclipse • JRuby Wednesday, June 11, 14
  • 12. • A bytecode manipulation framework is likely already on your stack • Spring • Hibernate • Groovy • Clojure • Eclipse • JRuby • Its really fun!!! Why learn about Java bytecode manipulation frameworks ? Wednesday, June 11, 14
  • 13. Why learn about Java bytecode manipulation frameworks ? • program analysis • find bugs in code • examine code complexity • generate classes • 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, June 11, 14
  • 14. Why learn about Java bytecode manipulation frameworks ? • program analysis • find bugs in code • examine code complexity • generate classes • 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, June 11, 14
  • 18. Logging Audit Log: a message should be logged every time an important method is called Source: http://www.vforteachers.com/About_NetSupport.htm Wednesday, June 11, 14
  • 19. Logging Source: http://www.vforteachers.com/About_NetSupport.htm log important input parameters to the method Audit Log: a message should be logged every time an important method is called Wednesday, June 11, 14
  • 20. Logging public class BankTransactions { public static void main(String[] args) { BankTransactions bank = new BankTransactions(); // login and add i dollars to account for (int i = 0; i < 100; i++) { String accountId = "account" + i; bank.login("password", accountId, "Ashley"); bank.unimportantProcessing(accountId); bank.finalizeTransaction(accountId, Double.valueOf(i)); } System.out.println(“Transactions completed”); } } Wednesday, June 11, 14
  • 21. Logging public class BankTransactions { public static void main(String[] args) { BankTransactions bank = new BankTransactions(); // login and add i dollars to each account for (int i = 0; i < 100; i++) { String accountId = "account" + i; bank.login("password", accountId, "Ashley"); bank.unimportantProcessing(accountId); bank.finalizeTransaction(accountId, Double.valueOf(i)); } System.out.println(“Transactions completed”); } } Wednesday, June 11, 14
  • 22. Logging /** * 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, June 11, 14
  • 23. Logging public void login(String password, String accountId, String userName) { // login logic } public void finalizeTransaction(String accountId, Double moneyToAdd) { // transaction logic } Wednesday, June 11, 14
  • 24. Logging @ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic } @ImportantLog(fields = { "0", "1" }) public void finalizeTransaction(String accountId, Double moneyToAdd) { // transaction logic } Wednesday, June 11, 14
  • 25. Logging @ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic } @ImportantLog(fields = { "0", "1" }) public void finalizeTransaction(String accountId, Double moneyToAdd) { // transaction logic } A call was made to method "login" on class "com/example/qcon/mains/BankTransactions". Important params: Index 1 value: ${accountId} Index 2 value: ${userName} Wednesday, June 11, 14
  • 26. Logging @ImportantLog(fields = { "1", "2" }) public void login(String password, String accountId, String userName) { // login logic } @ImportantLog(fields = { "0", "1" }) public void finalizeTransaction(String accountId, Double moneyToAdd) { // transaction logic } A call was made to method "finalizeTransaction" on class "com/example/qcon/ mains/BankTransactions". Important params: Index 0 value: ${accountId} Index 1 value: ${moneyToAdd} A call was made to method "login" on class "com/example/qcon/mains/BankTransactions". Important params: Index 1 value: ${accountId} Index 2 value: ${userName} Wednesday, June 11, 14
  • 27. Java Agent • Ability to modify the bytecode without modifying the application source code • Added in Java 1.5 • Docs: http://docs.oracle.com/javase/8/docs/api/ java/lang/instrument/package-summary.html Wednesday, June 11, 14
  • 28. Typical Java Process JVM public static void main(String[] args) BankTransactions.class Classloader java com/example/qcon/mains/BankTransactions Transactions completed Wednesday, June 11, 14
  • 29. Java Agent JVMAgent java -javaagent:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Wednesday, June 11, 14
  • 30. Java Agent JVM java -javaagent:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Wednesday, June 11, 14
  • 31. Java Agent JVMAgent java -javaagent:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Wednesday, June 11, 14
  • 32. Java Agent JVM void premain(String agentArgs, Instrumentation inst) Agent.class MyTransformer.class byte[] transform( . . . , byte[] bankTransBytes) Agent 1. call Agent premain in manifest 2. JVM registers my transformer java -javaagent:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Wednesday, June 11, 14
  • 33. Java Agent JVM void premain(String agentArgs, Instrumentation inst) Agent.class MyTransformer.class byte[] transform( . . . , byte[] bankTransBytes) Agent 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:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Wednesday, June 11, 14
  • 34. Java Agent JVM void main(String[] args) BankTransactions.class void premain(String agentArgs, Instrumentation inst) Agent.class MyTransformer.class byte[] transform( . . . , byte[] bankTransBytes) Agent 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:/path/to/agent.jar com/example/qcon/mains/ BankTransactions Transactions completed Wednesday, June 11, 14
  • 36. lets code package com.example.qcon.agent; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); } } Manifest: Premain-Class: com.example.qcon.agent.Agent Wednesday, June 11, 14
  • 37. lets code package com.example.qcon.agent; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); } } Manifest: Premain-Class: com.example.qcon.agent.Agent Wednesday, June 11, 14
  • 38. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 39. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 40. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; 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, June 11, 14
  • 41. lets code 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/qcon/mains/BankTransactions Loading class: sun/launcher/LauncherHelper$FXHelper Wednesday, June 11, 14
  • 42. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; 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, June 11, 14
  • 43. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // manipulate the bytes here and then return them return null; } } Wednesday, June 11, 14
  • 44. 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 • Soot: http://www.sable.mcgill.ca/soot/ Wednesday, June 11, 14
  • 45. 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 • Soot: http://www.sable.mcgill.ca/soot/ Wednesday, June 11, 14
  • 46. Javassist • Java Programming Assistant • Subproject of JBoss • Actively updated • Version 3.18.2 released May 2014 • Source code: https://github.com/jboss-javassist/ javassist • Documentation: http://www.csg.ci.i.u-tokyo.ac.jp/ ~chiba/javassist/tutorial/tutorial.html Wednesday, June 11, 14
  • 47. Javassist ArrayList HashMap CtClass: represents a compile time class Wednesday, June 11, 14
  • 48. Javassist ClassPool: container of CtClass objects ArrayList HashMap CtClass: represents a compile time class Wednesday, June 11, 14
  • 49. Javassist ClassPool: container of CtClass objects ArrayList HashMap ArrayList HashMap CtClass: represents a compile time class Wednesday, June 11, 14
  • 50. Javassist ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap CtClass: represents a compile time class Wednesday, June 11, 14
  • 51. Javassist CtClass: represents a compile time class ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap ByteArrayClassPath: converts a byte[] to a CtClass Wednesday, June 11, 14
  • 52. Javassist ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes); ByteArrayClassPath: converts a byte[] to a CtClass CtClass: represents a compile time class Wednesday, June 11, 14
  • 53. Javassist ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap BankTrans Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes); ByteArrayClassPath: converts a byte[] to a CtClass CtClass: represents a compile time class Wednesday, June 11, 14
  • 54. Javassist ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap BankTrans Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes); ByteArrayClassPath: converts a byte[] to a CtClass CtClass: represents a compile time class CtClass bank = Classpool.getDefault().get(“BankTrans”); Wednesday, June 11, 14
  • 55. Javassist ClassPool: container of CtClass objects ArrayList HashMap BankTrans ArrayList HashMap BankTrans ByteArrayClassPath: converts a byte[] to a CtClass CtClass: represents a compile time class BankTrans Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes); CtClass bank = Classpool.getDefault().get(“BankTrans”); Wednesday, June 11, 14
  • 56. Javassist BankTrans CtClass: represents a compile time class Wednesday, June 11, 14
  • 57. Javassist CtConstructor: represents a compile time constructor init BankTrans CtClass: represents a compile time class Wednesday, June 11, 14
  • 58. Javassist CtConstructor: represents a compile time constructor init BankTrans CtClass: represents a compile time class CtMethod: represents a compile time class login process Wednesday, June 11, 14
  • 59. Javassist CtConstructor: represents a compile time constructor init CtClass: represents a compile time class CtMethod: represents a compile time method CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”); BankTrans login process Wednesday, June 11, 14
  • 60. Javassist CtConstructor: represents a compile time constructor init CtClass: represents a compile time class CtMethod: represents a compile time method CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”); login BankTrans login process Wednesday, June 11, 14
  • 61. Javassist login CtMethod: represents a compile time method Wednesday, June 11, 14
  • 62. Javassist Modifications: login CtMethod: represents a compile time method Wednesday, June 11, 14
  • 63. Javassist Modifications: login CtMethod: represents a compile time method putMethod.insertAfter(“System.out.println(”after method”);”); putMethod.insertBefore(“System.out.println(“before method”);”); Wednesday, June 11, 14
  • 64. Javassist Modifications: System.out.println(“before method”); CtMethod: represents a compile time method putMethod.insertAfter(“System.out.println(”after method”);”); putMethod.insertBefore(“System.out.println(“before method”);”); System.out.println(“after method”); login Wednesday, June 11, 14
  • 65. Javassist Modifications: CtMethod: represents a compile time method CtClass exceptionType = classpool.get(“java.io.IOException”); putMethod.addCatch(“{System.out.println($e); throw $e}”, exceptionType); login Wednesday, June 11, 14
  • 66. Javassist Modifications: CtMethod: represents a compile time method CtClass exceptionType = classpool.get(“java.io.IOException”); System.out.println(“exception”); throw exception; putMethod.addCatch(“{System.out.println($e); throw $e}”, exceptionType); login Wednesday, June 11, 14
  • 67. Javassist Modifications: CtMethod: represents a compile time method putMethod.insertAt(5, true, “System.out.println(“hello”);”); login Wednesday, June 11, 14
  • 68. Javassist Modifications: CtMethod: represents a compile time method putMethod.insertAt(5, true, “System.out.println(“hello”);”); System.out.println(“hello”); login Wednesday, June 11, 14
  • 69. Javassist Modifications: CtMethod: represents a compile time method insertBefore insertAfter insertAt addCatch System.out.println(“before method”); System.out.println(“after method”); System.out.println(“hello”); System.out.println(“exception”); throw exception; login Wednesday, June 11, 14
  • 70. Javassist Modifications: CtMethod: represents a compile time method insertBefore insertAfter insertAt addCatch Freeze Class: System.out.println(“before method”); System.out.println(“after method”); System.out.println(“hello”); System.out.println(“exception”); throw exception; login Wednesday, June 11, 14
  • 71. Javassist Modifications: CtMethod: represents a compile time method insertBefore insertAfter insertAt addCatch Freeze Class: System.out.println(“before method”); System.out.println(“after method”); System.out.println(“hello”); System.out.println(“exception”); throw exception; writeFile() toClass() toBytecode() login Wednesday, June 11, 14
  • 73. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 74. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 75. lets code 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 present on method, then a. get important method parameter indexes b. add logging statement to beginning of the method return null; } Wednesday, June 11, 14
  • 76. lets code 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, June 11, 14
  • 77. lets code 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, June 11, 14
  • 78. lets code 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, June 11, 14
  • 79. lets code 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, June 11, 14
  • 80. lets code 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, June 11, 14
  • 81. lets code 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.qcon.mains.ImportantLog"); } return null; } Wednesday, June 11, 14
  • 82. lets code 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, June 11, 14
  • 83. lets code 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, June 11, 14
  • 84. lets code 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, June 11, 14
  • 85. lets code 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, June 11, 14
  • 86. lets code 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, June 11, 14
  • 87. lets code 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, June 11, 14
  • 88. lets code 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, ... can be used to access “this” and the actual method parameters Wednesday, June 11, 14
  • 89. lets code 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, June 11, 14
  • 90. Javassist • Positives • Simplicity • Do not have to write actual bytecode • Decent documentation • Negatives • Generally slower than ASM • Less functionality Wednesday, June 11, 14
  • 91. ASM • Released in Open Source in 2002 • Actively updated • Version 5.0.3 released May 2014 • Two ASM libraries • Event based (SAX like) • Visitor design pattern • Object based (DOM like) • Documentation: http://download.forge.objectweb.org/ asm/asm4-guide.pdf Wednesday, June 11, 14
  • 95. ASM ClassReader: given a byte[], parses a compiled class ClassReader: event producer BankTrans ClassWriter: event consumer ClassVisitor: event filter Wednesday, June 11, 14
  • 96. ASM ClassReader: given a byte[], parses a compiled class ClassReader: event producer BankTrans ClassWriter: event consumer visitField visitMethod ClassVisitor: event filter Wednesday, June 11, 14
  • 97. ASM ClassVisitor: delegates class events, event filter visitAttribute visitField visitMethod visitInnerClass visitEnd ClassReader: given a byte[], parses a compiled class ClassReader: event producer BankTrans ClassWriter: event consumer visitField visitMethod ClassVisitor: event filter Wednesday, June 11, 14
  • 98. ASM ClassVisitor: delegates class events, event filter visitAttribute visitField visitMethod visitInnerClass visitEnd ClassWriter: produces output byte[] ClassReader: given a byte[], parses a compiled class ClassReader: event producer BankTrans ClassWriter: event consumer visitField visitMethod BankTrans ClassVisitor: event filter Wednesday, June 11, 14
  • 99. lets code package com.example.qcon.agent; import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); } } Wednesday, June 11, 14
  • 100. lets code package com.example.qcon.agent; import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("Starting the agent"); inst.addTransformer(new ImportantLogClassTransformer()); } } Wednesday, June 11, 14
  • 101. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 102. lets code package com.example.qcon.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; public class ImportantLogClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //TODO return null; } } Wednesday, June 11, 14
  • 103. lets code 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(); } Wednesday, June 11, 14
  • 104. lets code 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(); } Wednesday, June 11, 14
  • 105. ASM ClassVisitor: delegates class events, event filter visitAttribute visitField visitMethod visitInnerClass visitEnd ClassWriter: produces output byte[] ClassReader: given a byte[], parses a compiled class ClassReader: event producer BankTrans ClassWriter: event consumer visitMethod BankTrans LogMethod ClassVisitor: event filter Wednesday, June 11, 14
  • 107. lets code public class LogMethodClassVisitor extends ClassVisitor { private String className; public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) { super(Opcodes.ASM5, cv); className = pClassName; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // put our logic in here } } Wednesday, June 11, 14
  • 108. lets code public class LogMethodClassVisitor extends ClassVisitor { private String className; public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) { super(Opcodes.ASM5, cv); className = pClassName; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // put our logic in here } } Wednesday, June 11, 14
  • 112. ASM MethodVisitor: method event filter MethodVisitor MethodVisitor visitCode login method visitor login method visitor visitAnnotation visitEnd visitCode visitTryCatchBlock visitEnd visitAttribute Wednesday, June 11, 14
  • 114. lets code public class LogMethodClassVisitor extends ClassVisitor { private String className; public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) { super(Opcodes.ASM5, cv); className = pClassName; } @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, June 11, 14
  • 115. lets code public class PrintMessageMethodVisitor extends MethodVisitor { private String methodName; private String className; private boolean isAnnotationPresent; private List<String> parameterIndexes; public PrintMessageMethodVisitor(MethodVisitor mv, String methodName, String className) { } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { } @Override public void visitCode() { } } Wednesday, June 11, 14
  • 116. lets code public class PrintMessageMethodVisitor extends MethodVisitor { private String methodName; private String className; private boolean isAnnotationPresent; private List<String> parameterIndexes; public PrintMessageMethodVisitor(MethodVisitor mv, String methodName, String className) { // initialize instance variables } @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, June 11, 14
  • 117. lets code public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if ("Lcom/example/qcon/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, June 11, 14
  • 118. lets code public class PrintMessageMethodVisitor extends MethodVisitor { private String methodName; private String className; private boolean isAnnotationPresent; private List<String> parameterIndexes; public PrintMessageMethodVisitor(MethodVisitor mv, String methodName, String className) { // initialize instance variables } @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 statement to beginning of the method } } Wednesday, June 11, 14
  • 119. lets code 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, June 11, 14
  • 120. What is Java bytecode? use javap to examine the bytecode Wednesday, June 11, 14
  • 121. What is Java bytecode? public 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, June 11, 14
  • 122. What is Java bytecode? javap -c bin/com/example/qcon/mains/PrintMessage Wednesday, June 11, 14
  • 123. What is Java bytecode? javap -c bin/com/example/qcon/mains/PrintMessage 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; . . . Wednesday, June 11, 14
  • 124. What is Java bytecode? each line represents one JVM instruction 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; . . . Wednesday, June 11, 14
  • 125. What is Java bytecode? JVM instruction = 1 opcode with 0 or more operands 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; . . . Wednesday, June 11, 14
  • 126. What is Java bytecode? Opcode Description load load a reference onto the stack from a local variable iload, lload, fload, dload, aload store stores a value from the stack into a local variable istore, lstore, fstore, dstore, astore lcd push a constant from a constant pool onto the stack return method return instruction ireturn, lreturn, freturn, dreturn, areturn, return invokevirtual invoke an instance method on an object invokespecial invokes an instance method requiring special handling such as an initialization method See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf Wednesday, June 11, 14
  • 127. What is Java bytecode? Opcode Description invokestatic invoke a class (static) method on a named class athrow throws an exception new create a new class newarray create a new array if<cond> branch if int comparison with 0 succeeds ifeq, ifne, iflt, ifge, ifgt, ifle dup duplicate the top operand stack value See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf Wednesday, June 11, 14
  • 128. What is Java bytecode? Wednesday, June 11, 14
  • 129. What is Java bytecode? Wednesday, June 11, 14
  • 130. What is Java bytecode? Wednesday, June 11, 14
  • 131. lets code 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, June 11, 14
  • 132. ASM • Positives • Speed and Size • Decent documentation • Depth of Functionality • Number of people using framework • Negatives • Need to write actual byte code • longer developer ramp-up Wednesday, June 11, 14
  • 133. Why learn about Java bytecode manipulation frameworks ? • program analysis • find bugs in code • examine code complexity • generate classes • 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, June 11, 14
  • 134. 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 • Soot: http://www.sable.mcgill.ca/soot/ Wednesday, June 11, 14
  • 135. Source: http://www.kulfoto.com/dog-pictures/15799/mans-best-friend Make bytecode manipulation frameworks your friend. They might save your job one day. Wednesday, June 11, 14
  • 138. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations/asm-cglib- javassist