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