You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sirona.apache.org by rm...@apache.org on 2013/12/08 22:07:12 UTC

svn commit: r1549252 - in /incubator/sirona/trunk/agent/javaagent/src: main/java/org/apache/sirona/javaagent/ main/java/org/apache/sirona/javaagent/listener/ test/java/org/apache/sirona/javaagent/ test/java/org/apache/test/sirona/javaagent/ test/resour...

Author: rmannibucau
Date: Sun Dec  8 21:07:11 2013
New Revision: 1549252

URL: http://svn.apache.org/r1549252
Log:
SIRONA-17 get rid of includes/excludes on javaagent and use listeners to instrument only if needed

Added:
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/Instrumented.java
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaTransformer.java
    incubator/sirona/trunk/agent/javaagent/src/test/resources/sirona.properties
Removed:
    incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/test/sirona/javaagent/IncludeExcludeTest.java
Modified:
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/AgentContext.java
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaAgent.java
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaClassVisitor.java
    incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/listener/CounterListener.java
    incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/sirona/javaagent/JavaAgentRunner.java

Modified: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/AgentContext.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/AgentContext.java?rev=1549252&r1=1549251&r2=1549252&view=diff
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/AgentContext.java (original)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/AgentContext.java Sun Dec  8 21:07:11 2013
@@ -24,13 +24,7 @@ import org.apache.sirona.javaagent.spi.I
 import org.apache.sirona.javaagent.spi.Order;
 import org.apache.sirona.spi.SPI;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -88,10 +82,14 @@ public class AgentContext {
         listeners.add(autoset);
     }
 
