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 }