2016-07-27 2 views
1

мне нужна ваша помощь в поиске того, что случилось с моей реализации ...Java Agent с помощью Byte-Buddy не работает

Я пытаюсь реализовать простой JVM во время выполнения профилирования с использованием байт-дружище. В общем, мне нужно, чтобы каждый вызов метода записывался в стек, который я управляю отдельным объектом.

После прочтения нескольких постов, я понял, что лучше использовать «Посоветуйте» подход вместо «MethodDelegation», и вот что я пришел с:

Agent.java:

package com.panaya.java.agent; 

import net.bytebuddy.agent.builder.AgentBuilder; 
import net.bytebuddy.asm.Advice; 
import net.bytebuddy.description.type.TypeDescription; 
import net.bytebuddy.matcher.ElementMatchers; 
import net.bytebuddy.utility.JavaModule; 

import java.lang.instrument.Instrumentation; 
import java.security.ProtectionDomain; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
import java.util.regex.PatternSyntaxException; 

public class Agent { 
    private static List<Pattern> pkgIncl = new ArrayList<Pattern>(); 
    private static List<Pattern> pkgExcl = new ArrayList<Pattern>(); 

    private static void initMatcherPatterns(String argument) { 
     if (argument == null) { 
      System.out.println("Missing configuration argument"); 
      return; 
     } 

     System.out.println("Argument is: " + argument); 

     String[] tokens = argument.split(";"); 

     if (tokens.length < 1) { 
      System.out.println("Missing delimeter ;"); 
      return; 
     } 

     for (String token : tokens) { 
      String[] args = token.split("="); 
      if (args.length < 2) { 
       System.out.println("Missing argument delimeter =:" + token); 
       return; 
      } 

      String argtype = args[0]; 

      if (!argtype.equals("incl") && !argtype.equals("excl")) { 
       System.out.println("Wrong argument: " + argtype); 
       return; 
      } 

      String[] patterns = args[1].split(","); 

      for (String pattern : patterns) { 
       Pattern p = null; 
       System.out.println("Compiling " + argtype + " pattern:" + pattern + "$"); 
       try { 
        p = Pattern.compile(pattern + "$"); 
       } catch (PatternSyntaxException pse) { 
        System.out.println("pattern: " + pattern + " not valid, ignoring"); 
       } 
       if (argtype.equals("incl")) 
        pkgIncl.add(p); 
       else 
        pkgExcl.add(p); 
      } 
     } 

    } 

    private static boolean isShouldInstrumentClass(String className) { 
     System.out.println("Testing " + className + " for match."); 
     boolean match = false; 
     String name = className.replace("/", "."); 

     for (Pattern p : pkgIncl) { 
      Matcher m = p.matcher(name); 
      if (m.matches()) { 

       match = true; 
       break; 
      } 
     } 

     for (Pattern p : pkgExcl) { 
      Matcher m = p.matcher(name); 
      if (m.matches()) { 

       match = false; 
       break; 
      } 
     } 

     if (match) { 
      System.out.println("Class " + name + "should be instrumented."); 
     } else { 
      System.out.println("Skipping class: " + name); 
     } 
     return match; 
    } 

    public static void premain(String agentArgument, Instrumentation instrumentation) { 
     System.out.println("Premain started"); 
     try { 
      initMatcherPatterns(agentArgument); 

      new AgentBuilder.Default() 
        .with(AgentBuilder.TypeStrategy.Default.REBASE) 
        .type(new AgentBuilder.RawMatcher() { 
         public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, Class<?> aClass, ProtectionDomain protectionDomain) { 
          return isShouldInstrumentClass(typeDescription.getActualName()); 
         } 
        }) 
        .transform((builder, typeDescription, classLoader) -> builder 
          .visit(Advice.to(ProfilingAdvice.class) 
            .on(ElementMatchers.any()))).installOn(instrumentation); 

     } catch (RuntimeException e) { 
      System.out.println("Exception instrumenting code : " + e); 
      e.printStackTrace(); 
     } 

    } 
} 

и ProfilingAdvice.java:

package com.panaya.java.agent; 

import com.panaya.java.profiler.MethodStackCollector; 
import net.bytebuddy.asm.Advice; 

public class ProfilingAdvice { 
    @Advice.OnMethodEnter 
    public static void enter(@Advice.Origin("#t.#m") String signature) { 
     System.out.println("OnEnter :" + signature); 
     try { 
      MethodStackCollector.getInstance().push(signature); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    @Advice.OnMethodExit 
    public static void exit(@Advice.Return long value) { 
     System.out.println("OnExit - value = " + value); 
     try { 
      MethodStackCollector.getInstance().pop(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 
} 

по какой-то причине «войти» и «выход» методы в классе ProfilingAdvice, не вызываются вовсе.

Что я делаю неправильно?

Thanks, Elad.

+0

Как выглядит ваш агент? Вы зарегистрировали 'AgentBuilder.Listener', чтобы узнать, есть ли какие-либо проблемы с прибором? –

+0

@RafaelWinterhalter, я не уверен, что понимаю ваш вопрос ... Что это значит «Как выглядит ваш агент»? не могли бы вы объяснить свой вопрос .... Мой класс агента указан выше ... Он загружен ОК, но механизм консультаций не работает. – esaar

+0

Интересно, как выглядит ваш метод 'premem' /' agentmain'. Вы должны собрать совет где-нибудь, используя 'AgentBuilder'. –

ответ

0

Я попробовал ваш пример и после снижения рекомендации выхода к командам печати и вашему согласовани только соответствовать некоторому классу foo.Bar:

package foo; 
public class Bar { 
    public long qux() { return 0L; } 
} 

инструментовка работает без проблем. Я считаю это немного подозрительным, что ваш Advice matcher указывает any(), а ваш ProfilingAdvice требует типа возврата long. Вы пытались только печатать на консоль без каких-либо комментариев в своем совете?

Вы можете отлаживать такие вопросы по settting:

new AgentBuilder.Default() 
    .with(AgentBuilder.Listener.StreamWriting.toSystemOut()); 

где потенциальные ошибки во время приборов выводятся на консоль.

+0

При подключении слушателя я заметил, что методы вызова были вызваны, но действительно возникла проблема с возвращаемым типом. После удаления возвращаемого параметра из метода выхода большая часть инструментария работает нормально, но в конкретном случае я получаю следующее исключение: java.lang.IllegalStateException: Неожиданный остаток в стеке операнда: -1 \t at net.bytebuddy.utility.StackAwareMethodVisitor.drainStack (StackAwareMethodVisitor.java:120) .... Я отправлю отдельную проблему. – esaar

+0

сделайте пожалуйста. Если вы можете обеспечить реконструкцию, это было бы здорово. Пожалуйста, обновите до 1.4.16, я недавно исправил связанную с этим проблему. –

+0

Несмотря на то, что обновление до 1.4.16 разрешило предыдущую проблему, кажется, что когда я меняю комбинацию «ElementMatchers», она снова вызывает это исключение. – esaar

Смежные вопросы