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