taming java agents

49
Taming Java Agents Anton Arhipov ZeroTurnaround, JRebel @antonarhipov

Upload: anton-arhipov

Post on 01-Nov-2014

7.333 views

Category:

Technology


3 download

DESCRIPTION

A Java agents are pluggable self contained components that run embedded in a JVM and intercept the classloading process. They were introduced in Java 5 along with the powerful java.lang.instrument package. Java agents can be loaded statically at startup or dynamically (programmatically) at runtime to attach to a running process in a fail-safe fashion. Java agents were an awesome addition to the JVM as it opened a lot of opportunities for tool designers and changed Java tooling landscape quite drastically. In conjunction with Java bytecode manipulation libraries it is now possible to do amazing things to Java classes: we can experiment with programming models, redefine classes in runtime, record execution flow, etc. The presentation provides an overview of Java agents’ functionality along with the usage examples and real world experiences. You will learn, how to implement an agent and apply Instrumentation API in combination with bytecode manipulation libraries to solve interesting tasks.

TRANSCRIPT

Page 1: Taming Java Agents

Taming  Java  Agents  

Anton  Arhipov  ZeroTurnaround,  JRebel  @antonarhipov  

Page 2: Taming Java Agents

-­‐javaagent    

java.lang.instrument  

Page 3: Taming Java Agents

java.lang.instrument  import  java.lang.instrument.ClassFileTransformer;  import  java.lang.instrument.InstrumentaBon;    public  class  Agent  {        public  staBc  void  premain(String  args,  InstrumentaBon  inst)  {                inst.addTransformer(new  ClassFileTransformer  {  …  });        }        public  staBc  void  agentmain(String  args,  InstrumentaBon  inst)  {              premain(args,  inst);        }  }  

META-INF/MANIFEST.MF Premain-Class: Agent Agent-Class: Agent

java  –javaagent:agent.jar  …  

Page 4: Taming Java Agents

META-­‐INF/MANIFEST.MF  

•  Premain-­‐Class  •  Agent-­‐Main  •  Boot-­‐Class-­‐Path  •  Can-­‐Redefine-­‐Classes  •  Can-­‐Retransform-­‐Classes  

Page 5: Taming Java Agents

j.l.instrument.ClassFileTransformer                    byte[]  transform(ClassLoader  loader,                                                                              String  className,                                                                            Class<?>  classBeingRedefined,                                                                            ProtecBonDomain  protecBonDomain,                                                                            byte[]  classfileBuffer);  

Page 6: Taming Java Agents

public  class  MyTransformer  implements  ClassFileTransformer  {                public  void  byte[]  transform(ClassLoader  loader,                                                                              String  className,                                                                            Class<?>  classBeingRedefined,                                                                            ProtecBonDomain  protecBonDomain,                                                                            byte[]  classfileBuffer){                                ClassPool  cp  =  ClassPool.getDefault();                              CtClass  ct  =  cp.makeClass(                                                                            new  ByteArrayInputStream(classfileBuffer));                                  …  …  …                                return  ct.toBytecode();  

Page 7: Taming Java Agents

public  class  MyTransformer  implements  ClassFileTransformer  {                public  void  byte[]  transform(ClassLoader  loader,                                                                              String  className,                                                                            Class<?>  classBeingRedefined,                                                                            ProtecBonDomain  protecBonDomain,                                                                            byte[]  classfileBuffer){                                ClassPool  cp  =  ClassPool.getDefault();                              CtClass  ct  =  cp.makeClass(                                                                            new  ByteArrayInputStream(classfileBuffer));                                  …  …  …                                return  ct.toBytecode();              

Enter  Javassist!  

Page 8: Taming Java Agents

public  class  MyTransformer  implements  ClassFileTransformer  {                public  void  byte[]  transform(ClassLoader  loader,                                                                              String  className,                                                                            Class<?>  classBeingRedefined,                                                                            ProtecBonDomain  protecBonDomain,                                                                            byte[]  classfileBuffer){      

                         ct.addMethod(CtNewMethod.make(                                  "public  void  foo()  {}  ",  ct));                    

Page 9: Taming Java Agents

public  class  MyTransformer  implements  ClassFileTransformer  {                public  void  byte[]  transform(ClassLoader  loader,                                                                              String  className,                                                                            Class<?>  classBeingRedefined,                                                                            ProtecBonDomain  protecBonDomain,                                                                            byte[]  classfileBuffer){      

                         CtMethod  m  =  ct.getMethod("foo",  "()V");                            m.insertA_er("System.out.println(\"..\")");                    

Page 10: Taming Java Agents

