179 lines
5.9 KiB
Java
179 lines
5.9 KiB
Java
|
/*
|
||
|
* 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 main/othervm 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.SA.SATestUtils;
|
||
|
|
||
|
|
||
|
public class UniqueVtableTest {
|
||
|
|
||
|
private HotSpotAgent agent;
|
||
|
|
||
|
public UniqueVtableTest() {
|
||
|
}
|
||
|
|
||
|
private static String type2String(Type t) {
|
||
|
return t + " (extends " + t.getSuperclass() + ")";
|
||
|
}
|
||
|
|
||
|
private static void log(Object o) {
|
||
|
System.out.println(o);
|
||
|
}
|
||
|
|
||
|
private void attach(long pid) throws Throwable {
|
||
|
agent = new HotSpotAgent();
|
||
|
log("Attaching to process ID " + pid + "...");
|
||
|
agent.attach((int) pid);
|
||
|
log("Attached successfully.");
|
||
|
}
|
||
|
|
||
|
private void detach() {
|
||
|
if (agent != null) {
|
||
|
agent.detach();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void runTest() throws Throwable {
|
||
|
Map<Address, List<Type>> vtableToTypesMap = new HashMap<>();
|
||
|
Iterator<Type> 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<Type> 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 void run() throws Throwable {
|
||
|
Throwable reasonToFail = null;
|
||
|
LingeredApp app = null;
|
||
|
try {
|
||
|
app = LingeredApp.startApp();
|
||
|
attach(app.getPid());
|
||
|
runTest();
|
||
|
} catch (Throwable ex) {
|
||
|
reasonToFail = ex;
|
||
|
} finally {
|
||
|
try {
|
||
|
detach();
|
||
|
} catch (Exception ex) {
|
||
|
log("detach error:");
|
||
|
ex.printStackTrace(System.out);
|
||
|
// do not override original error
|
||
|
if (reasonToFail != null) {
|
||
|
reasonToFail = ex;
|
||
|
}
|
||
|
}
|
||
|
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.
|
||
|
|
||
|
UniqueVtableTest test = new UniqueVtableTest();
|
||
|
|
||
|
test.run();
|
||
|
}
|
||
|
|
||
|
}
|