1 /* 2 * Copyright (c) 1997, 2023, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 /* 26 * The Original Code is HAT. The Initial Developer of the 27 * Original Code is Bill Foote, with contributions from others 28 * at JavaSoft/Sun. 29 */ 30 31 package jdk.test.lib.hprof.parser; 32 33 import java.io.*; 34 import java.util.Date; 35 import java.util.Hashtable; 36 import java.util.Map; 37 import jdk.test.lib.hprof.model.ArrayTypeCodes; 38 import jdk.test.lib.hprof.model.*; 39 40 /** 41 * Object that's used to read a hprof file. 42 * 43 * @author Bill Foote 44 */ 45 46 public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes { 47 48 final static int MAGIC_NUMBER = 0x4a415641; 49 // That's "JAVA", the first part of "JAVA PROFILE ..." 50 private final static String[] VERSIONS = { 51 " PROFILE 1.0\0", 52 " PROFILE 1.0.1\0", 53 " PROFILE 1.0.2\0", 54 }; 55 56 private final static int VERSION_JDK12BETA3 = 0; 57 private final static int VERSION_JDK12BETA4 = 1; 58 private final static int VERSION_JDK6 = 2; 59 // These version numbers are indices into VERSIONS. The instance data 60 // member version is set to one of these, and it drives decisions when 61 // reading the file. 62 // 63 // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no 64 // version-sensitive parsing. 65 // 66 // Version 1.0.1 changed the type of a constant pool entry from a signature 67 // to a typecode. 68 // 69 // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END 70 // to allow a large heap to be dumped as a sequence of heap dump segments. 71 // 72 // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1 73 // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on 74 // the size of the heap (normally it will be 1.0.1 but for multi-GB 75 // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the 76 // dump is generated as version 1.0.2). 77 78 // 79 // Record types: 80 // 81 static final int HPROF_UTF8 = 0x01; 82 static final int HPROF_LOAD_CLASS = 0x02; 83 static final int HPROF_UNLOAD_CLASS = 0x03; 84 static final int HPROF_FRAME = 0x04; 85 static final int HPROF_TRACE = 0x05; 86 static final int HPROF_ALLOC_SITES = 0x06; 87 static final int HPROF_HEAP_SUMMARY = 0x07; 88 89 static final int HPROF_START_THREAD = 0x0a; 90 static final int HPROF_END_THREAD = 0x0b; 91 92 static final int HPROF_HEAP_DUMP = 0x0c; 93 94 static final int HPROF_CPU_SAMPLES = 0x0d; 95 static final int HPROF_CONTROL_SETTINGS = 0x0e; 96 static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10; 97 static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11; 98 99 static final int HPROF_GC_ROOT_UNKNOWN = 0xff; 100 static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; 101 static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; 102 static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; 103 static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; 104 static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; 105 static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; 106 static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; 107 static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; 108 109 static final int HPROF_GC_CLASS_DUMP = 0x20; 110 static final int HPROF_GC_INSTANCE_DUMP = 0x21; 111 static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; 112 static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; 113 114 static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c; 115 static final int HPROF_HEAP_DUMP_END = 0x2c; 116 117 private final static int T_CLASS = 2; 118 119 private int version; // The version of .hprof being read 120 121 private int debugLevel; 122 private long currPos; // Current position in the file 123 124 private int dumpsToSkip; 125 private boolean callStack; // If true, read the call stack of objects 126 127 private int identifierSize; // Size, in bytes, of identifiers. 128 private Hashtable<Long, String> names; 129 130 // Hashtable<Integer, ThreadObject>, used to map the thread sequence number 131 // (aka "serial number") to the thread object ID for 132 // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class, 133 // at the end of this file. 134 private Hashtable<Integer, ThreadObject> threadObjects; 135 136 // Hashtable<Long, String>, maps class object ID to class name 137 // (with / converted to .) 138 private Hashtable<Long, String> classNameFromObjectID; 139 140 // Hashtable<Integer, Integer>, maps class serial # to class object ID 141 private Hashtable<Integer, String> classNameFromSerialNo; 142 143 // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame. 144 // Null if we're not tracking them. 145 private Hashtable<Long, StackFrame> stackFrames; 146 147 // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace 148 // Null if we're not tracking them. 149 private Hashtable<Integer, StackTrace> stackTraces; 150 151 private Snapshot snapshot; 152 153 public static boolean verifyMagicNumber(int numberRead) { 154 return (numberRead == MAGIC_NUMBER); 155 } 156 157 public HprofReader(ReadBuffer readBuffer, PositionDataInputStream in, 158 int dumpNumber, boolean callStack, int debugLevel) 159 throws IOException { 160 super(in); 161 this.snapshot = new Snapshot(readBuffer); 162 this.dumpsToSkip = dumpNumber - 1; 163 this.callStack = callStack; 164 this.debugLevel = debugLevel; 165 names = new Hashtable<Long, String>(); 166 threadObjects = new Hashtable<Integer, ThreadObject>(43); 167 classNameFromObjectID = new Hashtable<Long, String>(); 168 if (callStack) { 169 stackFrames = new Hashtable<Long, StackFrame>(43); 170 stackTraces = new Hashtable<Integer, StackTrace>(43); 171 classNameFromSerialNo = new Hashtable<Integer, String>(); 172 } 173 } 174 175 public HprofReader(String fileName, PositionDataInputStream in, 176 int dumpNumber, boolean callStack, int debugLevel) 177 throws IOException { 178 this(MappedReadBuffer.create(new RandomAccessFile(fileName, "r")), 179 in, dumpNumber, callStack, debugLevel); 180 } 181 182 public Snapshot read() throws IOException { 183 currPos = 4; // 4 because of the magic number 184 version = readVersionHeader(); 185 identifierSize = in.readInt(); 186 snapshot.setIdentifierSize(identifierSize); 187 if (version >= VERSION_JDK12BETA4) { 188 snapshot.setNewStyleArrayClass(true); 189 } else { 190 snapshot.setNewStyleArrayClass(false); 191 } 192 193 currPos += 4; 194 if (identifierSize != 4 && identifierSize != 8) { 195 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."); 196 } 197 System.out.println("Dump file created " + (new Date(in.readLong()))); 198 currPos += 8; 199 200 for (;;) { 201 int type; 202 try { 203 type = in.readUnsignedByte(); 204 } catch (EOFException ignored) { 205 break; 206 } 207 in.readInt(); // Timestamp of this record 208 // Length of record: readInt() will return negative value for record 209 // length >2GB. so store 32bit value in long to keep it unsigned. 210 long length = in.readInt() & 0xffffffffL; 211 if (debugLevel > 0) { 212 System.out.println("Read record type " + type 213 + ", length " + length 214 + " at position " + toHex(currPos)); 215 } 216 if (length < 0) { 217 throw new IOException("Bad record length of " + length 218 + " at byte " + toHex(currPos+5) 219 + " of file."); 220 } 221 currPos += 9 + length; 222 switch (type) { 223 case HPROF_UTF8: { 224 long id = readID(); 225 byte[] chars = new byte[(int)length - identifierSize]; 226 in.readFully(chars); 227 names.put(id, new String(chars)); 228 break; 229 } 230 case HPROF_LOAD_CLASS: { 231 int serialNo = in.readInt(); // Not used 232 long classID = readID(); 233 int stackTraceSerialNo = in.readInt(); 234 long classNameID = readID(); 235 Long classIdI = classID; 236 String nm = getNameFromID(classNameID).replace('/', '.'); 237 classNameFromObjectID.put(classIdI, nm); 238 if (classNameFromSerialNo != null) { 239 classNameFromSerialNo.put(serialNo, nm); 240 } 241 break; 242 } 243 244 case HPROF_HEAP_DUMP: { 245 if (dumpsToSkip <= 0) { 246 try { 247 readHeapDump(length, currPos); 248 } catch (EOFException exp) { 249 handleEOF(exp, snapshot); 250 } 251 if (debugLevel > 0) { 252 System.out.println(" Finished processing instances in heap dump."); 253 } 254 return snapshot; 255 } else { 256 dumpsToSkip--; 257 skipBytes(length); 258 } 259 break; 260 } 261 262 case HPROF_HEAP_DUMP_END: { 263 if (version >= VERSION_JDK6) { 264 if (dumpsToSkip <= 0) { 265 skipBytes(length); // should be no-op 266 return snapshot; 267 } else { 268 // skip this dump (of the end record for a sequence of dump segments) 269 dumpsToSkip--; 270 } 271 } else { 272 // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2 273 warn("Ignoring unrecognized record type " + type); 274 } 275 skipBytes(length); // should be no-op 276 break; 277 } 278 279 case HPROF_HEAP_DUMP_SEGMENT: { 280 if (version >= VERSION_JDK6) { 281 if (dumpsToSkip <= 0) { 282 try { 283 // read the dump segment 284 readHeapDump(length, currPos); 285 } catch (EOFException exp) { 286 handleEOF(exp, snapshot); 287 } 288 } else { 289 // all segments comprising the heap dump will be skipped 290 skipBytes(length); 291 } 292 } else { 293 // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2 294 warn("Ignoring unrecognized record type " + type); 295 skipBytes(length); 296 } 297 break; 298 } 299 300 case HPROF_FRAME: { 301 if (stackFrames == null) { 302 skipBytes(length); 303 } else { 304 long id = readID(); 305 String methodName = getNameFromID(readID()); 306 String methodSig = getNameFromID(readID()); 307 String sourceFile = getNameFromID(readID()); 308 int classSer = in.readInt(); 309 String className = classNameFromSerialNo.get(classSer); 310 int lineNumber = in.readInt(); 311 if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) { 312 warn("Weird stack frame line number: " + lineNumber); 313 lineNumber = StackFrame.LINE_NUMBER_UNKNOWN; 314 } 315 stackFrames.put(id, 316 new StackFrame(methodName, methodSig, 317 className, sourceFile, 318 lineNumber)); 319 } 320 break; 321 } 322 case HPROF_TRACE: { 323 if (stackTraces == null) { 324 skipBytes(length); 325 } else { 326 int serialNo = in.readInt(); 327 int threadSeq = in.readInt(); // Not used 328 StackFrame[] frames = new StackFrame[in.readInt()]; 329 for (int i = 0; i < frames.length; i++) { 330 long fid = readID(); 331 frames[i] = stackFrames.get(fid); 332 if (frames[i] == null) { 333 throw new IOException("Stack frame " + toHex(fid) + " not found"); 334 } 335 } 336 stackTraces.put(serialNo, 337 new StackTrace(frames)); 338 } 339 break; 340 } 341 case HPROF_UNLOAD_CLASS: 342 case HPROF_ALLOC_SITES: 343 case HPROF_START_THREAD: 344 case HPROF_END_THREAD: 345 case HPROF_HEAP_SUMMARY: 346 case HPROF_CPU_SAMPLES: 347 case HPROF_CONTROL_SETTINGS: 348 case HPROF_LOCKSTATS_WAIT_TIME: 349 case HPROF_LOCKSTATS_HOLD_TIME: 350 { 351 // Ignore these record types 352 skipBytes(length); 353 break; 354 } 355 default: { 356 skipBytes(length); 357 warn("Ignoring unrecognized record type " + type); 358 } 359 } 360 } 361 362 return snapshot; 363 } 364 365 public String printStackTraces() { 366 StringBuffer output = new StringBuffer(); 367 for (Map.Entry<Integer, StackTrace> entry : stackTraces.entrySet()) { 368 StackFrame[] frames = entry.getValue().getFrames(); 369 output.append("SerialNo " + entry.getKey() + "\n"); 370 for (int i = 0; i < frames.length; i++) { 371 output.append(" " + frames[i].getClassName() + "." + frames[i].getMethodName() 372 + frames[i].getMethodSignature() + " (" + frames[i].getSourceFileName() 373 + ":" + frames[i].getLineNumber() + ")" + "\n"); 374 } 375 } 376 377 System.out.println(output); 378 return output.toString(); 379 } 380 381 private void skipBytes(long length) throws IOException { 382 while (length > 0) { 383 long skipped = in.skip(length); 384 if (skipped == 0) { 385 // EOF or other problem, throw exception 386 throw new EOFException("Couldn't skip enough bytes"); 387 } 388 length -= skipped; 389 } 390 } 391 392 private int readVersionHeader() throws IOException { 393 int candidatesLeft = VERSIONS.length; 394 boolean[] matched = new boolean[VERSIONS.length]; 395 for (int i = 0; i < candidatesLeft; i++) { 396 matched[i] = true; 397 } 398 399 int pos = 0; 400 while (candidatesLeft > 0) { 401 char c = (char) in.readByte(); 402 currPos++; 403 for (int i = 0; i < VERSIONS.length; i++) { 404 if (matched[i]) { 405 if (c != VERSIONS[i].charAt(pos)) { // Not matched 406 matched[i] = false; 407 --candidatesLeft; 408 } else if (pos == VERSIONS[i].length() - 1) { // Full match 409 return i; 410 } 411 } 412 } 413 ++pos; 414 } 415 throw new IOException("Version string not recognized at byte " + (pos+3)); 416 } 417 418 private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { 419 while (bytesLeft > 0) { 420 int type = in.readUnsignedByte(); 421 if (debugLevel > 0) { 422 System.out.println(" Read heap sub-record type " + type 423 + " at position " 424 + toHex(posAtEnd - bytesLeft)); 425 } 426 bytesLeft--; 427 switch(type) { 428 case HPROF_GC_ROOT_UNKNOWN: { 429 long id = readID(); 430 bytesLeft -= identifierSize; 431 snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, "")); 432 break; 433 } 434 case HPROF_GC_ROOT_THREAD_OBJ: { 435 long id = readID(); 436 int threadSeq = in.readInt(); 437 int stackSeq = in.readInt(); 438 bytesLeft -= identifierSize + 8; 439 StackTrace st = getStackTraceFromSerial(stackSeq); 440 ThreadObject threadObj = new ThreadObject(id, st); 441 threadObjects.put(threadSeq, threadObj); 442 snapshot.addThreadObject(threadObj); 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 = to.getStackTrace(); 459 if (st != null) { 460 st = st.traceForDepth(depth+1); 461 } 462 snapshot.addRoot(new Root(id, to.getId(), 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 = to.getStackTrace();; 473 if (st != null) { 474 st = st.traceForDepth(depth+1); 475 } 476 snapshot.addRoot(new Root(id, to.getId(), 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 = to.getStackTrace();; 486 snapshot.addRoot(new Root(id, to.getId(), 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 = to.getStackTrace(); 502 snapshot.addRoot(new Root(id, to.getId(), 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 }