           void  addTransformer(ClassFileTransformer  transformer,                                                                                          boolean  canRetransform);              void  appendToBootstrapClassLoaderSearch(JarFile  jarfile);            void  appendToSystemClassLoaderSearch(JarFile  jarfile);              Class[]  getAllLoadedClasses();            Class[]  getIniBatedClasses(ClassLoader  loader);                      void  redefineClasses(ClassDefiniBon...  classes);            void  retransformClasses(Class<?>...  classes);            .  .  .  

j.l.instrument.InstrumentaBon  

Page 11: Taming Java Agents
Page 12: Taming Java Agents
Page 13: Taming Java Agents
Page 14: Taming Java Agents
Page 15: Taming Java Agents

Example  1:  Simple  Trace  public  class  Main  {              public  staBc  void  main(String[]  args)  {                        for  (int  i  =  0;  i  <  args.length;  i++)                                System.out.println(args[i]);              }    }      $  java  Main  1  2  3    1  2  3  

Page 16: Taming Java Agents

Example  1:  Simple  Trace  

$  java  –javaagent:byteman.jar=script:trace.btm  Main  

RULE  trace  main  entry  CLASS  Main  METHOD  main  AT  ENTRY  IF  true  DO  traceln("entering  main")  ENDRULE  

RULE  trace  main  exit  CLASS  Main  METHOD  main  AT  EXIT  IF  true  DO  traceln("exiBng  main")  ENDRULE  

entering  main  1  2  3  exiBng  main  

Page 17: Taming Java Agents

Example  2:  Tracing  Threads  

for  (int  i  =  0;  i  <  args.length;  i++)  {              final  String  arg  =  args[i];            Thread  thread  =  new  Thread(arg)  {                          public  void  run()  {                                      System.out.println(arg);                        }            };              thread.start();              thread.join();  }  

Page 18: Taming Java Agents

Example  2:  Tracing  Threads  RULE  trace  thread  start    CLASS  java.lang.Thread    METHOD  start()    IF  true    DO  traceln("***  start  for  thread  "  +  $0.getName())    ENDRULE    

Page 19: Taming Java Agents

Example  2:  Tracing  Threads  $    java  -­‐Dorg.jboss.byteman.transform.all  \        -­‐javaagent:byteman.jar=script:thread.btm,boot:byteman.jar  \        Main  foo  bar  baz    ***  start  for  thread  foo  foo  ***  start  for  thread  bar  bar  ***  start  for  thread  baz  baz  

Page 20: Taming Java Agents

Example  3:  Side  Effects  

RULE  skip  loop  iteraBon  CLASS  Main  METHOD  main  AFTER  CALL  join  IF  ($args[$i]).contains("foo")  DO  $i  =  $i  +  1  ;        traceln("skipping  iteraBon  "  +  $i)  ENDRULE  

Page 21: Taming Java Agents

Example  4:  Arach  to  Process  BufferedReader  in  =  new  BufferedReader(new  InputStreamReader(System.in));  String  next  =  in.readLine();    while  (next  !=  null  &&  next.length()  >  0  &&  !next.contains("end"))  {          final  String  arg  =  next;          Thread  thread  =  new  Thread(arg)  {                    public  void  run()  {                              System.out.println(arg);                    }              };            thread.start();            thread.join();            next  =  in.readLine();  }  

Page 22: Taming Java Agents

Example  4:  Arach  to  Process  

$  bminstall.sh  -­‐b  –Dorg…  <PID>  $  bmsubmit.sh  script/ex2.btm    install  rule  trace  thread  start  

$  java  Main  Hello  Hello    

$  bmsubmit.sh  -­‐u  script/ex2.btm    uninstall  RULE  trace  thread  start  

Test  ***  start  for  thread  Test  Test    

Test  Test  end  $    

Page 23: Taming Java Agents

Arach  API  

VirtualMachine  vm  =  VirtualMachine.arach("2177");    vm.loadAgent("agent.jar",  "arg1=x,arg2=y");    vm.detach();      

LimitaBon:  can’t  change  class  schema  

Page 24: Taming Java Agents

Fault  InjecBon  VS  

Mocking  

Page 25: Taming Java Agents

Example  5:  Fault  InjecBon  @RunWith(BMUnitRunner.class)  public  class  BytemanJUnitTests  {              @Test(expected=MyServiceUnavailableExcepBon.class)            @BMRule(name="throw  Bmeout  at  1st  call",                                                  targetClass  =  "Socket",                                                  targetMethod  =  "connect",                                                  acBon  =  "throw  new  java.io.IOExcepBon()")                public  void  testErrorInPipeline()  throws  ExcepBon  {                        //  Invokes  internally  Socket.connect(..):                        new  MyHrpClient("hrp://example.com/data").read();              }  }  

Page 26: Taming Java Agents

Example  5:  Fault  InjecBon  @RunWith(BMUnitRunner.class)  public  class  BytemanJUnitTests  {              @Test(expected=MyServiceUnavailableExcepBon.class)            @BMRule(name="throw  Bmeout  at  1st  call",                                                  targetClass  =  "Socket",                                                  targetMethod  =  "connect",                                                  acBon  =  "throw  new  java.io.IOExcepBon()")                public  void  testErrorInPipeline()  throws  ExcepBon  {                        //  Invokes  internally  Socket.connect(..):                        new  MyHrpClient("hrp://example.com/data").read();              }  }  

Page 27: Taming Java Agents

Example  5:  Fault  InjecBon  @RunWith(BMUnitRunner.class)  public  class  BytemanJUnitTests  {              @Test(expected=MyServiceUnavailableExcepBon.class)            @BMRule(name="throw  Bmeout  at  1st  call",                                                  targetClass  =  "Socket",                                                  targetMethod  =  "connect",                                                  acBon  =  "throw  new  java.io.IOExcepBon()")                public  void  testErrorInPipeline()  throws  ExcepBon  {                        //  Invokes  internally  Socket.connect(..):                        new  MyHrpClient("hrp://example.com/data").read();              }  }  

Page 28: Taming Java Agents

Example  5:  Fault  InjecBon  @RunWith(BMUnitRunner.class)  public  class  BytemanJUnitTests  {              @Test(expected=MyServiceUnavailableExcepBon.class)            @BMRule(name="throw  Bmeout  at  1st  call",                                                  targetClass  =  "Socket",                                                  targetMethod  =  "connect",                                                  acBon  =  "throw  new  java.io.IOExcepBon()")                public  void  testErrorInPipeline()  throws  ExcepBon  {                        //  Invokes  internally  Socket.connect(..):                        new  MyHrpClient("hrp://example.com/data").read();              }  }  

Page 29: Taming Java Agents

Example  5:  Fault  InjecBon  @RunWith(BMUnitRunner.class)  public  class  BytemanJUnitTests  {              @Test(expected=MyServiceUnavailableExcepBon.class)            @BMRule(name="throw  Bmeout  at  1st  call",                                                  targetClass  =  "Socket",                                                  targetMethod  =  "connect",                                                  acBon  =  "throw  new  java.io.IOExcepBon()")                public  void  testErrorInPipeline()  throws  ExcepBon  {                        //  Invokes  internally  Socket.connect(..):                        new  MyHrpClient("hrp://example.com/data").read();              }  }  

Page 30: Taming Java Agents
Page 31: Taming Java Agents

@Path("/")  public  String  foo()  {      return  "FooBar";  }  

Page 32: Taming Java Agents

@Path("/")  public  String  foo()  {      return  "FooBar";  }  

@Path("/")  public  FooBar  foo()  {      return  new  FooBar();  }  

Page 33: Taming Java Agents

@Path("/")  public  String  foo()  {      return  "FooBar";  }  

@Path("/foobar")  public  FooBar  foo()  {      return  new  FooBar();  }  

Page 34: Taming Java Agents

MyObject

MyObject.class  

OldClassLoader

Code  101000101  100010010   New  code  

111000100  101010010  

Make  changes  in  IDE  

JRebel  

Fram

ework  

ConfiguraDon  (XML,  annotaDons,..)  

JRebel  

Page 35: Taming Java Agents

java  –javaagent:jrebel.jar  App  

Page 36: Taming Java Agents
Page 37: Taming Java Agents

Chronon  

•  A  back-­‐in-­‐Bme  debugger  for  Java  – Recording  a  program  execuBon  – Debugging  the  recording  

•  What  is  recorded?  – Variable  history  – Method  calls  – ExcepBons  – Console  output  – Threads  

Page 38: Taming Java Agents

Recorded  data  

Memory  buffer  

ApplicaBon  threads  

Flusher  threads  

Recording  

Page 39: Taming Java Agents

java  -­‐javaagent:recorder.jar=config.txt  

Page 40: Taming Java Agents
Page 41: Taming Java Agents

Recording  Local  Variable  Changes  

•  Just  a  brute-­‐force  idea:  –  Instrument  *store  (astore,  istore,  etc)  instrucBons  to  write  the  value  to  a  store  

int  i  =  2;   iconst_2  istore_1  

iconst_2  istore_1  iload_1  invokestaBc  …  

Page 42: Taming Java Agents
Page 43: Taming Java Agents

Plumbr:  Memory  leak  detecBon  

•  RunBme  applicaBon  analysis  – vs  post-­‐mortem  heap  dump  analysis  – vs  profilers  –  human  operated,  rarely  usable  in  producBon  

•  AdapBve  introspecBon  – Monitor  high  level  metrics,  cheap  to  obtain  – Collect  expensive  detailed  informaBon  when  suspects  are  found  

Page 44: Taming Java Agents

Plumbr  

•  Memory  leak  in  Java  –  some  objects  are  created  Bme  a_er  Bme  but  a  reference  is  forgoren  

•  Look  at  when  object  was  created  and  how  long  it  lives  

 

Page 45: Taming Java Agents

What  is  leaking?  

Common JDK class instances:

Class instances that handle user interaction:

Instances of a leaking class:

Instances of a class that belong to base application framework:

Time (garbage collection cycles)

Page 46: Taming Java Agents
Page 47: Taming Java Agents
Page 48: Taming Java Agents

-­‐javaagent    

java.lang.instrument  

Page 49: Taming Java Agents

@antonarhipov  [email protected]