Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/J9DDRClassLoader.java
6004 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
package com.ibm.j9ddr;
23
24
import java.io.ByteArrayOutputStream;
25
import java.io.IOException;
26
import java.io.InputStream;
27
import java.security.SecureClassLoader;
28
import java.util.Collection;
29
import java.util.Collections;
30
import java.util.HashMap;
31
import java.util.Objects;
32
33
import com.ibm.j9ddr.StructureReader.PackageNameType;
34
import com.ibm.j9ddr.StructureReader.StructureDescriptor;
35
import com.ibm.j9ddr.corereaders.memory.IProcess;
36
37
/**
38
* This ClassLoader serves two purposes.
39
*
40
* 1) Based on partitioning rules and package namespaces it ensures that
41
* certain classes are loaded once per runtime invocation while others
42
* are loaded once per CORE file being inspected.
43
*
44
* 2) Generate bytecode at runtime based on the data in the core file (or
45
* structure metadata file) for the J9 structure constants and offsets;
46
* as well as pointer classes. These classes are explicitly loaded on a
47
* per CORE file basis.
48
*
49
* The isolation is accomplished by removing the Application Class Loader
50
* from the class load delegation chain AND setting this class loader's
51
* classpath to be equal to the Application class loader's classpath.
52
*
53
* While the user may replace the application class loader with their own
54
* implementation, the application class loader MUST be a subclass of
55
* URLClassLoader.
56
*/
57
public class J9DDRClassLoader extends SecureClassLoader {
58
59
private static boolean shouldGeneratePointerClasses(StructureReader reader) {
60
if (reader.getPackageVersion() < 29) {
61
// Prior to VM version 29, pointers classes are loaded from j9ddr.jar.
62
return false;
63
}
64
65
// This constant will only be present in a build using OMR tooling and in which
66
// the pointer classes generated from the associated DDR blob have been validated.
67
long coreVersion = reader.getConstantValue("DDRAlgorithmVersions", "J9DDR_GENERATE_VERSION", 0);
68
69
// The minimum valid version, which may be overridden.
70
long threshold = Long.getLong("openj9.dtfj.version-threshold", 1);
71
72
return coreVersion >= threshold;
73
}
74
75
private static String withTrailingDot(String name) {
76
if (name.endsWith(".")) {
77
return name;
78
} else {
79
return name + '.';
80
}
81
}
82
83
// Byte code cache
84
private final HashMap<String, Class<?>> cache;
85
86
private final boolean generatePointers;
87
88
// the reader which contains the raw structure data
89
private final StructureReader reader;
90
91
private final String pointerPackageDotName;
92
private final String structurePackageDotName;
93
private final String streamPackageDotName;
94
95
// We need the vmData to live as long as this classloader.
96
// That makes sure that any objects created from this keep
97
// the vmData alive and in the cache.
98
private IVMData vmData;
99
100
public J9DDRClassLoader(StructureReader reader, ClassLoader parent) {
101
super(parent);
102
this.cache = new HashMap<String, Class<?>>();
103
this.reader = Objects.requireNonNull(reader);
104
this.generatePointers = shouldGeneratePointerClasses(reader);
105
this.pointerPackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.POINTER_PACKAGE_DOT_NAME));
106
this.structurePackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.STRUCTURE_PACKAGE_DOT_NAME));
107
this.streamPackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.PACKAGE_DOT_BASE_NAME));
108
this.vmData = null;
109
}
110
111
public StructureHeader getHeader() {
112
return reader.getHeader();
113
}
114
115
@Override
116
protected Class<?> findClass(String binaryName) throws ClassNotFoundException {
117
Class<?> clazz = cache.get(binaryName);
118
119
if (clazz == null) {
120
byte[] data;
121
boolean generated;
122
123
if (binaryName.startsWith(structurePackageDotName)) {
124
// generate the requested structure class
125
data = getStructureClass(binaryName);
126
generated = true;
127
} else if (generatePointers && binaryName.startsWith(pointerPackageDotName)) {
128
// generate the requested pointer class
129
data = getPointerClass(binaryName);
130
generated = true;
131
} else {
132
// This is a regular class that we need to duplicate for this classloader.
133
data = loadClassBytes(binaryName);
134
generated = false;
135
}
136
137
clazz = defineClass(binaryName, data, 0, data.length);
138
139
if (generated) {
140
// cache generated classes for future use
141
cache.put(binaryName, clazz);
142
}
143
}
144
145
return clazz;
146
}
147
148
/* Load the bytes of a class, accessible from the parent classloader */
149
private byte[] loadClassBytes(String binaryName) throws ClassNotFoundException {
150
String resourceName = '/' + binaryName.replace('.', '/') + ".class";
151
InputStream stream = J9DDRClassLoader.class.getResourceAsStream(resourceName);
152
153
if (stream == null) {
154
throw new ClassNotFoundException("Couldn't duplicate class " + binaryName + ". Couldn't load resource " + resourceName + ", parent classloader " + getParent());
155
}
156
157
ByteArrayOutputStream baos = new ByteArrayOutputStream();
158
byte[] localBuffer = new byte[1024];
159
int read;
160
161
try {
162
while ((read = stream.read(localBuffer)) != -1) {
163
baos.write(localBuffer, 0, read);
164
}
165
} catch (IOException ex) {
166
throw new ClassNotFoundException("IOException reading resource " + resourceName + " for class " + binaryName, ex);
167
} finally {
168
try {
169
stream.close();
170
} catch (IOException e) {
171
// Not much we can do about this here.
172
}
173
}
174
175
return baos.toByteArray();
176
}
177
178
private void definePackage(String name) {
179
// Split off the class name
180
int finalSeparator = name.lastIndexOf('.');
181
182
if (finalSeparator != -1) {
183
String packageName = name.substring(0, finalSeparator);
184
185
if (getPackage(packageName) != null) {
186
return;
187
}
188
189
// TODO think about the correct values here
190
definePackage(packageName, "J9DDR", "0.1", "IBM", "J9DDR", "0.1", "IBM", null);
191
}
192
}
193
194
public Class<?> loadClassRelativeToStream(String name, boolean resolve) throws ClassNotFoundException {
195
return loadClass(streamPackageDotName + name, resolve);
196
}
197
198
// Cause per core file classes to be loaded once per core file, and shared classes to be loaded once per runtime.
199
@Override
200
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
201
if (name.startsWith(streamPackageDotName)) {
202
Class<?> clazz = findLoadedClass(name);
203
204
if (null == clazz) {
205
//We don't delegate here
206
clazz = findClass(name);
207
208
definePackage(name);
209
210
if (resolve) {
211
resolveClass(clazz);
212
}
213
}
214
215
return clazz;
216
} else if (name.startsWith(reader.getBasePackage())) {
217
// If we're loading any other DDR versioned package, there's been a mistake (we're trying to load 2.6 classes for a 2.3 core dump for example).
218
throw new ClassNotFoundException("Cannot load " + name + ". J9DDRClassLoader is configured to load " + streamPackageDotName + " DDR classes only.");
219
} else {
220
// Anything else can be loaded normally by the parent (which will take care of delegation)
221
return getParent().loadClass(name);
222
}
223
}
224
225
private byte[] getPointerClass(String binaryName) throws ClassNotFoundException {
226
try {
227
return reader.getPointerClassBytes(binaryName);
228
} catch (IllegalArgumentException e) {
229
// thrown in many places in StructureReader if things go poorly
230
throw new ClassNotFoundException(binaryName);
231
}
232
}
233
234
/**
235
* Read the requested class from the structure file and return as a valid Java class
236
* @param binaryName name of the J9 structure to retrieve
237
* @return the bytes which represent a valid Java class
238
* @throws ClassNotFoundException thrown if the J9 structure has not been defined in the file
239
*/
240
private byte[] getStructureClass(String binaryName) throws ClassNotFoundException {
241
try {
242
return reader.getStructureClassBytes(binaryName);
243
} catch (IllegalArgumentException e) {
244
// thrown in many places in StructureReader if things go poorly
245
throw new ClassNotFoundException(binaryName);
246
}
247
}
248
249
public Collection<StructureDescriptor> getStructures() {
250
return Collections.unmodifiableCollection(reader.getStructures());
251
}
252
253
public IVMData getIVMData(IProcess process, long address) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
254
if (vmData == null) {
255
// Associate this IVMData instance with this classloader by storing
256
// it in an instance field so that it lives as long as the classloader
257
// and any classes it generates. (In other words as long as we are
258
// still using this dump.)
259
Class<?> vmDataClazz = loadClassRelativeToStream("j9.VMData", false);
260
vmData = (IVMData) vmDataClazz.newInstance();
261
}
262
return vmData;
263
}
264
265
@Override
266
public String toString() {
267
return "J9DDRClassloader for " + streamPackageDotName;
268
}
269
270
/**
271
* Get the structure reader used by this classloader.
272
*
273
* @return
274
*/
275
public StructureReader getReader() {
276
return reader;
277
}
278
279
}
280
281