Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/J9DDRClassLoader.java
6004 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/21package com.ibm.j9ddr;2223import java.io.ByteArrayOutputStream;24import java.io.IOException;25import java.io.InputStream;26import java.security.SecureClassLoader;27import java.util.Collection;28import java.util.Collections;29import java.util.HashMap;30import java.util.Objects;3132import com.ibm.j9ddr.StructureReader.PackageNameType;33import com.ibm.j9ddr.StructureReader.StructureDescriptor;34import com.ibm.j9ddr.corereaders.memory.IProcess;3536/**37* This ClassLoader serves two purposes.38*39* 1) Based on partitioning rules and package namespaces it ensures that40* certain classes are loaded once per runtime invocation while others41* are loaded once per CORE file being inspected.42*43* 2) Generate bytecode at runtime based on the data in the core file (or44* structure metadata file) for the J9 structure constants and offsets;45* as well as pointer classes. These classes are explicitly loaded on a46* per CORE file basis.47*48* The isolation is accomplished by removing the Application Class Loader49* from the class load delegation chain AND setting this class loader's50* classpath to be equal to the Application class loader's classpath.51*52* While the user may replace the application class loader with their own53* implementation, the application class loader MUST be a subclass of54* URLClassLoader.55*/56public class J9DDRClassLoader extends SecureClassLoader {5758private static boolean shouldGeneratePointerClasses(StructureReader reader) {59if (reader.getPackageVersion() < 29) {60// Prior to VM version 29, pointers classes are loaded from j9ddr.jar.61return false;62}6364// This constant will only be present in a build using OMR tooling and in which65// the pointer classes generated from the associated DDR blob have been validated.66long coreVersion = reader.getConstantValue("DDRAlgorithmVersions", "J9DDR_GENERATE_VERSION", 0);6768// The minimum valid version, which may be overridden.69long threshold = Long.getLong("openj9.dtfj.version-threshold", 1);7071return coreVersion >= threshold;72}7374private static String withTrailingDot(String name) {75if (name.endsWith(".")) {76return name;77} else {78return name + '.';79}80}8182// Byte code cache83private final HashMap<String, Class<?>> cache;8485private final boolean generatePointers;8687// the reader which contains the raw structure data88private final StructureReader reader;8990private final String pointerPackageDotName;91private final String structurePackageDotName;92private final String streamPackageDotName;9394// We need the vmData to live as long as this classloader.95// That makes sure that any objects created from this keep96// the vmData alive and in the cache.97private IVMData vmData;9899public J9DDRClassLoader(StructureReader reader, ClassLoader parent) {100super(parent);101this.cache = new HashMap<String, Class<?>>();102this.reader = Objects.requireNonNull(reader);103this.generatePointers = shouldGeneratePointerClasses(reader);104this.pointerPackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.POINTER_PACKAGE_DOT_NAME));105this.structurePackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.STRUCTURE_PACKAGE_DOT_NAME));106this.streamPackageDotName = withTrailingDot(reader.getPackageName(PackageNameType.PACKAGE_DOT_BASE_NAME));107this.vmData = null;108}109110public StructureHeader getHeader() {111return reader.getHeader();112}113114@Override115protected Class<?> findClass(String binaryName) throws ClassNotFoundException {116Class<?> clazz = cache.get(binaryName);117118if (clazz == null) {119byte[] data;120boolean generated;121122if (binaryName.startsWith(structurePackageDotName)) {123// generate the requested structure class124data = getStructureClass(binaryName);125generated = true;126} else if (generatePointers && binaryName.startsWith(pointerPackageDotName)) {127// generate the requested pointer class128data = getPointerClass(binaryName);129generated = true;130} else {131// This is a regular class that we need to duplicate for this classloader.132data = loadClassBytes(binaryName);133generated = false;134}135136clazz = defineClass(binaryName, data, 0, data.length);137138if (generated) {139// cache generated classes for future use140cache.put(binaryName, clazz);141}142}143144return clazz;145}146147/* Load the bytes of a class, accessible from the parent classloader */148private byte[] loadClassBytes(String binaryName) throws ClassNotFoundException {149String resourceName = '/' + binaryName.replace('.', '/') + ".class";150InputStream stream = J9DDRClassLoader.class.getResourceAsStream(resourceName);151152if (stream == null) {153throw new ClassNotFoundException("Couldn't duplicate class " + binaryName + ". Couldn't load resource " + resourceName + ", parent classloader " + getParent());154}155156ByteArrayOutputStream baos = new ByteArrayOutputStream();157byte[] localBuffer = new byte[1024];158int read;159160try {161while ((read = stream.read(localBuffer)) != -1) {162baos.write(localBuffer, 0, read);163}164} catch (IOException ex) {165throw new ClassNotFoundException("IOException reading resource " + resourceName + " for class " + binaryName, ex);166} finally {167try {168stream.close();169} catch (IOException e) {170// Not much we can do about this here.171}172}173174return baos.toByteArray();175}176177private void definePackage(String name) {178// Split off the class name179int finalSeparator = name.lastIndexOf('.');180181if (finalSeparator != -1) {182String packageName = name.substring(0, finalSeparator);183184if (getPackage(packageName) != null) {185return;186}187188// TODO think about the correct values here189definePackage(packageName, "J9DDR", "0.1", "IBM", "J9DDR", "0.1", "IBM", null);190}191}192193public Class<?> loadClassRelativeToStream(String name, boolean resolve) throws ClassNotFoundException {194return loadClass(streamPackageDotName + name, resolve);195}196197// Cause per core file classes to be loaded once per core file, and shared classes to be loaded once per runtime.198@Override199public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {200if (name.startsWith(streamPackageDotName)) {201Class<?> clazz = findLoadedClass(name);202203if (null == clazz) {204//We don't delegate here205clazz = findClass(name);206207definePackage(name);208209if (resolve) {210resolveClass(clazz);211}212}213214return clazz;215} else if (name.startsWith(reader.getBasePackage())) {216// 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).217throw new ClassNotFoundException("Cannot load " + name + ". J9DDRClassLoader is configured to load " + streamPackageDotName + " DDR classes only.");218} else {219// Anything else can be loaded normally by the parent (which will take care of delegation)220return getParent().loadClass(name);221}222}223224private byte[] getPointerClass(String binaryName) throws ClassNotFoundException {225try {226return reader.getPointerClassBytes(binaryName);227} catch (IllegalArgumentException e) {228// thrown in many places in StructureReader if things go poorly229throw new ClassNotFoundException(binaryName);230}231}232233/**234* Read the requested class from the structure file and return as a valid Java class235* @param binaryName name of the J9 structure to retrieve236* @return the bytes which represent a valid Java class237* @throws ClassNotFoundException thrown if the J9 structure has not been defined in the file238*/239private byte[] getStructureClass(String binaryName) throws ClassNotFoundException {240try {241return reader.getStructureClassBytes(binaryName);242} catch (IllegalArgumentException e) {243// thrown in many places in StructureReader if things go poorly244throw new ClassNotFoundException(binaryName);245}246}247248public Collection<StructureDescriptor> getStructures() {249return Collections.unmodifiableCollection(reader.getStructures());250}251252public IVMData getIVMData(IProcess process, long address) throws ClassNotFoundException, IllegalAccessException, InstantiationException {253if (vmData == null) {254// Associate this IVMData instance with this classloader by storing255// it in an instance field so that it lives as long as the classloader256// and any classes it generates. (In other words as long as we are257// still using this dump.)258Class<?> vmDataClazz = loadClassRelativeToStream("j9.VMData", false);259vmData = (IVMData) vmDataClazz.newInstance();260}261return vmData;262}263264@Override265public String toString() {266return "J9DDRClassloader for " + streamPackageDotName;267}268269/**270* Get the structure reader used by this classloader.271*272* @return273*/274public StructureReader getReader() {275return reader;276}277278}279280281