Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/StructureReader.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 static java.util.logging.Level.FINE;24import static java.util.logging.Level.FINER;25import static java.util.logging.Level.FINEST;2627import java.io.BufferedReader;28import java.io.IOException;29import java.io.InputStream;30import java.io.InputStreamReader;31import java.io.PrintWriter;32import java.io.Reader;33import java.io.StringWriter;34import java.nio.charset.StandardCharsets;35import java.util.ArrayList;36import java.util.Collection;37import java.util.Collections;38import java.util.HashMap;39import java.util.List;40import java.util.Map;41import java.util.Set;42import java.util.logging.Logger;43import java.util.regex.Matcher;44import java.util.regex.Pattern;4546import javax.imageio.stream.ImageInputStream;4748import com.ibm.j9ddr.logging.LoggerNames;4950// TODO: Lazy initializing has been removed. Need to decide if it stays out.51public class StructureReader {52public static final int VERSION = 1;53public static final int J9_STRUCTURES_EYECATCHER = 0xFACEDEB8; // eyecatcher / magic identifier for a J9 structure file54private Map<String, StructureDescriptor> structures = null;5556private String packageDotBaseName;57private String pointerDotName;58private String pointerSlashName;59private String structureDotName;60private String structureSlashName;6162private static final Logger logger = Logger.getLogger(LoggerNames.LOGGER_STRUCTURE_READER);63private StructureHeader header;6465@SuppressWarnings("rawtypes")66public static final Class<?>[] STRUCTURE_CONSTRUCTOR_SIGNATURE = new Class[] { Long.TYPE };67public static final byte BIT_FIELD_FORMAT_LITTLE_ENDIAN = 1;68public static final byte BIT_FIELD_FORMAT_BIG_ENDIAN = 2;69public static final int BIT_FIELD_CELL_SIZE = 32;7071private static final Pattern MULTI_LINE_COMMENT_PATTERN = Pattern.compile(Pattern.quote("/*") + ".*?" + Pattern.quote("*/") , Pattern.DOTALL);72private static final Pattern SINGLE_LINE_COMMENT_PATTERN = Pattern.compile(Pattern.quote("//") + ".*$", Pattern.MULTILINE);73private static final Pattern MAP_PATTERN = Pattern.compile("(.*?)=(.*?)$", Pattern.MULTILINE);74private static final Pattern CONSTANT_PATTERN = Pattern.compile("(.+?)\\.(.+?)=(.+)$", Pattern.MULTILINE);7576private Long packageVersion;77private String basePackage = DDR_VERSIONED_PACKAGE_PREFIX;7879private final StructureTypeManager typeManager;8081/* Patterns for cleaning types */82/* Pattern that matches a 1 or more characters not including ']' that occur after [ */83private static final Pattern CONTENTS_OF_ARRAY_PATTERN = Pattern.compile("(?<=\\[).*?(?=\\])");8485private static final Pattern SPACES_AFTER_SQUARE_BRACKETS_PATTERN = Pattern.compile("(?<=\\])\\s+");8687private static final Pattern SPACES_BEFORE_SQUARE_BRACKETS_PATTERN = Pattern.compile("\\s+(?=\\[)");8889private static final Pattern SPACES_AFTER_ASTERISKS_PATTERN = Pattern.compile("(?<=\\*)\\s+");9091private static final Pattern SPACES_BEFORE_ASTERISKS_PATTERN = Pattern.compile("\\s+(?=\\*)");9293// VM Version constants94private static final String VM_MAJOR_VERSION = "VM_MAJOR_VERSION";95private static final String VM_MINOR_VERSION = "VM_MINOR_VERSION";96private static final String ARM_SPLIT_DDR_HACK = "ARM_SPLIT_DDR_HACK";97private static final String DDRALGORITHM_STRUCTURE_NAME = "DDRAlgorithmVersions";9899public static final String DDR_VERSIONED_PACKAGE_PREFIX = "com.ibm.j9ddr.vm";100101public static enum PackageNameType {102PACKAGE_DOT_BASE_NAME,103POINTER_PACKAGE_DOT_NAME,104POINTER_PACKAGE_SLASH_NAME,105STRUCTURE_PACKAGE_DOT_NAME,106STRUCTURE_PACKAGE_SLASH_NAME;107}108109/**110* Initialize this reader from the supplied data.111* The ImageInputStream must be validated and already be positioned to point to112* the first byte of the DDR Structure Data. The ByteOrder of the ImageInputStream113* will be adjusted to match the file being read. (The DDR Structure data is written114* in the ByteOrder of the platform that created the core file.)115*/116public StructureReader(ImageInputStream in) throws IOException {117parse(in);118setStream();119applyAliases();120addCompatibilityConstants();121loadAuxFieldInfo();122typeManager = new StructureTypeManager(getStructures());123}124125/**126* Read a stream in the J9DDRStructStore superset format.127*/128public StructureReader(InputStream in) throws IOException {129parse(in);130setStream();131typeManager = new StructureTypeManager(getStructures());132}133134public StructureHeader getHeader() {135return header;136}137138/**139* Sets the based on the DDRAlgorithmVersions field in the blob.140*/141private void setStream() {142StructureDescriptor version = structures.get(DDRALGORITHM_STRUCTURE_NAME);143long vmMajorVersion = 2; // default: stream 23144long vmMinorVersion = 3;145146/* JAZZ 103906 : A hack was introduced when we split jvm.29 for ARM development.147* The goal was to keep using the same values of VM_MAJOR_VERSION and148* VM_MINOR_VERSION as in jvm.29 but now support ARM in this separate stream.149* Therefore, we needed a new way to differentiate the special ARM jvm.29 stream150* and this is the reason why the ARM_SPLIT_DDR_HACK field was added.151*/152String versionFormat = "%02d";153if (version != null) {154for (ConstantDescriptor constant : version.getConstants()) {155String constantName = constant.getName();156if (constantName.equals(VM_MAJOR_VERSION)) {157vmMajorVersion = constant.getValue();158} else if (constantName.equals(VM_MINOR_VERSION)) {159vmMinorVersion = constant.getValue() / 10;160} else if (constantName.equals(ARM_SPLIT_DDR_HACK) && constant.getValue() == 1) {161versionFormat = "%02d_00";162}163}164}165166packageVersion = Long.valueOf((vmMajorVersion * 10) + vmMinorVersion); // stream is a 2 digit value167168String versionSuffix = String.format(versionFormat, packageVersion);169170packageDotBaseName = DDR_VERSIONED_PACKAGE_PREFIX + versionSuffix;171pointerDotName = packageDotBaseName + ".pointer.generated";172pointerSlashName = pointerDotName.replace('.', '/') + '/';173structureDotName = packageDotBaseName + ".structure";174structureSlashName = structureDotName.replace('.', '/') + '/';175}176177public String getBasePackage() {178return basePackage;179}180181/**182* Get the package version number, derived from fields of DDRAlgorithmVersions in the blob.183*184* @return the package version number185*/186public long getPackageVersion() {187if (packageVersion == null) {188throw new IllegalStateException("The DDR version information is not yet available");189}190191return packageVersion.longValue();192}193194/**195* Get the package name that should be used including the version information196* @param type197* @return198*/199public String getPackageName(PackageNameType type) {200if (packageVersion == null) {201throw new IllegalStateException("The DDR version information is not yet available");202}203204switch (type) {205case PACKAGE_DOT_BASE_NAME:206return packageDotBaseName;207case POINTER_PACKAGE_DOT_NAME:208return pointerDotName;209case POINTER_PACKAGE_SLASH_NAME:210return pointerSlashName;211case STRUCTURE_PACKAGE_DOT_NAME:212return structureDotName;213case STRUCTURE_PACKAGE_SLASH_NAME:214return structureSlashName;215default:216throw new IllegalStateException("Unexpected PackageNameType");217}218}219220private void applyAliases() throws IOException {221Map<String, String> aliasMap = loadAliasMap(getAliasVersion());222223for (StructureDescriptor thisStruct : structures.values()) {224for (FieldDescriptor thisField : thisStruct.fields) {225thisField.applyAliases(aliasMap);226}227}228}229230private void addCompatibilityConstants() throws IOException {231String resource = "/com/ibm/j9ddr/CompatibilityConstants" + getPackageVersion() + ".dat";232233try (InputStream inputStream = StructureReader.class.getResourceAsStream(resource)) {234if (inputStream != null) {235addCompatibilityConstants(inputStream);236}237}238}239240public void addCompatibilityConstants(InputStream inputStream) throws IOException {241Map<String, Map<String, Long>> map = new HashMap<>();242String text = stripComments(loadUTF8(inputStream));243244for (Matcher matcher = CONSTANT_PATTERN.matcher(text); matcher.find();) {245String type = matcher.group(1).trim();246String name = matcher.group(2).trim();247String value = matcher.group(3).trim();248249if (type.isEmpty() || name.isEmpty() || value.isEmpty()) {250throw new IOException("Malformed constant: " + matcher.group());251}252253Map<String, Long> constants = map.get(type);254255if (constants == null) {256constants = new HashMap<>();257map.put(type, constants);258}259260try {261constants.put(name, Long.decode(value));262} catch (NumberFormatException e) {263throw new IOException("Malformed constant: " + matcher.group(), e);264}265}266267for (StructureDescriptor structure : structures.values()) {268Map<String, Long> constants = map.remove(structure.getName());269270if (constants == null) {271/* nothing applies to this structure */272continue;273}274275/* remove entries that are already present */276for (ConstantDescriptor constant : structure.constants) {277constants.remove(constant.name);278}279280/* add missing constants */281for (Map.Entry<String, Long> entry : constants.entrySet()) {282structure.constants.add(new ConstantDescriptor(entry.getKey(), entry.getValue()));283}284}285286/* add default constants for new types */287for (Map.Entry<String, Map<String, Long>> entry : map.entrySet()) {288String name = entry.getKey();289String line = "S|" + name + "|" + name + "Pointer|";290StructureDescriptor structure = new StructureDescriptor(line);291292for (Map.Entry<String, Long> constant : entry.getValue().entrySet()) {293structure.constants.add(new ConstantDescriptor(constant.getKey(), constant.getValue()));294}295296structures.put(structure.getName(), structure);297}298}299300public static Map<String, String> loadAliasMap(long version) throws IOException {301return loadAliasMap(Long.toString(version));302}303304private static Map<String, String> loadAliasMap(String version) throws IOException {305String mapData = loadAliasMapData(version);306307mapData = stripComments(mapData);308309Map<String, String> aliasMap = new HashMap<>();310Matcher mapMatcher = MAP_PATTERN.matcher(mapData);311312while (mapMatcher.find()) {313String from = mapMatcher.group(1);314String to = mapMatcher.group(2);315316aliasMap.put(from.trim(), to.trim());317}318319return Collections.unmodifiableMap(aliasMap);320}321322private void loadAuxFieldInfo() throws IOException {323String resource = "/com/ibm/j9ddr/AuxFieldInfo" + getPackageVersion() + ".dat";324325try (InputStream stream = StructureReader.class.getResourceAsStream(resource)) {326if (stream != null) {327loadAuxFieldInfo(stream);328}329}330}331332public void loadAuxFieldInfo(InputStream stream) throws IOException {333Map<String, Map<String, String>> fieldMap = new HashMap<>();334Pattern fieldPattern = Pattern.compile("(.+?)\\.(.+?)=(.+)$", Pattern.MULTILINE);335String text = stripComments(loadUTF8(stream));336337for (Matcher matcher = fieldPattern.matcher(text); matcher.find();) {338String typeName = matcher.group(1).trim();339String fieldName = matcher.group(2).trim();340String fieldType = matcher.group(3).trim();341342if (typeName.isEmpty() || fieldName.isEmpty() || fieldType.isEmpty()) {343throw new IOException("Malformed field: " + matcher.group());344}345346Map<String, String> infoMap = fieldMap.get(typeName);347348if (infoMap == null) {349infoMap = new HashMap<>();350fieldMap.put(typeName, infoMap);351}352353String previous = infoMap.put(fieldName, fieldType);354355if (previous != null) {356throw new IOException("Duplicate field: " + matcher.group());357}358}359360boolean debug = Boolean.getBoolean("aux.field.info.debug");361362for (StructureDescriptor structure : structures.values()) {363String typeName = structure.getName();364Map<String, String> infoMap = fieldMap.remove(typeName);365366if (infoMap == null) {367// No required or optional fields for this structure.368continue;369}370371List<FieldDescriptor> fields = structure.getFields();372373// Mark required fields and remove corresponding entries from infoMap.374for (FieldDescriptor field : fields) {375String fieldName = field.getName();376String fieldType = infoMap.remove(fieldName);377378if (fieldType == null) {379// No auxilliary information for this field.380} else if (fieldType.equals("required")) {381field.required = true;382} else {383field.optional = true;384if (debug) {385field.declaredType = fieldType;386field.type = fieldType;387}388}389}390391// Remaining entries in infoMap are for missing fields.392for (Map.Entry<String, String> entry : infoMap.entrySet()) {393String fieldName = entry.getKey();394String fieldType = entry.getValue();395FieldDescriptor field;396397if (fieldType.equals("required")) {398field = new FieldDescriptor(0, "?", "?", fieldName, fieldName);399field.required = true;400} else {401field = new FieldDescriptor(0, fieldType, fieldType, fieldName, fieldName);402field.optional = true;403}404405field.present = false;406fields.add(field);407}408}409410// Remaining entries in fieldMap are for missing types.411for (Map.Entry<String, Map<String, String>> typeEntry : fieldMap.entrySet()) {412StructureDescriptor type = new StructureDescriptor();413String typeName = typeEntry.getKey();414List<FieldDescriptor> fields = new ArrayList<>();415416type.name = typeName;417type.constants = new ArrayList<>();418type.fields = fields;419420structures.put(typeName, type);421422for (Map.Entry<String, String> fieldEntry : typeEntry.getValue().entrySet()) {423String fieldName = fieldEntry.getKey();424String fieldType = fieldEntry.getValue();425FieldDescriptor field;426427if (fieldType.equals("required")) {428field = new FieldDescriptor(0, "?", "?", fieldName, fieldName);429field.required = true;430} else {431field = new FieldDescriptor(0, fieldType, fieldType, fieldName, fieldName);432field.optional = true;433}434435field.present = false;436fields.add(field);437}438}439}440441private static String stripComments(String mapData) {442mapData = filterOutPattern(mapData, MULTI_LINE_COMMENT_PATTERN);443mapData = filterOutPattern(mapData, SINGLE_LINE_COMMENT_PATTERN);444return mapData;445}446447private String getAliasVersion() {448long version = getPackageVersion();449String variant = "";450451if ((version == 29) && !getBuildFlagValue("J9BuildFlags", "J9VM_OPT_USE_OMR_DDR", false)) {452/*453* For blobs generated from the current stream but with legacy tools,454* load a variant of the alias map.455*/456variant = "-edg";457}458459return version + variant;460}461462private static String loadAliasMapData(String version) throws IOException {463String streamAliasMapResource = "/com/ibm/j9ddr/StructureAliases" + version + ".dat";464465try (InputStream is = StructureReader.class.getResourceAsStream(streamAliasMapResource)) {466if (null == is) {467throw new RuntimeException("Failed to load alias map from resource: " + streamAliasMapResource + " - cannot continue");468}469470return loadUTF8(is);471}472}473474private static String loadUTF8(InputStream is) throws IOException {475try (Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {476StringBuilder builder = new StringBuilder();477char[] buffer = new char[4096];478int read;479480while ((read = reader.read(buffer)) != -1) {481builder.append(buffer, 0, read);482}483484return builder.toString();485}486}487488/**489* Get a list of the structures defined by this reader490* @return Set interface for the structure names491*/492public Set<String> getStructureNames() {493return structures.keySet();494}495496/**497* Test to see if a structure has been read in and hence available498* @param name name of the structure to look for499* @return true if the structure exists, false if not500*/501public boolean hasStructure(String name) {502return structures.containsKey(name);503}504505public int getStructureSizeOf(String structureName) {506if (!hasStructure(structureName)) {507return 0;508}509return structures.get(structureName).getSizeOf();510}511512public Collection<StructureDescriptor> getStructures() {513return structures.values();514}515516/**517* Get a map of the fields for a particular structure and their offsets.518* @param structureName name of the structure519* @return map of the fields as FieldName, Offset pair520*/521public List<FieldDescriptor> getFields(String structureName) {522StructureDescriptor structure = structures.get(structureName);523524if (structure == null) {525throw new IllegalArgumentException("The structure [" + structureName + "] was not found");526}527528return structure.fields;529}530531public List<ConstantDescriptor> getConstants(String structureName) {532StructureDescriptor structure = structures.get(structureName);533534if (structure == null) {535throw new IllegalArgumentException("The structure [" + structureName + "] was not found");536}537538return structure.constants;539}540541private void parse(InputStream inputStream) throws IOException {542header = new StructureHeader(inputStream);543structures = new HashMap<>();544BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));545StructureDescriptor structure = null;546547for (;;) {548String line = reader.readLine();549if (line == null) {550break;551}552int hash = line.indexOf('#');553if (hash >= 0) {554// remove comments555line = line.substring(0, hash);556}557if (line.isEmpty()) {558// ignore blank lines559continue;560}561char type = line.charAt(0);562switch (type) {563case 'S':564structure = new StructureDescriptor(line);565structures.put(structure.getName(), structure);566break;567case 'F':568if (structure == null) {569throw new IllegalArgumentException("Superset stream is missing structure start line");570}571Collection<FieldDescriptor> fields = FieldDescriptor.inflate(line);572structure.fields.addAll(fields);573break;574case 'C':575if (structure == null) {576throw new IllegalArgumentException("Superset stream is missing structure start line");577}578ConstantDescriptor constant = new ConstantDescriptor(line);579structure.constants.add(constant);580break;581default:582throw new IllegalArgumentException("Superset stream contains unknown line: " + line);583}584}585}586587/**588* Add to the existing set of structures already contained in this reader. Note that589* this will replace any definitions already contained within the reader.590*591* @param ddrStream stream to add the structures from592* @throws IOException593*/594public void addStructures(ImageInputStream ddrStream) throws IOException {595StructureHeader fragmentHeader = new StructureHeader(ddrStream);596checkBlobVersion();597if (header.getSizeofBool() != fragmentHeader.getSizeofBool()) {598throw new IOException("Invalid fragment definition : size of boolean is not the same");599}600if (header.getSizeofUDATA() != fragmentHeader.getSizeofUDATA()) {601throw new IOException("Invalid fragment definition : size of UDATA is not the same");602}603parseStructures(ddrStream, fragmentHeader);604}605606/**607* Parse the supplied data and extract the structure information.608* It is expected that the ImageInputStream already points to the start of the609* DDR structure data. The ByteOrder of the ImageInputStream will be adjusted610* to match the file being read. (The DDR Structure data is written in the611* ByteOrder of the platform that created the core file.)612*613* This parse is called when the blob is initially loaded.614*615* The structures are lazily loaded in that the name of the structure is616* identified but not processed until a subsequent call requests it.617*618* @param ddrStream an open stream on the blob to be read619* @throws IOException re-throws any exceptions from the ImageInputStream620*/621private void parse(ImageInputStream ddrStream) throws IOException {622logger.logp(FINE, null, null, "Parsing structures. Start address = {0}", Long.toHexString(ddrStream.getStreamPosition()));623header = new StructureHeader(ddrStream);624checkBlobVersion();625parseStructures(ddrStream, header);626}627628/**629* Checks that the blob version is supported and correctly sets the ByteOrder.630*631* @param ddrStream blob stream to check632* @throws IOException thrown if the version is not supported633*/634private void checkBlobVersion() throws IOException {635logger.logp(FINE, null, null, "Stream core structure version = {0}", header.getCoreVersion());636637if (header.getCoreVersion() > VERSION) {638throw new IOException("Core structure version " + header.getCoreVersion() + " > StructureReader version " + VERSION);639}640}641642/**643* Parse the structures from a supplied stream.644*645* @param ddrStream stream to read from, assumes that the stream is correctly positioned646* @param header blob header647* @throws IOException648*/649private void parseStructures(ImageInputStream ddrStream, StructureHeader header) throws IOException {650logger.logp(FINER, null, null, "structDataSize={0}, stringTableDataSize={1}, structureCount={2}",651new Object[] { header.getStructDataSize(), header.getStringTableDataSize(), header.getStructureCount() });652653// This line must come after the header reads above or the offsets will be wrong.654long ddrStringTableStart = ddrStream.getStreamPosition() + header.getStructDataSize();655656logger.logp(FINER, null, null, "ddrStringTableStart=0x{0}", Long.toHexString(ddrStringTableStart));657658if (structures == null) {659// initialize the structure map with a sensible initial capacity660structures = new HashMap<>(header.getStructureCount());661}662663for (int i = 0; i < header.getStructureCount(); i++) {664logger.logp(FINER, null, null, "Reading structure on iteration {0}", i);665StructureDescriptor structure = new StructureDescriptor();666structure.name = decodeTypeName(readString(ddrStream, ddrStringTableStart));667if (structure.name == null) {668logger.logp(FINE, null, null, "Structure name was null for structure {0}", i);669throw new IllegalArgumentException("Structure name was null for structure " + i);670} else if (structure.name.isEmpty()) {671logger.logp(FINE, null, null, "Structure name was blank for structure {0}", i);672throw new IllegalArgumentException("No name found for structure " + i);673}674logger.logp(FINE, null, null, "Reading structure {0}", structure.name);675676structure.superName = decodeTypeName(readString(ddrStream, ddrStringTableStart));677structure.sizeOf = ddrStream.readInt();678int numberOfFields = ddrStream.readInt();679structure.fields = new ArrayList<>(numberOfFields);680int numberOfConstants = ddrStream.readInt();681structure.constants = new ArrayList<>(numberOfConstants);682683logger.logp(FINER, null, null, "{0} super {1} sizeOf {2}",684new Object[] { structure.name, structure.superName, structure.sizeOf });685686for (int j = 0; j < numberOfFields; j++) {687String declaredName = readString(ddrStream, ddrStringTableStart);688689// Inline anonymous structures are handled by stacking the fields, separated by ".".690String name = declaredName.replace(".", "$");691692String declaredType = readString(ddrStream, ddrStringTableStart);693if (name.equals("hashCode")) {694name = "_hashCode";695}696697// Type is unaliased later698int offset = ddrStream.readInt();699FieldDescriptor field = new FieldDescriptor(offset, declaredType, declaredType, name, declaredName);700structure.fields.add(field);701logger.logp(FINEST, null, null, "Field: {0}.{1} offset {2}, declaredType {3}",702new Object[] { structure.name, name, offset, declaredType, declaredType });703}704705for (int j = 0; j < numberOfConstants; j++) {706String name = readString(ddrStream, ddrStringTableStart);707long value = ddrStream.readLong();708ConstantDescriptor constant = new ConstantDescriptor(name, value);709structure.constants.add(constant);710logger.logp(FINEST, null, null, "Constant: {0}.{1}={2}",711new Object[] { structure.name, name, value });712}713714structures.put(structure.name, structure);715}716717logger.logp(FINE, null, null, "Finished parsing structures");718}719720private static String decodeTypeName(String typeName) {721if (typeName != null) {722int index = typeName.indexOf("__");723724if (index >= 0) {725StringBuilder buffer = new StringBuilder();726int start = 0;727728do {729if (index == start) {730buffer.append("__");731} else {732buffer.append(typeName, start, index).append("$");733}734735start = index + 2;736index = typeName.indexOf("__", start);737} while (index >= 0);738739buffer.append(typeName, start, typeName.length());740typeName = buffer.toString();741}742}743744return typeName;745}746747private static String readString(ImageInputStream ddrStream, long ddrStringTableStart) {748try {749int stringOffset = ddrStream.readInt();750if (stringOffset == -1) {751return "";752}753754long pos = ddrStream.getStreamPosition();755long seekPos = ddrStringTableStart + stringOffset;756ddrStream.seek(seekPos);757int length = ddrStream.readUnsignedShort();758if (length > 200) {759throw new IOException("Improbable string length: " + length);760}761// TODO: Reuse buffer762byte[] buffer = new byte[length];763int read = ddrStream.read(buffer);764765if (read != length) {766throw new IOException("StructureReader readString() Failed to read " + length + " at " + Long.toHexString(seekPos) + ". Result: " + read);767}768769String result = new String(buffer, StandardCharsets.UTF_8);770ddrStream.seek(pos);771return result;772} catch (IOException e) {773// put the stack trace to the log774StringWriter sw = new StringWriter();775PrintWriter pw = new PrintWriter(sw);776e.printStackTrace(pw);777logger.logp(FINE, null, null, sw.toString());778// TODO Auto-generated catch block779e.printStackTrace();780}781return null;782}783784public static class StructureDescriptor {785String name;786String superName;787int sizeOf;788List<FieldDescriptor> fields;789List<ConstantDescriptor> constants;790791public StructureDescriptor() {792super();793}794795public StructureDescriptor(String line) {796super();797inflate(line);798}799800@Override801public String toString() {802if (superName == null || superName.isEmpty()) {803return String.valueOf(name);804} else {805return name + " extends " + superName;806}807}808809public String getPointerName() {810return name + "Pointer";811}812813public String getName() {814return name;815}816817public String getSuperName() {818return superName != null ? superName : "";819}820821public List<FieldDescriptor> getFields() {822return fields;823}824825public List<ConstantDescriptor> getConstants() {826return constants;827}828829public int getSizeOf() {830return sizeOf;831}832833private void inflate(String line) {834String[] parts = line.split("\\|");835if (parts.length < 3 || parts.length > 4) {836throw new IllegalArgumentException("Superset file line is invalid: " + line);837}838constants = new ArrayList<>();839fields = new ArrayList<>();840name = parts[1];841842if (parts.length == 4) {843superName = parts[3];844} else {845superName = "";846}847}848849public String deflate() {850StringBuilder result = new StringBuilder();851result.append("S|"); // 0852result.append(getName()); // 1853result.append("|");854result.append(getPointerName()); // 2855result.append("|");856result.append(getSuperName()); // 3857return result.toString();858}859860}861862public static class ConstantDescriptor implements Comparable<ConstantDescriptor> {863String name;864long value; // U_64865// TODO: what can hold a U_64 in Java866867public ConstantDescriptor(String name, long value) {868super();869this.name = name;870this.value = value;871}872873public ConstantDescriptor(String line) {874super();875inflate(line);876}877878@Override879public String toString() {880return name + " = " + value;881}882883public String getName() {884return name;885}886887public long getValue() {888return value;889}890891@Override892public int compareTo(ConstantDescriptor o) {893return getName().compareTo(o.getName());894}895896private void inflate(String line) {897String[] parts = line.split("\\|");898if (parts.length != 2) {899throw new IllegalArgumentException("Superset file line is invalid: " + line);900}901name = parts[1];902}903904public String deflate() {905return "C|" + getName(); //0 & 1906}907908@Override909public boolean equals(Object obj) {910if ((obj == null) || !(obj instanceof ConstantDescriptor)) {911return false;912}913ConstantDescriptor compareTo = (ConstantDescriptor) obj;914return name.equals(compareTo.name) && (value == compareTo.value);915}916917@Override918public int hashCode() {919return name.hashCode();920}921922}923924public static class FieldDescriptor implements Comparable<FieldDescriptor> {925String type; // Type as declared in Java926String declaredType; // Type as declared in C or C++927String name; // Name as declared in Java928String declaredName; // Name as declared in C or C++929int offset;930boolean optional;931boolean present;932boolean required;933934public FieldDescriptor(int offset, String type, String declaredType, String name, String declaredName) {935super();936this.type = type;937this.declaredType = declaredType;938this.name = name;939this.declaredName = declaredName;940this.offset = offset;941this.optional = false;942this.present = true;943this.required = false;944}945946public void applyAliases(Map<String, String> aliasMap) {947type = unalias(declaredType, aliasMap);948cleanUpTypes();949}950951/**952* Cleans up this type by mapping U_32 -> U32, removing any const declaration etc.953*/954public void cleanUpTypes() {955type = stripUnderscore(type);956type = stripTypeQualifiers(type);957declaredType = stripUnderscore(declaredType);958}959960private static final Pattern QualifierPattern = Pattern.compile("\\s*\\b(const|volatile)\\b\\s*");961962private static String stripTypeQualifiers(String type) {963return filterOutPattern(type, QualifierPattern).trim();964}965966private static final Pattern ScalarPattern = Pattern.compile("\\b([IU])_(?=\\d+|DATA\\b)");967968/*969* remove underscores to map to J9 types970* e.g. U_8 -> U8 or I_DATA -> IDATA971*/972private static String stripUnderscore(String type) {973return ScalarPattern.matcher(type).replaceAll("$1");974}975976/*977* Check the type name against the known type aliases.978* Probably want a better solution for this.979*/980private static String unalias(String type, Map<String, String> aliasMap) {981CTypeParser parser = new CTypeParser(type);982String result = parser.getCoreType();983984/* Unalias the type */985if (aliasMap.containsKey(result)) {986result = aliasMap.get(result);987}988989return parser.getPrefix() + result + parser.getSuffix();990}991992public String getName() {993return name;994}995996public String getDeclaredName() {997return declaredName;998}9991000public String getType() {1001return type;1002}10031004public String getDeclaredType() {1005return declaredType;1006}10071008public int getOffset() {1009return offset;1010}10111012public final boolean isOptional() {1013return optional;1014}10151016public final boolean isPresent() {1017return present;1018}10191020public final boolean isRequired() {1021return required;1022}10231024@Override1025public String toString() {1026return type + " " + name + " Offset: " + offset;1027}10281029@Override1030public int compareTo(FieldDescriptor o) {1031return getName().compareTo(o.getName());1032}10331034public static Collection<FieldDescriptor> inflate(String line) {1035String[] parts = line.split("\\|");1036if (parts.length < 5 || ((parts.length - 3) % 2) != 0) {1037throw new IllegalArgumentException("Superset file line is invalid: " + line);1038}10391040int count = (parts.length - 3) / 2;1041Collection<FieldDescriptor> result = new ArrayList<>(count);10421043final String declaredName = parts[2];1044for (int i = 0; i < count; i++) {1045String fieldName = parts[1];1046if (i > 0) {1047fieldName = fieldName + "_v" + i;1048}10491050FieldDescriptor fd = new FieldDescriptor(0, parts[3 + i * 2], parts[4 + i * 2], fieldName, declaredName);1051result.add(fd);1052}1053return result;1054}10551056public String deflate() {1057StringBuilder result = new StringBuilder();1058result.append("F|"); // 01059result.append(getName()); // 11060result.append("|");1061result.append(getDeclaredName()); // 21062result.append("|");1063result.append(StructureReader.simplifyType(getType())); // 31064result.append("|");1065result.append(StructureReader.simplifyType(getDeclaredType())); // 41066return result.toString();1067}10681069@Override1070public boolean equals(Object obj) {1071if ((obj == null) || !(obj instanceof FieldDescriptor)) {1072return false;1073}1074FieldDescriptor compareTo = (FieldDescriptor) obj;1075return compareTo.deflate().equals(deflate());1076}10771078@Override1079public int hashCode() {1080return name.hashCode();1081}10821083}10841085public byte[] getStructureClassBytes(String binaryName) throws ClassNotFoundException {1086/*1087* Extract the simple class name (e.g. J9JavaClassFlags1088* from com.ibm.j9ddr.vm29.structure.J9JavaClassFlags).1089*/1090String clazzName = binaryName.substring(binaryName.lastIndexOf('.') + 1);10911092/* The structure name is the simple name of the requested class. */1093StructureDescriptor structure = structures.get(clazzName);10941095if (structure == null) {1096throw new ClassNotFoundException(clazzName + " is not in core file");1097}10981099String fullClassName = getPackageName(PackageNameType.STRUCTURE_PACKAGE_SLASH_NAME) + clazzName;11001101return BytecodeGenerator.getStructureClassBytes(structure, fullClassName);1102}11031104public byte[] getPointerClassBytes(String binaryName) throws ClassNotFoundException {1105/*1106* Extract the simple class name (e.g. J9ClassPointer from1107* com.ibm.j9ddr.vm29.pointer.generated.J9ClassPointer).1108*/1109String clazzName = binaryName.substring(binaryName.lastIndexOf('.') + 1);11101111/*1112* The structure name is derived by removing the 'Pointer' suffix.1113* Names ending with 'Flags' are used directly (e.g. J9BuildFlags).1114*/1115String structureName;11161117if (clazzName.endsWith("Pointer")) {1118structureName = clazzName.substring(0, clazzName.length() - 7);1119} else {1120structureName = clazzName;1121}11221123StructureDescriptor structure = structures.get(structureName);11241125if (structure == null) {1126throw new ClassNotFoundException(clazzName + " is not in core file");1127}11281129String fullClassName = getPackageName(PackageNameType.POINTER_PACKAGE_SLASH_NAME) + clazzName;11301131return BytecodeGenerator.getPointerClassBytes(this, typeManager, structure, fullClassName);1132}11331134// TODO: Make this more efficient. Probably change representation of fields and constants in Structure1135public long getConstantValue(String structureName, String constantName, long defaultValue) {1136if (constantName.equals("SIZEOF")) {1137return getStructureSizeOf(structureName);1138}11391140for (ConstantDescriptor constant : getConstants(structureName)) {1141if (constant.getName().equals(constantName)) {1142return constant.getValue();1143}1144}11451146return defaultValue;1147}11481149public boolean getBuildFlagValue(String structureName, String constantName, boolean defaultValue) {1150long defaultLongValue = defaultValue ? 1 : 0;1151long value = getConstantValue(structureName, constantName, defaultLongValue);1152return value != 0;1153}11541155public byte getSizeOfBool() {1156return header.getSizeofBool();1157}11581159public byte getSizeOfUDATA() {1160return header.getSizeofUDATA();1161}11621163public byte getBitFieldFormat() {1164return header.getBitfieldFormat();1165}11661167public static String simplifyType(String type) {1168String working = type;11691170/* Strip out the contents of array declarations */1171working = filterOutPattern(working, CONTENTS_OF_ARRAY_PATTERN);1172working = filterOutPattern(working, SPACES_BEFORE_SQUARE_BRACKETS_PATTERN);1173working = filterOutPattern(working, SPACES_AFTER_SQUARE_BRACKETS_PATTERN);1174working = filterOutPattern(working, SPACES_BEFORE_ASTERISKS_PATTERN);1175working = filterOutPattern(working, SPACES_AFTER_ASTERISKS_PATTERN);11761177return working;1178}11791180static String filterOutPattern(String input, Pattern pattern) {1181return pattern.matcher(input).replaceAll("");1182}11831184}118511861187