1 /*
2 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24
25 /*
26 * The Original Code is HAT. The Initial Developer of the
27 * Original Code is Bill Foote, with contributions from others
28 * at JavaSoft/Sun.
29 */
30
31 package jdk.test.lib.hprof.parser;
32
33 import java.io.*;
34 import java.util.Date;
35 import java.util.Hashtable;
36 import java.util.Map;
37 import jdk.test.lib.hprof.model.ArrayTypeCodes;
38 import jdk.test.lib.hprof.model.*;
39
40 /**
41 * Object that's used to read a hprof file.
42 *
43 * @author Bill Foote
44 */
45
46 public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes {
47
48 final static int MAGIC_NUMBER = 0x4a415641;
49 // That's "JAVA", the first part of "JAVA PROFILE ..."
50 private final static String[] VERSIONS = {
51 " PROFILE 1.0\0",
52 " PROFILE 1.0.1\0",
53 " PROFILE 1.0.2\0",
54 };
55
56 private final static int VERSION_JDK12BETA3 = 0;
57 private final static int VERSION_JDK12BETA4 = 1;
58 private final static int VERSION_JDK6 = 2;
59 // These version numbers are indices into VERSIONS. The instance data
60 // member version is set to one of these, and it drives decisions when
61 // reading the file.
62 //
63 // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no
64 // version-sensitive parsing.
65 //
66 // Version 1.0.1 changed the type of a constant pool entry from a signature
67 // to a typecode.
68 //
69 // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END
70 // to allow a large heap to be dumped as a sequence of heap dump segments.
71 //
72 // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1
73 // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on
74 // the size of the heap (normally it will be 1.0.1 but for multi-GB
75 // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the
76 // dump is generated as version 1.0.2).
77
78 //
79 // Record types:
80 //
81 static final int HPROF_UTF8 = 0x01;
82 static final int HPROF_LOAD_CLASS = 0x02;
83 static final int HPROF_UNLOAD_CLASS = 0x03;
84 static final int HPROF_FRAME = 0x04;
85 static final int HPROF_TRACE = 0x05;
86 static final int HPROF_ALLOC_SITES = 0x06;
87 static final int HPROF_HEAP_SUMMARY = 0x07;
88
89 static final int HPROF_START_THREAD = 0x0a;
90 static final int HPROF_END_THREAD = 0x0b;
91
92 static final int HPROF_HEAP_DUMP = 0x0c;
93
94 static final int HPROF_CPU_SAMPLES = 0x0d;
95 static final int HPROF_CONTROL_SETTINGS = 0x0e;
96 static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10;
97 static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11;
98
99 static final int HPROF_FLAT_ARRAYS = 0x12;
100 static final int HPROF_INLINED_FIELDS = 0x13;
101
102 static final int HPROF_FLAT_ARRAY = 0x01;
103 static final int HPROF_CLASS_WITH_INLINED_FIELDS = 0x01;
104
105 static final int HPROF_GC_ROOT_UNKNOWN = 0xff;
106 static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01;
107 static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02;
108 static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03;
109 static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04;
110 static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05;
111 static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06;
112 static final int HPROF_GC_ROOT_MONITOR_USED = 0x07;
113 static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08;
114
115 static final int HPROF_GC_CLASS_DUMP = 0x20;
116 static final int HPROF_GC_INSTANCE_DUMP = 0x21;
117 static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22;
118 static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23;
119
120 static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c;
121 static final int HPROF_HEAP_DUMP_END = 0x2c;
122
123 private final static int T_CLASS = 2;
124
125 private int version; // The version of .hprof being read
126
127 private int debugLevel;
128 private long currPos; // Current position in the file
129
130 private int dumpsToSkip;
131 private boolean callStack; // If true, read the call stack of objects
132
133 private int identifierSize; // Size, in bytes, of identifiers.
134 private Hashtable<Long, String> names;
135
136 // Hashtable<Integer, ThreadObject>, used to map the thread sequence number
137 // (aka "serial number") to the thread object ID for
138 // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class,
139 // at the end of this file.
140 private Hashtable<Integer, ThreadObject> threadObjects;
141
142 // Hashtable<Long, String>, maps class object ID to class name
143 // (with / converted to .)
144 private Hashtable<Long, String> classNameFromObjectID;
145
146 // Hashtable<Integer, Integer>, maps class serial # to class object ID
147 private Hashtable<Integer, String> classNameFromSerialNo;
148
149 // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame.
150 // Null if we're not tracking them.
151 private Hashtable<Long, StackFrame> stackFrames;
152
153 // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace
154 // Null if we're not tracking them.
155 private Hashtable<Integer, StackTrace> stackTraces;
156
157 private Snapshot snapshot;
158
159 public static boolean verifyMagicNumber(int numberRead) {
160 return (numberRead == MAGIC_NUMBER);
161 }
162
163 public HprofReader(ReadBuffer readBuffer, PositionDataInputStream in,
164 int dumpNumber, boolean callStack, int debugLevel)
165 throws IOException {
166 super(in);
167 this.snapshot = new Snapshot(readBuffer);
168 this.dumpsToSkip = dumpNumber - 1;
169 this.callStack = callStack;
170 this.debugLevel = debugLevel;
171 names = new Hashtable<Long, String>();
172 threadObjects = new Hashtable<Integer, ThreadObject>(43);
173 classNameFromObjectID = new Hashtable<Long, String>();
174 if (callStack) {
175 stackFrames = new Hashtable<Long, StackFrame>(43);
176 stackTraces = new Hashtable<Integer, StackTrace>(43);
177 classNameFromSerialNo = new Hashtable<Integer, String>();
178 }
179 }
180
181 public HprofReader(String fileName, PositionDataInputStream in,
182 int dumpNumber, boolean callStack, int debugLevel)
183 throws IOException {
184 this(MappedReadBuffer.create(new RandomAccessFile(fileName, "r")),
185 in, dumpNumber, callStack, debugLevel);
186 }
187
188 public Snapshot read() throws IOException {
189 currPos = 4; // 4 because of the magic number
190 version = readVersionHeader();
191 identifierSize = in.readInt();
192 snapshot.setIdentifierSize(identifierSize);
193 if (version >= VERSION_JDK12BETA4) {
194 snapshot.setNewStyleArrayClass(true);
195 } else {
196 snapshot.setNewStyleArrayClass(false);
197 }
198
199 currPos += 4;
200 if (identifierSize != 4 && identifierSize != 8) {
201 throw new IOException("I'm sorry, but I can't deal with an identifier size of " + identifierSize + ". I can only deal with 4 or 8.");
202 }
203 System.out.println("Dump file created " + (new Date(in.readLong())));
204 currPos += 8;
205
206 for (;;) {
207 int type;
208 try {
209 type = in.readUnsignedByte();
210 } catch (EOFException ignored) {
211 break;
212 }
213 in.readInt(); // Timestamp of this record
214 // Length of record: readInt() will return negative value for record
215 // length >2GB. so store 32bit value in long to keep it unsigned.
216 long length = in.readInt() & 0xffffffffL;
217 if (debugLevel > 0) {
218 System.out.println("Read record type " + type
219 + ", length " + length
220 + " at position " + toHex(currPos));
221 }
222 if (length < 0) {
223 throw new IOException("Bad record length of " + length
224 + " at byte " + toHex(currPos+5)
225 + " of file.");
226 }
227 currPos += 9 + length;
228 switch (type) {
229 case HPROF_UTF8: {
230 long id = readID();
231 byte[] chars = new byte[(int)length - identifierSize];
232 in.readFully(chars);
233 names.put(id, new String(chars));
234 break;
235 }
236 case HPROF_LOAD_CLASS: {
237 int serialNo = in.readInt(); // Not used
238 long classID = readID();
239 int stackTraceSerialNo = in.readInt();
240 long classNameID = readID();
241 Long classIdI = classID;
242 String nm = getNameFromID(classNameID).replace('/', '.');
243 classNameFromObjectID.put(classIdI, nm);
244 if (classNameFromSerialNo != null) {
245 classNameFromSerialNo.put(serialNo, nm);
246 }
247 break;
248 }
249
250 case HPROF_HEAP_DUMP: {
251 if (dumpsToSkip == 0) {
252 try {
253 readHeapDump(length, currPos);
254 } catch (EOFException exp) {
255 handleEOF(exp, snapshot);
256 }
257 if (debugLevel > 0) {
258 System.out.println(" Finished processing instances in heap dump.");
259 }
260 } else {
261 dumpsToSkip--;
262 skipBytes(length);
263 }
264 break;
265 }
266
267 case HPROF_HEAP_DUMP_END: {
268 if (version >= VERSION_JDK6) {
269 if (dumpsToSkip == 0) {
270 // update dumpsToSkip to skip other dumps
271 dumpsToSkip--;
272 } else {
273 // skip this dump (of the end record for a sequence of dump segments)
274 dumpsToSkip--;
275 }
276 } else {
277 // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2
278 warn("Ignoring unrecognized record type " + type);
279 }
280 skipBytes(length); // should be no-op
281 break;
282 }
283
284 case HPROF_HEAP_DUMP_SEGMENT: {
285 if (version >= VERSION_JDK6) {
286 if (dumpsToSkip == 0) {
287 try {
288 // read the dump segment
289 readHeapDump(length, currPos);
290 } catch (EOFException exp) {
291 handleEOF(exp, snapshot);
292 }
293 } else {
294 // all segments comprising the heap dump will be skipped
295 skipBytes(length);
296 }
297 } else {
298 // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2
299 warn("Ignoring unrecognized record type " + type);
300 skipBytes(length);
301 }
302 break;
303 }
304
305 case HPROF_FRAME: {
306 if (stackFrames == null) {
307 skipBytes(length);
308 } else {
309 long id = readID();
310 String methodName = getNameFromID(readID());
311 String methodSig = getNameFromID(readID());
312 String sourceFile = getNameFromID(readID());
313 int classSer = in.readInt();
314 String className = classNameFromSerialNo.get(classSer);
315 int lineNumber = in.readInt();
316 if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) {
317 warn("Weird stack frame line number: " + lineNumber);
318 lineNumber = StackFrame.LINE_NUMBER_UNKNOWN;
319 }
320 stackFrames.put(id,
321 new StackFrame(methodName, methodSig,
322 className, sourceFile,
323 lineNumber));
324 }
325 break;
326 }
327 case HPROF_TRACE: {
328 if (stackTraces == null) {
329 skipBytes(length);
330 } else {
331 int serialNo = in.readInt();
332 int threadSeq = in.readInt(); // Not used
333 StackFrame[] frames = new StackFrame[in.readInt()];
334 for (int i = 0; i < frames.length; i++) {
335 long fid = readID();
336 frames[i] = stackFrames.get(fid);
337 if (frames[i] == null) {
338 throw new IOException("Stack frame " + toHex(fid) + " not found");
339 }
340 }
341 stackTraces.put(serialNo,
342 new StackTrace(frames));
343 }
344 break;
345 }
346 case HPROF_UNLOAD_CLASS:
347 case HPROF_ALLOC_SITES:
348 case HPROF_START_THREAD:
349 case HPROF_END_THREAD:
350 case HPROF_HEAP_SUMMARY:
351 case HPROF_CPU_SAMPLES:
352 case HPROF_CONTROL_SETTINGS:
353 case HPROF_LOCKSTATS_WAIT_TIME:
354 case HPROF_LOCKSTATS_HOLD_TIME:
355 {
356 // Ignore these record types
357 skipBytes(length);
358 break;
359 }
360 case HPROF_FLAT_ARRAYS: {
361 readFlatArrays(length);
362 break;
363 }
364 case HPROF_INLINED_FIELDS: {
365 readInlinedFields(length);
366 break;
367 }
368
369 default: {
370 skipBytes(length);
371 warn("Ignoring unrecognized record type " + type);
372 }
373 }
374 }
375
376 return snapshot;
377 }
378
379 public String printStackTraces() {
380 StringBuffer output = new StringBuffer();
381 for (Map.Entry<Integer, StackTrace> entry : stackTraces.entrySet()) {
382 StackFrame[] frames = entry.getValue().getFrames();
383 output.append("SerialNo " + entry.getKey() + "\n");
384 for (int i = 0; i < frames.length; i++) {
385 output.append(" " + frames[i].getClassName() + "." + frames[i].getMethodName()
386 + frames[i].getMethodSignature() + " (" + frames[i].getSourceFileName()
387 + ":" + frames[i].getLineNumber() + ")" + "\n");
388 }
389 }
390
391 System.out.println(output);
392 return output.toString();
393 }
394
395 private void skipBytes(long length) throws IOException {
396 while (length > 0) {
397 long skipped = in.skip(length);
398 if (skipped == 0) {
399 // EOF or other problem, throw exception
400 throw new EOFException("Couldn't skip enough bytes");
401 }
402 length -= skipped;
403 }
404 }
405
406 private int readVersionHeader() throws IOException {
407 int candidatesLeft = VERSIONS.length;
408 boolean[] matched = new boolean[VERSIONS.length];
409 for (int i = 0; i < candidatesLeft; i++) {
410 matched[i] = true;
411 }
412
413 int pos = 0;
414 while (candidatesLeft > 0) {
415 char c = (char) in.readByte();
416 currPos++;
417 for (int i = 0; i < VERSIONS.length; i++) {
418 if (matched[i]) {
419 if (c != VERSIONS[i].charAt(pos)) { // Not matched
420 matched[i] = false;
421 --candidatesLeft;
422 } else if (pos == VERSIONS[i].length() - 1) { // Full match
423 return i;
424 }
425 }
426 }
427 ++pos;
428 }
429 throw new IOException("Version string not recognized at byte " + (pos+3));
430 }
431
432 private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException {
433 while (bytesLeft > 0) {
434 int type = in.readUnsignedByte();
435 if (debugLevel > 0) {
436 System.out.println(" Read heap sub-record type " + type
437 + " at position "
438 + toHex(posAtEnd - bytesLeft));
439 }
440 bytesLeft--;
441 switch(type) {
442 case HPROF_GC_ROOT_UNKNOWN: {
443 long id = readID();
444 bytesLeft -= identifierSize;
445 snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, ""));
446 break;
447 }
448 case HPROF_GC_ROOT_THREAD_OBJ: {
449 long id = readID();
450 int threadSeq = in.readInt();
451 int stackSeq = in.readInt();
452 bytesLeft -= identifierSize + 8;
453 StackTrace st = getStackTraceFromSerial(stackSeq);
454 ThreadObject threadObj = new ThreadObject(id, st);
455 threadObjects.put(threadSeq, threadObj);
456 snapshot.addThreadObject(threadObj);
457 break;
458 }
459 case HPROF_GC_ROOT_JNI_GLOBAL: {
460 long id = readID();
461 long globalRefId = readID(); // Ignored, for now
462 bytesLeft -= 2*identifierSize;
463 snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, ""));
464 break;
465 }
466 case HPROF_GC_ROOT_JNI_LOCAL: {
467 long id = readID();
468 int threadSeq = in.readInt();
469 int depth = in.readInt();
470 bytesLeft -= identifierSize + 8;
471 ThreadObject to = getThreadObjectFromSequence(threadSeq);
472 StackTrace st = to.getStackTrace();
473 if (st != null) {
474 st = st.traceForDepth(depth+1);
475 }
476 snapshot.addRoot(new Root(id, to.getId(),
477 Root.NATIVE_LOCAL, "", st));
478 break;
479 }
480 case HPROF_GC_ROOT_JAVA_FRAME: {
481 long id = readID();
482 int threadSeq = in.readInt();
483 int depth = in.readInt();
484 bytesLeft -= identifierSize + 8;
485 ThreadObject to = getThreadObjectFromSequence(threadSeq);
486 StackTrace st = to.getStackTrace();;
487 if (st != null) {
488 st = st.traceForDepth(depth+1);
489 }
490 snapshot.addRoot(new Root(id, to.getId(),
491 Root.JAVA_LOCAL, "", st));
492 break;
493 }
494 case HPROF_GC_ROOT_NATIVE_STACK: {
495 long id = readID();
496 int threadSeq = in.readInt();
497 bytesLeft -= identifierSize + 4;
498 ThreadObject to = getThreadObjectFromSequence(threadSeq);
499 StackTrace st = to.getStackTrace();;
500 snapshot.addRoot(new Root(id, to.getId(),
501 Root.NATIVE_STACK, "", st));
502 break;
503 }
504 case HPROF_GC_ROOT_STICKY_CLASS: {
505 long id = readID();
506 bytesLeft -= identifierSize;
507 snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, ""));
508 break;
509 }
510 case HPROF_GC_ROOT_THREAD_BLOCK: {
511 long id = readID();
512 int threadSeq = in.readInt();
513 bytesLeft -= identifierSize + 4;
514 ThreadObject to = getThreadObjectFromSequence(threadSeq);
515 StackTrace st = to.getStackTrace();
516 snapshot.addRoot(new Root(id, to.getId(),
517 Root.THREAD_BLOCK, "", st));
518 break;
519 }
520 case HPROF_GC_ROOT_MONITOR_USED: {
521 long id = readID();
522 bytesLeft -= identifierSize;
523 snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, ""));
524 break;
525 }
526 case HPROF_GC_CLASS_DUMP: {
527 int bytesRead = readClass();
528 bytesLeft -= bytesRead;
529 break;
530 }
531 case HPROF_GC_INSTANCE_DUMP: {
532 int bytesRead = readInstance();
533 bytesLeft -= bytesRead;
534 break;
535 }
536 case HPROF_GC_OBJ_ARRAY_DUMP: {
537 long bytesRead = readArray(false);
538 bytesLeft -= bytesRead;
539 break;
540 }
541 case HPROF_GC_PRIM_ARRAY_DUMP: {
542 long bytesRead = readArray(true);
543 bytesLeft -= bytesRead;
544 break;
545 }
546 default: {
547 throw new IOException("Unrecognized heap dump sub-record type: " + type);
548 }
549 }
550 }
551 if (bytesLeft != 0) {
552 warn("Error reading heap dump or heap dump segment: Byte count is " + bytesLeft + " instead of 0");
553 skipBytes(bytesLeft);
554 }
555 if (debugLevel > 0) {
556 System.out.println(" Finished heap sub-records.");
557 }
558 }
559
560 private long readID() throws IOException {
561 return (identifierSize == 4)?
562 (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong();
563 }
564
565 //
566 // Read a java value. If result is non-null, it's expected to be an
567 // array of one element. We use it to fake multiple return values.
568 // @returns the number of bytes read
569 //
570 private int readValue(JavaThing[] resultArr) throws IOException {
571 byte type = in.readByte();
572 return 1 + readValueForType(type, resultArr);
573 }
574
575 private int readValueForType(byte type, JavaThing[] resultArr)
576 throws IOException {
577 if (version >= VERSION_JDK12BETA4) {
578 type = signatureFromTypeId(type);
579 }
580 return readValueForTypeSignature(type, resultArr);
581 }
582
583 private int readValueForTypeSignature(byte type, JavaThing[] resultArr)
584 throws IOException {
585 switch (type) {
586 case '[':
587 case 'L': {
588 long id = readID();
589 if (resultArr != null) {
590 resultArr[0] = new JavaObjectRef(id);
591 }
592 return identifierSize;
593 }
594 case 'Z': {
595 int b = in.readByte();
596 if (b != 0 && b != 1) {
597 warn("Illegal boolean value read");
598 }
599 if (resultArr != null) {
600 resultArr[0] = new JavaBoolean(b != 0);
601 }
602 return 1;
603 }
604 case 'B': {
605 byte b = in.readByte();
606 if (resultArr != null) {
607 resultArr[0] = new JavaByte(b);
608 }
609 return 1;
610 }
611 case 'S': {
612 short s = in.readShort();
613 if (resultArr != null) {
614 resultArr[0] = new JavaShort(s);
615 }
616 return 2;
617 }
618 case 'C': {
619 char ch = in.readChar();
620 if (resultArr != null) {
621 resultArr[0] = new JavaChar(ch);
622 }
623 return 2;
624 }
625 case 'I': {
626 int val = in.readInt();
627 if (resultArr != null) {
628 resultArr[0] = new JavaInt(val);
629 }
630 return 4;
631 }
632 case 'J': {
633 long val = in.readLong();
634 if (resultArr != null) {
635 resultArr[0] = new JavaLong(val);
636 }
637 return 8;
638 }
639 case 'F': {
640 float val = in.readFloat();
641 if (resultArr != null) {
642 resultArr[0] = new JavaFloat(val);
643 }
644 return 4;
645 }
646 case 'D': {
647 double val = in.readDouble();
648 if (resultArr != null) {
649 resultArr[0] = new JavaDouble(val);
650 }
651 return 8;
652 }
653 default: {
654 throw new IOException("Bad value signature: " + type);
655 }
656 }
657 }
658
659 private ThreadObject getThreadObjectFromSequence(int threadSeq)
660 throws IOException {
661 ThreadObject to = threadObjects.get(threadSeq);
662 if (to == null) {
663 throw new IOException("Thread " + threadSeq +
664 " not found for JNI local ref");
665 }
666 return to;
667 }
668
669 private String getNameFromID(long id) throws IOException {
670 return getNameFromID(Long.valueOf(id));
671 }
672
673 private String getNameFromID(Long id) throws IOException {
674 if (id.longValue() == 0L) {
675 return "";
676 }
677 String result = names.get(id);
678 if (result == null) {
679 warn("Name not found at " + toHex(id.longValue()));
680 return "unresolved name " + toHex(id.longValue());
681 }
682 return result;
683 }
684
685 private StackTrace getStackTraceFromSerial(int ser) throws IOException {
686 if (stackTraces == null) {
687 return null;
688 }
689 StackTrace result = stackTraces.get(ser);
690 if (result == null) {
691 warn("Stack trace not found for serial # " + ser);
692 }
693 return result;
694 }
695
696 //
697 // Handle a HPROF_GC_CLASS_DUMP
698 // Return number of bytes read
699 //
700 private int readClass() throws IOException {
701 long id = readID();
702 StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
703 long superId = readID();
704 long classLoaderId = readID();
705 long signersId = readID();
706 long protDomainId = readID();
707 long reserved1 = readID();
708 long reserved2 = readID();
709 int instanceSize = in.readInt();
710 int bytesRead = 7 * identifierSize + 8;
711
712 int numConstPoolEntries = in.readUnsignedShort();
713 bytesRead += 2;
714 for (int i = 0; i < numConstPoolEntries; i++) {
715 int index = in.readUnsignedShort(); // unused
716 bytesRead += 2;
717 bytesRead += readValue(null); // We ignore the values
718 }
719
720 int numStatics = in.readUnsignedShort();
721 bytesRead += 2;
722 JavaThing[] valueBin = new JavaThing[1];
723 JavaStatic[] statics = new JavaStatic[numStatics];
724 for (int i = 0; i < numStatics; i++) {
725 long nameId = readID();
726 bytesRead += identifierSize;
727 byte type = in.readByte();
728 bytesRead++;
729 bytesRead += readValueForType(type, valueBin);
730 String fieldName = getNameFromID(nameId);
731 if (version >= VERSION_JDK12BETA4) {
732 type = signatureFromTypeId(type);
733 }
734 String signature = "" + ((char) type);
735 JavaField f = new JavaField(fieldName, signature);
736 statics[i] = new JavaStatic(f, valueBin[0]);
737 }
738
739 int numFields = in.readUnsignedShort();
740 bytesRead += 2;
741 JavaField[] fields = new JavaField[numFields];
742 for (int i = 0; i < numFields; i++) {
743 long nameId = readID();
744 bytesRead += identifierSize;
745 byte type = in.readByte();
746 bytesRead++;
747 String fieldName = getNameFromID(nameId);
748 if (version >= VERSION_JDK12BETA4) {
749 type = signatureFromTypeId(type);
750 }
751 String signature = "" + ((char) type);
752 fields[i] = new JavaField(fieldName, signature);
753 }
754 String name = classNameFromObjectID.get(id);
755 if (name == null) {
756 warn("Class name not found for " + toHex(id));
757 name = "unknown-name@" + toHex(id);
758 }
759 JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId,
760 protDomainId, fields, statics,
761 instanceSize);
762 snapshot.addClass(id, c);
763 snapshot.setSiteTrace(c, stackTrace);
764
765 return bytesRead;
766 }
767
768 private String toHex(long addr) {
769 return jdk.test.lib.hprof.util.Misc.toHex(addr);
770 }
771
772 //
773 // Handle a HPROF_GC_INSTANCE_DUMP
774 // Return number of bytes read
775 //
776 private int readInstance() throws IOException {
777 long start = in.position();
778 long id = readID();
779 StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
780 long classID = readID();
781 JavaClass searchedClass = snapshot.findClass(
782 "0x" + Long.toHexString(classID));
783 if (searchedClass == null) {
784 throw new IOException(
785 "Class Record for 0x" + Long.toHexString(classID) + " not found");
786 }
787 int bytesFollowing = in.readInt();
788 int bytesRead = (2 * identifierSize) + 8 + bytesFollowing;
789 JavaObject jobj = new JavaObject(classID, start);
790 skipBytes(bytesFollowing);
791 snapshot.addHeapObject(id, jobj);
792 snapshot.setSiteTrace(jobj, stackTrace);
793 return bytesRead;
794 }
795
796 //
797 // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP
798 // Return number of bytes read
799 //
800 private long readArray(boolean isPrimitive) throws IOException {
801 long start = in.position();
802 long id = readID();
803 StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
804 int num = in.readInt();
805 long bytesRead = identifierSize + 8;
806 long elementClassID;
807 if (isPrimitive) {
808 elementClassID = in.readByte();
809 bytesRead++;
810 } else {
811 elementClassID = readID();
812 bytesRead += identifierSize;
813 }
814
815 // Check for primitive arrays:
816 byte primitiveSignature = 0x00;
817 int elSize = 0;
818 if (isPrimitive || version < VERSION_JDK12BETA4) {
819 switch ((int)elementClassID) {
820 case T_BOOLEAN: {
821 primitiveSignature = (byte) 'Z';
822 elSize = 1;
823 break;
824 }
825 case T_CHAR: {
826 primitiveSignature = (byte) 'C';
827 elSize = 2;
828 break;
829 }
830 case T_FLOAT: {
831 primitiveSignature = (byte) 'F';
832 elSize = 4;
833 break;
834 }
835 case T_DOUBLE: {
836 primitiveSignature = (byte) 'D';
837 elSize = 8;
838 break;
839 }
840 case T_BYTE: {
841 primitiveSignature = (byte) 'B';
842 elSize = 1;
843 break;
844 }
845 case T_SHORT: {
846 primitiveSignature = (byte) 'S';
847 elSize = 2;
848 break;
849 }
850 case T_INT: {
851 primitiveSignature = (byte) 'I';
852 elSize = 4;
853 break;
854 }
855 case T_LONG: {
856 primitiveSignature = (byte) 'J';
857 elSize = 8;
858 break;
859 }
860 }
861 if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) {
862 throw new IOException("Unrecognized typecode: "
863 + elementClassID);
864 }
865 }
866 if (primitiveSignature != 0x00) {
867 long size = elSize * (long)num;
868 bytesRead += size;
869 JavaValueArray va = new JavaValueArray(id, primitiveSignature, start);
870 skipBytes(size);
871 snapshot.addHeapObject(id, va);
872 snapshot.setSiteTrace(va, stackTrace);
873 } else {
874 long sz = (long)num * identifierSize;
875 bytesRead += sz;
876 JavaObjectArray arr = new JavaObjectArray(elementClassID, start);
877 skipBytes(sz);
878 snapshot.addHeapObject(id, arr);
879 snapshot.setSiteTrace(arr, stackTrace);
880 }
881 return bytesRead;
882 }
883
884 private byte signatureFromTypeId(byte typeId) throws IOException {
885 switch (typeId) {
886 case T_CLASS: {
887 return (byte) 'L';
888 }
889 case T_BOOLEAN: {
890 return (byte) 'Z';
891 }
892 case T_CHAR: {
893 return (byte) 'C';
894 }
895 case T_FLOAT: {
896 return (byte) 'F';
897 }
898 case T_DOUBLE: {
899 return (byte) 'D';
900 }
901 case T_BYTE: {
902 return (byte) 'B';
903 }
904 case T_SHORT: {
905 return (byte) 'S';
906 }
907 case T_INT: {
908 return (byte) 'I';
909 }
910 case T_LONG: {
911 return (byte) 'J';
912 }
913 default: {
914 throw new IOException("Invalid type id of " + typeId);
915 }
916 }
917 }
918
919 private void readFlatArrays(long length) throws IOException {
920 while (length > 0) {
921 byte tag = in.readByte();
922 length--;
923 switch (tag) {
924 case HPROF_FLAT_ARRAY: {
925 long objId = readID();
926 length -= identifierSize;
927 long elementClassId = readID();
928 length -= identifierSize;
929 snapshot.addFlatArray(objId, elementClassId);
930 break;
931 }
932 default: {
933 throw new IOException("Invalid tag " + tag);
934 }
935 }
936 }
937 }
938
939 private void readInlinedFields(long length) throws IOException {
940 while (length > 0) {
941 byte tag = in.readByte();
942 length--;
943 switch (tag) {
944 case HPROF_CLASS_WITH_INLINED_FIELDS: {
945 long classID = readID();
946 length -= identifierSize;
947 int fieldNum = in.readUnsignedShort();
948 length -= 2;
949 Snapshot.ClassInlinedFields[] fields = new Snapshot.ClassInlinedFields[fieldNum];
950 for (int i = 0; i < fieldNum; i++) {
951 int fieldIndex = in.readUnsignedShort();
952 length -= 2;
953 int synthFieldCount = in.readUnsignedShort();
954 length -= 2;
955 String fieldName = getNameFromID(readID());
956 length -= identifierSize;
957 long fieldClassId = readID();
958 length -= identifierSize;
959 fields[i] = new Snapshot.ClassInlinedFields(fieldIndex, synthFieldCount, fieldName, fieldClassId);
960 }
961 snapshot.addClassInlinedFields(classID, fields);
962 break;
963 }
964 default: {
965 throw new IOException("Invalid tag " + tag);
966 }
967 }
968 }
969 }
970
971 private void handleEOF(EOFException exp, Snapshot snapshot) {
972 if (debugLevel > 0) {
973 exp.printStackTrace();
974 }
975 warn("Unexpected EOF. Will miss information...");
976 // we have EOF, we have to tolerate missing references
977 snapshot.setUnresolvedObjectsOK(true);
978 }
979
980 private void warn(String msg) {
981 System.out.println("WARNING: " + msg);
982 }
983
984 }