| /* |
| * Copyright (c) 2023, 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. |
| * |
| * 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. |
| */ |
| |
| /* |
| * @test |
| * @bug 8309808 8309811 |
| * @summary Test the output of the HotSpot BytecodeTracer and ClassPrinter classes. |
| * @library /test/lib |
| * @build jdk.test.whitebox.WhiteBox Linked2 Unlinked2 |
| * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox |
| * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI BytecodeTracerTest |
| */ |
| |
| import java.io.BufferedWriter; |
| import java.io.FileWriter; |
| import java.lang.invoke.MethodHandle; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import jdk.test.lib.Asserts; |
| import jdk.test.whitebox.WhiteBox; |
| |
| public class BytecodeTracerTest { |
| private final static String Linked_className = Linked.class.getName(); |
| private final static String Linked2_className = Linked2.class.getName(); |
| |
| // This loads the "Unlinked" and "Unlinked2" classes, but doesn't link them. |
| private final static String Unlinked_className = Unlinked.class.getName(); |
| private final static String Unlinked2_className = Unlinked2.class.getName(); |
| |
| public static void staticMethod() { } |
| public void virtualMethod() { } |
| int x; |
| static long y; |
| |
| public static class Linked { |
| public static void doit(String args[]) { |
| System.out.println("num args = " + args.length); |
| } |
| public String test_ldc() { |
| Class c = Unloaded.class; |
| return "Literal\u5678"; |
| } |
| public void test_invoke(BytecodeTracerTest obj) { |
| obj.virtualMethod(); |
| staticMethod(); |
| } |
| public void test_field(BytecodeTracerTest obj) { |
| y = obj.x; |
| } |
| public void test_invokehandle(MethodHandle mh) throws Throwable { |
| mh.invokeExact(4.0f, "String", this); |
| } |
| } |
| |
| public static class Unlinked { |
| public String toString() { |
| return "Unlinked" + this.hashCode(); |
| } |
| public String test_ldc() { |
| Class c = Unloaded.class; |
| return "Literal\u1234"; |
| } |
| public void test_invoke(BytecodeTracerTest obj) { |
| obj.virtualMethod(); |
| staticMethod(); |
| } |
| public void test_field(BytecodeTracerTest obj) { |
| y = obj.x; |
| } |
| public void test_invokehandle(MethodHandle mh) throws Throwable { |
| mh.invokeExact(4.0f, "String", this); |
| } |
| } |
| |
| // This class is never loaded during the execution of BytecodeTracerTest |
| public static class Unloaded { } |
| |
| static int testCount = 0; |
| String testNote; |
| String output; |
| |
| BytecodeTracerTest(String note) { |
| ++ testCount; |
| testNote = "======================================================================\nTest case " |
| + testCount + ": " + note + "\n "; |
| } |
| |
| void logOutput() throws Exception { |
| String logFileName = "log-" + testCount + ".txt"; |
| System.out.println("Output saved in " + logFileName); |
| BufferedWriter writer = new BufferedWriter(new FileWriter(logFileName)); |
| writer.write(output); |
| writer.close(); |
| } |
| |
| BytecodeTracerTest printClasses(String classNamePattern, int flags) throws Exception { |
| System.out.println(testNote + "printClasses(\"" + classNamePattern + "\", " + flags + ")"); |
| output = WhiteBox.getWhiteBox().printClasses(classNamePattern, flags); |
| logOutput(); |
| return this; |
| } |
| |
| BytecodeTracerTest printMethods(String classNamePattern, String methodPattern, int flags) throws Exception { |
| System.out.println(testNote + "printMethods(\"" + classNamePattern + "\", \"" + methodPattern + "\", " + flags + ")"); |
| output = WhiteBox.getWhiteBox().printMethods(classNamePattern, methodPattern, flags); |
| logOutput(); |
| return this; |
| } |
| |
| BytecodeTracerTest printLinkedMethods(String methodPattern) throws Exception { |
| return printMethods(Linked_className, methodPattern, 0xff); |
| } |
| |
| BytecodeTracerTest printLinked2Methods(String methodPattern) throws Exception { |
| return printMethods(Linked2_className, methodPattern, 0xff); |
| } |
| |
| BytecodeTracerTest printUnlinkedMethods(String methodPattern) throws Exception { |
| return printMethods(Unlinked_className, methodPattern, 0xff); |
| } |
| |
| BytecodeTracerTest printUnlinked2Methods(String methodPattern) throws Exception { |
| return printMethods(Unlinked2_className, methodPattern, 0xff); |
| } |
| |
| BytecodeTracerTest mustMatch(String pattern) { |
| Pattern p = Pattern.compile(pattern, Pattern.MULTILINE); |
| Matcher m = p.matcher(output); |
| boolean found = m.find(); |
| if (!found) { |
| System.err.println("********* output ********"); |
| System.err.println(output); |
| System.err.println("*************************"); |
| } |
| Asserts.assertTrue(found, |
| "Missing pattern: \"" + pattern + "\""); |
| System.out.println("Found pattern: " + pattern); |
| System.out.println(" ==>: " + m.group()); |
| return this; |
| } |
| |
| static BytecodeTracerTest test(String note) { |
| return new BytecodeTracerTest(note); |
| } |
| |
| public static void main(String args[]) throws Exception { |
| // Force "Linked" and "Linked2" classes to be linked (and rewritten); |
| Linked.doit(args); |
| Asserts.assertTrue(Linked2.test_ldc() == 12345, "must be"); |
| |
| test("invokedynamic in linked class") |
| .printClasses("BytecodeTracerTest$Linked", 0xff) |
| .mustMatch("invokedynamic bsm=[0-9]+ [0-9]+ <makeConcatWithConstants[(]I[)]Ljava/lang/String;>") |
| .mustMatch("BSM: REF_invokeStatic [0-9]+ <java/lang/invoke/StringConcatFactory.makeConcatWithConstants[(]") |
| .mustMatch("\"num args = [\\\\]u0001\"") // static param for string concat |
| .mustMatch(".*getstatic [0-9]+ <java/lang/System.out:Ljava/io/PrintStream;>"); |
| |
| test("invokedynamic in unlinked class") |
| .printUnlinkedMethods("toString") |
| .mustMatch("invokedynamic bsm=[0-9]+ [0-9]+ <makeConcatWithConstants[(]I[)]Ljava/lang/String;>"); |
| |
| test("ldc in linked class") |
| .printLinkedMethods("test_ldc") |
| .mustMatch("ldc BytecodeTracerTest[$]Unloaded") |
| .mustMatch("fast_aldc \"Literal[\\\\]u5678\""); // ldc of String has been rewritten during linking |
| |
| test("ldc in unlinked class") |
| .printUnlinkedMethods("test_ldc") |
| .mustMatch(" ldc BytecodeTracerTest[$]Unloaded") |
| .mustMatch(" ldc \"Literal[\\\\]u1234\""); // ldc of String is not rewritten |
| |
| test("More ldc tests in linked class") |
| .printLinked2Methods("test_ldc") |
| .mustMatch("ldc_w 2") |
| .mustMatch("fast_aldc_w \"Hello\"") |
| .mustMatch("BSM: REF_invokeStatic [0-9]+ <Linked2.condyBSM[(]Ljava/lang/invoke/MethodHandles") |
| .mustMatch("fast_aldc <MethodHandle of kind [0-9]+ index at [0-9]+> [0-9]+ <Linked2.test_ldc[(][)]I>"); |
| |
| test("More ldc tests in unlinked class") |
| .printUnlinked2Methods("test_ldc") |
| .mustMatch("ldc_w 2") |
| .mustMatch("ldc_w \"Hello\"") |
| .mustMatch("BSM: REF_invokeStatic [0-9]+ <Unlinked2.condyBSM[(]Ljava/lang/invoke/MethodHandles") |
| .mustMatch("ldc <MethodHandle of kind [0-9]+ index at [0-9]+> [0-9]+ <Unlinked2.test_ldc[(][)]I>"); |
| |
| test("plain old invoke in linked class") |
| .printLinkedMethods("test_invoke") |
| .mustMatch("invokevirtual [0-9]+ <BytecodeTracerTest.virtualMethod[(][)]V>") |
| .mustMatch("invokestatic [0-9]+ <BytecodeTracerTest.staticMethod[(][)]V>"); |
| |
| test("plain old invoke in unlinked class") |
| .printUnlinkedMethods("test_invoke") |
| .mustMatch("invokevirtual [0-9]+ <BytecodeTracerTest.virtualMethod[(][)]V>") |
| .mustMatch("invokestatic [0-9]+ <BytecodeTracerTest.staticMethod[(][)]V>"); |
| |
| test("invokehandle in linked class") |
| .printLinkedMethods("test_invokehandle") |
| .mustMatch("invokehandle [0-9]+ <java/lang/invoke/MethodHandle.invokeExact[(]FLjava/lang/String;LBytecodeTracerTest[$]Linked;[)]V>"); |
| |
| test("invokehandle in unlinked class") |
| .printUnlinkedMethods("test_invokehandle") |
| .mustMatch("invokevirtual [0-9]+ <java/lang/invoke/MethodHandle.invokeExact[(]FLjava/lang/String;LBytecodeTracerTest[$]Unlinked;[)]V>"); |
| |
| test("field in linked class") |
| .printLinkedMethods("test_field") |
| .mustMatch("getfield [0-9]+ <BytecodeTracerTest.x:I>") |
| .mustMatch("putstatic [0-9]+ <BytecodeTracerTest.y:J>"); |
| |
| test("field in unlinked class") |
| .printUnlinkedMethods("test_field") |
| .mustMatch("getfield [0-9]+ <BytecodeTracerTest.x:I>") |
| .mustMatch("putstatic [0-9]+ <BytecodeTracerTest.y:J>"); |
| } |
| } |
| |