2016-04-22 5 views
1

Короче говоря:Java агент не может преобразовать все классы в моем проекте

  • Мне нужно преобразовать каждый класс в моей программе (даже Java
    библиотеки, которые загружаются перед моим агентом).
  • У меня есть способ сделать это , но не работает полностью. Я открыт для новых идей.
  • Мой фактический метод действует странно: он должен печатать одинаковые имена в файле и консоли, но это не так. Я уверен, что эти классы достигли моего метода преобразования, потому что, если я попытаюсь измерить их , я получаю сообщение об ошибке.

Полная история: Я создал средство для того, чтобы придать некоторый пользовательский код в каждом классе, который загружен в мой проект. Я добавляю этот агент во время выполнения, используя параметр -javaagent, и эта работа просто прекрасна.

Проблема заключается в том, что когда мой агент подключен к JVM, многие классы уже загружены (например, классы java.io. *), и поэтому моя трансформация пропускает целую кучу классов. Итак, мой вопрос: есть ли способ, которым я могу измерить все те классы, которые мне не хватает? Каждое предложение более приветствуется.

Пока я пытался так:

public class MyTransformer implements ClassFileTransformer { 


public static void premain(String agentArgs, Instrumentation inst) { 
    final MyTransformer t = MyTransformer .getInstance(); 
    inst.addTransformer(t, true); 
    MyTransformer .log.info(t + " registered via JVM option -javaagent"); 

    // TEST 
    // by the time we are attached, the classes to be 
    // dumped may have been loaded already. So, check 
    // for candidates in the loaded classes. 
    Class[] classes = inst.getAllLoadedClasses(); 
    List<Class> candidates = new ArrayList<Class>(); 
    for (Class c : classes) { 
     if (inst.isModifiableClass(c) && inst.isRetransformClassesSupported()){ 
      candidates.add(c); 
     } 
    } 
    System.out.println("There are "+candidates.size()+" classes"); 
    try { 
     // if we have matching candidates, then 
     // retransform those classes so that we 
     // will get callback to transform. 
     if (! candidates.isEmpty()) { 
      Iterator it = candidates.iterator(); 
      while(it.hasNext()){ 
       Class c = (Class)it.next(); 
       if(!c.getName().startsWith("javassist")){ 
        System.out.println(" ========================> In Progress:"+c.getName()); 
        inst.retransformClasses(c); 
       } 
      } 
     }else{ 
      System.out.println("candidates.isEmpty()"); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 

    public byte[] transform(ClassLoader loader, String className, 
     Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 
     byte[] classfileBuffer) throws IllegalClassFormatException { 

    byte[] byteCode = classfileBuffer; 

    final String dot_classname = className.replace('/', '.'); 
    // log classes that are going throug the instrumentor 
    // just log if the java.io. is coming here 
    try { 
     PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path, true))); 
     out.println(dot_classname); 
     out.close(); 
    } catch (IOException ex) { 
     Logger.getLogger(MyTransformer.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    return byteCode; 
    } 
} 

Так что моя первая попытка взять в методе «premain» каждый класс уже загружен и, если это возможно reTransfrom его с помощью метода «retransfromClasses».

Теперь в моем методе преобразования у меня есть только журнал, который записывает в файл каждый класс, который будет преобразован. Теперь мои результаты испытаний:

There are 1486 classes 
==> In Progress:com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser 
==> In Progress:java.lang.Enum 
==> In Progress:java.lang.ThreadGroup 
==> In Progress:java.nio.file.FileSystem 
==> In Progress:java.util.regex.Pattern$Prolog 
==> In Progress:com.sun.org.apache.xerces.internal.dom.AttributeMap 
==> In Progress:java.util.regex.Matcher 
==> In Progress:org.apache.commons.beanutils.converters.ShortConverter 
==> In Progress:com.google.gson.JsonNull 
==> In Progress:java.util.concurrent.CopyOnWriteArrayList$COWIterator 
==> In Progress:java.util.concurrent.locks.ReentrantLock 
==> In Progress:java.lang.NoSuchMethodError 
==> In Progress:org.apache.commons.lang.BooleanUtils 
==> In Progress:java.lang.reflect.WeakCache$CacheValue 
==> In Progress:com.google.gson.internal.bind.TypeAdapters$33 
==> In Progress:java.lang.reflect.Type 
==> In Progress:sun.reflect.generics.scope.AbstractScope 
==> In Progress:org.apache.log4j.helpers.DateTimeDateFormat 
==> In Progress:sun.nio.cs.MS1252 
==> In Progress:java.lang.Integer$IntegerCache 
==> In Progress:com.sun.org.apache.xerces.internal.utils.SecuritySupport$3 
==> In Progress:org.apache.commons.configuration.MapConfiguration 
==> In Progress:org.apache.commons.beanutils.IntrospectionContext 
==> In Progress:java.io.Reader 
==> In Progress:java.util.WeakHashMap$Holder 
==> In Progress:java.util.ServiceLoader$LazyIterator 
==> In Progress:java.util.regex.Pattern$Branch 
==> In Progress:java.lang.IllegalMonitorStateException 
==> In Progress:java.util.regex.Pattern$Curly 
==> In Progress:org.apache.commons.configuration.resolver.EntityRegistry 
==> In Progress:java.io.IOException 
==> In Progress:java.io.FilterOutputStream 
==> In Progress:org.apache.log4j.LogManager 
==> In Progress:sun.util.logging.PlatformLogger$Level 
==> In Progress:java.nio.charset.CoderResult$1 
==> In Progress:com.google.gson.FieldNamingPolicy$5 
==> In Progress:com.google.gson.internal.ObjectConstructor 
==> In Progress:sun.util.calendar.BaseCalendar$Date 

Таким образом, вы можете заметить, что я могу найти каждый класс (даже java.io), в которых я заинтересован. Теперь, если мы посмотрим на файл, который должен содержать каждый класс, который MyTransformer пытался преобразовать, мы замечаем, что нет обычных записей, и это действительно странно.

org.apache.commons.beanutils.converters.ShortConverter 
com.google.gson.JsonNull 
org.apache.commons.lang.BooleanUtils 
com.google.gson.internal.bind.TypeAdapters$33 
org.apache.log4j.helpers.DateTimeDateFormat 
org.apache.commons.configuration.MapConfiguration 
org.apache.commons.beanutils.IntrospectionContext 
org.apache.commons.configuration.resolver.EntityRegistry 
org.apache.log4j.LogManager 
com.google.gson.FieldNamingPolicy$5 
com.google.gson.internal.ObjectConstructor 
org.apache.log4j.Layout 
com.google.gson.internal.bind.TypeAdapters 
org.apache.log4j.PropertyConfigurator 
com.sap.psr.vulas.java.JavaEnumId 
org.apache.commons.collections.collection.AbstractSerializableCollectionDecorator 
org.apache.commons.logging.impl.LogFactoryImpl 
org.apache.commons.lang.text.StrTokenizer 

Еще одна проблема, которую я обнаружил, что если я пытаюсь вставить в Mytransformer.transform (..) метод некоторые вмешательства в байткод это нарушит мое исполнение. Я имею в виду, что операция преобразования, которую я использую, совершенно прекрасна, поскольку я использую ее для каждого класса, загружаемого после присоединения моего агента. Но так или иначе, если я пытаюсь использовать это один те «повторно трансформировали» классы Встанет эту ошибку:

==> In Progress:org.apache.commons.lang.text.StrMatcher$TrimMatcher java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields) 
     at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method) 
     at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144) 

