/* * 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 * * @library /test/lib * @requires vm.hasSA * @modules jdk.hotspot.agent/sun.jvm.hotspot * jdk.hotspot.agent/sun.jvm.hotspot.debugger * jdk.hotspot.agent/sun.jvm.hotspot.types * jdk.hotspot.agent/sun.jvm.hotspot.types.basic * * @run driver UniqueVtableTest */ import java.util.ArrayList; import java.util.Iterator; import java.util.HashMap; import java.util.List; import java.util.Map; import sun.jvm.hotspot.HotSpotAgent; import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.types.Type; import sun.jvm.hotspot.types.basic.BasicTypeDataBase; import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.SA.SATestUtils; public class UniqueVtableTest { private static String type2String(Type t) { return t + " (extends " + t.getSuperclass() + ")"; } private static void log(Object o) { System.out.println(o); } private static void runTest(long pid) throws Throwable { HotSpotAgent agent = new HotSpotAgent(); log("Attaching to process ID " + pid + "..."); agent.attach((int) pid); log("Attached successfully."); Throwable reasonToFail = null; try { runTest(agent); } catch (Throwable ex) { reasonToFail = ex; } finally { try { agent.detach(); } catch (Exception ex) { log("detach error:"); ex.printStackTrace(System.out); // do not override original error if (reasonToFail != null) { reasonToFail = ex; } } } if (reasonToFail != null) { throw reasonToFail; } } private static void runTest(HotSpotAgent agent) throws Throwable { Map> vtableToTypesMap = new HashMap<>(); Iterator it = agent.getTypeDataBase().getTypes(); int dupsFound = 0; // TypeDataBase knows nothing about vtables, // but actually agent.getTypeDataBase() returns HotSpotTypeDataBase (extends BasicTypeDataBase) // and BasicTypeDataBase has a method to get vtable for Types. BasicTypeDataBase typeDB = (BasicTypeDataBase)(agent.getTypeDataBase()); int total = 0; int vm_classes_with_vtable = 0; int vm_classes_without_vtable = 0; while (it.hasNext()) { total++; Type t = it.next(); Address vtable = typeDB.vtblForType(t); if (vtable != null) { vm_classes_with_vtable++; List typeList = vtableToTypesMap.get(vtable); if (typeList == null) { vtableToTypesMap.put(vtable, new ArrayList<>(List.of(t))); } else { // duplicate found dupsFound++; typeList.add(t); } } // IntegerType/StringType/JavaPrimitiveType/OopType/PointerType types // are expected to have no vtable. // Log classes which might need vtable. if (vtable == null && !t.isCIntegerType() && !t.isCStringType() && !t.isJavaPrimitiveType() && !t.isOopType() && !t.isPointerType()) { vm_classes_without_vtable++; log("vtable is null for " + type2String(t)); } } log("total: " + total + ", vm_classes_with_vtable: " + vm_classes_with_vtable + ", vm_classes_without_vtable: " + vm_classes_without_vtable); if (dupsFound > 0) { vtableToTypesMap.forEach((vtable, list) -> { if (list.size() > 1) { log("Duplicate vtable: " + vtable + ": "); list.forEach(t -> log(" - " + type2String(t))); } }); throw new RuntimeException("Duplicate vtable(s) found: " + dupsFound); } } private static void createAnotherToAttach(long lingeredAppPid) throws Throwable { // Start a new process to attach to the lingered app ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder( "--add-modules=jdk.hotspot.agent", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.types=ALL-UNNAMED", "--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.types.basic=ALL-UNNAMED", "UniqueVtableTest", Long.toString(lingeredAppPid)); SATestUtils.addPrivilegesIfNeeded(processBuilder); OutputAnalyzer output = ProcessTools.executeProcess(processBuilder); output.shouldHaveExitValue(0); System.out.println(output.getOutput()); } private static void runMain() throws Throwable { Throwable reasonToFail = null; LingeredApp app = null; try { app = LingeredApp.startApp(); createAnotherToAttach(app.getPid()); } catch (Throwable ex) { reasonToFail = ex; } finally { try { LingeredApp.stopApp(app); } catch (Exception ex) { log("LingeredApp.stopApp error:"); ex.printStackTrace(System.out); // do not override original error if (reasonToFail != null) { reasonToFail = ex; } } } if (reasonToFail != null) { throw reasonToFail; } } public static void main(String... args) throws Throwable { SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. if (args == null || args.length == 0) { // Main test process. runMain(); } else { // Sub-process to attach, arg[0] is the target process pid. runTest(Long.parseLong(args[0])); } } }