jdk/src/jdk.dev/share/classes/com/sun/tools/hat/internal/parser/HprofReader.java
changeset 30677 59b772a18fac
parent 30658 3b1ad397517c
parent 30676 0545deee4403
child 30678 a8b7fd8ede97
equal deleted inserted replaced
30658:3b1ad397517c 30677:59b772a18fac
     1 /*
       
     2  * Copyright (c) 1997, 2008, 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 
       
    33 package com.sun.tools.hat.internal.parser;
       
    34 
       
    35 import java.io.*;
       
    36 import java.util.Date;
       
    37 import java.util.Hashtable;
       
    38 import com.sun.tools.hat.internal.model.ArrayTypeCodes;
       
    39 import com.sun.tools.hat.internal.model.*;
       
    40 
       
    41 /**
       
    42  * Object that's used to read a hprof file.
       
    43  *
       
    44  * @author      Bill Foote
       
    45  */
       
    46 
       
    47 public 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         in.skipBytes((int)length);
       
   358     }
       
   359 
       
   360     private int readVersionHeader() throws IOException {
       
   361         int candidatesLeft = VERSIONS.length;
       
   362         boolean[] matched = new boolean[VERSIONS.length];
       
   363         for (int i = 0; i < candidatesLeft; i++) {
       
   364             matched[i] = true;
       
   365         }
       
   366 
       
   367         int pos = 0;
       
   368         while (candidatesLeft > 0) {
       
   369             char c = (char) in.readByte();
       
   370             currPos++;
       
   371             for (int i = 0; i < VERSIONS.length; i++) {
       
   372                 if (matched[i]) {
       
   373                     if (c != VERSIONS[i].charAt(pos)) {   // Not matched
       
   374                         matched[i] = false;
       
   375                         --candidatesLeft;
       
   376                     } else if (pos == VERSIONS[i].length() - 1) {  // Full match
       
   377                         return i;
       
   378                     }
       
   379                 }
       
   380             }
       
   381             ++pos;
       
   382         }
       
   383         throw new IOException("Version string not recognized at byte " + (pos+3));
       
   384     }
       
   385 
       
   386     private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException {
       
   387         while (bytesLeft > 0) {
       
   388             int type = in.readUnsignedByte();
       
   389             if (debugLevel > 0) {
       
   390                 System.out.println("    Read heap sub-record type " + type
       
   391                                    + " at position "
       
   392                                    + toHex(posAtEnd - bytesLeft));
       
   393             }
       
   394             bytesLeft--;
       
   395             switch(type) {
       
   396                 case HPROF_GC_ROOT_UNKNOWN: {
       
   397                     long id = readID();
       
   398                     bytesLeft -= identifierSize;
       
   399                     snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, ""));
       
   400                     break;
       
   401                 }
       
   402                 case HPROF_GC_ROOT_THREAD_OBJ: {
       
   403                     long id = readID();
       
   404                     int threadSeq = in.readInt();
       
   405                     int stackSeq = in.readInt();
       
   406                     bytesLeft -= identifierSize + 8;
       
   407                     threadObjects.put(threadSeq,
       
   408                                       new ThreadObject(id, stackSeq));
       
   409                     break;
       
   410                 }
       
   411                 case HPROF_GC_ROOT_JNI_GLOBAL: {
       
   412                     long id = readID();
       
   413                     long globalRefId = readID();        // Ignored, for now
       
   414                     bytesLeft -= 2*identifierSize;
       
   415                     snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, ""));
       
   416                     break;
       
   417                 }
       
   418                 case HPROF_GC_ROOT_JNI_LOCAL: {
       
   419                     long id = readID();
       
   420                     int threadSeq = in.readInt();
       
   421                     int depth = in.readInt();
       
   422                     bytesLeft -= identifierSize + 8;
       
   423                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
       
   424                     StackTrace st = getStackTraceFromSerial(to.stackSeq);
       
   425                     if (st != null) {
       
   426                         st = st.traceForDepth(depth+1);
       
   427                     }
       
   428                     snapshot.addRoot(new Root(id, to.threadId,
       
   429                                               Root.NATIVE_LOCAL, "", st));
       
   430                     break;
       
   431                 }
       
   432                 case HPROF_GC_ROOT_JAVA_FRAME: {
       
   433                     long id = readID();
       
   434                     int threadSeq = in.readInt();
       
   435                     int depth = in.readInt();
       
   436                     bytesLeft -= identifierSize + 8;
       
   437                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
       
   438                     StackTrace st = getStackTraceFromSerial(to.stackSeq);
       
   439                     if (st != null) {
       
   440                         st = st.traceForDepth(depth+1);
       
   441                     }
       
   442                     snapshot.addRoot(new Root(id, to.threadId,
       
   443                                               Root.JAVA_LOCAL, "", st));
       
   444                     break;
       
   445                 }
       
   446                 case HPROF_GC_ROOT_NATIVE_STACK: {
       
   447                     long id = readID();
       
   448                     int threadSeq = in.readInt();
       
   449                     bytesLeft -= identifierSize + 4;
       
   450                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
       
   451                     StackTrace st = getStackTraceFromSerial(to.stackSeq);
       
   452                     snapshot.addRoot(new Root(id, to.threadId,
       
   453                                               Root.NATIVE_STACK, "", st));
       
   454                     break;
       
   455                 }
       
   456                 case HPROF_GC_ROOT_STICKY_CLASS: {
       
   457                     long id = readID();
       
   458                     bytesLeft -= identifierSize;
       
   459                     snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, ""));
       
   460                     break;
       
   461                 }
       
   462                 case HPROF_GC_ROOT_THREAD_BLOCK: {
       
   463                     long id = readID();
       
   464                     int threadSeq = in.readInt();
       
   465                     bytesLeft -= identifierSize + 4;
       
   466                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
       
   467                     StackTrace st = getStackTraceFromSerial(to.stackSeq);
       
   468                     snapshot.addRoot(new Root(id, to.threadId,
       
   469                                      Root.THREAD_BLOCK, "", st));
       
   470                     break;
       
   471                 }
       
   472                 case HPROF_GC_ROOT_MONITOR_USED: {
       
   473                     long id = readID();
       
   474                     bytesLeft -= identifierSize;
       
   475                     snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, ""));
       
   476                     break;
       
   477                 }
       
   478                 case HPROF_GC_CLASS_DUMP: {
       
   479                     int bytesRead = readClass();
       
   480                     bytesLeft -= bytesRead;
       
   481                     break;
       
   482                 }
       
   483                 case HPROF_GC_INSTANCE_DUMP: {
       
   484                     int bytesRead = readInstance();
       
   485                     bytesLeft -= bytesRead;
       
   486                     break;
       
   487                 }
       
   488                 case HPROF_GC_OBJ_ARRAY_DUMP: {
       
   489                     int bytesRead = readArray(false);
       
   490                     bytesLeft -= bytesRead;
       
   491                     break;
       
   492                 }
       
   493                 case HPROF_GC_PRIM_ARRAY_DUMP: {
       
   494                     int bytesRead = readArray(true);
       
   495                     bytesLeft -= bytesRead;
       
   496                     break;
       
   497                 }
       
   498                 default: {
       
   499                     throw new IOException("Unrecognized heap dump sub-record type:  " + type);
       
   500                 }
       
   501             }
       
   502         }
       
   503         if (bytesLeft != 0) {
       
   504             warn("Error reading heap dump or heap dump segment:  Byte count is " + bytesLeft + " instead of 0");
       
   505             skipBytes(bytesLeft);
       
   506         }
       
   507         if (debugLevel > 0) {
       
   508             System.out.println("    Finished heap sub-records.");
       
   509         }
       
   510     }
       
   511 
       
   512     private long readID() throws IOException {
       
   513         return (identifierSize == 4)?
       
   514             (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong();
       
   515     }
       
   516 
       
   517     //
       
   518     // Read a java value.  If result is non-null, it's expected to be an
       
   519     // array of one element.  We use it to fake multiple return values.
       
   520     // @returns the number of bytes read
       
   521     //
       
   522     private int readValue(JavaThing[] resultArr) throws IOException {
       
   523         byte type = in.readByte();
       
   524         return 1 + readValueForType(type, resultArr);
       
   525     }
       
   526 
       
   527     private int readValueForType(byte type, JavaThing[] resultArr)
       
   528             throws IOException {
       
   529         if (version >= VERSION_JDK12BETA4) {
       
   530             type = signatureFromTypeId(type);
       
   531         }
       
   532         return readValueForTypeSignature(type, resultArr);
       
   533     }
       
   534 
       
   535     private int readValueForTypeSignature(byte type, JavaThing[] resultArr)
       
   536             throws IOException {
       
   537         switch (type) {
       
   538             case '[':
       
   539             case 'L': {
       
   540                 long id = readID();
       
   541                 if (resultArr != null) {
       
   542                     resultArr[0] = new JavaObjectRef(id);
       
   543                 }
       
   544                 return identifierSize;
       
   545             }
       
   546             case 'Z': {
       
   547                 int b = in.readByte();
       
   548                 if (b != 0 && b != 1) {
       
   549                     warn("Illegal boolean value read");
       
   550                 }
       
   551                 if (resultArr != null) {
       
   552                     resultArr[0] = new JavaBoolean(b != 0);
       
   553                 }
       
   554                 return 1;
       
   555             }
       
   556             case 'B': {
       
   557                 byte b = in.readByte();
       
   558                 if (resultArr != null) {
       
   559                     resultArr[0] = new JavaByte(b);
       
   560                 }
       
   561                 return 1;
       
   562             }
       
   563             case 'S': {
       
   564                 short s = in.readShort();
       
   565                 if (resultArr != null) {
       
   566                     resultArr[0] = new JavaShort(s);
       
   567                 }
       
   568                 return 2;
       
   569             }
       
   570             case 'C': {
       
   571                 char ch = in.readChar();
       
   572                 if (resultArr != null) {
       
   573                     resultArr[0] = new JavaChar(ch);
       
   574                 }
       
   575                 return 2;
       
   576             }
       
   577             case 'I': {
       
   578                 int val = in.readInt();
       
   579                 if (resultArr != null) {
       
   580                     resultArr[0] = new JavaInt(val);
       
   581                 }
       
   582                 return 4;
       
   583             }
       
   584             case 'J': {
       
   585                 long val = in.readLong();
       
   586                 if (resultArr != null) {
       
   587                     resultArr[0] = new JavaLong(val);
       
   588                 }
       
   589                 return 8;
       
   590             }
       
   591             case 'F': {
       
   592                 float val = in.readFloat();
       
   593                 if (resultArr != null) {
       
   594                     resultArr[0] = new JavaFloat(val);
       
   595                 }
       
   596                 return 4;
       
   597             }
       
   598             case 'D': {
       
   599                 double val = in.readDouble();
       
   600                 if (resultArr != null) {
       
   601                     resultArr[0] = new JavaDouble(val);
       
   602                 }
       
   603                 return 8;
       
   604             }
       
   605             default: {
       
   606                 throw new IOException("Bad value signature:  " + type);
       
   607             }
       
   608         }
       
   609     }
       
   610 
       
   611     private ThreadObject getThreadObjectFromSequence(int threadSeq)
       
   612             throws IOException {
       
   613         ThreadObject to = threadObjects.get(threadSeq);
       
   614         if (to == null) {
       
   615             throw new IOException("Thread " + threadSeq +
       
   616                                   " not found for JNI local ref");
       
   617         }
       
   618         return to;
       
   619     }
       
   620 
       
   621     private String getNameFromID(long id) throws IOException {
       
   622         return getNameFromID(Long.valueOf(id));
       
   623     }
       
   624 
       
   625     private String getNameFromID(Long id) throws IOException {
       
   626         if (id.longValue() == 0L) {
       
   627             return "";
       
   628         }
       
   629         String result = names.get(id);
       
   630         if (result == null) {
       
   631             warn("Name not found at " + toHex(id.longValue()));
       
   632             return "unresolved name " + toHex(id.longValue());
       
   633         }
       
   634         return result;
       
   635     }
       
   636 
       
   637     private StackTrace getStackTraceFromSerial(int ser) throws IOException {
       
   638         if (stackTraces == null) {
       
   639             return null;
       
   640         }
       
   641         StackTrace result = stackTraces.get(ser);
       
   642         if (result == null) {
       
   643             warn("Stack trace not found for serial # " + ser);
       
   644         }
       
   645         return result;
       
   646     }
       
   647 
       
   648     //
       
   649     // Handle a HPROF_GC_CLASS_DUMP
       
   650     // Return number of bytes read
       
   651     //
       
   652     private int readClass() throws IOException {
       
   653         long id = readID();
       
   654         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
       
   655         long superId = readID();
       
   656         long classLoaderId = readID();
       
   657         long signersId = readID();
       
   658         long protDomainId = readID();
       
   659         long reserved1 = readID();
       
   660         long reserved2 = readID();
       
   661         int instanceSize = in.readInt();
       
   662         int bytesRead = 7 * identifierSize + 8;
       
   663 
       
   664         int numConstPoolEntries = in.readUnsignedShort();
       
   665         bytesRead += 2;
       
   666         for (int i = 0; i < numConstPoolEntries; i++) {
       
   667             int index = in.readUnsignedShort(); // unused
       
   668             bytesRead += 2;
       
   669             bytesRead += readValue(null);       // We ignore the values
       
   670         }
       
   671 
       
   672         int numStatics = in.readUnsignedShort();
       
   673         bytesRead += 2;
       
   674         JavaThing[] valueBin = new JavaThing[1];
       
   675         JavaStatic[] statics = new JavaStatic[numStatics];
       
   676         for (int i = 0; i < numStatics; i++) {
       
   677             long nameId = readID();
       
   678             bytesRead += identifierSize;
       
   679             byte type = in.readByte();
       
   680             bytesRead++;
       
   681             bytesRead += readValueForType(type, valueBin);
       
   682             String fieldName = getNameFromID(nameId);
       
   683             if (version >= VERSION_JDK12BETA4) {
       
   684                 type = signatureFromTypeId(type);
       
   685             }
       
   686             String signature = "" + ((char) type);
       
   687             JavaField f = new JavaField(fieldName, signature);
       
   688             statics[i] = new JavaStatic(f, valueBin[0]);
       
   689         }
       
   690 
       
   691         int numFields = in.readUnsignedShort();
       
   692         bytesRead += 2;
       
   693         JavaField[] fields = new JavaField[numFields];
       
   694         for (int i = 0; i < numFields; i++) {
       
   695             long nameId = readID();
       
   696             bytesRead += identifierSize;
       
   697             byte type = in.readByte();
       
   698             bytesRead++;
       
   699             String fieldName = getNameFromID(nameId);
       
   700             if (version >= VERSION_JDK12BETA4) {
       
   701                 type = signatureFromTypeId(type);
       
   702             }
       
   703             String signature = "" + ((char) type);
       
   704             fields[i] = new JavaField(fieldName, signature);
       
   705         }
       
   706         String name = classNameFromObjectID.get(id);
       
   707         if (name == null) {
       
   708             warn("Class name not found for " + toHex(id));
       
   709             name = "unknown-name@" + toHex(id);
       
   710         }
       
   711         JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId,
       
   712                                     protDomainId, fields, statics,
       
   713                                     instanceSize);
       
   714         snapshot.addClass(id, c);
       
   715         snapshot.setSiteTrace(c, stackTrace);
       
   716 
       
   717         return bytesRead;
       
   718     }
       
   719 
       
   720     private String toHex(long addr) {
       
   721         return com.sun.tools.hat.internal.util.Misc.toHex(addr);
       
   722     }
       
   723 
       
   724     //
       
   725     // Handle a HPROF_GC_INSTANCE_DUMP
       
   726     // Return number of bytes read
       
   727     //
       
   728     private int readInstance() throws IOException {
       
   729         long start = in.position();
       
   730         long id = readID();
       
   731         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
       
   732         long classID = readID();
       
   733         int bytesFollowing = in.readInt();
       
   734         int bytesRead = (2 * identifierSize) + 8 + bytesFollowing;
       
   735         JavaObject jobj = new JavaObject(classID, start);
       
   736         skipBytes(bytesFollowing);
       
   737         snapshot.addHeapObject(id, jobj);
       
   738         snapshot.setSiteTrace(jobj, stackTrace);
       
   739         return bytesRead;
       
   740     }
       
   741 
       
   742     //
       
   743     // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP
       
   744     // Return number of bytes read
       
   745     //
       
   746     private int readArray(boolean isPrimitive) throws IOException {
       
   747         long start = in.position();
       
   748         long id = readID();
       
   749         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
       
   750         int num = in.readInt();
       
   751         int bytesRead = identifierSize + 8;
       
   752         long elementClassID;
       
   753         if (isPrimitive) {
       
   754             elementClassID = in.readByte();
       
   755             bytesRead++;
       
   756         } else {
       
   757             elementClassID = readID();
       
   758             bytesRead += identifierSize;
       
   759         }
       
   760 
       
   761         // Check for primitive arrays:
       
   762         byte primitiveSignature = 0x00;
       
   763         int elSize = 0;
       
   764         if (isPrimitive || version < VERSION_JDK12BETA4) {
       
   765             switch ((int)elementClassID) {
       
   766                 case T_BOOLEAN: {
       
   767                     primitiveSignature = (byte) 'Z';
       
   768                     elSize = 1;
       
   769                     break;
       
   770                 }
       
   771                 case T_CHAR: {
       
   772                     primitiveSignature = (byte) 'C';
       
   773                     elSize = 2;
       
   774                     break;
       
   775                 }
       
   776                 case T_FLOAT: {
       
   777                     primitiveSignature = (byte) 'F';
       
   778                     elSize = 4;
       
   779                     break;
       
   780                 }
       
   781                 case T_DOUBLE: {
       
   782                     primitiveSignature = (byte) 'D';
       
   783                     elSize = 8;
       
   784                     break;
       
   785                 }
       
   786                 case T_BYTE: {
       
   787                     primitiveSignature = (byte) 'B';
       
   788                     elSize = 1;
       
   789                     break;
       
   790                 }
       
   791                 case T_SHORT: {
       
   792                     primitiveSignature = (byte) 'S';
       
   793                     elSize = 2;
       
   794                     break;
       
   795                 }
       
   796                 case T_INT: {
       
   797                     primitiveSignature = (byte) 'I';
       
   798                     elSize = 4;
       
   799                     break;
       
   800                 }
       
   801                 case T_LONG: {
       
   802                     primitiveSignature = (byte) 'J';
       
   803                     elSize = 8;
       
   804                     break;
       
   805                 }
       
   806             }
       
   807             if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) {
       
   808                 throw new IOException("Unrecognized typecode:  "
       
   809                                         + elementClassID);
       
   810             }
       
   811         }
       
   812         if (primitiveSignature != 0x00) {
       
   813             int size = elSize * num;
       
   814             bytesRead += size;
       
   815             JavaValueArray va = new JavaValueArray(primitiveSignature, start);
       
   816             skipBytes(size);
       
   817             snapshot.addHeapObject(id, va);
       
   818             snapshot.setSiteTrace(va, stackTrace);
       
   819         } else {
       
   820             int sz = num * identifierSize;
       
   821             bytesRead += sz;
       
   822             JavaObjectArray arr = new JavaObjectArray(elementClassID, start);
       
   823             skipBytes(sz);
       
   824             snapshot.addHeapObject(id, arr);
       
   825             snapshot.setSiteTrace(arr, stackTrace);
       
   826         }
       
   827         return bytesRead;
       
   828     }
       
   829 
       
   830     private byte signatureFromTypeId(byte typeId) throws IOException {
       
   831         switch (typeId) {
       
   832             case T_CLASS: {
       
   833                 return (byte) 'L';
       
   834             }
       
   835             case T_BOOLEAN: {
       
   836                 return (byte) 'Z';
       
   837             }
       
   838             case T_CHAR: {
       
   839                 return (byte) 'C';
       
   840             }
       
   841             case T_FLOAT: {
       
   842                 return (byte) 'F';
       
   843             }
       
   844             case T_DOUBLE: {
       
   845                 return (byte) 'D';
       
   846             }
       
   847             case T_BYTE: {
       
   848                 return (byte) 'B';
       
   849             }
       
   850             case T_SHORT: {
       
   851                 return (byte) 'S';
       
   852             }
       
   853             case T_INT: {
       
   854                 return (byte) 'I';
       
   855             }
       
   856             case T_LONG: {
       
   857                 return (byte) 'J';
       
   858             }
       
   859             default: {
       
   860                 throw new IOException("Invalid type id of " + typeId);
       
   861             }
       
   862         }
       
   863     }
       
   864 
       
   865     private void handleEOF(EOFException exp, Snapshot snapshot) {
       
   866         if (debugLevel > 0) {
       
   867             exp.printStackTrace();
       
   868         }
       
   869         warn("Unexpected EOF. Will miss information...");
       
   870         // we have EOF, we have to tolerate missing references
       
   871         snapshot.setUnresolvedObjectsOK(true);
       
   872     }
       
   873 
       
   874     private void warn(String msg) {
       
   875         System.out.println("WARNING: " + msg);
       
   876     }
       
   877 
       
   878     //
       
   879     // A trivial data-holder class for HPROF_GC_ROOT_THREAD_OBJ.
       
   880     //
       
   881     private class ThreadObject {
       
   882 
       
   883         long threadId;
       
   884         int stackSeq;
       
   885 
       
   886         ThreadObject(long threadId, int stackSeq) {
       
   887             this.threadId = threadId;
       
   888             this.stackSeq = stackSeq;
       
   889         }
       
   890     }
       
   891 
       
   892 }