From cce988f426ac3c12f3bd28759b45287085c3551a Mon Sep 17 00:00:00 2001 From: Emmanuel Bourg <ebourg@apache.org> Date: Mon, 4 Jun 2018 22:58:28 +0200 Subject: [PATCH] New upstream version 3.1.0 --- README.md | 6 +- pom.xml | 146 ++++++++++++++---- .../AllocationClassAdapter.java | 4 +- .../AllocationInstrumenter.java | 4 +- .../AllocationMethodAdapter.java | 35 +++-- .../instrumentation/AllocationRecorder.java | 6 +- .../runtime/instrumentation/Bootstrap.java.in | 53 +++++++ .../instrumentation/ConstructorCallback.java | 1 - .../ConstructorInstrumenter.java | 5 +- .../runtime/instrumentation/Sampler.java | 1 - .../instrumentation/StaticClassWriter.java | 1 - .../VerifyingClassAdapter.java | 3 +- 12 files changed, 205 insertions(+), 60 deletions(-) create mode 100644 src/main/java/com/google/monitoring/runtime/instrumentation/Bootstrap.java.in diff --git a/README.md b/README.md index 20ce0d4..360e33d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The [latest release][] is available from [Maven Central][] as: <dependency> <groupId>com.google.code.java-allocation-instrumenter</groupId> <artifactId>java-allocation-instrumenter</artifactId> - <version>3.0</version> + <version>3.0.1</version> </dependency> ``` @@ -50,6 +50,6 @@ For more information on how to get or use the allocation instrumenter, see [Gett [java.lang.instrument]: http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html [ASM]: http://asm.ow2.org/ -[latest release]: https://github.com/google/allocation-instrumenter/releases/tag/java-allocation-instrumenter-3.0 -[Maven Central]: http://search.maven.org/#artifactdetails%7Ccom.google.code.java-allocation-instrumenter%7Cjava-allocation-instrumenter%7C3.0%7Cjar +[latest release]: https://github.com/google/allocation-instrumenter/releases/tag/java-allocation-instrumenter-3.0.1 +[Maven Central]: http://search.maven.org/#artifactdetails%7Ccom.google.code.java-allocation-instrumenter%7Cjava-allocation-instrumenter%7C3.0.1%7Cjar [Getting Started]: https://github.com/google/allocation-instrumenter/wiki diff --git a/pom.xml b/pom.xml index 559643c..67131f8 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ <groupId>com.google.code.java-allocation-instrumenter</groupId> <artifactId>java-allocation-instrumenter</artifactId> <packaging>jar</packaging> - <version>3.0.1</version> + <version>3.1.0</version> <name>java-allocation-instrumenter</name> <description> @@ -72,7 +72,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <projectAsmVersion>5.0.3</projectAsmVersion> + <projectAsmVersion>6.0</projectAsmVersion> <gpg.skip>true</gpg.skip> </properties> @@ -110,7 +110,7 @@ <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> - <version>18.0</version> + <version>23.5-android</version> </dependency> <dependency> <groupId>junit</groupId> @@ -123,11 +123,11 @@ <build> <defaultGoal>package</defaultGoal> <plugins> - <!-- ensure java version --> + <!-- specify target Java version --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.2</version> + <version>3.6.1</version> <configuration> <source>1.6</source> <target>1.6</target> @@ -137,7 +137,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> - <version>2.9</version> + <version>2.10</version> <configuration> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> @@ -148,7 +148,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>2.5.1</version> + <version>2.5.3</version> <configuration> <arguments>-DenableCiProfile=true</arguments> </configuration> @@ -157,7 +157,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> - <version>2.4</version> + <version>3.0.1</version> <executions> <execution> <id>attach-sources</id> @@ -171,7 +171,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>2.10.1</version> + <version>2.10.4</version> <executions> <execution> <id>attach-javadocs</id> @@ -188,7 +188,109 @@ <show>public</show> </configuration> </plugin> - <!-- embed dependencies --> + <!-- preprocess bootstrap method --> + <plugin> + <groupId>com.google.code.maven-replacer-plugin</groupId> + <artifactId>replacer</artifactId> + <version>1.5.3</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>replace</goal> + </goals> + </execution> + </executions> + <configuration> + <file>${project.basedir}/src/main/java/com/google/monitoring/runtime/instrumentation/Bootstrap.java.in</file> + <outputFile>${project.build.directory}/generated-sources/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenterBootstrap.java</outputFile> + <replacements> + <replacement> + <token>CLASS_NAME</token> + <value>AllocationInstrumenter</value> + </replacement> + <replacement> + <token>PACKAGE</token> + <value>com.google.monitoring.runtime.instrumentation</value> + </replacement> + <replacement> + <token>PATH_TO_CLASS</token> + <value>com/google/monitoring/runtime/instrumentation</value> + </replacement> + <replacement> + <token>PREMAIN_CLASS</token> + <value>com.google.monitoring.runtime.instrumentation.AllocationInstrumenter</value> + </replacement> + <replacement> + <token>GENERATOR</token> + <value>https://github.com/google/allocation-instrumenter/blob/master/pom.xml</value> + </replacement> + </replacements> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>3.0.0</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>${project.build.directory}/generated-sources/java/</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <!-- shade Guava and ASM; strip module-info file from ASM --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <phase>package</phase> + <goals><goal>shade</goal></goals> + </execution> + </executions> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <artifactSet> + <includes> + <include>org.ow2.asm:asm</include> + <include>org.ow2.asm:asm-analysis</include> + <include>org.ow2.asm:asm-commons</include> + <include>org.ow2.asm:asm-tree</include> + <include>org.ow2.asm:asm-util</include> + <include>org.ow2.asm:asm-xml</include> + <include>com.google.guava:guava</include> + </includes> + </artifactSet> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/module-info.class</exclude> + </excludes> + </filter> + </filters> + <relocations> + <relocation> + <pattern>com.google.common.</pattern> + <shadedPattern>com.google.monitoring.runtime.instrumentation.common.</shadedPattern> + </relocation> + <relocation> + <pattern>org.objectweb.asm.</pattern> + <shadedPattern>com.google.monitoring.runtime.instrumentation.asm.</shadedPattern> + </relocation> + </relocations> + </configuration> + </plugin> <plugin> <groupId>org.sonatype.plugins</groupId> <artifactId>jarjar-maven-plugin</artifactId> @@ -201,24 +303,7 @@ <goal>jarjar</goal> </goals> <configuration> - <includes> - <include>org.ow2.asm:asm</include> - <include>org.ow2.asm:asm-analysis</include> - <include>org.ow2.asm:asm-commons</include> - <include>org.ow2.asm:asm-tree</include> - <include>org.ow2.asm:asm-util</include> - <include>org.ow2.asm:asm-xml</include> - <include>com.google.guava:guava</include> - </includes> <rules> - <rule> - <pattern>org.objectweb.asm.**</pattern> - <result>com.google.monitoring.runtime.instrumentation.asm.@1</result> - </rule> - <rule> - <pattern>com.google.common.**</pattern> - <result>com.google.monitoring.runtime.instrumentation.common.@0</result> - </rule> <keep> <pattern>com.google.monitoring.runtime.instrumentation.common.collect.ComputingCache</pattern> <pattern>com.google.monitoring.runtime.instrumentation.common.collect.ComputingConcurrentHashMap</pattern> @@ -241,12 +326,11 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>2.5</version> + <version>3.0.2</version> <configuration> <archive> <manifestEntries> - <Boot-Class-Path>./${project.artifactId}-${project.version}.${project.packaging}</Boot-Class-Path> - <Premain-Class>com.google.monitoring.runtime.instrumentation.AllocationInstrumenter</Premain-Class> + <Premain-Class>com.google.monitoring.runtime.instrumentation.AllocationInstrumenterBootstrap</Premain-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> <Main-Class>NotSuitableAsMain</Main-Class> @@ -257,7 +341,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> - <version>1.4</version> + <version>1.6</version> <executions> <execution> <id>sign-artifacts</id> diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java index 14a6011..9d76247 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java @@ -27,8 +27,6 @@ import org.objectweb.asm.commons.JSRInlinerAdapter; * A <code>ClassVisitor</code> that processes methods with a * <code>AllocationMethodAdapter</code> to instrument heap allocations. * - * @author jeremymanson@google.com (Jeremy Manson) - * @author fischman@google.com (Ami Fischman) (Original Author) */ class AllocationClassAdapter extends ClassVisitor { private final String recorderClass; @@ -36,7 +34,7 @@ class AllocationClassAdapter extends ClassVisitor { public AllocationClassAdapter(ClassVisitor cv, String recorderClass, String recorderMethod) { - super(Opcodes.ASM5, cv); + super(Opcodes.ASM6, cv); this.recorderClass = recorderClass; this.recorderMethod = recorderMethod; } diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java index d09bc24..4867fa0 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java @@ -36,8 +36,6 @@ import java.util.logging.Logger; * looks like it will be allocating heap memory allowing users to implement heap * profiling schemes. * - * @author Ami Fischman - * @author Jeremy Manson */ public class AllocationInstrumenter implements ClassFileTransformer { static final Logger logger = @@ -161,7 +159,7 @@ public class AllocationInstrumenter implements ClassFileTransformer { /** * Given the bytes representing a class, go through all the bytecode in it and - * instrument any occurences of new/newarray/anewarray/multianewarray with + * instrument any occurrences of new/newarray/anewarray/multianewarray with * pre- and post-allocation hooks. Even more fun, intercept calls to the * reflection API's Array.newInstance() and instrument those too. * diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java index 115ea01..c4a47d6 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java @@ -32,7 +32,6 @@ import java.util.regex.Pattern; * to record the allocation being done for profiling. * Instruments bytecodes that allocate heap memory to call a recording hook. * - * @author Ami Fischman */ class AllocationMethodAdapter extends MethodVisitor { /** @@ -148,13 +147,6 @@ class AllocationMethodAdapter extends MethodVisitor { super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false); // -> stack: ... class classNameDotted - super.visitLdcInsn('.'); - // -> stack: ... class classNameDotted '.' - super.visitLdcInsn('/'); - // -> stack: ... class classNameDotted '.' '/' - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", - "replace", "(CC)Ljava/lang/String;", false); - // -> stack: ... class className } // Helper method to compute the product of an integer array and push it on @@ -551,14 +543,37 @@ class AllocationMethodAdapter extends MethodVisitor { // below we note the partial product of dimensions 1 to X-1 as productToX // (so productTo1 == 1 == no dimensions yet). We denote by aref0 the // array reference at the current nesting level (the containing aref's [0] - // element). If we hit a level whose arraylength is 0 there's no point - // continuing so we shortcut out. + // element). If we hit a level whose arraylength is 0 or whose + // reference is null, there's no point continuing, so we shortcut + // out. + + // This approach works pretty well when you create a new array with the + // newarray bytecodes. You can also create a new array by cloning an + // existing array; an existing multidimensional array might have had some + // of its [0] elements nulled out. We currently deal with this by bailing + // out, but arguably we should do something more principled (like calculate + // the size of the multidimensional array from scratch if you are using + // clone()). + // TODO(java-platform-team): Do something about modified multidimensional + // arrays and clone(). Label zeroDimension = new Label(); super.visitInsn(Opcodes.DUP); // -> stack: ... origaref aref0 super.visitLdcInsn(1); // -> stack: ... origaref aref0 productTo1 for (int i = 0; i < dimCount; ++i) { // pre: stack: ... origaref aref0 productToI super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref productToI aref + super.visitInsn(Opcodes.DUP); + + Label nonNullDimension = new Label(); + // -> stack: ... origaref productToI aref aref + super.visitJumpInsn(Opcodes.IFNONNULL, nonNullDimension); + // -> stack: ... origaref productToI aref + super.visitInsn(Opcodes.SWAP); + // -> stack: ... origaref aref productToI + super.visitJumpInsn(Opcodes.GOTO, zeroDimension); + super.visitLabel(nonNullDimension); + + // -> stack: ... origaref productToI aref super.visitInsn(Opcodes.DUP_X1); // -> stack: ... origaref aref0 productToI aref super.visitInsn(Opcodes.ARRAYLENGTH); diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java index 862e53f..da2b811 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java @@ -30,8 +30,6 @@ import com.google.common.collect.MapMaker; * The logic for recording allocations, called from bytecode rewritten by * {@link AllocationInstrumenter}. * - * @author jeremymanson@google.com (Jeremy Manson) - * @author fischman@google.com (Ami Fischman) */ public class AllocationRecorder { static { @@ -233,6 +231,10 @@ public class AllocationRecorder { // optional samplers. However, you don't need the optional samplers in // the common case, so I thought I'd save some space. + if (count >= 0) { + desc = desc.replace('.', '/'); + } + // Copy value into local variable to prevent NPE that occurs when // instrumentation field is set to null by this class's shutdown hook // after another thread passed the null check but has yet to call diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/Bootstrap.java.in b/src/main/java/com/google/monitoring/runtime/instrumentation/Bootstrap.java.in new file mode 100644 index 0000000..ad6560c --- /dev/null +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/Bootstrap.java.in @@ -0,0 +1,53 @@ +package PACKAGE; + +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; +import java.net.JarURLConnection; +import java.util.jar.JarFile; +import javax.annotation.Generated; + +/** + * Add the agent to the bootclasspath before invoking premain(). + * + * <p>In order to rewrite classes that are loaded by the bootstrap class loader to point to + * agent-provided instrumentation, the agent needs to be loaded by the bootstrap class loader. + * + * <p>There are three ways of doing this. The first requires that we add "Boot-Class-Path: <JAR file + * name>" to the agent's manifest. That only works when we know the exact name of the output JAR. + * That's not the case in various environments. For example, Maven-released JARs often bake a + * version number into their JAR names. The second is to add the JAR to the bootclasspath on the + * command line, but that's awful, as well as unsupported in Java 9. + * + * <p>The third is to use Instrumentation#appendToBootstrapClassLoaderSearch, which works as + * advertised. We can dynamically determine the name of the JAR containing this class (i.e., the + * agent), and then add that JAR to the bootstrap class path. The problem with this approach is that + * the agent's premain class is loaded prior to our ability to call that function, so it can't do + * the things we need the agent to be in the bootstrap class path to do. One workaround for this is + * to create a class that does nothing but call appendToBootstrapClassLoaderSearch on the agent's + * JAR file, and then calls into the real premain function (like the one below). Rather than + * replicating this class for every agent we might write, we use generated code. + * + * <p>This file is pre-processed to include the correct name of the class being used as a premain + * before being compiled and integrated into the agent. + */ +@Generated(value = "GENERATOR") +public class CLASS_NAMEBootstrap { + public static void premain(String agentArgs, Instrumentation inst) { + try { + // First, find the JAR file containing the agent and append it to the bootclasspath. + String resourceName = "PATH_TO_CLASS"; + java.net.URL url = ClassLoader.getSystemResource(resourceName); + JarFile jarfile = ((JarURLConnection) url.openConnection()).getJarFile(); + inst.appendToBootstrapClassLoaderSearch(jarfile); + + // Now invoke the real premain. We do not require that the premain class be available at + // compile time. Some agents bake it into the main JAR file. We use reflection to + // invoke it. + Class<?> premainClass = Class.forName("PREMAIN_CLASS"); + Method pm = premainClass.getDeclaredMethod("premain", String.class, Instrumentation.class); + pm.invoke(null, agentArgs, inst); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java b/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java index 02fb981..c2bd33d 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java @@ -27,7 +27,6 @@ package com.google.monitoring.runtime.instrumentation; * * @param <T> The class that will be sampled with this ConstructorCallback * - * @author Jeremy Manson */ public interface ConstructorCallback<T> { /** diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java index 87119a5..bc8e1da 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java @@ -39,7 +39,6 @@ import java.util.logging.Logger; * by a javaagent; end-users will want to add {@link ConstructorCallback}s by * invoking {@link #instrumentClass(Class, ConstructorCallback)}. * - * @author Jeremy Manson */ public class ConstructorInstrumenter implements ClassFileTransformer { // Implementation details: uses the java.lang.instrument API to @@ -161,7 +160,7 @@ public class ConstructorInstrumenter implements ClassFileTransformer { public LocalVariablesSorter lvs = null; Class<?> cl; ConstructorMethodAdapter(MethodVisitor mv, Class<?> cl) { - super(Opcodes.ASM5, mv); + super(Opcodes.ASM6, mv); this.cl = cl; } @@ -227,7 +226,7 @@ public class ConstructorInstrumenter implements ClassFileTransformer { static class ConstructorClassAdapter extends ClassVisitor { Class<?> cl; public ConstructorClassAdapter(ClassVisitor cv, Class<?> cl) { - super(Opcodes.ASM5, cv); + super(Opcodes.ASM6, cv); this.cl = cl; } diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java b/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java index 35b0a50..b75dada 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java @@ -19,7 +19,6 @@ package com.google.monitoring.runtime.instrumentation; /** * This interface describes a function that is used to sample an allocation. * - * @author jeremymanson@google.com (Jeremy Manson) */ public interface Sampler { /** diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java index 29e4119..a9fcc90 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java @@ -63,7 +63,6 @@ import java.io.IOException; * the same logic in a subclass. The code here has been slightly * cleaned up for readability. * - * @author jeremymanson@google.com (Jeremy Manson) */ class StaticClassWriter extends ClassWriter { diff --git a/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java b/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java index c0ab325..64c344c 100644 --- a/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java +++ b/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java @@ -33,7 +33,6 @@ import java.util.logging.Logger; * Currently, it only checks to see if the methods are of the correct length * for Java methods (<64K). * - * @author jeremymanson@google.com (Jeremy Manson) */ public class VerifyingClassAdapter extends ClassVisitor { private static final Logger logger = @@ -59,7 +58,7 @@ public class VerifyingClassAdapter extends ClassVisitor { */ public VerifyingClassAdapter(ClassWriter cw, byte [] original, String className) { - super(Opcodes.ASM5, cw); + super(Opcodes.ASM6, cw); state = State.UNKNOWN; message = "The class has not finished being examined"; this.cw = cw; -- GitLab