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