Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Как и зачем инструментировать
байткод в Java приложениях?
Специально для JUG.ua, 25 мая 2017
@antonarhipov
Антон Архипов
@antonarhipov
Javassist & ASM inside!
Привет!
@Entity

@Table(name = "owners")

public class Owner extends Person {

@Column(name = "address")

@NotEmpty

private Strin...
public class JavassistLazyInitializer
extends BasicLazyInitializer
implements MethodHandler {
final JavassistLazyInitializ...
public class JavassistLazyInitializer
extends BasicLazyInitializer
implements MethodHandler {
final JavassistLazyInitializ...
Зачем?
Модели программирования (AOP, ORM, итд)
Специализированные отладчики
Агенты для мониторинга (NewRelic, XRebel)
Инст...
Починить
ошибку в
open-source
библиотеке.
Потому, что я
могу!
Как?
java.lang.instrument
-javaagent
ASM, Javassist, Byte Buddy, cglib, итд
Classloaders
ASM
public class Hello {

public static void main(String[] args) {

System.out.println("Hello, World!");

}

}
public class Hello {

public static void main(String[] args) {

System.out.println("Hello, World!");

}

}
> javap -c Hell...
public class Hello {

public static void main(String[] args) {

System.out.println("Hello, World!");

}

}
> javap -c Hell...
java -cp asm-all-3.3.1.jar:asm-util-3.3.1.jar 
org.objectweb.asm.util.ASMifierClassVisitor 
Items.class
ASM
“Сделаю всё как хочу”
Приходится писать много кода
Надо понимать Java-байткод
byte
buddy
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.named("toString"))
.intercept(Fixed...
Byte Buddy
Модный API
Попурялный / активный проект
“Много магии”
JAVASSIST
API
ClassPool
CtClass
CtClass
CtClass
CtClass
CtField
CtMethod
CtConst
CtMethod
insertBefore
insertAfter
instrument
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
CtMethod foo = …
foo.insertBefore(…);
foo.insertAfter(…);
Можно реализовать
трассировку
… или добавить логирование
… или р...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(MethodCall m)
throws CannotCompileException...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(FieldAccess m)
throws CannotCompileExceptio...
Javassist
“Простой как тесак”
Неприхотлив в использовании
Производительность - так себе :(
Java-агенты
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
public class Agent {
public...
https://www.youtube.com/watch?v=SY5lMgWlHAw
DEMO
https://github.com/zeroturnaround/callspy
https://github.com/bsideup/javaagent-boilerplate
anton@zeroturnaround.com
@antonarhipov
JUG.ua 20170225 - Java bytecode instrumentation
JUG.ua 20170225 - Java bytecode instrumentation
JUG.ua 20170225 - Java bytecode instrumentation
Upcoming SlideShare
Loading in …5
×

JUG.ua 20170225 - Java bytecode instrumentation

1,884 views

Published on

how and why to instrument Java classes

Published in: Technology
  • Be the first to comment

JUG.ua 20170225 - Java bytecode instrumentation

  1. 1. Как и зачем инструментировать байткод в Java приложениях? Специально для JUG.ua, 25 мая 2017 @antonarhipov
  2. 2. Антон Архипов @antonarhipov Javassist & ASM inside! Привет!
  3. 3. @Entity
 @Table(name = "owners")
 public class Owner extends Person {
 @Column(name = "address")
 @NotEmpty
 private String address;
 
 @Column(name = "city")
 @NotEmpty
 private String city;
 
 @Column(name = "telephone")
 @NotEmpty
 @Digits(fraction = 0, integer = 10)
 private String telephone;
 
 @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
 private Set<Pet> pets;
  4. 4. public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler { final JavassistLazyInitializer instance = new JavassistLazyInitializer(…); 
 ProxyFactory factory = new ProxyFactory();
 factory.setSuperclass(interfaces.length == 1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER); 
 Class cl = factory.createClass();
 final HibernateProxy proxy = (HibernateProxy) cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed = true;
 return proxy;
  5. 5. public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler { final JavassistLazyInitializer instance = new JavassistLazyInitializer(…); 
 ProxyFactory factory = new ProxyFactory();
 factory.setSuperclass(interfaces.length == 1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER); 
 Class cl = factory.createClass();
 final HibernateProxy proxy = (HibernateProxy) cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed = true;
 return proxy;
  6. 6. Зачем? Модели программирования (AOP, ORM, итд) Специализированные отладчики Агенты для мониторинга (NewRelic, XRebel) Инструменты для разработки (JRebel) Обфускация (Proguard) итд
  7. 7. Починить ошибку в open-source библиотеке. Потому, что я могу!
  8. 8. Как? java.lang.instrument -javaagent ASM, Javassist, Byte Buddy, cglib, итд Classloaders
  9. 9. ASM
  10. 10. public class Hello {
 public static void main(String[] args) {
 System.out.println("Hello, World!");
 }
 }
  11. 11. public class Hello {
 public static void main(String[] args) {
 System.out.println("Hello, World!");
 }
 } > javap -c Hello Дизассемблировать Hello
  12. 12. public class Hello {
 public static void main(String[] args) {
 System.out.println("Hello, World!");
 }
 } > javap -c Hello Compiled from "Hello.java" public class Hello extends java.lang.Object{ public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
  13. 13. java -cp asm-all-3.3.1.jar:asm-util-3.3.1.jar org.objectweb.asm.util.ASMifierClassVisitor Items.class
  14. 14. ASM “Сделаю всё как хочу” Приходится писать много кода Надо понимать Java-байткод
  15. 15. byte buddy
  16. 16. Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value("Hello World!")) .make() .load(getClass().getClassLoader()) .getLoaded(); System.out.println(dynamicType.newInstance().toString()); //Hello World! System.out.println(dynamicType); //class net.bytebuddy.renamed.java.lang.Object$ByteBuddy$KUZX06bM
  17. 17. Byte Buddy Модный API Попурялный / активный проект “Много магии”
  18. 18. JAVASSIST
  19. 19. API ClassPool CtClass CtClass CtClass CtClass CtField CtMethod CtConst CtMethod insertBefore insertAfter instrument
  20. 20. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); }
  21. 21. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } ClassPool cp = new ClassPool(null); cp.appendSystemPath();
  22. 22. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } public class A extends Clazz { 
 public A() {
 }
 }
  23. 23. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); }
  24. 24. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } mars:output anton$ javap -c com/zt/A.class public class com.zt.A extends com.zt.Clazz { public com.zt.A(); Code: 0: aload_0 1: invokespecial #10 4: return
  25. 25. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); }
  26. 26. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  27. 27. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  28. 28. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } public void foo() { 
 }
  29. 29. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } public void foo(String s) { 
 }
  30. 30. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lja foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } Дескрипторы бывают длинноваты ;)
  31. 31. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1)"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } $1, $2, $3 — локальные переменные $0 — this (для виртуальных методов)
  32. 32. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1)"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } Exception in thread "main" javassist.CannotCompileException: [source error] ; is missing at javassist.CtBehavior.insertBefore(CtBehavior.java:774) at javassist.CtBehavior.insertBefore(CtBehavior.java:734) at com.zt.basics.Ex.main(Ex.java:35)
  33. 33. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1);"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  34. 34. CtMethod foo = … foo.insertBefore(…); foo.insertAfter(…); Можно реализовать трассировку … или добавить логирование … или реализовать свой “AOP”
  35. 35. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } });
  36. 36. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } });
  37. 37. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } });
  38. 38. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(MethodCall m) throws CannotCompileException {
 if(m.getMethodName().contains("println")) {
 m.replace("{}");
 } } });
  39. 39. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(FieldAccess m) throws CannotCompileException {
 if (f.isWriter()) {
 CtField field = f.getField();
 String setterName = findSetter(field);
 f.replace("{" + "$0." + setterName + "($$);" + "}");
 } } });
  40. 40. Javassist “Простой как тесак” Неприхотлив в использовании Производительность - так себе :(
  41. 41. Java-агенты
  42. 42. import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { // here be dragons }); } } $> java –javaagent:agent.jar application.Main META-INF/MANIFEST.MF Premain-Class: Agent
  43. 43. https://www.youtube.com/watch?v=SY5lMgWlHAw
  44. 44. DEMO https://github.com/zeroturnaround/callspy https://github.com/bsideup/javaagent-boilerplate
  45. 45. anton@zeroturnaround.com @antonarhipov

×