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_FLAT_ARRAYS = 0x12; 100 static final int HPROF_INLINED_FIELDS = 0x13; 101 102 static final int HPROF_FLAT_ARRAY = 0x01; 103 static final int HPROF_CLASS_WITH_INLINED_FIELDS = 0x01; 104 105 static final int HPROF_GC_ROOT_UNKNOWN = 0xff; 106 static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; 107 static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; 108 static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; 109 static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; 110 static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; 111 static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; 112 static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; 113 static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; 114 115 static final int HPROF_GC_CLASS_DUMP = 0x20; 116 static final int HPROF_GC_INSTANCE_DUMP = 0x21; 117 static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; 118 static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; 119 120 static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c; 121 static final int HPROF_HEAP_DUMP_END = 0x2c; 122 123 private final static int T_CLASS = 2; 124 125 private int version; // The version of .hprof being read 126 127 private int debugLevel; 128 private long currPos; // Current position in the file 129 130 private int dumpsToSkip; 131 private boolean callStack; // If true, read the call stack of objects 132 133 private int identifierSize; // Size, in bytes, of identifiers. 134 private Hashtable<Long, String> names; 135 136 // Hashtable<Integer, ThreadObject>, used to map the thread sequence number 137 // (aka "serial number") to the thread object ID for 138 // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class, 139 // at the end of this file. 140 private Hashtable<Integer, ThreadObject> threadObjects; 141 142 // Hashtable<Long, String>, maps class object ID to class name 143 // (with / converted to .) 144 private Hashtable<Long, String> classNameFromObjectID; 145 146 // Hashtable<Integer, Integer>, maps class serial # to class object ID 147 private Hashtable<Integer, String> classNameFromSerialNo; 148 149 // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame. 150 // Null if we're not tracking them. 151 private Hashtable<Long, StackFrame> stackFrames; 152 153 // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace 154 // Null if we're not tracking them. 155 private Hashtable<Integer, StackTrace> stackTraces; 156 157 private Snapshot snapshot; 158 159 public static boolean verifyMagicNumber(int numberRead) { 160 return (numberRead == MAGIC_NUMBER); 161 } 162 163 public HprofReader(ReadBuffer readBuffer, PositionDataInputStream in, 164 int dumpNumber, boolean callStack, int debugLevel) 165 throws IOException { 166 super(in); 167 this.snapshot = new Snapshot(readBuffer); 168 this.dumpsToSkip = dumpNumber - 1; 169 this.callStack = callStack; 170 this.debugLevel = debugLevel; 171 names = new Hashtable<Long, String>(); 172 threadObjects = new Hashtable<Integer, ThreadObject>(43); 173 classNameFromObjectID = new Hashtable<Long, String>(); 174 if (callStack) { 175 stackFrames = new Hashtable<Long, StackFrame>(43); 176 stackTraces = new Hashtable<Integer, StackTrace>(43); 177 classNameFromSerialNo = new Hashtable<Integer, String>(); 178 } 179 } 180 181 public HprofReader(String fileName, PositionDataInputStream in, 182 int dumpNumber, boolean callStack, int debugLevel) 183 throws IOException { 184 this(MappedReadBuffer.create(new RandomAccessFile(fileName, "r")), 185 in, dumpNumber, callStack, debugLevel); 186 } 187 188 public Snapshot read() throws IOException { 189 currPos = 4; // 4 because of the magic number 190 version = readVersionHeader(); 191 identifierSize = in.readInt(); 192 snapshot.setIdentifierSize(identifierSize); 193 if (version >= VERSION_JDK12BETA4) { 194 snapshot.setNewStyleArrayClass(true); 195 } else { 196 snapshot.setNewStyleArrayClass(false); 197 } 198 199 currPos += 4; 200 if (identifierSize != 4 && identifierSize != 8) { 201 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."); 202 } 203 System.out.println("Dump file created " + (new Date(in.readLong()))); 204 currPos += 8; 205 206 for (;;) { 207 int type; 208 try { 209 type = in.readUnsignedByte(); 210 } catch (EOFException ignored) { 211 break; 212 } 213 in.readInt(); // Timestamp of this record 214 // Length of record: readInt() will return negative value for record 215 // length >2GB. so store 32bit value in long to keep it unsigned. 216 long length = in.readInt() & 0xffffffffL; 217 if (debugLevel > 0) { 218 System.out.println("Read record type " + type 219 + ", length " + length 220 + " at position " + toHex(currPos)); 221 } 222 if (length < 0) { 223 throw new IOException("Bad record length of " + length 224 + " at byte " + toHex(currPos+5) 225 + " of file."); 226 } 227 currPos += 9 + length; 228 switch (type) { 229 case HPROF_UTF8: { 230 long id = readID(); 231 byte[] chars = new byte[(int)length - identifierSize]; 232 in.readFully(chars); 233 names.put(id, new String(chars)); 234 break; 235 } 236 case HPROF_LOAD_CLASS: { 237 int serialNo = in.readInt(); // Not used 238 long classID = readID(); 239 int stackTraceSerialNo = in.readInt(); 240 long classNameID = readID(); 241 Long classIdI = classID; 242 String nm = getNameFromID(classNameID).replace('/', '.'); 243 classNameFromObjectID.put(classIdI, nm); 244 if (classNameFromSerialNo != null) { 245 classNameFromSerialNo.put(serialNo, nm); 246 } 247 break; 248 } 249 250 case HPROF_HEAP_DUMP: { 251 if (dumpsToSkip == 0) { 252 try { 253 readHeapDump(length, currPos); 254 } catch (EOFException exp) { 255 handleEOF(exp, snapshot); 256 } 257 if (debugLevel > 0) { 258 System.out.println(" Finished processing instances in heap dump."); 259 } 260 } else { 261 dumpsToSkip--; 262 skipBytes(length); 263 } 264 break; 265 } 266 267 case HPROF_HEAP_DUMP_END: { 268 if (version >= VERSION_JDK6) { 269 if (dumpsToSkip == 0) { 270 // update dumpsToSkip to skip other dumps 271 dumpsToSkip--; 272 } else { 273 // skip this dump (of the end record for a sequence of dump segments) 274 dumpsToSkip--; 275 } 276 } else { 277 // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2 278 warn("Ignoring unrecognized record type " + type); 279 } 280 skipBytes(length); // should be no-op 281 break; 282 } 283 284 case HPROF_HEAP_DUMP_SEGMENT: { 285 if (version >= VERSION_JDK6) { 286 if (dumpsToSkip == 0) { 287 try { 288 // read the dump segment 289 readHeapDump(length, currPos); 290 } catch (EOFException exp) { 291 handleEOF(exp, snapshot); 292 } 293 } else { 294 // all segments comprising the heap dump will be skipped 295 skipBytes(length); 296 } 297 } else { 298 // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2 299 warn("Ignoring unrecognized record type " + type); 300 skipBytes(length); 301 } 302 break; 303 } 304 305 case HPROF_FRAME: { 306 if (stackFrames == null) { 307 skipBytes(length); 308 } else { 309 long id = readID(); 310 String methodName = getNameFromID(readID()); 311 String methodSig = getNameFromID(readID()); 312 String sourceFile = getNameFromID(readID()); 313 int classSer = in.readInt(); 314 String className = classNameFromSerialNo.get(classSer); 315 int lineNumber = in.readInt(); 316 if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) { 317 warn("Weird stack frame line number: " + lineNumber); 318 lineNumber = StackFrame.LINE_NUMBER_UNKNOWN; 319 } 320 stackFrames.put(id, 321 new StackFrame(methodName, methodSig, 322 className, sourceFile, 323 lineNumber)); 324 } 325 break; 326 } 327 case HPROF_TRACE: { 328 if (stackTraces == null) { 329 skipBytes(length); 330 } else { 331 int serialNo = in.readInt(); 332 int threadSeq = in.readInt(); // Not used 333 StackFrame[] frames = new StackFrame[in.readInt()]; 334 for (int i = 0; i < frames.length; i++) { 335 long fid = readID(); 336 frames[i] = stackFrames.get(fid); 337 if (frames[i] == null) { 338 throw new IOException("Stack frame " + toHex(fid) + " not found"); 339 } 340 } 341 stackTraces.put(serialNo, 342 new StackTrace(frames)); 343 } 344 break; 345 } 346 case HPROF_UNLOAD_CLASS: 347 case HPROF_ALLOC_SITES: 348 case HPROF_START_THREAD: 349 case HPROF_END_THREAD: 350 case HPROF_HEAP_SUMMARY: 351 case HPROF_CPU_SAMPLES: 352 case HPROF_CONTROL_SETTINGS: 353 case HPROF_LOCKSTATS_WAIT_TIME: 354 case HPROF_LOCKSTATS_HOLD_TIME: 355 { 356 // Ignore these record types 357 skipBytes(length); 358 break; 359 } 360 case HPROF_FLAT_ARRAYS: { 361 readFlatArrays(length); 362 break; 363 } 364 case HPROF_INLINED_FIELDS: { 365 readInlinedFields(length); 366 break; 367 } 368 369 default: { 370 skipBytes(length); 371 warn("Ignoring unrecognized record type " + type); 372 } 373 } 374 } 375 376 return snapshot; 377 } 378 379 public String printStackTraces() { 380 StringBuffer output = new StringBuffer(); 381 for (Map.Entry<Integer, StackTrace> entry : stackTraces.entrySet()) { 382 StackFrame[] frames = entry.getValue().getFrames(); 383 output.append("SerialNo " + entry.getKey() + "\n"); 384 for (int i = 0; i < frames.length; i++) { 385 output.append(" " + frames[i].getClassName() + "." + frames[i].getMethodName() 386 + frames[i].getMethodSignature() + " (" + frames[i].getSourceFileName() 387 + ":" + frames[i].getLineNumber() + ")" + "\n"); 388 } 389 } 390 391 System.out.println(output); 392 return output.toString(); 393 } 394 395 private void skipBytes(long length) throws IOException { 396 while (length > 0) { 397 long skipped = in.skip(length); 398 if (skipped == 0) { 399 // EOF or other problem, throw exception 400 throw new EOFException("Couldn't skip enough bytes"); 401 } 402 length -= skipped; 403 } 404 } 405 406 private int readVersionHeader() throws IOException { 407 int candidatesLeft = VERSIONS.length; 408 boolean[] matched = new boolean[VERSIONS.length]; 409 for (int i = 0; i < candidatesLeft; i++) { 410 matched[i] = true; 411 } 412 413 int pos = 0; 414 while (candidatesLeft > 0) { 415 char c = (char) in.readByte(); 416 currPos++; 417 for (int i = 0; i < VERSIONS.length; i++) { 418 if (matched[i]) { 419 if (c != VERSIONS[i].charAt(pos)) { // Not matched 420 matched[i] = false; 421 --candidatesLeft; 422 } else if (pos == VERSIONS[i].length() - 1) { // Full match 423 return i; 424 } 425 } 426 } 427 ++pos; 428 } 429 throw new IOException("Version string not recognized at byte " + (pos+3)); 430 } 431 432 private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { 433 while (bytesLeft > 0) { 434 int type = in.readUnsignedByte(); 435 if (debugLevel > 0) { 436 System.out.println(" Read heap sub-record type " + type 437 + " at position " 438 + toHex(posAtEnd - bytesLeft)); 439 } 440 bytesLeft--; 441 switch(type) { 442 case HPROF_GC_ROOT_UNKNOWN: { 443 long id = readID(); 444 bytesLeft -= identifierSize; 445 snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, "")); 446 break; 447 } 448 case HPROF_GC_ROOT_THREAD_OBJ: { 449 long id = readID(); 450 int threadSeq = in.readInt(); 451 int stackSeq = in.readInt(); 452 bytesLeft -= identifierSize + 8; 453 StackTrace st = getStackTraceFromSerial(stackSeq); 454 ThreadObject threadObj = new ThreadObject(id, st); 455 threadObjects.put(threadSeq, threadObj); 456 snapshot.addThreadObject(threadObj); 457 break; 458 } 459 case HPROF_GC_ROOT_JNI_GLOBAL: { 460 long id = readID(); 461 long globalRefId = readID(); // Ignored, for now 462 bytesLeft -= 2*identifierSize; 463 snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, "")); 464 break; 465 } 466 case HPROF_GC_ROOT_JNI_LOCAL: { 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.NATIVE_LOCAL, "", st)); 478 break; 479 } 480 case HPROF_GC_ROOT_JAVA_FRAME: { 481 long id = readID(); 482 int threadSeq = in.readInt(); 483 int depth = in.readInt(); 484 bytesLeft -= identifierSize + 8; 485 ThreadObject to = getThreadObjectFromSequence(threadSeq); 486 StackTrace st = to.getStackTrace();; 487 if (st != null) { 488 st = st.traceForDepth(depth+1); 489 } 490 snapshot.addRoot(new Root(id, to.getId(), 491 Root.JAVA_LOCAL, "", st)); 492 break; 493 } 494 case HPROF_GC_ROOT_NATIVE_STACK: { 495 long id = readID(); 496 int threadSeq = in.readInt(); 497 bytesLeft -= identifierSize + 4; 498 ThreadObject to = getThreadObjectFromSequence(threadSeq); 499 StackTrace st = to.getStackTrace();; 500 snapshot.addRoot(new Root(id, to.getId(), 501 Root.NATIVE_STACK, "", st)); 502 break; 503 } 504 case HPROF_GC_ROOT_STICKY_CLASS: { 505 long id = readID(); 506 bytesLeft -= identifierSize; 507 snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, "")); 508 break; 509 } 510 case HPROF_GC_ROOT_THREAD_BLOCK: { 511 long id = readID(); 512 int threadSeq = in.readInt(); 513 bytesLeft -= identifierSize + 4; 514 ThreadObject to = getThreadObjectFromSequence(threadSeq); 515 StackTrace st = to.getStackTrace(); 516 snapshot.addRoot(new Root(id, to.getId(), 517 Root.THREAD_BLOCK, "", st)); 518 break; 519 } 520 case HPROF_GC_ROOT_MONITOR_USED: { 521 long id = readID(); 522 bytesLeft -= identifierSize; 523 snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, "")); 524 break; 525 } 526 case HPROF_GC_CLASS_DUMP: { 527 int bytesRead = readClass(); 528 bytesLeft -= bytesRead; 529 break; 530 } 531 case HPROF_GC_INSTANCE_DUMP: { 532 int bytesRead = readInstance(); 533 bytesLeft -= bytesRead; 534 break; 535 } 536 case HPROF_GC_OBJ_ARRAY_DUMP: { 537 long bytesRead = readArray(false); 538 bytesLeft -= bytesRead; 539 break; 540 } 541 case HPROF_GC_PRIM_ARRAY_DUMP: { 542 long bytesRead = readArray(true); 543 bytesLeft -= bytesRead; 544 break; 545 } 546 default: { 547 throw new IOException("Unrecognized heap dump sub-record type: " + type); 548 } 549 } 550 } 551 if (bytesLeft != 0) { 552 warn("Error reading heap dump or heap dump segment: Byte count is " + bytesLeft + " instead of 0"); 553 skipBytes(bytesLeft); 554 } 555 if (debugLevel > 0) { 556 System.out.println(" Finished heap sub-records."); 557 } 558 } 559 560 private long readID() throws IOException { 561 return (identifierSize == 4)? 562 (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong(); 563 } 564 565 // 566 // Read a java value. If result is non-null, it's expected to be an 567 // array of one element. We use it to fake multiple return values. 568 // @returns the number of bytes read 569 // 570 private int readValue(JavaThing[] resultArr) throws IOException { 571 byte type = in.readByte(); 572 return 1 + readValueForType(type, resultArr); 573 } 574 575 private int readValueForType(byte type, JavaThing[] resultArr) 576 throws IOException { 577 if (version >= VERSION_JDK12BETA4) { 578 type = signatureFromTypeId(type); 579 } 580 return readValueForTypeSignature(type, resultArr); 581 } 582 583 private int readValueForTypeSignature(byte type, JavaThing[] resultArr) 584 throws IOException { 585 switch (type) { 586 case '[': 587 case 'L': { 588 long id = readID(); 589 if (resultArr != null) { 590 resultArr[0] = new JavaObjectRef(id); 591 } 592 return identifierSize; 593 } 594 case 'Z': { 595 int b = in.readByte(); 596 if (b != 0 && b != 1) { 597 warn("Illegal boolean value read"); 598 } 599 if (resultArr != null) { 600 resultArr[0] = new JavaBoolean(b != 0); 601 } 602 return 1; 603 } 604 case 'B': { 605 byte b = in.readByte(); 606 if (resultArr != null) { 607 resultArr[0] = new JavaByte(b); 608 } 609 return 1; 610 } 611 case 'S': { 612 short s = in.readShort(); 613 if (resultArr != null) { 614 resultArr[0] = new JavaShort(s); 615 } 616 return 2; 617 } 618 case 'C': { 619 char ch = in.readChar(); 620 if (resultArr != null) { 621 resultArr[0] = new JavaChar(ch); 622 } 623 return 2; 624 } 625 case 'I': { 626 int val = in.readInt(); 627 if (resultArr != null) { 628 resultArr[0] = new JavaInt(val); 629 } 630 return 4; 631 } 632 case 'J': { 633 long val = in.readLong(); 634 if (resultArr != null) { 635 resultArr[0] = new JavaLong(val); 636 } 637 return 8; 638 } 639 case 'F': { 640 float val = in.readFloat(); 641 if (resultArr != null) { 642 resultArr[0] = new JavaFloat(val); 643 } 644 return 4; 645 } 646 case 'D': { 647 double val = in.readDouble(); 648 if (resultArr != null) { 649 resultArr[0] = new JavaDouble(val); 650 } 651 return 8; 652 } 653 default: { 654 throw new IOException("Bad value signature: " + type); 655 } 656 } 657 } 658 659 private ThreadObject getThreadObjectFromSequence(int threadSeq) 660 throws IOException { 661 ThreadObject to = threadObjects.get(threadSeq); 662 if (to == null) { 663 throw new IOException("Thread " + threadSeq + 664 " not found for JNI local ref"); 665 } 666 return to; 667 } 668 669 private String getNameFromID(long id) throws IOException { 670 return getNameFromID(Long.valueOf(id)); 671 } 672 673 private String getNameFromID(Long id) throws IOException { 674 if (id.longValue() == 0L) { 675 return ""; 676 } 677 String result = names.get(id); 678 if (result == null) { 679 warn("Name not found at " + toHex(id.longValue())); 680 return "unresolved name " + toHex(id.longValue()); 681 } 682 return result; 683 } 684 685 private StackTrace getStackTraceFromSerial(int ser) throws IOException { 686 if (stackTraces == null) { 687 return null; 688 } 689 StackTrace result = stackTraces.get(ser); 690 if (result == null) { 691 warn("Stack trace not found for serial # " + ser); 692 } 693 return result; 694 } 695 696 // 697 // Handle a HPROF_GC_CLASS_DUMP 698 // Return number of bytes read 699 // 700 private int readClass() throws IOException { 701 long id = readID(); 702 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 703 long superId = readID(); 704 long classLoaderId = readID(); 705 long signersId = readID(); 706 long protDomainId = readID(); 707 long reserved1 = readID(); 708 long reserved2 = readID(); 709 int instanceSize = in.readInt(); 710 int bytesRead = 7 * identifierSize + 8; 711 712 int numConstPoolEntries = in.readUnsignedShort(); 713 bytesRead += 2; 714 for (int i = 0; i < numConstPoolEntries; i++) { 715 int index = in.readUnsignedShort(); // unused 716 bytesRead += 2; 717 bytesRead += readValue(null); // We ignore the values 718 } 719 720 int numStatics = in.readUnsignedShort(); 721 bytesRead += 2; 722 JavaThing[] valueBin = new JavaThing[1]; 723 JavaStatic[] statics = new JavaStatic[numStatics]; 724 for (int i = 0; i < numStatics; i++) { 725 long nameId = readID(); 726 bytesRead += identifierSize; 727 byte type = in.readByte(); 728 bytesRead++; 729 bytesRead += readValueForType(type, valueBin); 730 String fieldName = getNameFromID(nameId); 731 if (version >= VERSION_JDK12BETA4) { 732 type = signatureFromTypeId(type); 733 } 734 String signature = "" + ((char) type); 735 JavaField f = new JavaField(fieldName, signature); 736 statics[i] = new JavaStatic(f, valueBin[0]); 737 } 738 739 int numFields = in.readUnsignedShort(); 740 bytesRead += 2; 741 JavaField[] fields = new JavaField[numFields]; 742 for (int i = 0; i < numFields; i++) { 743 long nameId = readID(); 744 bytesRead += identifierSize; 745 byte type = in.readByte(); 746 bytesRead++; 747 String fieldName = getNameFromID(nameId); 748 if (version >= VERSION_JDK12BETA4) { 749 type = signatureFromTypeId(type); 750 } 751 String signature = "" + ((char) type); 752 fields[i] = new JavaField(fieldName, signature); 753 } 754 String name = classNameFromObjectID.get(id); 755 if (name == null) { 756 warn("Class name not found for " + toHex(id)); 757 name = "unknown-name@" + toHex(id); 758 } 759 JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId, 760 protDomainId, fields, statics, 761 instanceSize); 762 snapshot.addClass(id, c); 763 snapshot.setSiteTrace(c, stackTrace); 764 765 return bytesRead; 766 } 767 768 private String toHex(long addr) { 769 return jdk.test.lib.hprof.util.Misc.toHex(addr); 770 } 771 772 // 773 // Handle a HPROF_GC_INSTANCE_DUMP 774 // Return number of bytes read 775 // 776 private int readInstance() throws IOException { 777 long start = in.position(); 778 long id = readID(); 779 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 780 long classID = readID(); 781 JavaClass searchedClass = snapshot.findClass( 782 "0x" + Long.toHexString(classID)); 783 if (searchedClass == null) { 784 throw new IOException( 785 "Class Record for 0x" + Long.toHexString(classID) + " not found"); 786 } 787 int bytesFollowing = in.readInt(); 788 int bytesRead = (2 * identifierSize) + 8 + bytesFollowing; 789 JavaObject jobj = new JavaObject(classID, start); 790 skipBytes(bytesFollowing); 791 snapshot.addHeapObject(id, jobj); 792 snapshot.setSiteTrace(jobj, stackTrace); 793 return bytesRead; 794 } 795 796 // 797 // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP 798 // Return number of bytes read 799 // 800 private long readArray(boolean isPrimitive) throws IOException { 801 long start = in.position(); 802 long id = readID(); 803 StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); 804 int num = in.readInt(); 805 long bytesRead = identifierSize + 8; 806 long elementClassID; 807 if (isPrimitive) { 808 elementClassID = in.readByte(); 809 bytesRead++; 810 } else { 811 elementClassID = readID(); 812 bytesRead += identifierSize; 813 } 814 815 // Check for primitive arrays: 816 byte primitiveSignature = 0x00; 817 int elSize = 0; 818 if (isPrimitive || version < VERSION_JDK12BETA4) { 819 switch ((int)elementClassID) { 820 case T_BOOLEAN: { 821 primitiveSignature = (byte) 'Z'; 822 elSize = 1; 823 break; 824 } 825 case T_CHAR: { 826 primitiveSignature = (byte) 'C'; 827 elSize = 2; 828 break; 829 } 830 case T_FLOAT: { 831 primitiveSignature = (byte) 'F'; 832 elSize = 4; 833 break; 834 } 835 case T_DOUBLE: { 836 primitiveSignature = (byte) 'D'; 837 elSize = 8; 838 break; 839 } 840 case T_BYTE: { 841 primitiveSignature = (byte) 'B'; 842 elSize = 1; 843 break; 844 } 845 case T_SHORT: { 846 primitiveSignature = (byte) 'S'; 847 elSize = 2; 848 break; 849 } 850 case T_INT: { 851 primitiveSignature = (byte) 'I'; 852 elSize = 4; 853 break; 854 } 855 case T_LONG: { 856 primitiveSignature = (byte) 'J'; 857 elSize = 8; 858 break; 859 } 860 } 861 if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) { 862 throw new IOException("Unrecognized typecode: " 863 + elementClassID); 864 } 865 } 866 if (primitiveSignature != 0x00) { 867 long size = elSize * (long)num; 868 bytesRead += size; 869 JavaValueArray va = new JavaValueArray(id, primitiveSignature, start); 870 skipBytes(size); 871 snapshot.addHeapObject(id, va); 872 snapshot.setSiteTrace(va, stackTrace); 873 } else { 874 long sz = (long)num * identifierSize; 875 bytesRead += sz; 876 JavaObjectArray arr = new JavaObjectArray(elementClassID, start); 877 skipBytes(sz); 878 snapshot.addHeapObject(id, arr); 879 snapshot.setSiteTrace(arr, stackTrace); 880 } 881 return bytesRead; 882 } 883 884 private byte signatureFromTypeId(byte typeId) throws IOException { 885 switch (typeId) { 886 case T_CLASS: { 887 return (byte) 'L'; 888 } 889 case T_BOOLEAN: { 890 return (byte) 'Z'; 891 } 892 case T_CHAR: { 893 return (byte) 'C'; 894 } 895 case T_FLOAT: { 896 return (byte) 'F'; 897 } 898 case T_DOUBLE: { 899 return (byte) 'D'; 900 } 901 case T_BYTE: { 902 return (byte) 'B'; 903 } 904 case T_SHORT: { 905 return (byte) 'S'; 906 } 907 case T_INT: { 908 return (byte) 'I'; 909 } 910 case T_LONG: { 911 return (byte) 'J'; 912 } 913 default: { 914 throw new IOException("Invalid type id of " + typeId); 915 } 916 } 917 } 918 919 private void readFlatArrays(long length) throws IOException { 920 while (length > 0) { 921 byte tag = in.readByte(); 922 length--; 923 switch (tag) { 924 case HPROF_FLAT_ARRAY: { 925 long objId = readID(); 926 length -= identifierSize; 927 long elementClassId = readID(); 928 length -= identifierSize; 929 snapshot.addFlatArray(objId, elementClassId); 930 break; 931 } 932 default: { 933 throw new IOException("Invalid tag " + tag); 934 } 935 } 936 } 937 } 938 939 private void readInlinedFields(long length) throws IOException { 940 while (length > 0) { 941 byte tag = in.readByte(); 942 length--; 943 switch (tag) { 944 case HPROF_CLASS_WITH_INLINED_FIELDS: { 945 long classID = readID(); 946 length -= identifierSize; 947 int fieldNum = in.readUnsignedShort(); 948 length -= 2; 949 Snapshot.ClassInlinedFields[] fields = new Snapshot.ClassInlinedFields[fieldNum]; 950 for (int i = 0; i < fieldNum; i++) { 951 int fieldIndex = in.readUnsignedShort(); 952 length -= 2; 953 int synthFieldCount = in.readUnsignedShort(); 954 length -= 2; 955 String fieldName = getNameFromID(readID()); 956 length -= identifierSize; 957 long fieldClassId = readID(); 958 length -= identifierSize; 959 fields[i] = new Snapshot.ClassInlinedFields(fieldIndex, synthFieldCount, fieldName, fieldClassId); 960 } 961 snapshot.addClassInlinedFields(classID, fields); 962 break; 963 } 964 default: { 965 throw new IOException("Invalid tag " + tag); 966 } 967 } 968 } 969 } 970 971 private void handleEOF(EOFException exp, Snapshot snapshot) { 972 if (debugLevel > 0) { 973 exp.printStackTrace(); 974 } 975 warn("Unexpected EOF. Will miss information..."); 976 // we have EOF, we have to tolerate missing references 977 snapshot.setUnresolvedObjectsOK(true); 978 } 979 980 private void warn(String msg) { 981 System.out.println("WARNING: " + msg); 982 } 983 984 }