| /* |
| * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| /* |
| * The Original Code is HAT. The Initial Developer of the |
| * Original Code is Bill Foote, with contributions from others |
| * at JavaSoft/Sun. |
| */ |
| |
| package com.sun.tools.hat.internal.parser; |
| |
| import java.io.*; |
| import java.util.Date; |
| import java.util.Hashtable; |
| import com.sun.tools.hat.internal.model.ArrayTypeCodes; |
| import com.sun.tools.hat.internal.model.*; |
| |
| /** |
| * Object that's used to read a hprof file. |
| * |
| * @author Bill Foote |
| */ |
| |
| public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes { |
| |
| final static int MAGIC_NUMBER = 0x4a415641; |
| // That's "JAVA", the first part of "JAVA PROFILE ..." |
| private final static String[] VERSIONS = { |
| " PROFILE 1.0\0", |
| " PROFILE 1.0.1\0", |
| " PROFILE 1.0.2\0", |
| }; |
| |
| private final static int VERSION_JDK12BETA3 = 0; |
| private final static int VERSION_JDK12BETA4 = 1; |
| private final static int VERSION_JDK6 = 2; |
| // These version numbers are indices into VERSIONS. The instance data |
| // member version is set to one of these, and it drives decisions when |
| // reading the file. |
| // |
| // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no |
| // version-sensitive parsing. |
| // |
| // Version 1.0.1 changed the type of a constant pool entry from a signature |
| // to a typecode. |
| // |
| // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END |
| // to allow a large heap to be dumped as a sequence of heap dump segments. |
| // |
| // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1 |
| // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on |
| // the size of the heap (normally it will be 1.0.1 but for multi-GB |
| // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the |
| // dump is generated as version 1.0.2). |
| |
| // |
| // Record types: |
| // |
| static final int HPROF_UTF8 = 0x01; |
| static final int HPROF_LOAD_CLASS = 0x02; |
| static final int HPROF_UNLOAD_CLASS = 0x03; |
| static final int HPROF_FRAME = 0x04; |
| static final int HPROF_TRACE = 0x05; |
| static final int HPROF_ALLOC_SITES = 0x06; |
| static final int HPROF_HEAP_SUMMARY = 0x07; |
| |
| static final int HPROF_START_THREAD = 0x0a; |
| static final int HPROF_END_THREAD = 0x0b; |
| |
| static final int HPROF_HEAP_DUMP = 0x0c; |
| |
| static final int HPROF_CPU_SAMPLES = 0x0d; |
| static final int HPROF_CONTROL_SETTINGS = 0x0e; |
| static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10; |
| static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11; |
| |
| static final int HPROF_GC_ROOT_UNKNOWN = 0xff; |
| static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; |
| static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; |
| static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; |
| static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; |
| static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; |
| static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; |
| static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; |
| static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; |
| |
| static final int HPROF_GC_CLASS_DUMP = 0x20; |
| static final int HPROF_GC_INSTANCE_DUMP = 0x21; |
| static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; |
| static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; |
| |
| static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c; |
| static final int HPROF_HEAP_DUMP_END = 0x2c; |
| |
| private final static int T_CLASS = 2; |
| |
| private int version; // The version of .hprof being read |
| |
| private int debugLevel; |
| private long currPos; // Current position in the file |
| |
| private int dumpsToSkip; |
| private boolean callStack; // If true, read the call stack of objects |
| |
| private int identifierSize; // Size, in bytes, of identifiers. |
| private Hashtable<Long, String> names; |
| |
| // Hashtable<Integer, ThreadObject>, used to map the thread sequence number |
| // (aka "serial number") to the thread object ID for |
| // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class, |
| // at the end of this file. |
| private Hashtable<Integer, ThreadObject> threadObjects; |
| |
| // Hashtable<Long, String>, maps class object ID to class name |
| // (with / converted to .) |
| private Hashtable<Long, String> classNameFromObjectID; |
| |
| // Hashtable<Integer, Integer>, maps class serial # to class object ID |
| private Hashtable<Integer, String> classNameFromSerialNo; |
| |
| // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame. |
| // Null if we're not tracking them. |
| private Hashtable<Long, StackFrame> stackFrames; |
| |
| // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace |
| // Null if we're not tracking them. |
| private Hashtable<Integer, StackTrace> stackTraces; |
| |
| private Snapshot snapshot; |
| |
| public HprofReader(String fileName, PositionDataInputStream in, |
| int dumpNumber, boolean callStack, int debugLevel) |
| throws IOException { |
| super(in); |
| RandomAccessFile file = new RandomAccessFile(fileName, "r"); |
| this.snapshot = new Snapshot(MappedReadBuffer.create(file)); |
| this.dumpsToSkip = dumpNumber - 1; |
| this.callStack = callStack; |
| this.debugLevel = debugLevel; |
| names = new Hashtable<Long, String>(); |
| threadObjects = new Hashtable<Integer, ThreadObject>(43); |
| classNameFromObjectID = new Hashtable<Long, String>(); |
| if (callStack) { |
| stackFrames = new Hashtable<Long, StackFrame>(43); |
| stackTraces = new Hashtable<Integer, StackTrace>(43); |
| classNameFromSerialNo = new Hashtable<Integer, String>(); |
| } |
| } |
| |
| public Snapshot read() throws IOException { |
| currPos = 4; // 4 because of the magic number |
| version = readVersionHeader(); |
| identifierSize = in.readInt(); |
| snapshot.setIdentifierSize(identifierSize); |
| if (version >= VERSION_JDK12BETA4) { |
| snapshot.setNewStyleArrayClass(true); |
| } else { |
| snapshot.setNewStyleArrayClass(false); |
| } |
| |
| currPos += 4; |
| if (identifierSize != 4 && identifierSize != 8) { |
| 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."); |
| } |
| System.out.println("Dump file created " + (new Date(in.readLong()))); |
| currPos += 8; |
| |
| for (;;) { |
| int type; |
| try { |
| type = in.readUnsignedByte(); |
| } catch (EOFException ignored) { |
| break; |
| } |
| in.readInt(); // Timestamp of this record |
| // Length of record: readInt() will return negative value for record |
| // length >2GB. so store 32bit value in long to keep it unsigned. |
| long length = in.readInt() & 0xffffffffL; |
| if (debugLevel > 0) { |
| System.out.println("Read record type " + type |
| + ", length " + length |
| + " at position " + toHex(currPos)); |
| } |
| if (length < 0) { |
| throw new IOException("Bad record length of " + length |
| + " at byte " + toHex(currPos+5) |
| + " of file."); |
| } |
| currPos += 9 + length; |
| switch (type) { |
| case HPROF_UTF8: { |
| long id = readID(); |
| byte[] chars = new byte[(int)length - identifierSize]; |
| in.readFully(chars); |
| names.put(new Long(id), new String(chars)); |
| break; |
| } |
| case HPROF_LOAD_CLASS: { |
| int serialNo = in.readInt(); // Not used |
| long classID = readID(); |
| int stackTraceSerialNo = in.readInt(); |
| long classNameID = readID(); |
| Long classIdI = new Long(classID); |
| String nm = getNameFromID(classNameID).replace('/', '.'); |
| classNameFromObjectID.put(classIdI, nm); |
| if (classNameFromSerialNo != null) { |
| classNameFromSerialNo.put(new Integer(serialNo), nm); |
| } |
| break; |
| } |
| |
| case HPROF_HEAP_DUMP: { |
| if (dumpsToSkip <= 0) { |
| try { |
| readHeapDump(length, currPos); |
| } catch (EOFException exp) { |
| handleEOF(exp, snapshot); |
| } |
| if (debugLevel > 0) { |
| System.out.println(" Finished processing instances in heap dump."); |
| } |
| return snapshot; |
| } else { |
| dumpsToSkip--; |
| skipBytes(length); |
| } |
| break; |
| } |
| |
| case HPROF_HEAP_DUMP_END: { |
| if (version >= VERSION_JDK6) { |
| if (dumpsToSkip <= 0) { |
| skipBytes(length); // should be no-op |
| return snapshot; |
| } else { |
| // skip this dump (of the end record for a sequence of dump segments) |
| dumpsToSkip--; |
| } |
| } else { |
| // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2 |
| warn("Ignoring unrecognized record type " + type); |
| } |
| skipBytes(length); // should be no-op |
| break; |
| } |
| |
| case HPROF_HEAP_DUMP_SEGMENT: { |
| if (version >= VERSION_JDK6) { |
| if (dumpsToSkip <= 0) { |
| try { |
| // read the dump segment |
| readHeapDump(length, currPos); |
| } catch (EOFException exp) { |
| handleEOF(exp, snapshot); |
| } |
| } else { |
| // all segments comprising the heap dump will be skipped |
| skipBytes(length); |
| } |
| } else { |
| // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2 |
| warn("Ignoring unrecognized record type " + type); |
| skipBytes(length); |
| } |
| break; |
| } |
| |
| case HPROF_FRAME: { |
| if (stackFrames == null) { |
| skipBytes(length); |
| } else { |
| long id = readID(); |
| String methodName = getNameFromID(readID()); |
| String methodSig = getNameFromID(readID()); |
| String sourceFile = getNameFromID(readID()); |
| int classSer = in.readInt(); |
| String className = classNameFromSerialNo.get(new Integer(classSer)); |
| int lineNumber = in.readInt(); |
| if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) { |
| warn("Weird stack frame line number: " + lineNumber); |
| lineNumber = StackFrame.LINE_NUMBER_UNKNOWN; |
| } |
| stackFrames.put(new Long(id), |
| new StackFrame(methodName, methodSig, |
| className, sourceFile, |
| lineNumber)); |
| } |
| break; |
| } |
| case HPROF_TRACE: { |
| if (stackTraces == null) { |
| skipBytes(length); |
| } else { |
| int serialNo = in.readInt(); |
| int threadSeq = in.readInt(); // Not used |
| StackFrame[] frames = new StackFrame[in.readInt()]; |
| for (int i = 0; i < frames.length; i++) { |
| long fid = readID(); |
| frames[i] = stackFrames.get(new Long(fid)); |
| if (frames[i] == null) { |
| throw new IOException("Stack frame " + toHex(fid) + " not found"); |
| } |
| } |
| stackTraces.put(new Integer(serialNo), |
| new StackTrace(frames)); |
| } |
| break; |
| } |
| case HPROF_UNLOAD_CLASS: |
| case HPROF_ALLOC_SITES: |
| case HPROF_START_THREAD: |
| case HPROF_END_THREAD: |
| case HPROF_HEAP_SUMMARY: |
| case HPROF_CPU_SAMPLES: |
| case HPROF_CONTROL_SETTINGS: |
| case HPROF_LOCKSTATS_WAIT_TIME: |
| case HPROF_LOCKSTATS_HOLD_TIME: |
| { |
| // Ignore these record types |
| skipBytes(length); |
| break; |
| } |
| default: { |
| skipBytes(length); |
| warn("Ignoring unrecognized record type " + type); |
| } |
| } |
| } |
| |
| return snapshot; |
| } |
| |
| private void skipBytes(long length) throws IOException { |
| in.skipBytes((int)length); |
| } |
| |
| private int readVersionHeader() throws IOException { |
| int candidatesLeft = VERSIONS.length; |
| boolean[] matched = new boolean[VERSIONS.length]; |
| for (int i = 0; i < candidatesLeft; i++) { |
| matched[i] = true; |
| } |
| |
| int pos = 0; |
| while (candidatesLeft > 0) { |
| char c = (char) in.readByte(); |
| currPos++; |
| for (int i = 0; i < VERSIONS.length; i++) { |
| if (matched[i]) { |
| if (c != VERSIONS[i].charAt(pos)) { // Not matched |
| matched[i] = false; |
| --candidatesLeft; |
| } else if (pos == VERSIONS[i].length() - 1) { // Full match |
| return i; |
| } |
| } |
| } |
| ++pos; |
| } |
| throw new IOException("Version string not recognized at byte " + (pos+3)); |
| } |
| |
| private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { |
| while (bytesLeft > 0) { |
| int type = in.readUnsignedByte(); |
| if (debugLevel > 0) { |
| System.out.println(" Read heap sub-record type " + type |
| + " at position " |
| + toHex(posAtEnd - bytesLeft)); |
| } |
| bytesLeft--; |
| switch(type) { |
| case HPROF_GC_ROOT_UNKNOWN: { |
| long id = readID(); |
| bytesLeft -= identifierSize; |
| snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, "")); |
| break; |
| } |
| case HPROF_GC_ROOT_THREAD_OBJ: { |
| long id = readID(); |
| int threadSeq = in.readInt(); |
| int stackSeq = in.readInt(); |
| bytesLeft -= identifierSize + 8; |
| threadObjects.put(new Integer(threadSeq), |
| new ThreadObject(id, stackSeq)); |
| break; |
| } |
| case HPROF_GC_ROOT_JNI_GLOBAL: { |
| long id = readID(); |
| long globalRefId = readID(); // Ignored, for now |
| bytesLeft -= 2*identifierSize; |
| snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, "")); |
| break; |
| } |
| case HPROF_GC_ROOT_JNI_LOCAL: { |
| long id = readID(); |
| int threadSeq = in.readInt(); |
| int depth = in.readInt(); |
| bytesLeft -= identifierSize + 8; |
| ThreadObject to = getThreadObjectFromSequence(threadSeq); |
| StackTrace st = getStackTraceFromSerial(to.stackSeq); |
| if (st != null) { |
| st = st.traceForDepth(depth+1); |
| } |
| snapshot.addRoot(new Root(id, to.threadId, |
| Root.NATIVE_LOCAL, "", st)); |
| break; |
| } |
| case HPROF_GC_ROOT_JAVA_FRAME: { |
| long id = readID(); |
| int threadSeq = in.readInt(); |
| int depth = in.readInt(); |
| bytesLeft -= identifierSize + 8; |
| ThreadObject to = getThreadObjectFromSequence(threadSeq); |
| StackTrace st = getStackTraceFromSerial(to.stackSeq); |
| if (st != null) { |
| st = st.traceForDepth(depth+1); |
| } |
| snapshot.addRoot(new Root(id, to.threadId, |
| Root.JAVA_LOCAL, "", st)); |
| break; |
| } |
| case HPROF_GC_ROOT_NATIVE_STACK: { |
| long id = readID(); |
| int threadSeq = in.readInt(); |
| bytesLeft -= identifierSize + 4; |
| ThreadObject to = getThreadObjectFromSequence(threadSeq); |
| StackTrace st = getStackTraceFromSerial(to.stackSeq); |
| snapshot.addRoot(new Root(id, to.threadId, |
| Root.NATIVE_STACK, "", st)); |
| break; |
| } |
| case HPROF_GC_ROOT_STICKY_CLASS: { |
| long id = readID(); |
| bytesLeft -= identifierSize; |
| snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, "")); |
| break; |
| } |
| case HPROF_GC_ROOT_THREAD_BLOCK: { |
| long id = readID(); |
| int threadSeq = in.readInt(); |
| bytesLeft -= identifierSize + 4; |
| ThreadObject to = getThreadObjectFromSequence(threadSeq); |
| StackTrace st = getStackTraceFromSerial(to.stackSeq); |
| snapshot.addRoot(new Root(id, to.threadId, |
| Root.THREAD_BLOCK, "", st)); |
| break; |
| } |
| case HPROF_GC_ROOT_MONITOR_USED: { |
| long id = readID(); |
| bytesLeft -= identifierSize; |
| snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, "")); |
| break; |
| } |
| case HPROF_GC_CLASS_DUMP: { |
| int bytesRead = readClass(); |
| bytesLeft -= bytesRead; |
| break; |
| } |
| case HPROF_GC_INSTANCE_DUMP: { |
| int bytesRead = readInstance(); |
| bytesLeft -= bytesRead; |
| break; |
| } |
| case HPROF_GC_OBJ_ARRAY_DUMP: { |
| int bytesRead = readArray(false); |
| bytesLeft -= bytesRead; |
| break; |
| } |
| case HPROF_GC_PRIM_ARRAY_DUMP: { |
| int bytesRead = readArray(true); |
| bytesLeft -= bytesRead; |
| break; |
| } |
| default: { |
| throw new IOException("Unrecognized heap dump sub-record type: " + type); |
| } |
| } |
| } |
| if (bytesLeft != 0) { |
| warn("Error reading heap dump or heap dump segment: Byte count is " + bytesLeft + " instead of 0"); |
| skipBytes(bytesLeft); |
| } |
| if (debugLevel > 0) { |
| System.out.println(" Finished heap sub-records."); |
| } |
| } |
| |
| private long readID() throws IOException { |
| return (identifierSize == 4)? |
| (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong(); |
| } |
| |
| // |
| // Read a java value. If result is non-null, it's expected to be an |
| // array of one element. We use it to fake multiple return values. |
| // @returns the number of bytes read |
| // |
| private int readValue(JavaThing[] resultArr) throws IOException { |
| byte type = in.readByte(); |
| return 1 + readValueForType(type, resultArr); |
| } |
| |
| private int readValueForType(byte type, JavaThing[] resultArr) |
| throws IOException { |
| if (version >= VERSION_JDK12BETA4) { |
| type = signatureFromTypeId(type); |
| } |
| return readValueForTypeSignature(type, resultArr); |
| } |
| |
| private int readValueForTypeSignature(byte type, JavaThing[] resultArr) |
| throws IOException { |
| switch (type) { |
| case '[': |
| case 'L': { |
| long id = readID(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaObjectRef(id); |
| } |
| return identifierSize; |
| } |
| case 'Z': { |
| int b = in.readByte(); |
| if (b != 0 && b != 1) { |
| warn("Illegal boolean value read"); |
| } |
| if (resultArr != null) { |
| resultArr[0] = new JavaBoolean(b != 0); |
| } |
| return 1; |
| } |
| case 'B': { |
| byte b = in.readByte(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaByte(b); |
| } |
| return 1; |
| } |
| case 'S': { |
| short s = in.readShort(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaShort(s); |
| } |
| return 2; |
| } |
| case 'C': { |
| char ch = in.readChar(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaChar(ch); |
| } |
| return 2; |
| } |
| case 'I': { |
| int val = in.readInt(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaInt(val); |
| } |
| return 4; |
| } |
| case 'J': { |
| long val = in.readLong(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaLong(val); |
| } |
| return 8; |
| } |
| case 'F': { |
| float val = in.readFloat(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaFloat(val); |
| } |
| return 4; |
| } |
| case 'D': { |
| double val = in.readDouble(); |
| if (resultArr != null) { |
| resultArr[0] = new JavaDouble(val); |
| } |
| return 8; |
| } |
| default: { |
| throw new IOException("Bad value signature: " + type); |
| } |
| } |
| } |
| |
| private ThreadObject getThreadObjectFromSequence(int threadSeq) |
| throws IOException { |
| ThreadObject to = threadObjects.get(new Integer(threadSeq)); |
| if (to == null) { |
| throw new IOException("Thread " + threadSeq + |
| " not found for JNI local ref"); |
| } |
| return to; |
| } |
| |
| private String getNameFromID(long id) throws IOException { |
| return getNameFromID(new Long(id)); |
| } |
| |
| private String getNameFromID(Long id) throws IOException { |
| if (id.longValue() == 0L) { |
| return ""; |
| } |
| String result = names.get(id); |
| if (result == null) { |
| warn("Name not found at " + toHex(id.longValue())); |
| return "unresolved name " + toHex(id.longValue()); |
| } |
| return result; |
| } |
| |
| private StackTrace getStackTraceFromSerial(int ser) throws IOException { |
| if (stackTraces == null) { |
| return null; |
| } |
| StackTrace result = stackTraces.get(new Integer(ser)); |
| if (result == null) { |
| warn("Stack trace not found for serial # " + ser); |
| } |
| return result; |
| } |
| |
| // |
| // Handle a HPROF_GC_CLASS_DUMP |
| // Return number of bytes read |
| // |
| private int readClass() throws IOException { |
| long id = readID(); |
| StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); |
| long superId = readID(); |
| long classLoaderId = readID(); |
| long signersId = readID(); |
| long protDomainId = readID(); |
| long reserved1 = readID(); |
| long reserved2 = readID(); |
| int instanceSize = in.readInt(); |
| int bytesRead = 7 * identifierSize + 8; |
| |
| int numConstPoolEntries = in.readUnsignedShort(); |
| bytesRead += 2; |
| for (int i = 0; i < numConstPoolEntries; i++) { |
| int index = in.readUnsignedShort(); // unused |
| bytesRead += 2; |
| bytesRead += readValue(null); // We ignore the values |
| } |
| |
| int numStatics = in.readUnsignedShort(); |
| bytesRead += 2; |
| JavaThing[] valueBin = new JavaThing[1]; |
| JavaStatic[] statics = new JavaStatic[numStatics]; |
| for (int i = 0; i < numStatics; i++) { |
| long nameId = readID(); |
| bytesRead += identifierSize; |
| byte type = in.readByte(); |
| bytesRead++; |
| bytesRead += readValueForType(type, valueBin); |
| String fieldName = getNameFromID(nameId); |
| if (version >= VERSION_JDK12BETA4) { |
| type = signatureFromTypeId(type); |
| } |
| String signature = "" + ((char) type); |
| JavaField f = new JavaField(fieldName, signature); |
| statics[i] = new JavaStatic(f, valueBin[0]); |
| } |
| |
| int numFields = in.readUnsignedShort(); |
| bytesRead += 2; |
| JavaField[] fields = new JavaField[numFields]; |
| for (int i = 0; i < numFields; i++) { |
| long nameId = readID(); |
| bytesRead += identifierSize; |
| byte type = in.readByte(); |
| bytesRead++; |
| String fieldName = getNameFromID(nameId); |
| if (version >= VERSION_JDK12BETA4) { |
| type = signatureFromTypeId(type); |
| } |
| String signature = "" + ((char) type); |
| fields[i] = new JavaField(fieldName, signature); |
| } |
| String name = classNameFromObjectID.get(new Long(id)); |
| if (name == null) { |
| warn("Class name not found for " + toHex(id)); |
| name = "unknown-name@" + toHex(id); |
| } |
| JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId, |
| protDomainId, fields, statics, |
| instanceSize); |
| snapshot.addClass(id, c); |
| snapshot.setSiteTrace(c, stackTrace); |
| |
| return bytesRead; |
| } |
| |
| private String toHex(long addr) { |
| return com.sun.tools.hat.internal.util.Misc.toHex(addr); |
| } |
| |
| // |
| // Handle a HPROF_GC_INSTANCE_DUMP |
| // Return number of bytes read |
| // |
| private int readInstance() throws IOException { |
| long start = in.position(); |
| long id = readID(); |
| StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); |
| long classID = readID(); |
| int bytesFollowing = in.readInt(); |
| int bytesRead = (2 * identifierSize) + 8 + bytesFollowing; |
| JavaObject jobj = new JavaObject(classID, start); |
| skipBytes(bytesFollowing); |
| snapshot.addHeapObject(id, jobj); |
| snapshot.setSiteTrace(jobj, stackTrace); |
| return bytesRead; |
| } |
| |
| // |
| // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP |
| // Return number of bytes read |
| // |
| private int readArray(boolean isPrimitive) throws IOException { |
| long start = in.position(); |
| long id = readID(); |
| StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); |
| int num = in.readInt(); |
| int bytesRead = identifierSize + 8; |
| long elementClassID; |
| if (isPrimitive) { |
| elementClassID = in.readByte(); |
| bytesRead++; |
| } else { |
| elementClassID = readID(); |
| bytesRead += identifierSize; |
| } |
| |
| // Check for primitive arrays: |
| byte primitiveSignature = 0x00; |
| int elSize = 0; |
| if (isPrimitive || version < VERSION_JDK12BETA4) { |
| switch ((int)elementClassID) { |
| case T_BOOLEAN: { |
| primitiveSignature = (byte) 'Z'; |
| elSize = 1; |
| break; |
| } |
| case T_CHAR: { |
| primitiveSignature = (byte) 'C'; |
| elSize = 2; |
| break; |
| } |
| case T_FLOAT: { |
| primitiveSignature = (byte) 'F'; |
| elSize = 4; |
| break; |
| } |
| case T_DOUBLE: { |
| primitiveSignature = (byte) 'D'; |
| elSize = 8; |
| break; |
| } |
| case T_BYTE: { |
| primitiveSignature = (byte) 'B'; |
| elSize = 1; |
| break; |
| } |
| case T_SHORT: { |
| primitiveSignature = (byte) 'S'; |
| elSize = 2; |
| break; |
| } |
| case T_INT: { |
| primitiveSignature = (byte) 'I'; |
| elSize = 4; |
| break; |
| } |
| case T_LONG: { |
| primitiveSignature = (byte) 'J'; |
| elSize = 8; |
| break; |
| } |
| } |
| if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) { |
| throw new IOException("Unrecognized typecode: " |
| + elementClassID); |
| } |
| } |
| if (primitiveSignature != 0x00) { |
| int size = elSize * num; |
| bytesRead += size; |
| JavaValueArray va = new JavaValueArray(primitiveSignature, start); |
| skipBytes(size); |
| snapshot.addHeapObject(id, va); |
| snapshot.setSiteTrace(va, stackTrace); |
| } else { |
| int sz = num * identifierSize; |
| bytesRead += sz; |
| JavaObjectArray arr = new JavaObjectArray(elementClassID, start); |
| skipBytes(sz); |
| snapshot.addHeapObject(id, arr); |
| snapshot.setSiteTrace(arr, stackTrace); |
| } |
| return bytesRead; |
| } |
| |
| private byte signatureFromTypeId(byte typeId) throws IOException { |
| switch (typeId) { |
| case T_CLASS: { |
| return (byte) 'L'; |
| } |
| case T_BOOLEAN: { |
| return (byte) 'Z'; |
| } |
| case T_CHAR: { |
| return (byte) 'C'; |
| } |
| case T_FLOAT: { |
| return (byte) 'F'; |
| } |
| case T_DOUBLE: { |
| return (byte) 'D'; |
| } |
| case T_BYTE: { |
| return (byte) 'B'; |
| } |
| case T_SHORT: { |
| return (byte) 'S'; |
| } |
| case T_INT: { |
| return (byte) 'I'; |
| } |
| case T_LONG: { |
| return (byte) 'J'; |
| } |
| default: { |
| throw new IOException("Invalid type id of " + typeId); |
| } |
| } |
| } |
| |
| private void handleEOF(EOFException exp, Snapshot snapshot) { |
| if (debugLevel > 0) { |
| exp.printStackTrace(); |
| } |
| warn("Unexpected EOF. Will miss information..."); |
| // we have EOF, we have to tolerate missing references |
| snapshot.setUnresolvedObjectsOK(true); |
| } |
| |
| private void warn(String msg) { |
| System.out.println("WARNING: " + msg); |
| } |
| |
| // |
| // A trivial data-holder class for HPROF_GC_ROOT_THREAD_OBJ. |
| // |
| private class ThreadObject { |
| |
| long threadId; |
| int stackSeq; |
| |
| ThreadObject(long threadId, int stackSeq) { |
| this.threadId = threadId; |
| this.stackSeq = stackSeq; |
| } |
| } |
| |
| } |