1 /* 2 * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 /* 28 * The Original Code is HAT. The Initial Developer of the 29 * Original Code is Bill Foote, with contributions from others 30 * at JavaSoft/Sun. 31 */ 32 33 package jdk.test.lib.hprof.parser; 34 35 import java.io.*; 36 import java.util.Date; 37 import java.util.Hashtable; 38 import java.util.Map; 39 import jdk.test.lib.hprof.model.ArrayTypeCodes; 40 import jdk.test.lib.hprof.model.*; 41 42 /** 43 * Object that's used to read a hprof file. 44 * 45 * @author Bill Foote 46 */ 47 48 public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes { 49 50 final static int MAGIC_NUMBER = 0x4a415641; 51 // That's "JAVA", the first part of "JAVA PROFILE ..." 52 private final static String[] VERSIONS = { 53 " PROFILE 1.0\0", 54 " PROFILE 1.0.1\0", 55 " PROFILE 1.0.2\0", 56 }; 57 58 private final static int VERSION_JDK12BETA3 = 0; 59 private final static int VERSION_JDK12BETA4 = 1; 60 private final static int VERSION_JDK6 = 2; 61 // These version numbers are indices into VERSIONS. The instance data 62 // member version is set to one of these, and it drives decisions when 63 // reading the file. 64 // 65 // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no 66 // version-sensitive parsing. 67 // 68 // Version 1.0.1 changed the type of a constant pool entry from a signature 69 // to a typecode. 70 // 71 // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END 72 // to allow a large heap to be dumped as a sequence of heap dump segments. 73 // 74 // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1 75 // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on 76 // the size of the heap (normally it will be 1.0.1 but for multi-GB 77 // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the 78 // dump is generated as version 1.0.2). 79 80 // 81 // Record types: 82 // 83 static final int HPROF_UTF8 = 0x01; 84 static final int HPROF_LOAD_CLASS = 0x02; 85 static final int HPROF_UNLOAD_CLASS = 0x03; 86 static final int HPROF_FRAME = 0x04; 87 static final int HPROF_TRACE = 0x05; 88 static final int HPROF_ALLOC_SITES = 0x06; 89 static final int HPROF_HEAP_SUMMARY = 0x07; 90 91 static final int HPROF_START_THREAD = 0x0a; 92 static final int HPROF_END_THREAD = 0x0b; 93 94 static final int HPROF_HEAP_DUMP = 0x0c; 95 96 static final int HPROF_CPU_SAMPLES = 0x0d; 97 static final int HPROF_CONTROL_SETTINGS = 0x0e; 98 static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10; 99 static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11; 100 101 static final int HPROF_GC_ROOT_UNKNOWN = 0xff; 102 static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; 103 static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; 104 static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; 105 static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; 106 static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; 107 static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; 108 static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; 109 static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; 110 111 static final int HPROF_GC_CLASS_DUMP = 0x20; 112 static final int HPROF_GC_INSTANCE_DUMP = 0x21; 113 static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; 114 static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; 115 116 static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c; 117 static final int HPROF_HEAP_DUMP_END = 0x2c; 118 119 private final static int T_CLASS = 2; 120 121 private int version; // The version of .hprof being read 122 123 private int debugLevel; 124 private long currPos; // Current position in the file 125 126 private int dumpsToSkip; 127 private boolean callStack; // If true, read the call stack of objects 128 129 private int identifierSize; // Size, in bytes, of identifiers. 130 private Hashtable<Long, String> names; 131 132 // Hashtable<Integer, ThreadObject>, used to map the thread sequence number 133 // (aka "serial number") to the thread object ID for 134 // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class, 135 // at the end of this file. 136 private Hashtable<Integer, ThreadObject> threadObjects; 137 138 // Hashtable<Long, String>, maps class object ID to class name 139 // (with / converted to .) 140 private Hashtable<Long, String> classNameFromObjectID; 141 142 // Hashtable<Integer, Integer>, maps class serial # to class object ID 143 private Hashtable<Integer, String> classNameFromSerialNo; 144 145 // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame. 146 // Null if we're not tracking them. 147 private Hashtable<Long, StackFrame> stackFrames; 148 149 // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace 150 // Null if we're not tracking them. 151 private Hashtable<Integer, StackTrace> stackTraces; 152 153 private Snapshot snapshot; 154 155 public static boolean verifyMagicNumber(int numberRead) { 156 return (numberRead == MAGIC_NUMBER); 157 } 158 159 public HprofReader(ReadBuffer readBuffer, PositionDataInputStream in, 160 int dumpNumber, boolean callStack, int debugLevel) 161 throws IOException { 162 super(in); 163 this.snapshot = new Snapshot(readBuffer); 164 this.dumpsToSkip = dumpNumber - 1; 165 this.callStack = callStack; 166 this.debugLevel = debugLevel; 167 names = new Hashtable<Long, String>(); 168 threadObjects = new Hashtable<Integer, ThreadObject>(43); 169 classNameFromObjectID = new Hashtable<Long, String>(); 170 if (callStack) { 171 stackFrames = new Hashtable<Long, StackFrame>(43); 172 stackTraces = new Hashtable<Integer, StackTrace>(43); 173 classNameFromSerialNo = new Hashtable<Integer, String>(); 174 } 175 } 176 177 public HprofReader(String fileName, PositionDataInputStream in, 178 int dumpNumber, boolean callStack, int debugLevel) 179 throws IOException { 180 this(MappedReadBuffer.create(new RandomAccessFile(fileName, "r")), 181 in, dumpNumber, callStack, debugLevel); 182 } 183 184 public Snapshot read() throws IOException { 185 currPos = 4; // 4 because of the magic number 186 version = readVersionHeader(); 187 identifierSize = in.readInt(); 188 snapshot.setIdentifierSize(identifierSize); 189 if (version >= VERSION_JDK12BETA4) { 190 snapshot.setNewStyleArrayClass(true); 191 } else { 192 snapshot.setNewStyleArrayClass(false); 193 } 194 195 currPos += 4; 196 if (identifierSize != 4 && identifierSize != 8) { 197 throw new IOException("I'm sorry, but I can't deal with an identifier size of " + identifierSize + ". I can only deal with 4 or 8."); 198 } 199 System.out.println("Dump file created " + (new Date(in.readLong()))); 200 currPos += 8; 201 202 for (;;) { 203 int type; 204 try { 205 type = in.readUnsignedByte(); 206 } catch (EOFException ignored) { 207 break; 208 } 209 in.readInt(); // Timestamp of this record 210 // Length of record: readInt() will return negative value for record 211 // length >2GB. so store 32bit value in long to keep it unsigned. 212 long length = in.readInt() & 0xffffffffL; 213 if (debugLevel > 0) { 214 System.out.println("Read record type " + type 215 + ", length " + length 216 + " at position " + toHex(currPos)); 217 } 218 if (length < 0) { 219 throw new IOException("Bad record length of " + length 220 + " at byte " + toHex(currPos+5) 221 + " of file."); 222 } 223 currPos += 9 + length; 224 switch (type) { 225 case HPROF_UTF8: { 226 long id = readID(); 227 byte[] chars = new byte[(int)length - identifierSize]; 228 in.readFully(chars); 229 names.put(id, new String(chars)); 230 break; 231 } 232 case HPROF_LOAD_CLASS: { 233 int serialNo = in.readInt(); // Not used 234 long classID = readID(); 235 int stackTraceSerialNo = in.readInt(); 236 long classNameID = readID(); 237 Long classIdI = classID; 238 String nm = getNameFromID(classNameID).replace('/', '.'); 239 classNameFromObjectID.put(classIdI, nm); 240 if (classNameFromSerialNo != null) { 241 classNameFromSerialNo.put(serialNo, nm); 242 } 243 break; 244 } 245 246 case HPROF_HEAP_DUMP: { 247 if (dumpsToSkip <= 0) { 248 try { 249 readHeapDump(length, currPos); 250 } catch (EOFException exp) { 251 handleEOF(exp, snapshot); 252 } 253 if (debugLevel > 0) { 254 System.out.println(" Finished processing instances in heap dump."); 255 } 256 return snapshot; 257 } else { 258 dumpsToSkip--; 259 skipBytes(length); 260 } 261 break; 262 } 263 264 case HPROF_HEAP_DUMP_END: { 265 if (version >= VERSION_JDK6) { 266 if (dumpsToSkip <= 0) { 267 skipBytes(length); // should be no-op 268 return snapshot; 269 } else { 270 // skip this dump (of the end record for a sequence of dump segments) 271 dumpsToSkip--; 272 } 273 } else { 274 // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2 275 warn("Ignoring unrecognized record type " + type); 276 } 277 skipBytes(length); // should be no-op 278 break; 279 } 280 281 case HPROF_HEAP_DUMP_SEGMENT: { 282 if (version >= VERSION_JDK6) { 283 if (dumpsToSkip <= 0) { 284 try { 285 // read the dump segment 286 readHeapDump(length, currPos); 287 } catch (EOFException exp) { 288 handleEOF(exp, snapshot); 289 } 290 } else { 291 // all segments comprising the heap dump will be skipped 292 skipBytes(length); 293 } 294 } else { 295 // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2 296 warn("Ignoring unrecognized record type " + type); 297 skipBytes(length); 298 } 299 break; 300 } 301 302 case HPROF_FRAME: { 303 if (stackFrames == null) { 304 skipBytes(length); 305 } else { 306 long id = readID(); 307 String methodName = getNameFromID(readID()); 308 String methodSig = getNameFromID(readID()); 309 String sourceFile = getNameFromID(readID()); 310 int classSer = in.readInt(); 311 String className = classNameFromSerialNo.get(classSer); 312 int lineNumber = in.readInt(); 313 if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) { 314 warn("Weird stack frame line number: " + lineNumber); 315 lineNumber = StackFrame.LINE_NUMBER_UNKNOWN; 316 } 317 stackFrames.put(id, 318 new StackFrame(methodName, methodSig, 319 className, sourceFile, 320 lineNumber)); 321 } 322 break; 323 } 324 case HPROF_TRACE: { 325 if (stackTraces == null) { 326 skipBytes(length); 327 } else { 328 int serialNo = in.readInt(); 329 int threadSeq = in.readInt(); // Not used 330 StackFrame[] frames = new StackFrame[in.readInt()]; 331 for (int i = 0; i < frames.length; i++) { 332 long fid = readID(); 333 frames[i] = stackFrames.get(fid); 334 if (frames[i] == null) { 335 throw new IOException("Stack frame " + toHex(fid) + " not found"); 336 } 337 } 338 stackTraces.put(serialNo, 339 new StackTrace(frames)); 340 } 341 break; 342 } 343 case HPROF_UNLOAD_CLASS: 344 case HPROF_ALLOC_SITES: 345 case HPROF_START_THREAD: 346 case HPROF_END_THREAD: 347 case HPROF_HEAP_SUMMARY: 348 case HPROF_CPU_SAMPLES: 349 case HPROF_CONTROL_SETTINGS: 350 case HPROF_LOCKSTATS_WAIT_TIME: 351 case HPROF_LOCKSTATS_HOLD_TIME: 352 { 353 // Ignore these record types 354 skipBytes(length); 355 break; 356 } 357 default: { 358 skipBytes(length); 359 warn("Ignoring unrecognized record type " + type); 360 } 361 } 362 } 363 364 return snapshot; 365 } 366 367 public String printStackTraces() { 368 StringBuffer output = new StringBuffer(); 369 for (Map.Entry<Integer, StackTrace> entry : stackTraces.entrySet()) { 370 StackFrame[] frames = entry.getValue().getFrames(); 371 output.append("SerialNo " + entry.getKey() + "\n"); 372 for (int i = 0; i < frames.length; i++) { 373 output.append(" " + frames[i].getClassName() + "." + frames[i].getMethodName() 374 + frames[i].getMethodSignature() + " (" + frames[i].getSourceFileName() 375 + ":" + frames[i].getLineNumber() + ")" + "\n"); 376 } 377 } 378 379 System.out.println(output); 380 return output.toString(); 381 } 382 383 private void skipBytes(long length) throws IOException { 384 while (length > 0) { 385 long skipped = in.skip(length); 386 if (skipped == 0) { 387 // EOF or other problem, throw exception 388 throw new EOFException("Couldn't skip enough bytes"); 389 } 390 length -= skipped; 391 } 392 } 393 394 private int readVersionHeader() throws IOException { 395 int candidatesLeft = VERSIONS.length; 396 boolean[] matched = new boolean[VERSIONS.length]; 397 for (int i = 0; i < candidatesLeft; i++) { 398 matched[i] = true; 399 } 400 401 int pos = 0; 402 while (candidatesLeft > 0) { 403 char c = (char) in.readByte(); 404 currPos++; 405 for (int i = 0; i < VERSIONS.length; i++) { 406 if (matched[i]) { 407 if (c != VERSIONS[i].charAt(pos)) { // Not matched 408 matched[i] = false; 409 --candidatesLeft; 410 } else if (pos == VERSIONS[i].length() - 1) { // Full match 411 return i; 412 } 413 } 414 } 415 ++pos; 416 } 417 throw new IOException("Version string not recognized at byte " + (pos+3)); 418 } 419 420 private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { 421 while (bytesLeft > 0) { 422 int type = in.readUnsignedByte(); 423 if (debugLevel > 0) { 424 System.out.println(" Read heap sub-record type " + type 425 + " at position " 426 + toHex(posAtEnd - bytesLeft)); 427 } 428 bytesLeft--; 429 switch(type) { 430 case HPROF_GC_ROOT_UNKNOWN: { 431 long id = readID(); 432 bytesLeft -= identifierSize; 433 snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, "")); 434 break; 435 } 436 case HPROF_GC_ROOT_THREAD_OBJ: { 437 long id = readID(); 438 int threadSeq = in.readInt(); 439 int stackSeq = in.readInt(); 440 bytesLeft -= identifierSize + 8; 441 threadObjects.put(threadSeq, 442 new ThreadObject(id, stackSeq)); 443 break; 444 } 445 case HPROF_GC_ROOT_JNI_GLOBAL: { 446 long id = readID(); 447 long globalRefId = readID(); // Ignored, for now 448 bytesLeft -= 2*identifierSize; 449 snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, "")); 450 break; 451 } 452 case HPROF_GC_ROOT_JNI_LOCAL: { 453 long id = readID(); 454 int threadSeq = in.readInt(); 455 int depth = in.readInt(); 456 bytesLeft -= identifierSize + 8; 457 ThreadObject to = getThreadObjectFromSequence(threadSeq); 458 StackTrace st = getStackTraceFromSerial(to.stackSeq); 459 if (st != null) { 460 st = st.traceForDepth(depth+1); 461 } 462 snapshot.addRoot(new Root(id, to.threadId, 463 Root.NATIVE_LOCAL, "", st)); 464 break; 465 } 466 case HPROF_GC_ROOT_JAVA_FRAME: { 467 long id = readID(); 468 int threadSeq = in.readInt(); 469 int depth = in.readInt(); 470 bytesLeft -= identifierSize + 8; 471 ThreadObject to = getThreadObjectFromSequence(threadSeq); 472 StackTrace st = getStackTraceFromSerial(to.stackSeq); 473 if (st != null) { 474 st = st.traceForDepth(depth+1); 475 } 476 snapshot.addRoot(new Root(id, to.threadId, 477 Root.JAVA_LOCAL, "", st)); 478 break; 479 } 480 case HPROF_GC_ROOT_NATIVE_STACK: { 481 long id = readID(); 482 int threadSeq = in.readInt(); 483 bytesLeft -= identifierSize + 4; 484 ThreadObject to = getThreadObjectFromSequence(threadSeq); 485 StackTrace st = getStackTraceFromSerial(to.stackSeq); 486 snapshot.addRoot(new Root(id, to.threadId, 487 Root.NATIVE_STACK, "", st)); 488 break; 489 } 490 case HPROF_GC_ROOT_STICKY_CLASS: { 491 long id = readID(); 492 bytesLeft -= identifierSize; 493 snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, "")); 494 break; 495 } 496 case HPROF_GC_ROOT_THREAD_BLOCK: { 497 long id = readID(); 498 int threadSeq = in.readInt(); 499 bytesLeft -= identifierSize + 4; 500 ThreadObject to = getThreadObjectFromSequence(threadSeq); 501 StackTrace st = getStackTraceFromSerial(to.stackSeq); 502 snapshot.addRoot(new Root(id, to.threadId, 503 Root.THREAD_BLOCK, "", st)); 504 break; 505 } 506 case HPROF_GC_ROOT_MONITOR_USED: { 507 long id = readID(); 508 bytesLeft -= identifierSize; 509 snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, "")); 510 break; 511 } 512 case HPROF_GC_CLASS_DUMP: { 513 int bytesRead = readClass(); 514 bytesLeft -= bytesRead; 515 break; 516 } 517 case HPROF_GC_INSTANCE_DUMP: { 518 int bytesRead = readInstance(); 519 bytesLeft -= bytesRead; 520 break; 521 } 522 case HPROF_GC_OBJ_ARRAY_DUMP: { 523 long bytesRead = readArray(false); 524 bytesLeft -= bytesRead; 525 break; 526 } 527 case HPROF_GC_PRIM_ARRAY_DUMP: { 528 long bytesRead = readArray(true); 529 bytesLeft -= bytesRead; 530 break; 531 } 532 default: { 533 throw new IOException("Unrecognized heap dump sub-record type: " + type); 534 } 535 } 536 } 537 if (bytesLeft != 0) { 538 warn("Error reading heap dump or heap dump segment: Byte count is " + bytesLeft + " instead of 0"); 539 skipBytes(bytesLeft); 540 } 541 if (debugLevel > 0) { 542 System.out.println(" Finished heap sub-records."); 543 } 544 } 545 546 private long readID() throws IOException { 547 return (identifierSize == 4)? 548 (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong(); 549 } 550 551 // 552 // Read a java value. If result is non-null, it's expected to be an 553 // array of one element. We use it to fake multiple return values. 554 // @returns the number of bytes read 555 // 556 private int readValue(JavaThing[] resultArr) throws IOException { 557 byte type = in.readByte(); 558 return 1 + readValueForType(type, resultArr); 559 } 560 561 private int readValueForType(byte type, JavaThing[] resultArr) 562 throws IOException { 563 if (version >= VERSION_JDK12BETA4) { 564 type = signatureFromTypeId(type); 565 } 566 return readValueForTypeSignature(type, resultArr); 567 } 568 569 private int readValueForTypeSignature(byte type, JavaThing[] resultArr) 570 throws IOException { 571 switch (type) { 572 case '[': 573 case 'L': { 574 long id = readID(); 575 if (resultArr != null) { 576 resultArr[0] = new JavaObjectRef(id); 577 } 578 return identifierSize; 579 } 580 case 'Z': { 581 int b = in.readByte(); 582 if (b != 0 && b != 1) { 583 warn("Illegal boolean value read"); 584 } 585 if (resultArr != null) { 586 resultArr[0] = new JavaBoolean(b != 0); 587 } 588 return 1; 589 } 590 case 'B': { 591 byte b = in.readByte(); 592 if (resultArr != null) { 593 resultArr[0] = new JavaByte(b); 594 } 595 return 1; 596 } 597 case 'S': { 598 short s = in.readShort(); 599 if (resultArr != null) { 600 resultArr[0] = new JavaShort(s); 601 } 602 return 2; 603 } 604 case 'C': { 605 char ch = in.readChar(); 606 if (resultArr != null) { 607 resultArr[0] = new JavaChar(ch); 608 } 609 return 2; 610 } 611 case 'I': { 612 int val = in.readInt(); 613 if (resultArr != null) { 614 resultArr[0] = new JavaInt(val); 615 } 616 return 4; 617 } 618 case 'J': { 619 long val = in.readLong(); 620 if (resultArr != null) { 621 resultArr[0] = new JavaLong(val); 622 } 623 return 8; 624 } 625 case 'F': { 626 float val = in.readFloat(); 627 if (resultArr != null) { 628 resultArr[0] = new JavaFloat(val); 629 } 630 return 4; 631 } 632 case 'D': { 633 double val = in.readDouble(); 634 if (resultArr != null) { 635 resultArr[0] = new JavaDouble(val); 636 } 637 return 8; 638 } 639 default: { 640 throw new IOException("Bad value signature: " + type); 641 } 642 } 643 } 644 645 private ThreadObject getThreadObjectFromSequence(int threadSeq) 646 throws IOException { 647 ThreadObject to = threadObjects.get(threadSeq); 648 if (to == null) { 649 throw new IOException("Thread " + threadSeq + 650 " not found for JNI local ref"); 651 } 652 return to; 653 } 654 655 private String getNameFromID(long id) throws IOException { 656 return getNameFromID(Long.valueOf(id)); 657 } 658 659 private String getNameFromID(Long id) throws IOException { 660 if (id.longValue() == 0L) { 661 return ""; 662 } 663 String result = names.get(id); 664 if (result == null) { 665 warn("Name not found at " + toHex(id.longValue())); 666 return "unresolved name " + toHex(id.longValue()); 667 } 668 return result; 669 } 670 671 private StackTrace getStackTraceFromSerial(int ser) throws IOException { 672 if (stackTraces == null) { 673 return null; 674 } 675 StackTrace result = stackTraces.get(ser); 676 if (result == null) { 677 warn("Stack trace not found for serial # " + ser); 678 } 679 return result; 680 } 681 682 // 683 // Handle a HPROF_GC_CLASS_DUMP 684 // Return number of bytes read 685 // 686 private int readClass() throws IOException { 687 long id = readID(); 688 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 689 long superId = readID(); 690 long classLoaderId = readID(); 691 long signersId = readID(); 692 long protDomainId = readID(); 693 long reserved1 = readID(); 694 long reserved2 = readID(); 695 int instanceSize = in.readInt(); 696 int bytesRead = 7 * identifierSize + 8; 697 698 int numConstPoolEntries = in.readUnsignedShort(); 699 bytesRead += 2; 700 for (int i = 0; i < numConstPoolEntries; i++) { 701 int index = in.readUnsignedShort(); // unused 702 bytesRead += 2; 703 bytesRead += readValue(null); // We ignore the values 704 } 705 706 int numStatics = in.readUnsignedShort(); 707 bytesRead += 2; 708 JavaThing[] valueBin = new JavaThing[1]; 709 JavaStatic[] statics = new JavaStatic[numStatics]; 710 for (int i = 0; i < numStatics; i++) { 711 long nameId = readID(); 712 bytesRead += identifierSize; 713 byte type = in.readByte(); 714 bytesRead++; 715 bytesRead += readValueForType(type, valueBin); 716 String fieldName = getNameFromID(nameId); 717 if (version >= VERSION_JDK12BETA4) { 718 type = signatureFromTypeId(type); 719 } 720 String signature = "" + ((char) type); 721 JavaField f = new JavaField(fieldName, signature); 722 statics[i] = new JavaStatic(f, valueBin[0]); 723 } 724 725 int numFields = in.readUnsignedShort(); 726 bytesRead += 2; 727 JavaField[] fields = new JavaField[numFields]; 728 for (int i = 0; i < numFields; i++) { 729 long nameId = readID(); 730 bytesRead += identifierSize; 731 byte type = in.readByte(); 732 bytesRead++; 733 String fieldName = getNameFromID(nameId); 734 if (version >= VERSION_JDK12BETA4) { 735 type = signatureFromTypeId(type); 736 } 737 String signature = "" + ((char) type); 738 fields[i] = new JavaField(fieldName, signature); 739 } 740 String name = classNameFromObjectID.get(id); 741 if (name == null) { 742 warn("Class name not found for " + toHex(id)); 743 name = "unknown-name@" + toHex(id); 744 } 745 JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId, 746 protDomainId, fields, statics, 747 instanceSize); 748 snapshot.addClass(id, c); 749 snapshot.setSiteTrace(c, stackTrace); 750 751 return bytesRead; 752 } 753 754 private String toHex(long addr) { 755 return jdk.test.lib.hprof.util.Misc.toHex(addr); 756 } 757 758 // 759 // Handle a HPROF_GC_INSTANCE_DUMP 760 // Return number of bytes read 761 // 762 private int readInstance() throws IOException { 763 long start = in.position(); 764 long id = readID(); 765 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 766 long classID = readID(); 767 JavaClass searchedClass = snapshot.findClass( 768 "0x" + Long.toHexString(classID)); 769 if (searchedClass == null) { 770 throw new IOException( 771 "Class Record for 0x" + Long.toHexString(classID) + " not found"); 772 } 773 int bytesFollowing = in.readInt(); 774 int bytesRead = (2 * identifierSize) + 8 + bytesFollowing; 775 JavaObject jobj = new JavaObject(classID, start); 776 skipBytes(bytesFollowing); 777 snapshot.addHeapObject(id, jobj); 778 snapshot.setSiteTrace(jobj, stackTrace); 779 return bytesRead; 780 } 781 782 // 783 // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP 784 // Return number of bytes read 785 // 786 private long readArray(boolean isPrimitive) throws IOException { 787 long start = in.position(); 788 long id = readID(); 789 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 790 int num = in.readInt(); 791 long bytesRead = identifierSize + 8; 792 long elementClassID; 793 if (isPrimitive) { 794 elementClassID = in.readByte(); 795 bytesRead++; 796 } else { 797 elementClassID = readID(); 798 bytesRead += identifierSize; 799 } 800 801 // Check for primitive arrays: 802 byte primitiveSignature = 0x00; 803 int elSize = 0; 804 if (isPrimitive || version < VERSION_JDK12BETA4) { 805 switch ((int)elementClassID) { 806 case T_BOOLEAN: { 807 primitiveSignature = (byte) 'Z'; 808 elSize = 1; 809 break; 810 } 811 case T_CHAR: { 812 primitiveSignature = (byte) 'C'; 813 elSize = 2; 814 break; 815 } 816 case T_FLOAT: { 817 primitiveSignature = (byte) 'F'; 818 elSize = 4; 819 break; 820 } 821 case T_DOUBLE: { 822 primitiveSignature = (byte) 'D'; 823 elSize = 8; 824 break; 825 } 826 case T_BYTE: { 827 primitiveSignature = (byte) 'B'; 828 elSize = 1; 829 break; 830 } 831 case T_SHORT: { 832 primitiveSignature = (byte) 'S'; 833 elSize = 2; 834 break; 835 } 836 case T_INT: { 837 primitiveSignature = (byte) 'I'; 838 elSize = 4; 839 break; 840 } 841 case T_LONG: { 842 primitiveSignature = (byte) 'J'; 843 elSize = 8; 844 break; 845 } 846 } 847 if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) { 848 throw new IOException("Unrecognized typecode: " 849 + elementClassID); 850 } 851 } 852 if (primitiveSignature != 0x00) { 853 long size = elSize * (long)num; 854 bytesRead += size; 855 JavaValueArray va = new JavaValueArray(primitiveSignature, start); 856 skipBytes(size); 857 snapshot.addHeapObject(id, va); 858 snapshot.setSiteTrace(va, stackTrace); 859 } else { 860 long sz = (long)num * identifierSize; 861 bytesRead += sz; 862 JavaObjectArray arr = new JavaObjectArray(elementClassID, start); 863 skipBytes(sz); 864 snapshot.addHeapObject(id, arr); 865 snapshot.setSiteTrace(arr, stackTrace); 866 } 867 return bytesRead; 868 } 869 870 private byte signatureFromTypeId(byte typeId) throws IOException { 871 switch (typeId) { 872 case T_CLASS: { 873 return (byte) 'L'; 874 } 875 case T_BOOLEAN: { 876 return (byte) 'Z'; 877 } 878 case T_CHAR: { 879 return (byte) 'C'; 880 } 881 case T_FLOAT: { 882 return (byte) 'F'; 883 } 884 case T_DOUBLE: { 885 return (byte) 'D'; 886 } 887 case T_BYTE: { 888 return (byte) 'B'; 889 } 890 case T_SHORT: { 891 return (byte) 'S'; 892 } 893 case T_INT: { 894 return (byte) 'I'; 895 } 896 case T_LONG: { 897 return (byte) 'J'; 898 } 899 default: { 900 throw new IOException("Invalid type id of " + typeId); 901 } 902 } 903 } 904 905 private void handleEOF(EOFException exp, Snapshot snapshot) { 906 if (debugLevel > 0) { 907 exp.printStackTrace(); 908 } 909 warn("Unexpected EOF. Will miss information..."); 910 // we have EOF, we have to tolerate missing references 911 snapshot.setUnresolvedObjectsOK(true); 912 } 913 914 private void warn(String msg) { 915 System.out.println("WARNING: " + msg); 916 } 917 918 // 919 // A trivial data-holder class for HPROF_GC_ROOT_THREAD_OBJ. 920 // 921 private class ThreadObject { 922 923 long threadId; 924 int stackSeq; 925 926 ThreadObject(long threadId, int stackSeq) { 927 this.threadId = threadId; 928 this.stackSeq = stackSeq; 929 } 930 } 931 932 }