2
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
29
* @modules jdk.hotspot.agent/sun.jvm.hotspot
30
* jdk.hotspot.agent/sun.jvm.hotspot.debugger
31
* jdk.hotspot.agent/sun.jvm.hotspot.types
32
* jdk.hotspot.agent/sun.jvm.hotspot.types.basic
34
* @run driver UniqueVtableTest
37
import java.util.ArrayList;
38
import java.util.Iterator;
39
import java.util.HashMap;
43
import sun.jvm.hotspot.HotSpotAgent;
44
import sun.jvm.hotspot.debugger.Address;
45
import sun.jvm.hotspot.types.Type;
46
import sun.jvm.hotspot.types.basic.BasicTypeDataBase;
48
import jdk.test.lib.apps.LingeredApp;
49
import jdk.test.lib.process.ProcessTools;
50
import jdk.test.lib.process.OutputAnalyzer;
51
import jdk.test.lib.SA.SATestUtils;
54
public class UniqueVtableTest {
56
private static String type2String(Type t) {
57
return t + " (extends " + t.getSuperclass() + ")";
60
private static void log(Object o) {
61
System.out.println(o);
64
private static void runTest(long pid) throws Throwable {
65
HotSpotAgent agent = new HotSpotAgent();
66
log("Attaching to process ID " + pid + "...");
67
agent.attach((int) pid);
68
log("Attached successfully.");
70
Throwable reasonToFail = null;
74
} catch (Throwable ex) {
79
} catch (Exception ex) {
81
ex.printStackTrace(System.out);
82
// do not override original error
83
if (reasonToFail != null) {
88
if (reasonToFail != null) {
93
private static void runTest(HotSpotAgent agent) throws Throwable {
94
Map<Address, List<Type>> vtableToTypesMap = new HashMap<>();
95
Iterator<Type> it = agent.getTypeDataBase().getTypes();
97
// TypeDataBase knows nothing about vtables,
98
// but actually agent.getTypeDataBase() returns HotSpotTypeDataBase (extends BasicTypeDataBase)
99
// and BasicTypeDataBase has a method to get vtable for Types.
100
BasicTypeDataBase typeDB = (BasicTypeDataBase)(agent.getTypeDataBase());
102
int vm_classes_with_vtable = 0;
103
int vm_classes_without_vtable = 0;
104
while (it.hasNext()) {
107
Address vtable = typeDB.vtblForType(t);
108
if (vtable != null) {
109
vm_classes_with_vtable++;
110
List<Type> typeList = vtableToTypesMap.get(vtable);
111
if (typeList == null) {
112
vtableToTypesMap.put(vtable, new ArrayList<>(List.of(t)));
120
// IntegerType/StringType/JavaPrimitiveType/OopType/PointerType types
121
// are expected to have no vtable.
122
// Log classes which might need vtable.
124
&& !t.isCIntegerType()
125
&& !t.isCStringType()
126
&& !t.isJavaPrimitiveType()
128
&& !t.isPointerType()) {
129
vm_classes_without_vtable++;
130
log("vtable is null for " + type2String(t));
133
log("total: " + total
134
+ ", vm_classes_with_vtable: " + vm_classes_with_vtable
135
+ ", vm_classes_without_vtable: " + vm_classes_without_vtable);
137
vtableToTypesMap.forEach((vtable, list) -> {
138
if (list.size() > 1) {
139
log("Duplicate vtable: " + vtable + ": ");
140
list.forEach(t -> log(" - " + type2String(t)));
143
throw new RuntimeException("Duplicate vtable(s) found: " + dupsFound);
147
private static void createAnotherToAttach(long lingeredAppPid) throws Throwable {
148
// Start a new process to attach to the lingered app
149
ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder(
150
"--add-modules=jdk.hotspot.agent",
151
"--add-exports=jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED",
152
"--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED",
153
"--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.types=ALL-UNNAMED",
154
"--add-exports=jdk.hotspot.agent/sun.jvm.hotspot.types.basic=ALL-UNNAMED",
156
Long.toString(lingeredAppPid));
157
SATestUtils.addPrivilegesIfNeeded(processBuilder);
158
OutputAnalyzer output = ProcessTools.executeProcess(processBuilder);
159
output.shouldHaveExitValue(0);
160
System.out.println(output.getOutput());
163
private static void runMain() throws Throwable {
164
Throwable reasonToFail = null;
165
LingeredApp app = null;
167
app = LingeredApp.startApp();
168
createAnotherToAttach(app.getPid());
169
} catch (Throwable ex) {
173
LingeredApp.stopApp(app);
174
} catch (Exception ex) {
175
log("LingeredApp.stopApp error:");
176
ex.printStackTrace(System.out);
177
// do not override original error
178
if (reasonToFail != null) {
183
if (reasonToFail != null) {
188
public static void main(String... args) throws Throwable {
189
SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.
191
if (args == null || args.length == 0) {
192
// Main test process.
195
// Sub-process to attach, arg[0] is the target process pid.
196
runTest(Long.parseLong(args[0]));