Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
8239350: Add tests for JFR class redefinition events
Reviewed-by: mgronlun
- Loading branch information
Showing
with
426 additions
and 0 deletions.
- +92 −0 test/jdk/jdk/jfr/event/runtime/Bytes.java
- +34 −0 test/jdk/jdk/jfr/event/runtime/RedefinableClass.java
- +102 −0 test/jdk/jdk/jfr/event/runtime/TestClassRedefinition.java
- +98 −0 test/jdk/jdk/jfr/event/runtime/TestRedefineClasses.java
- +100 −0 test/jdk/jdk/jfr/event/runtime/TestRetransformClasses.java
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
|
||
package jdk.jfr.event.runtime; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Helper class for working with class files and byte arrays | ||
*/ | ||
public final class Bytes { | ||
public final static byte[] WORLD = Bytes.asBytes("world"); | ||
public final static byte[] EARTH = Bytes.asBytes("earth"); | ||
|
||
public static byte[] asBytes(String string) { | ||
byte[] result = new byte[string.length()]; | ||
for (int i = 0; i < string.length(); i++) { | ||
result[i] = (byte)string.charAt(i); | ||
} | ||
return result; | ||
} | ||
|
||
public static byte[] classBytes(ClassLoader classLoader, String className) throws IOException { | ||
String classFileName = className.replace(".", "/") + ".class"; | ||
try (InputStream is = classLoader.getResourceAsStream(classFileName)) { | ||
if (is == null) { | ||
throw new IOException("Could not find class file " + classFileName); | ||
} | ||
return is.readAllBytes(); | ||
} | ||
} | ||
|
||
public static byte[] classBytes(Class<?> clazz) throws IOException { | ||
return classBytes(clazz.getClassLoader(), clazz.getName()); | ||
} | ||
|
||
public static byte[] replaceAll(byte[] input, byte[] target, byte[] replacement) { | ||
List<Byte> result = new ArrayList<>(); | ||
for (int i = 0; i < input.length; i++) { | ||
if (hasTarget(input, i, target)) { | ||
for (int j = 0; j < replacement.length; j++) { | ||
result.add(replacement[j]); | ||
} | ||
i += target.length - 1; | ||
} else { | ||
result.add(input[i]); | ||
} | ||
} | ||
byte[] resultArray = new byte[result.size()]; | ||
for (int i = 0; i < resultArray.length; i++) { | ||
resultArray[i] = result.get(i); | ||
} | ||
return resultArray; | ||
} | ||
|
||
private static boolean hasTarget(byte[] input, int start, byte[] target) { | ||
for (int i = 0; i < target.length; i++) { | ||
if (start + i == input.length) { | ||
return false; | ||
} | ||
if (input[start + i] != target[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} |
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
|
||
package jdk.jfr.event.runtime; | ||
|
||
// Class used by redefinition events | ||
public class RedefinableClass { | ||
public static void sayHello() { | ||
System.out.println("hello, world!"); | ||
System.out.println(); | ||
} | ||
} |
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
|
||
package jdk.jfr.event.runtime; | ||
|
||
import java.lang.instrument.ClassDefinition; | ||
import java.lang.instrument.Instrumentation; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
|
||
import jdk.jfr.Recording; | ||
import jdk.jfr.consumer.RecordedClass; | ||
import jdk.jfr.consumer.RecordedEvent; | ||
import jdk.jfr.consumer.RecordingFile; | ||
import jdk.test.lib.Asserts; | ||
import jdk.test.lib.jfr.EventNames; | ||
import jdk.test.lib.jfr.Events; | ||
|
||
/** | ||
* @test | ||
* @summary Tests ClassRedefinition event by redefining classes in a Java agent | ||
* @key jfr | ||
* @requires vm.hasJFR | ||
* @library /test/lib /test/jdk | ||
* @modules java.instrument | ||
* | ||
* @build jdk.jfr.event.runtime.RedefinableClass | ||
* @build jdk.jfr.event.runtime.Bytes | ||
* @build jdk.jfr.event.runtime.TestClassRedefinition | ||
* | ||
* @run driver jdk.test.lib.util.JavaAgentBuilder | ||
* jdk.jfr.event.runtime.TestClassRedefinition TestClassRedefinition.jar | ||
* | ||
* @run main/othervm -javaagent:TestClassRedefinition.jar | ||
* jdk.jfr.event.runtime.TestClassRedefinition | ||
*/ | ||
public class TestClassRedefinition { | ||
private final static Path DUMP_PATH = Paths.get("dump.jfr"); | ||
|
||
// Called when agent is loaded from command line | ||
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { | ||
try (Recording r = new Recording()) { | ||
r.enable(EventNames.ClassRedefinition); | ||
r.start(); | ||
byte[] worldBytes = Bytes.classBytes(RedefinableClass.class); | ||
byte[] earthBytes = Bytes.replaceAll(worldBytes, Bytes.WORLD, Bytes.EARTH); | ||
RedefinableClass.sayHello(); | ||
ClassDefinition cd1 = new ClassDefinition(RedefinableClass.class, earthBytes); | ||
instrumentation.redefineClasses(cd1); | ||
RedefinableClass.sayHello(); | ||
ClassDefinition cd2 = new ClassDefinition(RedefinableClass.class, worldBytes); | ||
instrumentation.redefineClasses(cd2); | ||
RedefinableClass.sayHello(); | ||
r.stop(); | ||
r.dump(DUMP_PATH); | ||
} | ||
} | ||
|
||
public static void main(String[] args) throws Throwable { | ||
List<RecordedEvent> events = RecordingFile.readAllEvents(DUMP_PATH); | ||
|
||
Asserts.assertEquals(events.size(), 2, "Expected exactly two ClassRedefinition event"); | ||
RecordedEvent e1 = events.get(0); | ||
System.out.println(e1); | ||
RecordedEvent e2 = events.get(1); | ||
System.out.println(e2); | ||
|
||
Events.assertField(e1, "classModificationCount").equal(1); | ||
Events.assertField(e2, "classModificationCount").equal(2); | ||
|
||
Events.assertField(e1, "redefinitionId").atLeast(1L); | ||
Events.assertField(e2, "redefinitionId").notEqual(e1.getValue("redefinitionId")); | ||
|
||
RecordedClass clazz1 = e1.getClass("redefinedClass"); | ||
Asserts.assertEquals(clazz1.getName(), RedefinableClass.class.getName()); | ||
RecordedClass clazz2 = e1.getClass("redefinedClass"); | ||
Asserts.assertEquals(clazz2.getName(), RedefinableClass.class.getName()); | ||
} | ||
} |
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
package jdk.jfr.event.runtime; | ||
|
||
import java.io.IOException; | ||
import java.lang.instrument.ClassDefinition; | ||
import java.lang.instrument.ClassFileTransformer; | ||
import java.lang.instrument.IllegalClassFormatException; | ||
import java.lang.instrument.Instrumentation; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.security.ProtectionDomain; | ||
import java.util.List; | ||
|
||
import jdk.jfr.Recording; | ||
import jdk.jfr.consumer.RecordedEvent; | ||
import jdk.jfr.consumer.RecordingFile; | ||
import jdk.test.lib.Asserts; | ||
import jdk.test.lib.jfr.EventNames; | ||
import jdk.test.lib.jfr.Events; | ||
|
||
/** | ||
* @test | ||
* @summary Tests RedefinitionClasses event by redefining a class in a Java | ||
* agent | ||
* @key jfr | ||
* @requires vm.hasJFR | ||
* @library /test/lib /test/jdk | ||
* @modules java.instrument | ||
* | ||
* @build jdk.jfr.event.runtime.RedefinableClass | ||
* @build jdk.jfr.event.runtime.Bytes | ||
* @build jdk.jfr.event.runtime.TestRedefineClasses | ||
* | ||
* @run driver jdk.test.lib.util.JavaAgentBuilder | ||
* jdk.jfr.event.runtime.TestRedefineClasses | ||
* TestRedefineClasses.jar | ||
* | ||
* @run main/othervm -javaagent:TestRedefineClasses.jar | ||
* jdk.jfr.event.runtime.TestRedefineClasses | ||
*/ | ||
public class TestRedefineClasses { | ||
private final static Path DUMP_PATH = Paths.get("dump.jfr"); | ||
private final static String TEST_AGENT = "Test Agent"; | ||
|
||
// Called when agent is loaded at startup | ||
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { | ||
Thread.currentThread().setName(TEST_AGENT); | ||
try (Recording r = new Recording()) { | ||
r.enable(EventNames.RedefineClasses); | ||
r.start(); | ||
RedefinableClass.sayHello(); | ||
byte[] bytes = Bytes.classBytes(RedefinableClass.class); | ||
bytes = Bytes.replaceAll(bytes, Bytes.WORLD, Bytes.EARTH); | ||
ClassDefinition c1 = new ClassDefinition(RedefinableClass.class, bytes); | ||
instrumentation.redefineClasses(c1); | ||
RedefinableClass.sayHello(); | ||
r.stop(); | ||
r.dump(DUMP_PATH); | ||
} | ||
} | ||
|
||
public static void main(String[] args) throws Throwable { | ||
List<RecordedEvent> events = RecordingFile.readAllEvents(DUMP_PATH); | ||
Asserts.assertEquals(events.size(), 1, "Expected one RedefineClasses event"); | ||
RecordedEvent event = events.get(0); | ||
|
||
System.out.println(event); | ||
|
||
Events.assertField(event, "eventThread.javaName").equal(TEST_AGENT); | ||
Events.assertField(event, "classCount").equal(1); | ||
Events.assertField(event, "redefinitionId").atLeast(1L); | ||
Events.assertField(event, "duration").atLeast(1L); | ||
Events.assertFrame(event, TestRedefineClasses.class, "premain"); | ||
} | ||
} |
Oops, something went wrong.