Я знаю, что существует ограничения на трансформации нативных классов, но я не вещь, что моя трансформация операция пытаясь изменить схему. Я буду исследовать этот вопрос и публиковать обновления.

+0

Не могли бы вы рассказать нам, почему вы хотите, чтобы ввести код в нативных классов Java? Возможно, вам будет проще достичь своей цели, чем внедрить код в каждый класс вашей среды. – Aaron

+0

Сложно, но я гарантирую, что мне нужно изменить байтовый код этих классов. (моя цель в простых словах заключается в подстановке оператора return некоторых методов, например, замена возврата байта вызова [] java.io.readFile, чтобы я мог вернуть свои пользовательские байты, а не читать их. происходят при каждом вызове этого метода, даже если я не знаю, где используется) – rakwaht

ответ

0

This link похоже на вопрос, который вы задаете. Похоже, вы можете изменить тело собственного метода, но не может добавить методы или поля.

Я могу видеть в вашем коде, что вы используете Javassist для этого.Я потратил несколько месяцев на то, чтобы заставить его работать, но это не сработает для меня, поэтому я привел к использованию ASM (что, кстати, работает).

So Как вы это делаете? Во-первых, я бы использовал API-интерфейс Attach для привязки к rt.jar (потому что он уже загружен, вам нужно прикрепить ваш агент к java-процессу. Затем вы можете добавить свой трансформатор в java-агент из метода agentmain. класс читателя для класса, который вы хотите отредактировать, и принять ClassVisitor, который вы расширяете. Затем вы переопределите метод и, если он нужен вам, замените код другим кодом. Вы можете увидеть пример on my Github . Вам нужно будет преобразовать его каждый индивидуальный время, потому что он загружает на память - вам не нужно беспокоиться об удалении кода

Я надеюсь, что это помогает Прокомментируйте, если у вас есть какие-либо вопросы :)

0

.! добавили ли вы необходимые манифестные записи в ваш агент jar manifest.mf?

Как выглядит ваш 'manifest.mf'? Он должен иметь что-то вроде

Can-Redefine-Classes: true

Can-Retransform-Classes: true

С уважением,

Grzesiek

+0

Да, похоже, что это так. Я зарегистрировал свой агент и его способность переопределять и ретранслировать. – rakwaht

+0

Как я только что указал на https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html, кажется, что ретрансформация имеет ограничение. Но опять же, вы запускаете jvm с прикрепленным агентом или используете ли вы агент для запуска JVM? – Grzesuav

+0

Я присоединяю агента, передающего параметр -javaagent в строке cmd, так что он находится в начале. Проблема i в том, что классы, загруженные до моего агента, действительно привязаны. – rakwaht

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