Showing the changes to java/pkg/com/sun/tools/hat/internal/parser/HprofReader.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,892 @@ | |||
| 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(new Long(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 = new Long(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(new Long(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(new Long(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(new Long(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(new Long(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 | } | ||