2017-07-07 2 views
0

C'est la première fois que j'applique un agent java et j'essaie d'apprendre quelque chose sur l'instrumentation bytecode. Après avoir lu plusieurs introductions et tutoriels j'ai codé une petite application avec deux classes (été et application). Maintenant, je veux lancer un agent java via la méthode de premain pour montrer le chemin d'exécution en utilisant le code suivant:Problèmes avec l'agent Java pour l'instrumentation bytecode

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; 
        } 
      } 
     }); 
    } 
} 

j'ai commencé à l'agent via java -javaagent{Agent JAR} -jar {Application Jar} mais il n'a rien imprimer des messages insérés. Après avoir débogué le code j'ai réalisé tout après "ClassPool.getDefault()" ne sera pas atteint mais je ne sais pas pourquoi. Est-ce que quelqu'un peut m'aider?

Répondre

0

Le transformateur est censé transformer la classe transmise en paramètre, pas une classe arbitraire que vous aimez. Et après l'inscription, il sera appelé toutes les classes qui sont chargés, y compris les classes que vous utilisez vous-même (ClassPool au premier). Donc vous créez une dépendance circulaire.

Vous devez vérifier l'argument de nom de classe et attendre que votre méthode ait été appelée pour la classe que vous souhaitez transformer. Pour toutes les autres classes, renvoyez simplement 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; 
       } 
      } 
     }); 
    } 
} 

Notez que le retour null est préférable de retourner le tableau original si vous n'avez pas changé quoi que ce soit alors la machine virtuelle Java peut immédiatement reconnaître que vous n'avez pas changé quoi que ce soit, sans regarder dans le contenu du tableau.