2017-07-07 60 views
0

這是我第一次實現java代理,並試圖學習一些關於字節碼的工具。閱讀了幾個介紹和教程後,我編寫了一個包含兩個類(夏季和應用程序)的小應用程序。現在我想通過運行方法的premain一個Java代理使用下面的代碼顯示的執行路徑:Java Agent for Bytecode Instrumentation的問題

public class TestJavaAgent { 
    public static void premain(String agentArgument, 
           Instrumentation instrumentation){ 
     instrumentation.addTransformer(new ClassFileTransformer() { 

      @Override 
      public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { 

       ClassPool cp = ClassPool.getDefault(); 
        try { 
         CtClass cc = cp.get("Summer"); 
         CtMethod methods [] = cc.getMethods(); 

         for(CtMethod method : methods){ 
          System.out.println("Entering "+method.getName()); 
          method.addLocalVariable("elapsedTime", CtClass.longType); 
          method.insertBefore("elapsedTime = System.currentTimeMillis();"); 
          method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;" 
            + "System.out.println(\"Method Executed in ms: \" + elapsedTime);}"); 
         } 
         return cc.toBytecode(); 

        } catch (Exception ex) { 
         return bytes; 
        } 
      } 
     }); 
    } 
} 

我通過java -javaagent{Agent JAR} -jar {Application Jar}開始代理,但它沒有打印插入的消息什麼。調試完代碼後,我意識到「ClassPool.getDefault()」後面的所有內容都無法到達,但我不知道爲什麼。有人能幫我嗎?

回答

0

變換器應該將正在傳遞的類作爲參數進行轉換,而不是您喜歡的任意類。註冊後,將會調用加載的所有類,包括您自己使用的類(首先是ClassPool)。所以你正在創建一個循環依賴。

您必須檢查類名參數並等待直到您想要轉換的類的方法被調用。對於所有其他課程,只需返回null

public class TestJavaAgent { 
    public static void premain(String agentArgument, Instrumentation instrumentation) { 
     instrumentation.addTransformer(new ClassFileTransformer() { 

      @Override 
      public byte[] transform(ClassLoader classLoader, 
       String className, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { 

       if(!className.equals("Summer")) return null; 

       ClassPool cp = ClassPool.getDefault(); 
       try { 
        // use the class bytes your received as parameter 
        cp.insertClassPath(new ByteArrayClassPath(className, bytes)); 

        CtClass cc = cp.get("Summer"); 
        CtMethod[] methods = cc.getMethods(); 

        for(CtMethod method : methods){ 
         System.out.println("Entering "+method.getName()); 
         method.addLocalVariable("elapsedTime", CtClass.longType); 
         method.insertBefore("elapsedTime = System.currentTimeMillis();"); 
         method.insertAfter("{elapsedTime = System.currentTimeMillis() - elapsedTime;" 
           + "System.out.println(\"Method Executed in ms: \" + elapsedTime);}"); 
        } 
        return cc.toBytecode(); 

       } catch (Exception ex) { 
        return null; 
       } 
      } 
     }); 
    } 
} 

注意,返回null最好返回原來的數組,如果你沒有任何改變的,則JVM可以立即意識到你沒有任何改變,沒有尋找到數組內容。