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(new Integer(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(new Integer(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(new Integer(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(new Integer(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(new Integer(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(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(new Integer(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 }