Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/StructureReader.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 static java.util.logging.Level.FINE;
25
import static java.util.logging.Level.FINER;
26
import static java.util.logging.Level.FINEST;
27
28
import java.io.BufferedReader;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.InputStreamReader;
32
import java.io.PrintWriter;
33
import java.io.Reader;
34
import java.io.StringWriter;
35
import java.nio.charset.StandardCharsets;
36
import java.util.ArrayList;
37
import java.util.Collection;
38
import java.util.Collections;
39
import java.util.HashMap;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Set;
43
import java.util.logging.Logger;
44
import java.util.regex.Matcher;
45
import java.util.regex.Pattern;
46
47
import javax.imageio.stream.ImageInputStream;
48
49
import com.ibm.j9ddr.logging.LoggerNames;
50
51
// TODO: Lazy initializing has been removed. Need to decide if it stays out.
52
public class StructureReader {
53
public static final int VERSION = 1;
54
public static final int J9_STRUCTURES_EYECATCHER = 0xFACEDEB8; // eyecatcher / magic identifier for a J9 structure file
55
private Map<String, StructureDescriptor> structures = null;
56
57
private String packageDotBaseName;
58
private String pointerDotName;
59
private String pointerSlashName;
60
private String structureDotName;
61
private String structureSlashName;
62
63
private static final Logger logger = Logger.getLogger(LoggerNames.LOGGER_STRUCTURE_READER);
64
private StructureHeader header;
65
66
@SuppressWarnings("rawtypes")
67
public static final Class<?>[] STRUCTURE_CONSTRUCTOR_SIGNATURE = new Class[] { Long.TYPE };
68
public static final byte BIT_FIELD_FORMAT_LITTLE_ENDIAN = 1;
69
public static final byte BIT_FIELD_FORMAT_BIG_ENDIAN = 2;
70
public static final int BIT_FIELD_CELL_SIZE = 32;
71
72
private static final Pattern MULTI_LINE_COMMENT_PATTERN = Pattern.compile(Pattern.quote("/*") + ".*?" + Pattern.quote("*/") , Pattern.DOTALL);
73
private static final Pattern SINGLE_LINE_COMMENT_PATTERN = Pattern.compile(Pattern.quote("//") + ".*$", Pattern.MULTILINE);
74
private static final Pattern MAP_PATTERN = Pattern.compile("(.*?)=(.*?)$", Pattern.MULTILINE);
75
private static final Pattern CONSTANT_PATTERN = Pattern.compile("(.+?)\\.(.+?)=(.+)$", Pattern.MULTILINE);
76
77
private Long packageVersion;
78
private String basePackage = DDR_VERSIONED_PACKAGE_PREFIX;
79
80
private final StructureTypeManager typeManager;
81
82
/* Patterns for cleaning types */
83
/* Pattern that matches a 1 or more characters not including ']' that occur after [ */
84
private static final Pattern CONTENTS_OF_ARRAY_PATTERN = Pattern.compile("(?<=\\[).*?(?=\\])");
85
86
private static final Pattern SPACES_AFTER_SQUARE_BRACKETS_PATTERN = Pattern.compile("(?<=\\])\\s+");
87
88
private static final Pattern SPACES_BEFORE_SQUARE_BRACKETS_PATTERN = Pattern.compile("\\s+(?=\\[)");
89
90
private static final Pattern SPACES_AFTER_ASTERISKS_PATTERN = Pattern.compile("(?<=\\*)\\s+");
91
92
private static final Pattern SPACES_BEFORE_ASTERISKS_PATTERN = Pattern.compile("\\s+(?=\\*)");
93
94
// VM Version constants
95
private static final String VM_MAJOR_VERSION = "VM_MAJOR_VERSION";
96
private static final String VM_MINOR_VERSION = "VM_MINOR_VERSION";
97
private static final String ARM_SPLIT_DDR_HACK = "ARM_SPLIT_DDR_HACK";
98
private static final String DDRALGORITHM_STRUCTURE_NAME = "DDRAlgorithmVersions";
99
100
public static final String DDR_VERSIONED_PACKAGE_PREFIX = "com.ibm.j9ddr.vm";
101
102
public static enum PackageNameType {
103
PACKAGE_DOT_BASE_NAME,
104
POINTER_PACKAGE_DOT_NAME,
105
POINTER_PACKAGE_SLASH_NAME,
106
STRUCTURE_PACKAGE_DOT_NAME,
107
STRUCTURE_PACKAGE_SLASH_NAME;
108
}
109
110
/**
111
* Initialize this reader from the supplied data.
112
* The ImageInputStream must be validated and already be positioned to point to
113
* the first byte of the DDR Structure Data. The ByteOrder of the ImageInputStream
114
* will be adjusted to match the file being read. (The DDR Structure data is written
115
* in the ByteOrder of the platform that created the core file.)
116
*/
117
public StructureReader(ImageInputStream in) throws IOException {
118
parse(in);
119
setStream();
120
applyAliases();
121
addCompatibilityConstants();
122
loadAuxFieldInfo();
123
typeManager = new StructureTypeManager(getStructures());
124
}
125
126
/**
127
* Read a stream in the J9DDRStructStore superset format.
128
*/
129
public StructureReader(InputStream in) throws IOException {
130
parse(in);
131
setStream();
132
typeManager = new StructureTypeManager(getStructures());
133
}
134
135
public StructureHeader getHeader() {
136
return header;
137
}
138
139
/**
140
* Sets the based on the DDRAlgorithmVersions field in the blob.
141
*/
142
private void setStream() {
143
StructureDescriptor version = structures.get(DDRALGORITHM_STRUCTURE_NAME);
144
long vmMajorVersion = 2; // default: stream 23
145
long vmMinorVersion = 3;
146
147
/* JAZZ 103906 : A hack was introduced when we split jvm.29 for ARM development.
148
* The goal was to keep using the same values of VM_MAJOR_VERSION and
149
* VM_MINOR_VERSION as in jvm.29 but now support ARM in this separate stream.
150
* Therefore, we needed a new way to differentiate the special ARM jvm.29 stream
151
* and this is the reason why the ARM_SPLIT_DDR_HACK field was added.
152
*/
153
String versionFormat = "%02d";
154
if (version != null) {
155
for (ConstantDescriptor constant : version.getConstants()) {
156
String constantName = constant.getName();
157
if (constantName.equals(VM_MAJOR_VERSION)) {
158
vmMajorVersion = constant.getValue();
159
} else if (constantName.equals(VM_MINOR_VERSION)) {
160
vmMinorVersion = constant.getValue() / 10;
161
} else if (constantName.equals(ARM_SPLIT_DDR_HACK) && constant.getValue() == 1) {
162
versionFormat = "%02d_00";
163
}
164
}
165
}
166
167
packageVersion = Long.valueOf((vmMajorVersion * 10) + vmMinorVersion); // stream is a 2 digit value
168
169
String versionSuffix = String.format(versionFormat, packageVersion);
170
171
packageDotBaseName = DDR_VERSIONED_PACKAGE_PREFIX + versionSuffix;
172
pointerDotName = packageDotBaseName + ".pointer.generated";
173
pointerSlashName = pointerDotName.replace('.', '/') + '/';
174
structureDotName = packageDotBaseName + ".structure";
175
structureSlashName = structureDotName.replace('.', '/') + '/';
176
}
177
178
public String getBasePackage() {
179
return basePackage;
180
}
181
182
/**
183
* Get the package version number, derived from fields of DDRAlgorithmVersions in the blob.
184
*
185
* @return the package version number
186
*/
187
public long getPackageVersion() {
188
if (packageVersion == null) {
189
throw new IllegalStateException("The DDR version information is not yet available");
190
}
191
192
return packageVersion.longValue();
193
}
194
195
/**
196
* Get the package name that should be used including the version information
197
* @param type
198
* @return
199
*/
200
public String getPackageName(PackageNameType type) {
201
if (packageVersion == null) {
202
throw new IllegalStateException("The DDR version information is not yet available");
203
}
204
205
switch (type) {
206
case PACKAGE_DOT_BASE_NAME:
207
return packageDotBaseName;
208
case POINTER_PACKAGE_DOT_NAME:
209
return pointerDotName;
210
case POINTER_PACKAGE_SLASH_NAME:
211
return pointerSlashName;
212
case STRUCTURE_PACKAGE_DOT_NAME:
213
return structureDotName;
214
case STRUCTURE_PACKAGE_SLASH_NAME:
215
return structureSlashName;
216
default:
217
throw new IllegalStateException("Unexpected PackageNameType");
218
}
219
}
220
221
private void applyAliases() throws IOException {
222
Map<String, String> aliasMap = loadAliasMap(getAliasVersion());
223
224
for (StructureDescriptor thisStruct : structures.values()) {
225
for (FieldDescriptor thisField : thisStruct.fields) {
226
thisField.applyAliases(aliasMap);
227
}
228
}
229
}
230
231
private void addCompatibilityConstants() throws IOException {
232
String resource = "/com/ibm/j9ddr/CompatibilityConstants" + getPackageVersion() + ".dat";
233
234
try (InputStream inputStream = StructureReader.class.getResourceAsStream(resource)) {
235
if (inputStream != null) {
236
addCompatibilityConstants(inputStream);
237
}
238
}
239
}
240
241
public void addCompatibilityConstants(InputStream inputStream) throws IOException {
242
Map<String, Map<String, Long>> map = new HashMap<>();
243
String text = stripComments(loadUTF8(inputStream));
244
245
for (Matcher matcher = CONSTANT_PATTERN.matcher(text); matcher.find();) {
246
String type = matcher.group(1).trim();
247
String name = matcher.group(2).trim();
248
String value = matcher.group(3).trim();
249
250
if (type.isEmpty() || name.isEmpty() || value.isEmpty()) {
251
throw new IOException("Malformed constant: " + matcher.group());
252
}
253
254
Map<String, Long> constants = map.get(type);
255
256
if (constants == null) {
257
constants = new HashMap<>();
258
map.put(type, constants);
259
}
260
261
try {
262
constants.put(name, Long.decode(value));
263
} catch (NumberFormatException e) {
264
throw new IOException("Malformed constant: " + matcher.group(), e);
265
}
266
}
267
268
for (StructureDescriptor structure : structures.values()) {
269
Map<String, Long> constants = map.remove(structure.getName());
270
271
if (constants == null) {
272
/* nothing applies to this structure */
273
continue;
274
}
275
276
/* remove entries that are already present */
277
for (ConstantDescriptor constant : structure.constants) {
278
constants.remove(constant.name);
279
}
280
281
/* add missing constants */
282
for (Map.Entry<String, Long> entry : constants.entrySet()) {
283
structure.constants.add(new ConstantDescriptor(entry.getKey(), entry.getValue()));
284
}
285
}
286
287
/* add default constants for new types */
288
for (Map.Entry<String, Map<String, Long>> entry : map.entrySet()) {
289
String name = entry.getKey();
290
String line = "S|" + name + "|" + name + "Pointer|";
291
StructureDescriptor structure = new StructureDescriptor(line);
292
293
for (Map.Entry<String, Long> constant : entry.getValue().entrySet()) {
294
structure.constants.add(new ConstantDescriptor(constant.getKey(), constant.getValue()));
295
}
296
297
structures.put(structure.getName(), structure);
298
}
299
}
300
301
public static Map<String, String> loadAliasMap(long version) throws IOException {
302
return loadAliasMap(Long.toString(version));
303
}
304
305
private static Map<String, String> loadAliasMap(String version) throws IOException {
306
String mapData = loadAliasMapData(version);
307
308
mapData = stripComments(mapData);
309
310
Map<String, String> aliasMap = new HashMap<>();
311
Matcher mapMatcher = MAP_PATTERN.matcher(mapData);
312
313
while (mapMatcher.find()) {
314
String from = mapMatcher.group(1);
315
String to = mapMatcher.group(2);
316
317
aliasMap.put(from.trim(), to.trim());
318
}
319
320
return Collections.unmodifiableMap(aliasMap);
321
}
322
323
private void loadAuxFieldInfo() throws IOException {
324
String resource = "/com/ibm/j9ddr/AuxFieldInfo" + getPackageVersion() + ".dat";
325
326
try (InputStream stream = StructureReader.class.getResourceAsStream(resource)) {
327
if (stream != null) {
328
loadAuxFieldInfo(stream);
329
}
330
}
331
}
332
333
public void loadAuxFieldInfo(InputStream stream) throws IOException {
334
Map<String, Map<String, String>> fieldMap = new HashMap<>();
335
Pattern fieldPattern = Pattern.compile("(.+?)\\.(.+?)=(.+)$", Pattern.MULTILINE);
336
String text = stripComments(loadUTF8(stream));
337
338
for (Matcher matcher = fieldPattern.matcher(text); matcher.find();) {
339
String typeName = matcher.group(1).trim();
340
String fieldName = matcher.group(2).trim();
341
String fieldType = matcher.group(3).trim();
342
343
if (typeName.isEmpty() || fieldName.isEmpty() || fieldType.isEmpty()) {
344
throw new IOException("Malformed field: " + matcher.group());
345
}
346
347
Map<String, String> infoMap = fieldMap.get(typeName);
348
349
if (infoMap == null) {
350
infoMap = new HashMap<>();
351
fieldMap.put(typeName, infoMap);
352
}
353
354
String previous = infoMap.put(fieldName, fieldType);
355
356
if (previous != null) {
357
throw new IOException("Duplicate field: " + matcher.group());
358
}
359
}
360
361
boolean debug = Boolean.getBoolean("aux.field.info.debug");
362
363
for (StructureDescriptor structure : structures.values()) {
364
String typeName = structure.getName();
365
Map<String, String> infoMap = fieldMap.remove(typeName);
366
367
if (infoMap == null) {
368
// No required or optional fields for this structure.
369
continue;
370
}
371
372
List<FieldDescriptor> fields = structure.getFields();
373
374
// Mark required fields and remove corresponding entries from infoMap.
375
for (FieldDescriptor field : fields) {
376
String fieldName = field.getName();
377
String fieldType = infoMap.remove(fieldName);
378
379
if (fieldType == null) {
380
// No auxilliary information for this field.
381
} else if (fieldType.equals("required")) {
382
field.required = true;
383
} else {
384
field.optional = true;
385
if (debug) {
386
field.declaredType = fieldType;
387
field.type = fieldType;
388
}
389
}
390
}
391
392
// Remaining entries in infoMap are for missing fields.
393
for (Map.Entry<String, String> entry : infoMap.entrySet()) {
394
String fieldName = entry.getKey();
395
String fieldType = entry.getValue();
396
FieldDescriptor field;
397
398
if (fieldType.equals("required")) {
399
field = new FieldDescriptor(0, "?", "?", fieldName, fieldName);
400
field.required = true;
401
} else {
402
field = new FieldDescriptor(0, fieldType, fieldType, fieldName, fieldName);
403
field.optional = true;
404
}
405
406
field.present = false;
407
fields.add(field);
408
}
409
}
410
411
// Remaining entries in fieldMap are for missing types.
412
for (Map.Entry<String, Map<String, String>> typeEntry : fieldMap.entrySet()) {
413
StructureDescriptor type = new StructureDescriptor();
414
String typeName = typeEntry.getKey();
415
List<FieldDescriptor> fields = new ArrayList<>();
416
417
type.name = typeName;
418
type.constants = new ArrayList<>();
419
type.fields = fields;
420
421
structures.put(typeName, type);
422
423
for (Map.Entry<String, String> fieldEntry : typeEntry.getValue().entrySet()) {
424
String fieldName = fieldEntry.getKey();
425
String fieldType = fieldEntry.getValue();
426
FieldDescriptor field;
427
428
if (fieldType.equals("required")) {
429
field = new FieldDescriptor(0, "?", "?", fieldName, fieldName);
430
field.required = true;
431
} else {
432
field = new FieldDescriptor(0, fieldType, fieldType, fieldName, fieldName);
433
field.optional = true;
434
}
435
436
field.present = false;
437
fields.add(field);
438
}
439
}
440
}
441
442
private static String stripComments(String mapData) {
443
mapData = filterOutPattern(mapData, MULTI_LINE_COMMENT_PATTERN);
444
mapData = filterOutPattern(mapData, SINGLE_LINE_COMMENT_PATTERN);
445
return mapData;
446
}
447
448
private String getAliasVersion() {
449
long version = getPackageVersion();
450
String variant = "";
451
452
if ((version == 29) && !getBuildFlagValue("J9BuildFlags", "J9VM_OPT_USE_OMR_DDR", false)) {
453
/*
454
* For blobs generated from the current stream but with legacy tools,
455
* load a variant of the alias map.
456
*/
457
variant = "-edg";
458
}
459
460
return version + variant;
461
}
462
463
private static String loadAliasMapData(String version) throws IOException {
464
String streamAliasMapResource = "/com/ibm/j9ddr/StructureAliases" + version + ".dat";
465
466
try (InputStream is = StructureReader.class.getResourceAsStream(streamAliasMapResource)) {
467
if (null == is) {
468
throw new RuntimeException("Failed to load alias map from resource: " + streamAliasMapResource + " - cannot continue");
469
}
470
471
return loadUTF8(is);
472
}
473
}
474
475
private static String loadUTF8(InputStream is) throws IOException {
476
try (Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
477
StringBuilder builder = new StringBuilder();
478
char[] buffer = new char[4096];
479
int read;
480
481
while ((read = reader.read(buffer)) != -1) {
482
builder.append(buffer, 0, read);
483
}
484
485
return builder.toString();
486
}
487
}
488
489
/**
490
* Get a list of the structures defined by this reader
491
* @return Set interface for the structure names
492
*/
493
public Set<String> getStructureNames() {
494
return structures.keySet();
495
}
496
497
/**
498
* Test to see if a structure has been read in and hence available
499
* @param name name of the structure to look for
500
* @return true if the structure exists, false if not
501
*/
502
public boolean hasStructure(String name) {
503
return structures.containsKey(name);
504
}
505
506
public int getStructureSizeOf(String structureName) {
507
if (!hasStructure(structureName)) {
508
return 0;
509
}
510
return structures.get(structureName).getSizeOf();
511
}
512
513
public Collection<StructureDescriptor> getStructures() {
514
return structures.values();
515
}
516
517
/**
518
* Get a map of the fields for a particular structure and their offsets.
519
* @param structureName name of the structure
520
* @return map of the fields as FieldName, Offset pair
521
*/
522
public List<FieldDescriptor> getFields(String structureName) {
523
StructureDescriptor structure = structures.get(structureName);
524
525
if (structure == null) {
526
throw new IllegalArgumentException("The structure [" + structureName + "] was not found");
527
}
528
529
return structure.fields;
530
}
531
532
public List<ConstantDescriptor> getConstants(String structureName) {
533
StructureDescriptor structure = structures.get(structureName);
534
535
if (structure == null) {
536
throw new IllegalArgumentException("The structure [" + structureName + "] was not found");
537
}
538
539
return structure.constants;
540
}
541
542
private void parse(InputStream inputStream) throws IOException {
543
header = new StructureHeader(inputStream);
544
structures = new HashMap<>();
545
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
546
StructureDescriptor structure = null;
547
548
for (;;) {
549
String line = reader.readLine();
550
if (line == null) {
551
break;
552
}
553
int hash = line.indexOf('#');
554
if (hash >= 0) {
555
// remove comments
556
line = line.substring(0, hash);
557
}
558
if (line.isEmpty()) {
559
// ignore blank lines
560
continue;
561
}
562
char type = line.charAt(0);
563
switch (type) {
564
case 'S':
565
structure = new StructureDescriptor(line);
566
structures.put(structure.getName(), structure);
567
break;
568
case 'F':
569
if (structure == null) {
570
throw new IllegalArgumentException("Superset stream is missing structure start line");
571
}
572
Collection<FieldDescriptor> fields = FieldDescriptor.inflate(line);
573
structure.fields.addAll(fields);
574
break;
575
case 'C':
576
if (structure == null) {
577
throw new IllegalArgumentException("Superset stream is missing structure start line");
578
}
579
ConstantDescriptor constant = new ConstantDescriptor(line);
580
structure.constants.add(constant);
581
break;
582
default:
583
throw new IllegalArgumentException("Superset stream contains unknown line: " + line);
584
}
585
}
586
}
587
588
/**
589
* Add to the existing set of structures already contained in this reader. Note that
590
* this will replace any definitions already contained within the reader.
591
*
592
* @param ddrStream stream to add the structures from
593
* @throws IOException
594
*/
595
public void addStructures(ImageInputStream ddrStream) throws IOException {
596
StructureHeader fragmentHeader = new StructureHeader(ddrStream);
597
checkBlobVersion();
598
if (header.getSizeofBool() != fragmentHeader.getSizeofBool()) {
599
throw new IOException("Invalid fragment definition : size of boolean is not the same");
600
}
601
if (header.getSizeofUDATA() != fragmentHeader.getSizeofUDATA()) {
602
throw new IOException("Invalid fragment definition : size of UDATA is not the same");
603
}
604
parseStructures(ddrStream, fragmentHeader);
605
}
606
607
/**
608
* Parse the supplied data and extract the structure information.
609
* It is expected that the ImageInputStream already points to the start of the
610
* DDR structure data. The ByteOrder of the ImageInputStream will be adjusted
611
* to match the file being read. (The DDR Structure data is written in the
612
* ByteOrder of the platform that created the core file.)
613
*
614
* This parse is called when the blob is initially loaded.
615
*
616
* The structures are lazily loaded in that the name of the structure is
617
* identified but not processed until a subsequent call requests it.
618
*
619
* @param ddrStream an open stream on the blob to be read
620
* @throws IOException re-throws any exceptions from the ImageInputStream
621
*/
622
private void parse(ImageInputStream ddrStream) throws IOException {
623
logger.logp(FINE, null, null, "Parsing structures. Start address = {0}", Long.toHexString(ddrStream.getStreamPosition()));
624
header = new StructureHeader(ddrStream);
625
checkBlobVersion();
626
parseStructures(ddrStream, header);
627
}
628
629
/**
630
* Checks that the blob version is supported and correctly sets the ByteOrder.
631
*
632
* @param ddrStream blob stream to check
633
* @throws IOException thrown if the version is not supported
634
*/
635
private void checkBlobVersion() throws IOException {
636
logger.logp(FINE, null, null, "Stream core structure version = {0}", header.getCoreVersion());
637
638
if (header.getCoreVersion() > VERSION) {
639
throw new IOException("Core structure version " + header.getCoreVersion() + " > StructureReader version " + VERSION);
640
}
641
}
642
643
/**
644
* Parse the structures from a supplied stream.
645
*
646
* @param ddrStream stream to read from, assumes that the stream is correctly positioned
647
* @param header blob header
648
* @throws IOException
649
*/
650
private void parseStructures(ImageInputStream ddrStream, StructureHeader header) throws IOException {
651
logger.logp(FINER, null, null, "structDataSize={0}, stringTableDataSize={1}, structureCount={2}",
652
new Object[] { header.getStructDataSize(), header.getStringTableDataSize(), header.getStructureCount() });
653
654
// This line must come after the header reads above or the offsets will be wrong.
655
long ddrStringTableStart = ddrStream.getStreamPosition() + header.getStructDataSize();
656
657
logger.logp(FINER, null, null, "ddrStringTableStart=0x{0}", Long.toHexString(ddrStringTableStart));
658
659
if (structures == null) {
660
// initialize the structure map with a sensible initial capacity
661
structures = new HashMap<>(header.getStructureCount());
662
}
663
664
for (int i = 0; i < header.getStructureCount(); i++) {
665
logger.logp(FINER, null, null, "Reading structure on iteration {0}", i);
666
StructureDescriptor structure = new StructureDescriptor();
667
structure.name = decodeTypeName(readString(ddrStream, ddrStringTableStart));
668
if (structure.name == null) {
669
logger.logp(FINE, null, null, "Structure name was null for structure {0}", i);
670
throw new IllegalArgumentException("Structure name was null for structure " + i);
671
} else if (structure.name.isEmpty()) {
672
logger.logp(FINE, null, null, "Structure name was blank for structure {0}", i);
673
throw new IllegalArgumentException("No name found for structure " + i);
674
}
675
logger.logp(FINE, null, null, "Reading structure {0}", structure.name);
676
677
structure.superName = decodeTypeName(readString(ddrStream, ddrStringTableStart));
678
structure.sizeOf = ddrStream.readInt();
679
int numberOfFields = ddrStream.readInt();
680
structure.fields = new ArrayList<>(numberOfFields);
681
int numberOfConstants = ddrStream.readInt();
682
structure.constants = new ArrayList<>(numberOfConstants);
683
684
logger.logp(FINER, null, null, "{0} super {1} sizeOf {2}",
685
new Object[] { structure.name, structure.superName, structure.sizeOf });
686
687
for (int j = 0; j < numberOfFields; j++) {
688
String declaredName = readString(ddrStream, ddrStringTableStart);
689
690
// Inline anonymous structures are handled by stacking the fields, separated by ".".
691
String name = declaredName.replace(".", "$");
692
693
String declaredType = readString(ddrStream, ddrStringTableStart);
694
if (name.equals("hashCode")) {
695
name = "_hashCode";
696
}
697
698
// Type is unaliased later
699
int offset = ddrStream.readInt();
700
FieldDescriptor field = new FieldDescriptor(offset, declaredType, declaredType, name, declaredName);
701
structure.fields.add(field);
702
logger.logp(FINEST, null, null, "Field: {0}.{1} offset {2}, declaredType {3}",
703
new Object[] { structure.name, name, offset, declaredType, declaredType });
704
}
705
706
for (int j = 0; j < numberOfConstants; j++) {
707
String name = readString(ddrStream, ddrStringTableStart);
708
long value = ddrStream.readLong();
709
ConstantDescriptor constant = new ConstantDescriptor(name, value);
710
structure.constants.add(constant);
711
logger.logp(FINEST, null, null, "Constant: {0}.{1}={2}",
712
new Object[] { structure.name, name, value });
713
}
714
715
structures.put(structure.name, structure);
716
}
717
718
logger.logp(FINE, null, null, "Finished parsing structures");
719
}
720
721
private static String decodeTypeName(String typeName) {
722
if (typeName != null) {
723
int index = typeName.indexOf("__");
724
725
if (index >= 0) {
726
StringBuilder buffer = new StringBuilder();
727
int start = 0;
728
729
do {
730
if (index == start) {
731
buffer.append("__");
732
} else {
733
buffer.append(typeName, start, index).append("$");
734
}
735
736
start = index + 2;
737
index = typeName.indexOf("__", start);
738
} while (index >= 0);
739
740
buffer.append(typeName, start, typeName.length());
741
typeName = buffer.toString();
742
}
743
}
744
745
return typeName;
746
}
747
748
private static String readString(ImageInputStream ddrStream, long ddrStringTableStart) {
749
try {
750
int stringOffset = ddrStream.readInt();
751
if (stringOffset == -1) {
752
return "";
753
}
754
755
long pos = ddrStream.getStreamPosition();
756
long seekPos = ddrStringTableStart + stringOffset;
757
ddrStream.seek(seekPos);
758
int length = ddrStream.readUnsignedShort();
759
if (length > 200) {
760
throw new IOException("Improbable string length: " + length);
761
}
762
// TODO: Reuse buffer
763
byte[] buffer = new byte[length];
764
int read = ddrStream.read(buffer);
765
766
if (read != length) {
767
throw new IOException("StructureReader readString() Failed to read " + length + " at " + Long.toHexString(seekPos) + ". Result: " + read);
768
}
769
770
String result = new String(buffer, StandardCharsets.UTF_8);
771
ddrStream.seek(pos);
772
return result;
773
} catch (IOException e) {
774
// put the stack trace to the log
775
StringWriter sw = new StringWriter();
776
PrintWriter pw = new PrintWriter(sw);
777
e.printStackTrace(pw);
778
logger.logp(FINE, null, null, sw.toString());
779
// TODO Auto-generated catch block
780
e.printStackTrace();
781
}
782
return null;
783
}
784
785
public static class StructureDescriptor {
786
String name;
787
String superName;
788
int sizeOf;
789
List<FieldDescriptor> fields;
790
List<ConstantDescriptor> constants;
791
792
public StructureDescriptor() {
793
super();
794
}
795
796
public StructureDescriptor(String line) {
797
super();
798
inflate(line);
799
}
800
801
@Override
802
public String toString() {
803
if (superName == null || superName.isEmpty()) {
804
return String.valueOf(name);
805
} else {
806
return name + " extends " + superName;
807
}
808
}
809
810
public String getPointerName() {
811
return name + "Pointer";
812
}
813
814
public String getName() {
815
return name;
816
}
817
818
public String getSuperName() {
819
return superName != null ? superName : "";
820
}
821
822
public List<FieldDescriptor> getFields() {
823
return fields;
824
}
825
826
public List<ConstantDescriptor> getConstants() {
827
return constants;
828
}
829
830
public int getSizeOf() {
831
return sizeOf;
832
}
833
834
private void inflate(String line) {
835
String[] parts = line.split("\\|");
836
if (parts.length < 3 || parts.length > 4) {
837
throw new IllegalArgumentException("Superset file line is invalid: " + line);
838
}
839
constants = new ArrayList<>();
840
fields = new ArrayList<>();
841
name = parts[1];
842
843
if (parts.length == 4) {
844
superName = parts[3];
845
} else {
846
superName = "";
847
}
848
}
849
850
public String deflate() {
851
StringBuilder result = new StringBuilder();
852
result.append("S|"); // 0
853
result.append(getName()); // 1
854
result.append("|");
855
result.append(getPointerName()); // 2
856
result.append("|");
857
result.append(getSuperName()); // 3
858
return result.toString();
859
}
860
861
}
862
863
public static class ConstantDescriptor implements Comparable<ConstantDescriptor> {
864
String name;
865
long value; // U_64
866
// TODO: what can hold a U_64 in Java
867
868
public ConstantDescriptor(String name, long value) {
869
super();
870
this.name = name;
871
this.value = value;
872
}
873
874
public ConstantDescriptor(String line) {
875
super();
876
inflate(line);
877
}
878
879
@Override
880
public String toString() {
881
return name + " = " + value;
882
}
883
884
public String getName() {
885
return name;
886
}
887
888
public long getValue() {
889
return value;
890
}
891
892
@Override
893
public int compareTo(ConstantDescriptor o) {
894
return getName().compareTo(o.getName());
895
}
896
897
private void inflate(String line) {
898
String[] parts = line.split("\\|");
899
if (parts.length != 2) {
900
throw new IllegalArgumentException("Superset file line is invalid: " + line);
901
}
902
name = parts[1];
903
}
904
905
public String deflate() {
906
return "C|" + getName(); //0 & 1
907
}
908
909
@Override
910
public boolean equals(Object obj) {
911
if ((obj == null) || !(obj instanceof ConstantDescriptor)) {
912
return false;
913
}
914
ConstantDescriptor compareTo = (ConstantDescriptor) obj;
915
return name.equals(compareTo.name) && (value == compareTo.value);
916
}
917
918
@Override
919
public int hashCode() {
920
return name.hashCode();
921
}
922
923
}
924
925
public static class FieldDescriptor implements Comparable<FieldDescriptor> {
926
String type; // Type as declared in Java
927
String declaredType; // Type as declared in C or C++
928
String name; // Name as declared in Java
929
String declaredName; // Name as declared in C or C++
930
int offset;
931
boolean optional;
932
boolean present;
933
boolean required;
934
935
public FieldDescriptor(int offset, String type, String declaredType, String name, String declaredName) {
936
super();
937
this.type = type;
938
this.declaredType = declaredType;
939
this.name = name;
940
this.declaredName = declaredName;
941
this.offset = offset;
942
this.optional = false;
943
this.present = true;
944
this.required = false;
945
}
946
947
public void applyAliases(Map<String, String> aliasMap) {
948
type = unalias(declaredType, aliasMap);
949
cleanUpTypes();
950
}
951
952
/**
953
* Cleans up this type by mapping U_32 -> U32, removing any const declaration etc.
954
*/
955
public void cleanUpTypes() {
956
type = stripUnderscore(type);
957
type = stripTypeQualifiers(type);
958
declaredType = stripUnderscore(declaredType);
959
}
960
961
private static final Pattern QualifierPattern = Pattern.compile("\\s*\\b(const|volatile)\\b\\s*");
962
963
private static String stripTypeQualifiers(String type) {
964
return filterOutPattern(type, QualifierPattern).trim();
965
}
966
967
private static final Pattern ScalarPattern = Pattern.compile("\\b([IU])_(?=\\d+|DATA\\b)");
968
969
/*
970
* remove underscores to map to J9 types
971
* e.g. U_8 -> U8 or I_DATA -> IDATA
972
*/
973
private static String stripUnderscore(String type) {
974
return ScalarPattern.matcher(type).replaceAll("$1");
975
}
976
977
/*
978
* Check the type name against the known type aliases.
979
* Probably want a better solution for this.
980
*/
981
private static String unalias(String type, Map<String, String> aliasMap) {
982
CTypeParser parser = new CTypeParser(type);
983
String result = parser.getCoreType();
984
985
/* Unalias the type */
986
if (aliasMap.containsKey(result)) {
987
result = aliasMap.get(result);
988
}
989
990
return parser.getPrefix() + result + parser.getSuffix();
991
}
992
993
public String getName() {
994
return name;
995
}
996
997
public String getDeclaredName() {
998
return declaredName;
999
}
1000
1001
public String getType() {
1002
return type;
1003
}
1004
1005
public String getDeclaredType() {
1006
return declaredType;
1007
}
1008
1009
public int getOffset() {
1010
return offset;
1011
}
1012
1013
public final boolean isOptional() {
1014
return optional;
1015
}
1016
1017
public final boolean isPresent() {
1018
return present;
1019
}
1020
1021
public final boolean isRequired() {
1022
return required;
1023
}
1024
1025
@Override
1026
public String toString() {
1027
return type + " " + name + " Offset: " + offset;
1028
}
1029
1030
@Override
1031
public int compareTo(FieldDescriptor o) {
1032
return getName().compareTo(o.getName());
1033
}
1034
1035
public static Collection<FieldDescriptor> inflate(String line) {
1036
String[] parts = line.split("\\|");
1037
if (parts.length < 5 || ((parts.length - 3) % 2) != 0) {
1038
throw new IllegalArgumentException("Superset file line is invalid: " + line);
1039
}
1040
1041
int count = (parts.length - 3) / 2;
1042
Collection<FieldDescriptor> result = new ArrayList<>(count);
1043
1044
final String declaredName = parts[2];
1045
for (int i = 0; i < count; i++) {
1046
String fieldName = parts[1];
1047
if (i > 0) {
1048
fieldName = fieldName + "_v" + i;
1049
}
1050
1051
FieldDescriptor fd = new FieldDescriptor(0, parts[3 + i * 2], parts[4 + i * 2], fieldName, declaredName);
1052
result.add(fd);
1053
}
1054
return result;
1055
}
1056
1057
public String deflate() {
1058
StringBuilder result = new StringBuilder();
1059
result.append("F|"); // 0
1060
result.append(getName()); // 1
1061
result.append("|");
1062
result.append(getDeclaredName()); // 2
1063
result.append("|");
1064
result.append(StructureReader.simplifyType(getType())); // 3
1065
result.append("|");
1066
result.append(StructureReader.simplifyType(getDeclaredType())); // 4
1067
return result.toString();
1068
}
1069
1070
@Override
1071
public boolean equals(Object obj) {
1072
if ((obj == null) || !(obj instanceof FieldDescriptor)) {
1073
return false;
1074
}
1075
FieldDescriptor compareTo = (FieldDescriptor) obj;
1076
return compareTo.deflate().equals(deflate());
1077
}
1078
1079
@Override
1080
public int hashCode() {
1081
return name.hashCode();
1082
}
1083
1084
}
1085
1086
public byte[] getStructureClassBytes(String binaryName) throws ClassNotFoundException {
1087
/*
1088
* Extract the simple class name (e.g. J9JavaClassFlags
1089
* from com.ibm.j9ddr.vm29.structure.J9JavaClassFlags).
1090
*/
1091
String clazzName = binaryName.substring(binaryName.lastIndexOf('.') + 1);
1092
1093
/* The structure name is the simple name of the requested class. */
1094
StructureDescriptor structure = structures.get(clazzName);
1095
1096
if (structure == null) {
1097
throw new ClassNotFoundException(clazzName + " is not in core file");
1098
}
1099
1100
String fullClassName = getPackageName(PackageNameType.STRUCTURE_PACKAGE_SLASH_NAME) + clazzName;
1101
1102
return BytecodeGenerator.getStructureClassBytes(structure, fullClassName);
1103
}
1104
1105
public byte[] getPointerClassBytes(String binaryName) throws ClassNotFoundException {
1106
/*
1107
* Extract the simple class name (e.g. J9ClassPointer from
1108
* com.ibm.j9ddr.vm29.pointer.generated.J9ClassPointer).
1109
*/
1110
String clazzName = binaryName.substring(binaryName.lastIndexOf('.') + 1);
1111
1112
/*
1113
* The structure name is derived by removing the 'Pointer' suffix.
1114
* Names ending with 'Flags' are used directly (e.g. J9BuildFlags).
1115
*/
1116
String structureName;
1117
1118
if (clazzName.endsWith("Pointer")) {
1119
structureName = clazzName.substring(0, clazzName.length() - 7);
1120
} else {
1121
structureName = clazzName;
1122
}
1123
1124
StructureDescriptor structure = structures.get(structureName);
1125
1126
if (structure == null) {
1127
throw new ClassNotFoundException(clazzName + " is not in core file");
1128
}
1129
1130
String fullClassName = getPackageName(PackageNameType.POINTER_PACKAGE_SLASH_NAME) + clazzName;
1131
1132
return BytecodeGenerator.getPointerClassBytes(this, typeManager, structure, fullClassName);
1133
}
1134
1135
// TODO: Make this more efficient. Probably change representation of fields and constants in Structure
1136
public long getConstantValue(String structureName, String constantName, long defaultValue) {
1137
if (constantName.equals("SIZEOF")) {
1138
return getStructureSizeOf(structureName);
1139
}
1140
1141
for (ConstantDescriptor constant : getConstants(structureName)) {
1142
if (constant.getName().equals(constantName)) {
1143
return constant.getValue();
1144
}
1145
}
1146
1147
return defaultValue;
1148
}
1149
1150
public boolean getBuildFlagValue(String structureName, String constantName, boolean defaultValue) {
1151
long defaultLongValue = defaultValue ? 1 : 0;
1152
long value = getConstantValue(structureName, constantName, defaultLongValue);
1153
return value != 0;
1154
}
1155
1156
public byte getSizeOfBool() {
1157
return header.getSizeofBool();
1158
}
1159
1160
public byte getSizeOfUDATA() {
1161
return header.getSizeofUDATA();
1162
}
1163
1164
public byte getBitFieldFormat() {
1165
return header.getBitfieldFormat();
1166
}
1167
1168
public static String simplifyType(String type) {
1169
String working = type;
1170
1171
/* Strip out the contents of array declarations */
1172
working = filterOutPattern(working, CONTENTS_OF_ARRAY_PATTERN);
1173
working = filterOutPattern(working, SPACES_BEFORE_SQUARE_BRACKETS_PATTERN);
1174
working = filterOutPattern(working, SPACES_AFTER_SQUARE_BRACKETS_PATTERN);
1175
working = filterOutPattern(working, SPACES_BEFORE_ASTERISKS_PATTERN);
1176
working = filterOutPattern(working, SPACES_AFTER_ASTERISKS_PATTERN);
1177
1178
return working;
1179
}
1180
1181
static String filterOutPattern(String input, Pattern pattern) {
1182
return pattern.matcher(input).replaceAll("");
1183
}
1184
1185
}
1186
1187