HprofReader.java revision 2224:2a8815d86b93
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 HprofReader(String fileName, PositionDataInputStream in,
155                       int dumpNumber, boolean callStack, int debugLevel)
156                       throws IOException {
157        super(in);
158        RandomAccessFile file = new RandomAccessFile(fileName, "r");
159        this.snapshot = new Snapshot(MappedReadBuffer.create(file));
160        this.dumpsToSkip = dumpNumber - 1;
161        this.callStack = callStack;
162        this.debugLevel = debugLevel;
163        names = new Hashtable<Long, String>();
164        threadObjects = new Hashtable<Integer, ThreadObject>(43);
165        classNameFromObjectID = new Hashtable<Long, String>();
166        if (callStack) {
167            stackFrames = new Hashtable<Long, StackFrame>(43);
168            stackTraces = new Hashtable<Integer, StackTrace>(43);
169            classNameFromSerialNo = new Hashtable<Integer, String>();
170        }
171    }
172
173    public Snapshot read() throws IOException {
174        currPos = 4;    // 4 because of the magic number
175        version = readVersionHeader();
176        identifierSize = in.readInt();
177        snapshot.setIdentifierSize(identifierSize);
178        if (version >= VERSION_JDK12BETA4) {
179            snapshot.setNewStyleArrayClass(true);
180        } else {
181            snapshot.setNewStyleArrayClass(false);
182        }
183
184        currPos += 4;
185        if (identifierSize != 4 && identifierSize != 8) {
186            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.");
187        }
188        System.out.println("Dump file created " + (new Date(in.readLong())));
189        currPos += 8;
190
191        for (;;) {
192            int type;
193            try {
194                type = in.readUnsignedByte();
195            } catch (EOFException ignored) {
196                break;
197            }
198            in.readInt();       // Timestamp of this record
199            // Length of record: readInt() will return negative value for record
200            // length >2GB.  so store 32bit value in long to keep it unsigned.
201            long length = in.readInt() & 0xffffffffL;
202            if (debugLevel > 0) {
203                System.out.println("Read record type " + type
204                                   + ", length " + length
205                                   + " at position " + toHex(currPos));
206            }
207            if (length < 0) {
208                throw new IOException("Bad record length of " + length
209                                      + " at byte " + toHex(currPos+5)
210                                      + " of file.");
211            }
212            currPos += 9 + length;
213            switch (type) {
214                case HPROF_UTF8: {
215                    long id = readID();
216                    byte[] chars = new byte[(int)length - identifierSize];
217                    in.readFully(chars);
218                    names.put(id, new String(chars));
219                    break;
220                }
221                case HPROF_LOAD_CLASS: {
222                    int serialNo = in.readInt();        // Not used
223                    long classID = readID();
224                    int stackTraceSerialNo = in.readInt();
225                    long classNameID = readID();
226                    Long classIdI = classID;
227                    String nm = getNameFromID(classNameID).replace('/', '.');
228                    classNameFromObjectID.put(classIdI, nm);
229                    if (classNameFromSerialNo != null) {
230                        classNameFromSerialNo.put(serialNo, nm);
231                    }
232                    break;
233                }
234
235                case HPROF_HEAP_DUMP: {
236                    if (dumpsToSkip <= 0) {
237                        try {
238                            readHeapDump(length, currPos);
239                        } catch (EOFException exp) {
240                            handleEOF(exp, snapshot);
241                        }
242                        if (debugLevel > 0) {
243                            System.out.println("    Finished processing instances in heap dump.");
244                        }
245                        return snapshot;
246                    } else {
247                        dumpsToSkip--;
248                        skipBytes(length);
249                    }
250                    break;
251                }
252
253                case HPROF_HEAP_DUMP_END: {
254                    if (version >= VERSION_JDK6) {
255                        if (dumpsToSkip <= 0) {
256                            skipBytes(length);  // should be no-op
257                            return snapshot;
258                        } else {
259                            // skip this dump (of the end record for a sequence of dump segments)
260                            dumpsToSkip--;
261                        }
262                    } else {
263                        // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2
264                        warn("Ignoring unrecognized record type " + type);
265                    }
266                    skipBytes(length);  // should be no-op
267                    break;
268                }
269
270                case HPROF_HEAP_DUMP_SEGMENT: {
271                    if (version >= VERSION_JDK6) {
272                        if (dumpsToSkip <= 0) {
273                            try {
274                                // read the dump segment
275                                readHeapDump(length, currPos);
276                            } catch (EOFException exp) {
277                                handleEOF(exp, snapshot);
278                            }
279                        } else {
280                            // all segments comprising the heap dump will be skipped
281                            skipBytes(length);
282                        }
283                    } else {
284                        // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2
285                        warn("Ignoring unrecognized record type " + type);
286                        skipBytes(length);
287                    }
288                    break;
289                }
290
291                case HPROF_FRAME: {
292                    if (stackFrames == null) {
293                        skipBytes(length);
294                    } else {
295                        long id = readID();
296                        String methodName = getNameFromID(readID());
297                        String methodSig = getNameFromID(readID());
298                        String sourceFile = getNameFromID(readID());
299                        int classSer = in.readInt();
300                        String className = classNameFromSerialNo.get(classSer);
301                        int lineNumber = in.readInt();
302                        if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) {
303                            warn("Weird stack frame line number:  " + lineNumber);
304                            lineNumber = StackFrame.LINE_NUMBER_UNKNOWN;
305                        }
306                        stackFrames.put(id,
307                                        new StackFrame(methodName, methodSig,
308                                                       className, sourceFile,
309                                                       lineNumber));
310                    }
311                    break;
312                }
313                case HPROF_TRACE: {
314                    if (stackTraces == null) {
315                        skipBytes(length);
316                    } else {
317                        int serialNo = in.readInt();
318                        int threadSeq = in.readInt();   // Not used
319                        StackFrame[] frames = new StackFrame[in.readInt()];
320                        for (int i = 0; i < frames.length; i++) {
321                            long fid = readID();
322                            frames[i] = stackFrames.get(fid);
323                            if (frames[i] == null) {
324                                throw new IOException("Stack frame " + toHex(fid) + " not found");
325                            }
326                        }
327                        stackTraces.put(serialNo,
328                                        new StackTrace(frames));
329                    }
330                    break;
331                }
332                case HPROF_UNLOAD_CLASS:
333                case HPROF_ALLOC_SITES:
334                case HPROF_START_THREAD:
335                case HPROF_END_THREAD:
336                case HPROF_HEAP_SUMMARY:
337                case HPROF_CPU_SAMPLES:
338                case HPROF_CONTROL_SETTINGS:
339                case HPROF_LOCKSTATS_WAIT_TIME:
340                case HPROF_LOCKSTATS_HOLD_TIME:
341                {
342                    // Ignore these record types
343                    skipBytes(length);
344                    break;
345                }
346                default: {
347                    skipBytes(length);
348                    warn("Ignoring unrecognized record type " + type);
349                }
350            }
351        }
352
353        return snapshot;
354    }
355
356    private void skipBytes(long length) throws IOException {
357        while (length > 0) {
358            long skipped = in.skip(length);
359            if (skipped == 0) {
360                // EOF or other problem, throw exception
361                throw new EOFException("Couldn't skip enough bytes");
362            }
363            length -= skipped;
364        }
365    }
366
367    private int readVersionHeader() throws IOException {
368        int candidatesLeft = VERSIONS.length;
369        boolean[] matched = new boolean[VERSIONS.length];
370        for (int i = 0; i < candidatesLeft; i++) {
371            matched[i] = true;
372        }
373
374        int pos = 0;
375        while (candidatesLeft > 0) {
376            char c = (char) in.readByte();
377            currPos++;
378            for (int i = 0; i < VERSIONS.length; i++) {
379                if (matched[i]) {
380                    if (c != VERSIONS[i].charAt(pos)) {   // Not matched
381                        matched[i] = false;
382                        --candidatesLeft;
383                    } else if (pos == VERSIONS[i].length() - 1) {  // Full match
384                        return i;
385                    }
386                }
387            }
388            ++pos;
389        }
390        throw new IOException("Version string not recognized at byte " + (pos+3));
391    }
392
393    private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException {
394        while (bytesLeft > 0) {
395            int type = in.readUnsignedByte();
396            if (debugLevel > 0) {
397                System.out.println("    Read heap sub-record type " + type
398                                   + " at position "
399                                   + toHex(posAtEnd - bytesLeft));
400            }
401            bytesLeft--;
402            switch(type) {
403                case HPROF_GC_ROOT_UNKNOWN: {
404                    long id = readID();
405                    bytesLeft -= identifierSize;
406                    snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, ""));
407                    break;
408                }
409                case HPROF_GC_ROOT_THREAD_OBJ: {
410                    long id = readID();
411                    int threadSeq = in.readInt();
412                    int stackSeq = in.readInt();
413                    bytesLeft -= identifierSize + 8;
414                    threadObjects.put(threadSeq,
415                                      new ThreadObject(id, stackSeq));
416                    break;
417                }
418                case HPROF_GC_ROOT_JNI_GLOBAL: {
419                    long id = readID();
420                    long globalRefId = readID();        // Ignored, for now
421                    bytesLeft -= 2*identifierSize;
422                    snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, ""));
423                    break;
424                }
425                case HPROF_GC_ROOT_JNI_LOCAL: {
426                    long id = readID();
427                    int threadSeq = in.readInt();
428                    int depth = in.readInt();
429                    bytesLeft -= identifierSize + 8;
430                    ThreadObject to = getThreadObjectFromSequence(threadSeq);
431                    StackTrace st = getStackTraceFromSerial(to.stackSeq);
432                    if (st != null) {
433                        st = st.traceForDepth(depth+1);
434                    }
435                    snapshot.addRoot(new Root(id, to.threadId,
436                                              Root.NATIVE_LOCAL, "", st));
437                    break;
438                }
439                case HPROF_GC_ROOT_JAVA_FRAME: {
440                    long id = readID();
441                    int threadSeq = in.readInt();
442                    int depth = in.readInt();
443                    bytesLeft -= identifierSize + 8;
444                    ThreadObject to = getThreadObjectFromSequence(threadSeq);
445                    StackTrace st = getStackTraceFromSerial(to.stackSeq);
446                    if (st != null) {
447                        st = st.traceForDepth(depth+1);
448                    }
449                    snapshot.addRoot(new Root(id, to.threadId,
450                                              Root.JAVA_LOCAL, "", st));
451                    break;
452                }
453                case HPROF_GC_ROOT_NATIVE_STACK: {
454                    long id = readID();
455                    int threadSeq = in.readInt();
456                    bytesLeft -= identifierSize + 4;
457                    ThreadObject to = getThreadObjectFromSequence(threadSeq);
458                    StackTrace st = getStackTraceFromSerial(to.stackSeq);
459                    snapshot.addRoot(new Root(id, to.threadId,
460                                              Root.NATIVE_STACK, "", st));
461                    break;
462                }
463                case HPROF_GC_ROOT_STICKY_CLASS: {
464                    long id = readID();
465                    bytesLeft -= identifierSize;
466                    snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, ""));
467                    break;
468                }
469                case HPROF_GC_ROOT_THREAD_BLOCK: {
470                    long id = readID();
471                    int threadSeq = in.readInt();
472                    bytesLeft -= identifierSize + 4;
473                    ThreadObject to = getThreadObjectFromSequence(threadSeq);
474                    StackTrace st = getStackTraceFromSerial(to.stackSeq);
475                    snapshot.addRoot(new Root(id, to.threadId,
476                                     Root.THREAD_BLOCK, "", st));
477                    break;
478                }
479                case HPROF_GC_ROOT_MONITOR_USED: {
480                    long id = readID();
481                    bytesLeft -= identifierSize;
482                    snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, ""));
483                    break;
484                }
485                case HPROF_GC_CLASS_DUMP: {
486                    int bytesRead = readClass();
487                    bytesLeft -= bytesRead;
488                    break;
489                }
490                case HPROF_GC_INSTANCE_DUMP: {
491                    int bytesRead = readInstance();
492                    bytesLeft -= bytesRead;
493                    break;
494                }
495                case HPROF_GC_OBJ_ARRAY_DUMP: {
496                    long bytesRead = readArray(false);
497                    bytesLeft -= bytesRead;
498                    break;
499                }
500                case HPROF_GC_PRIM_ARRAY_DUMP: {
501                    long bytesRead = readArray(true);
502                    bytesLeft -= bytesRead;
503                    break;
504                }
505                default: {
506                    throw new IOException("Unrecognized heap dump sub-record type:  " + type);
507                }
508            }
509        }
510        if (bytesLeft != 0) {
511            warn("Error reading heap dump or heap dump segment:  Byte count is " + bytesLeft + " instead of 0");
512            skipBytes(bytesLeft);
513        }
514        if (debugLevel > 0) {
515            System.out.println("    Finished heap sub-records.");
516        }
517    }
518
519    private long readID() throws IOException {
520        return (identifierSize == 4)?
521            (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong();
522    }
523
524    //
525    // Read a java value.  If result is non-null, it's expected to be an
526    // array of one element.  We use it to fake multiple return values.
527    // @returns the number of bytes read
528    //
529    private int readValue(JavaThing[] resultArr) throws IOException {
530        byte type = in.readByte();
531        return 1 + readValueForType(type, resultArr);
532    }
533
534    private int readValueForType(byte type, JavaThing[] resultArr)
535            throws IOException {
536        if (version >= VERSION_JDK12BETA4) {
537            type = signatureFromTypeId(type);
538        }
539        return readValueForTypeSignature(type, resultArr);
540    }
541
542    private int readValueForTypeSignature(byte type, JavaThing[] resultArr)
543            throws IOException {
544        switch (type) {
545            case '[':
546            case 'L': {
547                long id = readID();
548                if (resultArr != null) {
549                    resultArr[0] = new JavaObjectRef(id);
550                }
551                return identifierSize;
552            }
553            case 'Z': {
554                int b = in.readByte();
555                if (b != 0 && b != 1) {
556                    warn("Illegal boolean value read");
557                }
558                if (resultArr != null) {
559                    resultArr[0] = new JavaBoolean(b != 0);
560                }
561                return 1;
562            }
563            case 'B': {
564                byte b = in.readByte();
565                if (resultArr != null) {
566                    resultArr[0] = new JavaByte(b);
567                }
568                return 1;
569            }
570            case 'S': {
571                short s = in.readShort();
572                if (resultArr != null) {
573                    resultArr[0] = new JavaShort(s);
574                }
575                return 2;
576            }
577            case 'C': {
578                char ch = in.readChar();
579                if (resultArr != null) {
580                    resultArr[0] = new JavaChar(ch);
581                }
582                return 2;
583            }
584            case 'I': {
585                int val = in.readInt();
586                if (resultArr != null) {
587                    resultArr[0] = new JavaInt(val);
588                }
589                return 4;
590            }
591            case 'J': {
592                long val = in.readLong();
593                if (resultArr != null) {
594                    resultArr[0] = new JavaLong(val);
595                }
596                return 8;
597            }
598            case 'F': {
599                float val = in.readFloat();
600                if (resultArr != null) {
601                    resultArr[0] = new JavaFloat(val);
602                }
603                return 4;
604            }
605            case 'D': {
606                double val = in.readDouble();
607                if (resultArr != null) {
608                    resultArr[0] = new JavaDouble(val);
609                }
610                return 8;
611            }
612            default: {
613                throw new IOException("Bad value signature:  " + type);
614            }
615        }
616    }
617
618    private ThreadObject getThreadObjectFromSequence(int threadSeq)
619            throws IOException {
620        ThreadObject to = threadObjects.get(threadSeq);
621        if (to == null) {
622            throw new IOException("Thread " + threadSeq +
623                                  " not found for JNI local ref");
624        }
625        return to;
626    }
627
628    private String getNameFromID(long id) throws IOException {
629        return getNameFromID(Long.valueOf(id));
630    }
631
632    private String getNameFromID(Long id) throws IOException {
633        if (id.longValue() == 0L) {
634            return "";
635        }
636        String result = names.get(id);
637        if (result == null) {
638            warn("Name not found at " + toHex(id.longValue()));
639            return "unresolved name " + toHex(id.longValue());
640        }
641        return result;
642    }
643
644    private StackTrace getStackTraceFromSerial(int ser) throws IOException {
645        if (stackTraces == null) {
646            return null;
647        }
648        StackTrace result = stackTraces.get(ser);
649        if (result == null) {
650            warn("Stack trace not found for serial # " + ser);
651        }
652        return result;
653    }
654
655    //
656    // Handle a HPROF_GC_CLASS_DUMP
657    // Return number of bytes read
658    //
659    private int readClass() throws IOException {
660        long id = readID();
661        StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
662        long superId = readID();
663        long classLoaderId = readID();
664        long signersId = readID();
665        long protDomainId = readID();
666        long reserved1 = readID();
667        long reserved2 = readID();
668        int instanceSize = in.readInt();
669        int bytesRead = 7 * identifierSize + 8;
670
671        int numConstPoolEntries = in.readUnsignedShort();
672        bytesRead += 2;
673        for (int i = 0; i < numConstPoolEntries; i++) {
674            int index = in.readUnsignedShort(); // unused
675            bytesRead += 2;
676            bytesRead += readValue(null);       // We ignore the values
677        }
678
679        int numStatics = in.readUnsignedShort();
680        bytesRead += 2;
681        JavaThing[] valueBin = new JavaThing[1];
682        JavaStatic[] statics = new JavaStatic[numStatics];
683        for (int i = 0; i < numStatics; i++) {
684            long nameId = readID();
685            bytesRead += identifierSize;
686            byte type = in.readByte();
687            bytesRead++;
688            bytesRead += readValueForType(type, valueBin);
689            String fieldName = getNameFromID(nameId);
690            if (version >= VERSION_JDK12BETA4) {
691                type = signatureFromTypeId(type);
692            }
693            String signature = "" + ((char) type);
694            JavaField f = new JavaField(fieldName, signature);
695            statics[i] = new JavaStatic(f, valueBin[0]);
696        }
697
698        int numFields = in.readUnsignedShort();
699        bytesRead += 2;
700        JavaField[] fields = new JavaField[numFields];
701        for (int i = 0; i < numFields; i++) {
702            long nameId = readID();
703            bytesRead += identifierSize;
704            byte type = in.readByte();
705            bytesRead++;
706            String fieldName = getNameFromID(nameId);
707            if (version >= VERSION_JDK12BETA4) {
708                type = signatureFromTypeId(type);
709            }
710            String signature = "" + ((char) type);
711            fields[i] = new JavaField(fieldName, signature);
712        }
713        String name = classNameFromObjectID.get(id);
714        if (name == null) {
715            warn("Class name not found for " + toHex(id));
716            name = "unknown-name@" + toHex(id);
717        }
718        JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId,
719                                    protDomainId, fields, statics,
720                                    instanceSize);
721        snapshot.addClass(id, c);
722        snapshot.setSiteTrace(c, stackTrace);
723
724        return bytesRead;
725    }
726
727    private String toHex(long addr) {
728        return jdk.test.lib.hprof.util.Misc.toHex(addr);
729    }
730
731    //
732    // Handle a HPROF_GC_INSTANCE_DUMP
733    // Return number of bytes read
734    //
735    private int readInstance() throws IOException {
736        long start = in.position();
737        long id = readID();
738        StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
739        long classID = readID();
740        int bytesFollowing = in.readInt();
741        int bytesRead = (2 * identifierSize) + 8 + bytesFollowing;
742        JavaObject jobj = new JavaObject(classID, start);
743        skipBytes(bytesFollowing);
744        snapshot.addHeapObject(id, jobj);
745        snapshot.setSiteTrace(jobj, stackTrace);
746        return bytesRead;
747    }
748
749    //
750    // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP
751    // Return number of bytes read
752    //
753    private long readArray(boolean isPrimitive) throws IOException {
754        long start = in.position();
755        long id = readID();
756        StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
757        int num = in.readInt();
758        long bytesRead = identifierSize + 8;
759        long elementClassID;
760        if (isPrimitive) {
761            elementClassID = in.readByte();
762            bytesRead++;
763        } else {
764            elementClassID = readID();
765            bytesRead += identifierSize;
766        }
767
768        // Check for primitive arrays:
769        byte primitiveSignature = 0x00;
770        int elSize = 0;
771        if (isPrimitive || version < VERSION_JDK12BETA4) {
772            switch ((int)elementClassID) {
773                case T_BOOLEAN: {
774                    primitiveSignature = (byte) 'Z';
775                    elSize = 1;
776                    break;
777                }
778                case T_CHAR: {
779                    primitiveSignature = (byte) 'C';
780                    elSize = 2;
781                    break;
782                }
783                case T_FLOAT: {
784                    primitiveSignature = (byte) 'F';
785                    elSize = 4;
786                    break;
787                }
788                case T_DOUBLE: {
789                    primitiveSignature = (byte) 'D';
790                    elSize = 8;
791                    break;
792                }
793                case T_BYTE: {
794                    primitiveSignature = (byte) 'B';
795                    elSize = 1;
796                    break;
797                }
798                case T_SHORT: {
799                    primitiveSignature = (byte) 'S';
800                    elSize = 2;
801                    break;
802                }
803                case T_INT: {
804                    primitiveSignature = (byte) 'I';
805                    elSize = 4;
806                    break;
807                }
808                case T_LONG: {
809                    primitiveSignature = (byte) 'J';
810                    elSize = 8;
811                    break;
812                }
813            }
814            if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) {
815                throw new IOException("Unrecognized typecode:  "
816                                        + elementClassID);
817            }
818        }
819        if (primitiveSignature != 0x00) {
820            long size = elSize * (long)num;
821            bytesRead += size;
822            JavaValueArray va = new JavaValueArray(primitiveSignature, start);
823            skipBytes(size);
824            snapshot.addHeapObject(id, va);
825            snapshot.setSiteTrace(va, stackTrace);
826        } else {
827            long sz = (long)num * identifierSize;
828            bytesRead += sz;
829            JavaObjectArray arr = new JavaObjectArray(elementClassID, start);
830            skipBytes(sz);
831            snapshot.addHeapObject(id, arr);
832            snapshot.setSiteTrace(arr, stackTrace);
833        }
834        return bytesRead;
835    }
836
837    private byte signatureFromTypeId(byte typeId) throws IOException {
838        switch (typeId) {
839            case T_CLASS: {
840                return (byte) 'L';
841            }
842            case T_BOOLEAN: {
843                return (byte) 'Z';
844            }
845            case T_CHAR: {
846                return (byte) 'C';
847            }
848            case T_FLOAT: {
849                return (byte) 'F';
850            }
851            case T_DOUBLE: {
852                return (byte) 'D';
853            }
854            case T_BYTE: {
855                return (byte) 'B';
856            }
857            case T_SHORT: {
858                return (byte) 'S';
859            }
860            case T_INT: {
861                return (byte) 'I';
862            }
863            case T_LONG: {
864                return (byte) 'J';
865            }
866            default: {
867                throw new IOException("Invalid type id of " + typeId);
868            }
869        }
870    }
871
872    private void handleEOF(EOFException exp, Snapshot snapshot) {
873        if (debugLevel > 0) {
874            exp.printStackTrace();
875        }
876        warn("Unexpected EOF. Will miss information...");
877        // we have EOF, we have to tolerate missing references
878        snapshot.setUnresolvedObjectsOK(true);
879    }
880
881    private void warn(String msg) {
882        System.out.println("WARNING: " + msg);
883    }
884
885    //
886    // A trivial data-holder class for HPROF_GC_ROOT_THREAD_OBJ.
887    //
888    private class ThreadObject {
889
890        long threadId;
891        int stackSeq;
892
893        ThreadObject(long threadId, int stackSeq) {
894            this.threadId = threadId;
895            this.stackSeq = stackSeq;
896        }
897    }
898
899}
900