Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Oredev 2015 - Taming Java Agents

Oredev 2015 - Taming Java Agents

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.

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 at runtime, record execution flow, etc.

I’d like to give 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.

Anton Arhipov

November 05, 2015
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. Agenda • Overview of Java agent technology • Instrumentation API

    • Attach API • HacksApplications http://xkcd.com/138/
  2. my.war ClassLoader getResource("hello.html"); read("src/main/.../hello.html"); rebel.xml • Maps the running application

    to IDE workspace • Reloads Java classes and framework configurations in a running JVM process
  3. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //....
 }
 }).start(); Java app
  4. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //.....
 }
 }).start(); RULE  trace  thread  start   CLASS  java.lang.Thread   METHOD  start()   IF  true   DO  traceln("***  start  for  thread:  "+  $0.getName())   ENDRULE Java app Byteman rule
  5. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //.....
 }
 }).start(); RULE  trace  thread  start   CLASS  java.lang.Thread   METHOD  start()   IF  true   DO  traceln("***  start  for  thread:  "+  $0.getName())   ENDRULE > java -javaagent:byteman.jar=script:thread.btm,boot:byteman.jar \ -Dorg.jboss.byteman.transform.all org.my.AppMain2 foo bar baz *** start for thread: foo foo *** start for thread: bar bar *** start for thread: baz baz Java app Byteman rule
  6. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //.....
 }
 }).start(); RULE  trace  thread  start   CLASS  java.lang.Thread   METHOD  start()   IF  true   DO  traceln("***  start  for  thread:  "+  $0.getName())   ENDRULE Java app Byteman rule
  7. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //.....
 }
 }).start(); RULE  trace  thread  start   CLASS  java.lang.Thread   METHOD  start()   IF  true   DO  traceln("***  start  for  thread:  "+  $0.getName())   ENDRULE > java -classpath byteman-install.jar \ org.jboss.byteman.agent.install.Install 13101 Java app Byteman rule Install agent:
  8. public static void main(String[] args) {
 for (String arg :

    args) {
 new Thread(new Runnable() {
 public void run() {
 //.....
 }
 }).start(); RULE  trace  thread  start   CLASS  java.lang.Thread   METHOD  start()   IF  true   DO  traceln("***  start  for  thread:  "+  $0.getName())   ENDRULE > java -classpath byteman-install.jar \ org.jboss.byteman.agent.install.Install 13101 Java app Byteman rule > java -classpath byteman-submit.jar \ org.jboss.byteman.agent.submit.Submit -l thread.btm Install agent: Submit rules:
  9. import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public static

    void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer { … }); } public static void agentmain(String args, Instrumentation inst) { premain(args, inst); } } META-­‐INF/MANIFEST.MF   Premain-­‐Class:  Agent $>  java  –javaagent:agent.jar  application.Main
  10. java.lang.instrument.ClassFileTransformer public class MyTransformer implements ClassFileTransformer { public void byte[]

    transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain pd, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); // transform the bytes as required, // for instance - with Javassist return ct.toBytecode(); } }
  11. java.lang.instrument.Instrumentation void addTransformer(ClassFileTransformer transformer, boolean canRetransform); void appendToBootstrapClassLoaderSearch(JarFile jarfile); void

    appendToSystemClassLoaderSearch(JarFile jarfile); Class[] getAllLoadedClasses(); Class[] getInitiatedClasses(ClassLoader loader); void redefineClasses(ClassDefinition... classes); void retransformClasses(Class<?>... classes);
  12. • Executed when the agent attaches to the running JVM

    • Instrumentation parameter is optional • META-INF/MANIFEST.MF is required • Requires support for loading agents in JVM • Allows adding the code to JVM post-factum public static void agentmain(String args, Instrumentation inst)
  13. import com.sun.tools.attach.VirtualMachine; //attach to target VM VirtualMachine vm = VirtualMachine.attach("2177");

    //get system properties in the target VM Properties props = vm.getSystemProperties(); //load agent into the VM vm.loadAgent("agent.jar", "arg1=x,arg2=y"); //detach from VM vm.detach(); http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html