-    private static InvocationListener[] listeners(final String key) {
+    public static InvocationListener[] listeners(final String key) {
         InvocationListener[] listeners = LISTENERS_BY_KEY.get(key);
         if (listeners == null) {
             listeners = findListeners(key);
+            if (listeners.length == 0) {
+                return null;
+            }
+
             final InvocationListener[] old = LISTENERS_BY_KEY.putIfAbsent(key, listeners);
             if (old != null) {
                 listeners = old;

Added: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/Instrumented.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/Instrumented.java?rev=1549252&view=auto
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/Instrumented.java (added)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/Instrumented.java Sun Dec  8 21:07:11 2013
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sirona.javaagent;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Instrumented {
+}

Modified: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaAgent.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaAgent.java?rev=1549252&r1=1549251&r2=1549252&view=diff
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaAgent.java (original)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaAgent.java Sun Dec  8 21:07:11 2013
@@ -16,28 +16,15 @@
  */
 package org.apache.sirona.javaagent;
 
-import org.apache.sirona.configuration.predicate.PredicateEvaluator;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
+import java.lang.annotation.Annotation;
 import java.lang.instrument.Instrumentation;
-import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
-import java.security.ProtectionDomain;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.jar.JarFile;
 
 public class SironaAgent {
-    private static final Collection<String> JVM_ENHANCED = Arrays.asList(
-        "sun/net/www/protocol/http/HttpURLConnection"
-    );
-
     public static void premain(final String agentArgs, final Instrumentation instrumentation) {
         agentmain(agentArgs, instrumentation);
     }
@@ -77,23 +64,32 @@ public class SironaAgent {
             }
         }
 
-        try { // eager init
-            loader.loadClass("org.apache.sirona.javaagent.AgentContext")
-                    .getMethod("touch").invoke(null);
-            loader.loadClass("org.apache.sirona.configuration.Configuration")
-                    .getMethod("is", String.class, boolean.class).invoke(null, "", true);
+        try { // eager init of static blocks
+            Class.forName("org.apache.sirona.configuration.Configuration", true, loader);
+            Class.forName("org.apache.sirona.javaagent.AgentContext", true, loader);
         } catch (final Exception e) {
             e.printStackTrace();
         }
 
         try {
-            instrumentation.addTransformer(
-                    ClassFileTransformer.class.cast(loader.loadClass("org.apache.sirona.javaagent.SironaAgent$SironaTransformer")
-                        .getConstructor(String.class).newInstance(agentArgs)),
-                    instrumentation.isRetransformClassesSupported());
+            final SironaTransformer transformer = SironaTransformer.class.cast(loader.loadClass("org.apache.sirona.javaagent.SironaTransformer").newInstance());
+            instrumentation.addTransformer(transformer, instrumentation.isRetransformClassesSupported());
 
-            for (final String jvm : JVM_ENHANCED) {
-                instrumentation.retransformClasses(loader.loadClass(jvm.replace('/', '.')));
+            final Class<? extends Annotation> instrumentedMarker = (Class<? extends Annotation>) loader.loadClass("org.apache.sirona.javaagent.Instrumented");
+            final Class<?> listener = loader.loadClass("org.apache.sirona.javaagent.spi.InvocationListener");
+            if (instrumentation.isRetransformClassesSupported()) {
+                for (final Class<?> jvm : instrumentation.getAllLoadedClasses()) {
+                    if (!jvm.isArray()
+                            && !listener.isAssignableFrom(jvm)
+                            && jvm.getAnnotation(instrumentedMarker) == null
+                            && instrumentation.isModifiableClass(jvm)) {
+                        try {
+                            instrumentation.retransformClasses(jvm);
+                        } catch (final Exception e) {
+                            System.err.println("Can't instrument: " + jvm.getName() + "[" + e.getMessage() + "]");
+                        }
+                    }
+                }
             }
         } catch (final Exception e) {
             e.printStackTrace();
@@ -153,62 +149,4 @@ public class SironaAgent {
         }
         return null;
     }
-
-    public static class SironaTransformer implements ClassFileTransformer {
-        private static final String DELEGATING_CLASS_LOADER = "sun.reflect.DelegatingClassLoader";
-
-        private final PredicateEvaluator includeEvaluator;
-        private final PredicateEvaluator excludeEvaluator;
-
-        // used by reflection so don't change visibility without testing
-        public SironaTransformer(final String agentArgs) {
-            includeEvaluator = createEvaluator(agentArgs, "includes=", new PredicateEvaluator("true:true", ","));
-            excludeEvaluator = createEvaluator(agentArgs, "excludes=", new PredicateEvaluator(null, null)); // no matching
-        }
-
-        private PredicateEvaluator createEvaluator(final String agentArgs, final String str, final PredicateEvaluator defaultEvaluator) {
-            final String configuration = extractConfig(agentArgs, str);
-            if (configuration != null) {
-                return new PredicateEvaluator(configuration, ",");
-            }
-            return defaultEvaluator;
-        }
-
-        @Override
-        public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined,
-                                final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException {
-            if (shouldTransform(className, loader)) {
-                return doTransform(className, classfileBuffer);
-            }
-            return classfileBuffer;
-        }
-
-        private byte[] doTransform(final String className, final byte[] classfileBuffer) {
-            try {
-                final ClassReader reader = new ClassReader(classfileBuffer);
-                final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
-                final SironaClassVisitor advisor = new SironaClassVisitor(writer, className);
-                reader.accept(advisor, ClassReader.SKIP_DEBUG);
-                return writer.toByteArray();
-            } catch (final RuntimeException re) {
-                if (Boolean.getBoolean("sirona.agent.debug")) {
-                    re.printStackTrace();
-                }
-                throw re;
-            }
-        }
-
-        private boolean shouldTransform(final String className, final ClassLoader loader) {
-            return JVM_ENHANCED.contains(className)
-                || !(loader == null // bootstrap classloader
-                    || className == null // framework with bug
-                    || loader.getClass().getName().equals(DELEGATING_CLASS_LOADER)
-                    || className.startsWith("sun/reflect")
-                    || className.startsWith("com/sun/proxy")
-                    || className.startsWith("org/apache/sirona"))
-
-                    && includeEvaluator.matches(className.replace("/", "."))
-                    && !excludeEvaluator.matches(className.replace("/", "."));
-        }
-    }
 }

Modified: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaClassVisitor.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaClassVisitor.java?rev=1549252&r1=1549251&r2=1549252&view=diff
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaClassVisitor.java (original)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaClassVisitor.java Sun Dec  8 21:07:11 2013
@@ -61,6 +61,12 @@ public class SironaClassVisitor extends 
     }
 
     @Override
+    public void visitSource(final String source, final String debug) {
+        super.visitSource(source, debug);
+        visitAnnotation("L" + Instrumented.class.getName().replace('.', '/') + ";", true).visitEnd();
+    }
+
+    @Override
     public MethodVisitor visitMethod(int access, final String name, final String desc, final String signature, final String[] exceptions) {
         final MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
         if (!isSironable(access, name)) {
@@ -68,25 +74,27 @@ public class SironaClassVisitor extends 
         }
 
         final String label = javaName.replace("/", ".") + "." + name;
-
-        { // generate "proxy" method and store the associated field for counter key (generated at the end)
-            final String fieldName = name + FIELD_SUFFIX;
-            if (!keys.containsKey(fieldName)) {
-                keys.put(fieldName, label);
+        if (AgentContext.listeners(label) != null) {
+            { // generate "proxy" method and store the associated field for counter key (generated at the end)
+                final String fieldName = name + FIELD_SUFFIX;
+                if (!keys.containsKey(fieldName)) {
+                    keys.put(fieldName, label);
+                }
+
+                final ProxyMethodsVisitor sironaVisitor = new ProxyMethodsVisitor(visitor, access, new Method(name, desc), classType);
+                sironaVisitor.visitCode();
+                sironaVisitor.visitEnd();
             }
 
-            final ProxyMethodsVisitor sironaVisitor = new ProxyMethodsVisitor(visitor, access, new Method(name, desc), classType);
-            sironaVisitor.visitCode();
-            sironaVisitor.visitEnd();
+            // generate internal method - the proxy (previous one) delegates to this one
+            return super.visitMethod(forcePrivate(access), name + METHOD_SUFFIX, desc, signature, exceptions);
         }
-
-        // generate internal method - the proxy (previous one) delegates to this one
-        return super.visitMethod(forcePrivate(access), name + METHOD_SUFFIX, desc, signature, exceptions);
+        return visitor;
     }
 
     @Override
     public void visitEnd() {
-        if (!keys.isEmpty()) {
+        if (hasAdviced()) {
             for (final String key : keys.keySet()) {
                 visitField(CONSTANT_ACCESS, key, KEY_TYPE.getDescriptor(), null, null).visitEnd();
             }
@@ -96,13 +104,15 @@ public class SironaClassVisitor extends 
             visitor.visitInsn(RETURN);
             visitor.visitMaxs(0, 0);
             visitor.visitEnd();
-
-            keys.clear();
         }
 
         super.visitEnd();
     }
 
+    public boolean hasAdviced() {
+        return !keys.isEmpty();
+    }
+
     private static int forcePrivate(final int access) {
         return (access & ~(Modifier.PRIVATE | Modifier.PUBLIC | Modifier.PROTECTED)) | Modifier.PRIVATE;
     }

Added: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaTransformer.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaTransformer.java?rev=1549252&view=auto
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaTransformer.java (added)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/SironaTransformer.java Sun Dec  8 21:07:11 2013
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sirona.javaagent;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+public class SironaTransformer implements ClassFileTransformer {
+    private static final String DELEGATING_CLASS_LOADER = "sun.reflect.DelegatingClassLoader";
+
+    @Override
+    public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined,
+                            final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException {
+        if (shouldTransform(className, loader)) {
+            return doTransform(className, classfileBuffer);
+        }
+        return classfileBuffer;
+    }
+
+    private byte[] doTransform(final String className, final byte[] classfileBuffer) {
+        try {
+            final ClassReader reader = new ClassReader(classfileBuffer);
+            final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
+            final SironaClassVisitor advisor = new SironaClassVisitor(writer, className);
+            reader.accept(advisor, ClassReader.SKIP_DEBUG);
+            if (advisor.hasAdviced()) {
+                return writer.toByteArray();
+            }
+            return classfileBuffer;
+        } catch (final RuntimeException re) {
+            if (Boolean.getBoolean("sirona.agent.debug")) {
+                re.printStackTrace();
+            }
+            throw re;
+        }
+    }
+
+    private static boolean shouldTransform(final String className, final ClassLoader loader) {
+        return !(className == null // framework with bug
+                || (loader != null && loader.getClass().getName().equals(DELEGATING_CLASS_LOADER))
+                || className.startsWith("sun/reflect")
+                || className.startsWith("com/sun/proxy")
+                || className.startsWith("org/apache/sirona"));
+    }
+}

Modified: incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/listener/CounterListener.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/listener/CounterListener.java?rev=1549252&r1=1549251&r2=1549252&view=diff
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/listener/CounterListener.java (original)
+++ incubator/sirona/trunk/agent/javaagent/src/main/java/org/apache/sirona/javaagent/listener/CounterListener.java Sun Dec  8 21:07:11 2013
@@ -30,7 +30,7 @@ public class CounterListener extends Abs
     private static final int KEY = 0;
 
     private PredicateEvaluator includes = new PredicateEvaluator("true:true", ",");
-    private PredicateEvaluator excludes = new PredicateEvaluator(null, null);
+    private PredicateEvaluator excludes = new PredicateEvaluator("prefix:java,prefix:sun,prefix:com.sun", ",");
 
     @Override
     public boolean accept(final String key) {

Modified: incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/sirona/javaagent/JavaAgentRunner.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/sirona/javaagent/JavaAgentRunner.java?rev=1549252&r1=1549251&r2=1549252&view=diff
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/sirona/javaagent/JavaAgentRunner.java (original)
+++ incubator/sirona/trunk/agent/javaagent/src/test/java/org/apache/sirona/javaagent/JavaAgentRunner.java Sun Dec  8 21:07:11 2013
@@ -119,9 +119,9 @@ public class JavaAgentRunner extends Blo
     private static String[] buildProcessArgs(final FrameworkMethod mtd) throws IOException {
         final Collection<String> args = new ArrayList<String>();
         args.add(findJava());
-        args.add("-javaagent:" + buildJavaagent() + "=excludes=regex:org.apache.test.*Test,prefix:org.junit,prefix:junit" /*+ "|=includes=regex:org.apache.test.sirona.*Transform"*/);
+        args.add("-javaagent:" + buildJavaagent());
         if (Boolean.getBoolean("test.debug.remote")) {
-            args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005");
+            args.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + System.getProperty("test.debug.remote.port", "5005"));
         }
         args.add("-cp");
         args.add(removeAgentFromCp(System.getProperty("surefire.test.class.path", System.getProperty("java.class.path"))));
@@ -129,6 +129,8 @@ public class JavaAgentRunner extends Blo
         args.add(mtd.getMethod().getDeclaringClass().getName());
         args.add(mtd.getName());
 
+        //  System.out.println("Running " + args.toString().replace(",", "").substring(1).replace("]", ""));
+
         return args.toArray(new String[args.size()]);
     }
 

Added: incubator/sirona/trunk/agent/javaagent/src/test/resources/sirona.properties
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/agent/javaagent/src/test/resources/sirona.properties?rev=1549252&view=auto
==============================================================================
--- incubator/sirona/trunk/agent/javaagent/src/test/resources/sirona.properties (added)
+++ incubator/sirona/trunk/agent/javaagent/src/test/resources/sirona.properties Sun Dec  8 21:07:11 2013
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.sirona.javaagent.listener.CounterListener.excludes = \
+  prefix:java,\
+  prefix:sun,\
+  prefix:com,\
+  regex:org.apache.test.*Test\\..*,\
+  prefix:org.junit,\
+  prefix:junit