/* * 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]+ ") .mustMatch("BSM: REF_invokeStatic [0-9]+ "); test("invokedynamic in unlinked class") .printUnlinkedMethods("toString") .mustMatch("invokedynamic bsm=[0-9]+ [0-9]+ "); 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]+ [0-9]+ "); test("More ldc tests in unlinked class") .printUnlinked2Methods("test_ldc") .mustMatch("ldc_w 2") .mustMatch("ldc_w \"Hello\"") .mustMatch("BSM: REF_invokeStatic [0-9]+ [0-9]+ "); test("plain old invoke in linked class") .printLinkedMethods("test_invoke") .mustMatch("invokevirtual [0-9]+ ") .mustMatch("invokestatic [0-9]+ "); test("plain old invoke in unlinked class") .printUnlinkedMethods("test_invoke") .mustMatch("invokevirtual [0-9]+ ") .mustMatch("invokestatic [0-9]+ "); test("invokehandle in linked class") .printLinkedMethods("test_invokehandle") .mustMatch("invokehandle [0-9]+ "); test("invokehandle in unlinked class") .printUnlinkedMethods("test_invokehandle") .mustMatch("invokevirtual [0-9]+ "); test("field in linked class") .printLinkedMethods("test_field") .mustMatch("getfield [0-9]+ ") .mustMatch("putstatic [0-9]+ "); test("field in unlinked class") .printUnlinkedMethods("test_field") .mustMatch("getfield [0-9]+ ") .mustMatch("putstatic [0-9]+ "); } }