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