1 /*
  2  * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import java.lang.instrument.ClassFileTransformer;
 25 import java.lang.instrument.IllegalClassFormatException;
 26 import java.lang.instrument.Instrumentation;
 27 import java.security.ProtectionDomain;
 28 import java.util.HashMap;
 29 
 30 // This is a test utility class used to transform
 31 // specified classes via initial transformation (ClassFileLoadHook).
 32 // Names of classes to be transformed are supplied as arguments,
 33 // the phrase to be transformed is a hard-coded predefined
 34 // fairly unique phrase.
 35 
 36 public class TransformerAgent {
 37     private static String[] classesToTransform;
 38 
 39 
 40     private static void log(String msg) {
 41         System.out.println("TransformerAgent: " + msg);
 42     }
 43 
 44 
 45     // arguments are comma-separated list of classes to transform
 46     public static void premain(String agentArguments, Instrumentation instrumentation) {
 47         log("premain() is called, arguments = " + agentArguments);
 48         classesToTransform = agentArguments.split(",");
 49         instrumentation.addTransformer(new SimpleTransformer(), /*canRetransform=*/true);
 50     }
 51 
 52 
 53     public static void agentmain(String args, Instrumentation inst) throws Exception {
 54         log("agentmain() is called");
 55         premain(args, inst);
 56     }
 57 
 58 
 59     static class SimpleTransformer implements ClassFileTransformer {
 60         public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
 61                                 ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
 62             try {
 63                 // Printing cause ClassCircularityError for java/util/concurrent/locks/AbstractQueuedSynchronizer$ExclusiveNode
 64                 // So don't try to print some system classes
 65                 if (!name.startsWith("java/")) {
 66                     log("SimpleTransformer called for: " + name + "@" + incrCounter(name));
 67                 }
 68                 if (!shouldTransform(name))
 69                     return null;
 70 
 71                 log("transforming: class name = " + name);
 72                 int nrOfReplacements = TransformUtil.replace(buffer, TransformUtil.BeforePattern,
 73                                                            TransformUtil.AfterPattern);
 74                 log("replaced the string, nrOfReplacements = " + nrOfReplacements);
 75             } catch (Throwable t) {
 76                 // The retransform native code that called this method does not propagate
 77                 // exceptions. Instead of getting an uninformative generic error, catch
 78                 // problems here and print it, then exit.
 79                 log("Transformation failed!");
 80                 t.printStackTrace();
 81                 System.exit(1);
 82             }
 83             return buffer;
 84         }
 85 
 86         // Check class name pattern, since test should only transform certain classes
 87         private static boolean shouldTransform(String name) {
 88             for (String match : classesToTransform) {
 89                 if (name.matches(match)) {
 90                     log("shouldTransform: match-found, match = " + match);
 91                     return true;
 92                 }
 93             }
 94 
 95             return false;
 96         }
 97     }
 98 
 99 
100     static HashMap<String, Integer> counterMap = new HashMap<>();
101 
102     static Integer incrCounter(String className) {
103         Integer i = counterMap.get(className);
104         if (i == null) {
105             i = Integer.valueOf(1);
106         } else {
107             i = Integer.valueOf(i.intValue() + 1);
108         }
109         counterMap.put(className, i);
110         return i;
111     }
112 }