changeset 58087:615b494384e4

8239350: Add tests for JFR class redefinition events Reviewed-by: mgronlun
author egahlin
date Tue, 18 Feb 2020 16:34:19 +0100
parents a502b482d5c3
children d1c0dc3719c6
files test/jdk/jdk/jfr/event/runtime/Bytes.java test/jdk/jdk/jfr/event/runtime/RedefinableClass.java test/jdk/jdk/jfr/event/runtime/TestClassRedefinition.java test/jdk/jdk/jfr/event/runtime/TestRedefineClasses.java test/jdk/jdk/jfr/event/runtime/TestRetransformClasses.java
diffstat 5 files changed, 426 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/Bytes.java	Tue Feb 18 16:34:19 2020 +0100
@@ -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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/RedefinableClass.java	Tue Feb 18 16:34:19 2020 +0100
@@ -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();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestClassRedefinition.java	Tue Feb 18 16:34:19 2020 +0100
@@ -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());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestRedefineClasses.java	Tue Feb 18 16:34:19 2020 +0100
@@ -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");
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestRetransformClasses.java	Tue Feb 18 16:34:19 2020 +0100
@@ -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");
+    }
+}
\ No newline at end of file