8239350: Add tests for JFR class redefinition events

Reviewed-by: mgronlun
This commit is contained in:
Erik Gahlin 2020-02-18 16:34:19 +01:00
parent f75f78ae3a
commit b6c1b49c97
5 changed files with 426 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.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 the RetransformClasses 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.TestRetransformClasses
*
* @run driver jdk.test.lib.util.JavaAgentBuilder
* jdk.jfr.event.runtime.TestRetransformClasses TestRetransformClasses.jar
*
* @run main/othervm -javaagent:TestRetransformClasses.jar
* jdk.jfr.event.runtime.TestRetransformClasses
*/
public class TestRetransformClasses {
private final static Path DUMP_PATH = Paths.get("dump.jfr");
private final static String TEST_AGENT = "Test Agent";
public static class TestClassFileTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
return Bytes.replaceAll(classfileBuffer, Bytes.WORLD, Bytes.EARTH);
}
}
// 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.RetransformClasses);
r.start();
RedefinableClass.sayHello();
instrumentation.addTransformer(new TestClassFileTransformer());
instrumentation.retransformClasses(RedefinableClass.class);
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 RetransformClasses 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, TestRetransformClasses.class, "premain");
}
}