/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.kato.hprof.datalayer;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.kato.common.BitMaskMappingArray;
import org.apache.kato.common.DataProviderBackedArrayProvider;
import org.apache.kato.common.DataReader;
import org.apache.kato.common.IArrayEntryProvider;
import org.apache.kato.common.IDataProvider;
import org.apache.kato.common.InvalidFileFormat;
import org.apache.kato.common.SubsetDataProvider;
import org.apache.kato.hprof.HProfRecordFormatter;
public class HProfFile {
class ControlSettingsHProfRecord extends HProfRecord implements
IControlSettingsHProfRecord {
public ControlSettingsHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public short getStackTraceDepth() {
return readShort(4);
}
public boolean isAllocTrace() {
return (readInt(0) & 1) == 1;
}
public boolean isCpuSampling() {
return (readInt(0) & 2) == 2;
}
}
class EndThreadHProfRecord extends HProfRecord implements
IThreadEndingHProfRecord {
public EndThreadHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public int getThreadSerialNumber() {
return readInt(0);
}
}
class FrameHProfRecord extends HProfRecord implements
IJavaStackFrameHProfRecord {
public FrameHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public int getClassSerialNumber() {
return readInt(idSize * 4);
}
public int getLineNumber() {
return readInt((idSize * 4) + 4);
}
public long getMethodNameID() {
return readNumber(idSize, idSize);
}
public long getMethodSignatureID() {
return readNumber(idSize + idSize, idSize);
}
public long getSourceFileNameID() {
return readNumber(idSize + idSize + idSize, idSize);
}
public long getStackFrameID() {
return readNumber(0, idSize);
}
/**
* Check equality based on stack frame ID.
*/
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof FrameHProfRecord)) {
return false;
}
FrameHProfRecord record = (FrameHProfRecord) obj;
return record.getStackFrameID() == getStackFrameID();
}
}
class HeapDumpHProfRecord extends HProfRecord implements
IHeapDumpHProfRecord {
private BitMaskMappingArray subRecordArray = null;
private int subRecordCount = -1;
private long size = 0;
private IDataProvider myP = null;
public HeapDumpHProfRecord(short tag, IDataProvider dataProvider)
throws IOException {
super(tag, null);
size = dataProvider.getDataLength();
myP = dataProvider;
subRecordArray = new BitMaskMappingArray(1000, 25,
new HProfDumpRecordArrayProvider(dataProvider));
}
public IHProfRecord getSubRecord(int index) {
return (IHProfRecord) subRecordArray.get(index);
}
@Override
public int getSubRecordCount() {
if (subRecordCount == -1) {
subRecordCount = 0;
while (true) {
if (subRecordCount == 5) {
int a = 1;
}
Object o = subRecordArray.get(subRecordCount);
if (o == null)
break;
subRecordCount++;
if (o instanceof UnknownHProfRecord) {
break;
}
}
}
return subRecordCount;
}
@Override
public long getDataLength() {
return (int) size;
}
@Override
public byte[] getData() {
try {
return peek(myP, 20);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public long getSubRecordLocation(int index) {
return subRecordArray.getLocation(index);
}
@Override
/**
* Return a record by its locations
*
* @param location
* @return IHProfRecord at given location
*/
public IHProfRecord getSubRecordByLocation(long location) {
return (IHProfRecord) subRecordArray.getAtLocation(location);
}
}
private final class HProfDumpRecordArrayProvider extends
DataProviderBackedArrayProvider {
private HProfDumpRecordArrayProvider(IDataProvider provider) {
super(provider);
}
private IHProfRecord buildClassDumpRecord(int tag,
IDataProvider dumpRecordProvider) throws IOException {
long loc = dumpRecordProvider.getCurrentLocation();
dumpRecordProvider.moveBy((idSize * 7) + 8);
short elements = dumpRecordProvider.readU2(); // size of constant
// pool
int constantPoolIndex = (int)(dumpRecordProvider.getCurrentLocation()
- loc);
for (int i = 0; i < elements; i++) {
short cpIndex = dumpRecordProvider.readU2();
short type = dumpRecordProvider.readByte();
int arrayLength = sizes.getLengthFromType(type);
dumpRecordProvider.moveBy(arrayLength);
}
short staticFields = dumpRecordProvider.readU2();
int staticFieldsIndex = (int)(dumpRecordProvider.getCurrentLocation()- loc);
for (int i = 0; i < staticFields; i++) {
long index = DataReader.readNumber(dumpRecordProvider, idSize);
short type = dumpRecordProvider.readByte();
int arrayLength = sizes.getLengthFromType(type);
dumpRecordProvider.moveBy(arrayLength);
}
short instFields = dumpRecordProvider.readU2();
int instFieldsIndex = (int) (dumpRecordProvider.getCurrentLocation()- loc);
for (int i = 0; i < instFields; i++) {
long index = DataReader.readNumber(dumpRecordProvider, idSize);
short type = dumpRecordProvider.readByte();
}
long datalength = dumpRecordProvider.getCurrentLocation() - loc;
SubsetDataProvider p = new SubsetDataProvider(dumpRecordProvider,
loc, datalength);
// Pass indices of static and instance fields - they are variable length.
return new GCClassHeapDumpRecord((byte) tag, p, staticFieldsIndex, instFieldsIndex );
}
@Override
public Object getCurrentElement() {
// read tag
try {
long l = getProvider().getCurrentLocation();
IDataProvider p = getProvider();
if (p.dataRemaining() < 1)
return null;
short tag = p.readByte();
int size = getRecordSize(tag);
byte[] data = null;
if (size >= 0) {
data = getProvider().readBytes(size);
}
switch (tag) {
case 0xFF: // unknown root
return new GCRootUnknownHeapRecord(tag, data);
case 0x01: // jni global root
return new GCRootJNIGlobalRefHeapRecord(tag, data);
case 0x02: // jni local
return new GCRootJNILocalHeapRecord(tag, data);
case 0x03: // java frame
return new GCRootJavaFrameHeapRecord(tag, data);
case 0x04: // native stack root
return new GCRootNativeStackHeapRecord(tag, data);
case 0x05: // stick class root
return new GCRootStickyClassHeapRecord(tag, data);
case 0x06: // thread block root
return new GCRoothreadBlockHeapRecord(tag, data);
case 0x07: // monitor used
return new GCRootMonitorUsedHeapRecord(tag, data);
case 0x08: // thread obj root
return new GCRootThreadObjHeapRecord(tag, data);
case 0x20: // class dump
return buildClassDumpRecord(tag, getProvider());
case 0x21: // instance
return buildInstanceDumpRecord(tag, getProvider());
case 0x22: // obj array
return buildObjArrayDumpRecord(tag, getProvider());
case 0x23: // primitive array
return buildPrimitiveArrayDumpRecord(tag, getProvider());
default:
// don't know what this is
return new UnknownHProfRecord(tag, null);
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private Object buildPrimitiveArrayDumpRecord(short tag,
IDataProvider arrayProvider) throws IOException {
long loc = arrayProvider.getCurrentLocation();
arrayProvider.moveBy(idSize + 4);
int elements = arrayProvider.readU4();
short elementType = arrayProvider.readByte();
int elementLength = sizes.getLengthFromType(elementType);
int datalength = idSize + 9 + (elementLength * elements);
arrayProvider.moveTo(loc);
SubsetDataProvider p = new SubsetDataProvider(arrayProvider,
datalength);
arrayProvider.moveTo(loc + datalength);
return new GCPrimitiveArrayHeapDumpRecord(tag, p);
}
/**
*
* id array object ID u4 stack trace serial number u4 number of elements
* id array class ID [id]* elements
*
* @param tag
* @param arrayProvider
* @return
* @throws IOException
*/
private Object buildObjArrayDumpRecord(short tag,
IDataProvider arrayProvider) throws IOException {
long loc = arrayProvider.getCurrentLocation();
arrayProvider.moveBy(idSize + 4);
int elements = arrayProvider.readU4();
int datalength = (idSize * 2) + 8 + (idSize * elements);
arrayProvider.moveTo(loc);
SubsetDataProvider p = new SubsetDataProvider(arrayProvider,
datalength);
arrayProvider.moveTo(loc + datalength);
return new GCObjectArrayHeapDumpRecord(tag, p);
}
/**
* id object ID u4 stack trace serial number id class object ID u4
* number of bytes that follow [vl]* instance field values (class,
* followed by super, super's super ...)
*
* @param tag
* @param arrayProvider
* @return
* @throws IOException
*/
private Object buildInstanceDumpRecord(short tag,
IDataProvider arrayProvider) throws IOException {
long loc = arrayProvider.getCurrentLocation();
arrayProvider.moveBy((idSize * 2) + 4);
int byteCount = arrayProvider.readU4();
int datalength = (idSize * 2) + 8 + byteCount;
arrayProvider.moveTo(loc);
SubsetDataProvider p = new SubsetDataProvider(arrayProvider,
datalength);
arrayProvider.moveTo(loc + datalength);
return new GCInstanceHeapDumpRecord(tag, p);
}
/**
* Returns record size for given tag.
*
* @param tag -
* tag to size
* @return record size or -1 if dynamic, -2 if unknown tag
*/
private int getRecordSize(int tag) {
int recordLength = 0;
switch (tag) {
case 0xFF: // unknown root
recordLength = idSize;
break;
case 0x01: // jni global root
recordLength = idSize * 2;
break;
case 0x02: // jni local
recordLength = idSize + 8;
break;
case 0x03: // java frame
recordLength = idSize + 8;
break;
case 0x04: // native stack root
recordLength = idSize + 4;
break;
case 0x05: // stick class root
recordLength = idSize;
break;
case 0x06: // thread block root
recordLength = idSize + 4;
break;
case 0x07: // monitor used
recordLength = idSize;
break;
case 0x08: // thread obj root
recordLength = idSize + 8;
break;
case 0x20: // class dump
case 0x21: // instance
case 0x22: // obj array
case 0x23: // primitive array
recordLength = -1;
break;
default:
// don't know what this is
recordLength = -2;
}
return recordLength;
}
}
private final class HPROFRecordProvider implements IArrayEntryProvider {
private IDataProvider recordProvider = null;
public HPROFRecordProvider(IDataProvider provider) {
this.recordProvider = provider;
}
@Override
public IHProfRecord getCurrentElement() {
// read tag
try {
short tag = recordProvider.readByte();
int ms = recordProvider.readU4();
int left = recordProvider.readU4();
byte[] data = null;
switch (tag) {
case 0x0c:
break;
default:
data = recordProvider.readBytes(left);
}
switch (tag) {
case 0x01:
return new UTF8HProfRecordImpl(tag, data);
case 0x02:
return new LoadClassHProfRecord(tag, data);
case 0x03:
return new UnloadClassHProfRecord(tag, data);
case 0x04:
return new FrameHProfRecord(tag, data);
case 0x05:
return new TraceHProfRecord(tag, data);
case 0x0e:
return new ControlSettingsHProfRecord(tag, data);
case 0x0a:
return new StartThreadHProfRecord(tag, data);
case 0x0b:
return new EndThreadHProfRecord(tag, data);
case 0x0c:
IHeapDumpHProfRecord rec = new HeapDumpHProfRecord(tag,
new SubsetDataProvider(recordProvider, left));
recordProvider.moveBy(left);
return rec;
default:
// don't know what the thing is - return an unknown
// type.
return new UnknownHProfRecord(tag, data);
}
} catch (IOException e) {
return null;
}
}
@Override
public long getCurrentLocation() {
try {
return recordProvider.getCurrentLocation();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
@Override
public void getState(List state) {
state.add("HProfRecordProvider");
recordProvider.addState(state);
}
@Override
public boolean moveRelativeElement(int seekNo) {
while (seekNo > 0) {
Object o = getCurrentElement();
if (o == null)
return false;
seekNo--;
}
return true;
}
@Override
public boolean moveToLocation(long l) {
try {
recordProvider.moveTo(l);
return true;
} catch (IOException e) {
return false;
}
}
}
class LoadClassHProfRecord extends HProfRecord implements
ILoadClassHProfRecord {
public LoadClassHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public long getClassNameID() {
return readNumber(8 + idSize, idSize);
}
public long getClassObjectID() {
return readNumber(4, idSize);
}
public int getClassSerialNumber() {
return readInt(0);
}
public int getStackTraceSerialNumber() {
return readInt(4 + idSize);
}
}
class UnloadClassHProfRecord extends HProfRecord implements
IUnloadClassHProfRecord {
public UnloadClassHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public int getClassSerialNumber() {
return readInt(0);
}
}
class StartThreadHProfRecord extends HProfRecord implements
IThreadActiveHProfRecord {
public StartThreadHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public int getStackTraceSerialNumber() {
return readInt(idSize + 4);
}
public long getThreadGroupNameID() {
return readNumber(8 + (idSize * 2), idSize);
}
public long getThreadGroupParentNameID() {
return readNumber(8 + (idSize * 3), idSize);
}
public long getThreadNameID() {
return readNumber(8 + idSize, idSize);
}
public long getThreadObjectID() {
return readNumber(4, idSize);
}
public int getThreadSerialNumber() {
return readInt(0);
}
}
class TraceHProfRecord extends HProfRecord implements
IJavaStackTraceHProfRecord {
public TraceHProfRecord(short tag, byte[] data) {
super(tag, data);
}
public int getFrameCount() {
return readInt(8);
}
public int getSerialNumber() {
return readInt(0);
}
public long getStackFrameID(int elementNo) {
return readNumber((idSize * elementNo) + 12, idSize);
}
public int getThreadSerialNumber() {
return readInt(4);
}
}
class UnknownHProfRecord extends HProfRecord {
public UnknownHProfRecord(short tag, byte[] data) {
super(tag, data);
}
}
class UTF8HProfRecordImpl extends HProfRecord implements IUTF8HProfRecord {
public UTF8HProfRecordImpl(short tag, byte[] data) {
super(tag, data);
}
@Override
public String getAsString() {
try {
return new String(getCharacters(), "UTF-8");
} catch (UnsupportedEncodingException e) {
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.apache.kato.hprof.IUTF8HProfRecord#getCharacters()
*/
public byte[] getCharacters() {
int length = ((int) getDataLength()) - idSize;
return readBytes(idSize, length);
}
/*
* (non-Javadoc)
*
* @see org.apache.kato.hprof.IUTF8HProfRecord#getNameID()
*/
public long getNameID() {
return readNumber(0, idSize);
}
}
private BitMaskMappingArray array = null;
private String header = null;
private Integer idSize = null;
private HProfDataTypeSizes sizes = null;
private IDataProvider mainProvider = null;
private int records = -1;
private long recordsOffset = 0;
private Long timeStamp = null;
private File source=null;
public HProfFile(IDataProvider provider) {
this.mainProvider = provider;
}
public byte[] peek(IDataProvider dumpRecordProvider, int i)
throws IOException {
long loc = dumpRecordProvider.getCurrentLocation();
long bytesLeft = dumpRecordProvider.getDataLength()
- dumpRecordProvider.getCurrentLocation();
if (i > bytesLeft)
i = (int) bytesLeft;
byte[] b = dumpRecordProvider.readBytes(i);
dumpRecordProvider.moveTo(loc);
return b;
}
private void buildHeader() throws IOException {
try {
header = mainProvider.readCString();
} catch (IOException ioe) {
throw new InvalidFileFormat("File is not an HPROF file", ioe);
}
if (header.equals("JAVA PROFILE 1.0.1") == false
&& header.equals("JAVA PROFILE 1.0.2") == false) {
throw new InvalidFileFormat("File is not an HPROF file");
}
try {
idSize = mainProvider.readU4();
timeStamp = mainProvider.readU8();
} catch (IOException ioe) {
throw new InvalidFileFormat("HPROF file is truncated", ioe);
}
sizes = new HProfDataTypeSizes(idSize);
recordsOffset = mainProvider.getCurrentLocation();
long maxOffsetLength = mainProvider.getDataLength() - recordsOffset;
SubsetDataProvider subset = new SubsetDataProvider(mainProvider,
maxOffsetLength);
array = new BitMaskMappingArray(1000, 25, new HPROFRecordProvider(
subset));
}
public void close() throws IOException {
mainProvider.close();
}
public String getHeader() {
return header;
}
public Integer getIdentifierSize() {
return idSize;
}
public long getRecordLocation(int index) {
if (index < 0)
throw new IllegalArgumentException("record index [" + index
+ "] is less than zero");
return array.getLocation(index) + recordsOffset + 9; // add 9 for
// header bytes
// of heap dump
// record
}
public IHProfRecord getRecord(int index) {
if (index < 0)
throw new IllegalArgumentException("record index [" + index
+ "] is less than zero");
return (IHProfRecord) array.get(index);
}
public int getRecordCount() {
if (records < 0)
records = readRecords();
return records;
}
public Long getTimeStamp() {
return timeStamp;
}
public void open() throws IOException {
mainProvider.open();
buildHeader();
}
private int readRecords() {
int counter = 0;
IHProfRecord record = null;
while (true) {
record = getRecord(counter);
if (record == null)
break;
counter++;
}
return counter;
}
class GCRoothreadBlockHeapRecord extends HProfRecord {
public GCRoothreadBlockHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getId() {
return readNumber(0, idSize);
}
public int getThreadSerialNumber() {
return readInt(idSize);
}
}
class GCRootJavaFrameHeapRecord extends HProfRecord {
public GCRootJavaFrameHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getId() {
return readNumber(0, idSize);
}
public int getThreadSerialNumber() {
return readInt(idSize);
}
public int getStackFrameNumber() {
return readInt(idSize + 4);
}
}
class GCRootJNIGlobalRefHeapRecord extends HProfRecord {
public GCRootJNIGlobalRefHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getObjid() {
return readNumber(0, idSize);
}
public long getJniid() {
return readNumber(idSize, idSize);
}
}
class GCRootJNILocalHeapRecord extends HProfRecord {
public GCRootJNILocalHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getId() {
return readNumber(0, idSize);
}
public int getThreadSerialNumber() {
return readInt(idSize);
}
public int getStackFrameNumber() {
return readInt(idSize + 4);
}
}
public abstract class IDEntryHeapRecord extends HProfRecord {
public IDEntryHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getID() {
return readNumber(0, idSize);
}
}
class GCRootMonitorUsedHeapRecord extends IDEntryHeapRecord {
public GCRootMonitorUsedHeapRecord(short tag, byte[] data) {
super(tag, data);
}
}
class GCRootStickyClassHeapRecord extends IDEntryHeapRecord {
public GCRootStickyClassHeapRecord(short tag, byte[] data) {
super(tag, data);
}
}
class GCRootUnknownHeapRecord extends HProfRecord {
public GCRootUnknownHeapRecord(short tag, byte[] data) {
super(tag, data);
}
}
class GCRootThreadObjHeapRecord extends HProfRecord {
public GCRootThreadObjHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getId() {
return readNumber(0, idSize);
}
public int getThreadSequenceNumber() {
return readInt(idSize);
}
public int getStackTraceSequenceNumber() {
return readInt(idSize + 4);
}
}
class GCRootNativeStackHeapRecord extends HProfRecord {
public GCRootNativeStackHeapRecord(short tag, byte[] data) {
super(tag, data);
}
public long getId() {
return readNumber(0, idSize);
}
public int getThreadSequenceNumber() {
return readInt(idSize);
}
}
abstract class LightHeapEntryRecord implements IHProfRecord {
private IDataProvider lightProvider = null;
private short tag = 0;
public LightHeapEntryRecord(short tag, IDataProvider provider) {
this.tag = tag;
this.lightProvider = provider;
}
@Override
public byte[] getData() {
try {
return peek(lightProvider, (int)Math.min(200, this.getDataLength()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public short getTag() {
return tag;
}
public long getDataLength() {
try {
return lightProvider.getDataLength();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
public IDataProvider getProvider() {
return lightProvider;
}
protected long readLong(int offset) {
try {
lightProvider.moveTo(offset);
return lightProvider.readU8();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
protected int readInt(int offset) {
try {
lightProvider.moveTo(offset);
return lightProvider.readU4();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
protected short readByte(int offset) {
try {
lightProvider.moveTo(offset);
return lightProvider.readByte();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
protected char readChar(int offset) {
try {
lightProvider.moveTo(offset);
return (char)lightProvider.readU2();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (char)-1;
}
protected short readShort(int offset) {
try {
lightProvider.moveTo(offset);
return lightProvider.readU2();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
protected long readNumber(int offset, int length) {
try {
lightProvider.moveTo(offset);
return DataReader.readNumber(lightProvider, length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
protected byte[] readBytes(int offset, int length) {
try {
lightProvider.moveTo(offset);
return lightProvider.readBytes(length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
StringBuffer output = new StringBuffer("LightHeapRecord: Tag: 0x");
output.append(Long.toHexString(getTag()));
output.append(", length:");
output.append(getDataLength());
output.append(", Data:");
for (int i=0; i < getDataLength(); i++) {
short val = (short) (((short)readByte(i))&0xff);
if (val <= 0xf) {
output.append('0');
}
output.append(Integer.toHexString(val));
}
return output.toString();
}
}
class GCPrimitiveArrayHeapDumpRecord extends LightHeapEntryRecord implements IGCPrimitiveArrayHeapDumpRecord {
public GCPrimitiveArrayHeapDumpRecord(short tag, IDataProvider provider) {
super(tag, provider);
}
@Override
public long getID() {
return readNumber(0, idSize);
}
@Override
public int getStackTraceNumber() {
return readInt(idSize);
}
@Override
public int getNumberOfElements() {
return readInt(idSize + 4);
}
@Override
public short getElementType() {
return readByte(idSize + 8);
}
@Override
public boolean getBooleanElement(int index) {
return getByteElement(index) != 0;
}
@Override
public short getByteElement(int index) {
return readByte(idSize+9+index);
}
@Override
public short getShortElement(int index) {
return readShort(idSize+ 9 + index * 2);
}
@Override
public char getCharElement(int index) {
return readChar(idSize+ 9 + index * 2);
}
@Override
public int getIntElement(int index) {
return readInt(idSize+ 9 + index * 4);
}
@Override
public long getLongElement(int index) {
return readLong(idSize+ 9 + index * 8);
}
@Override
public float getFloatElement(int index) {
return Float.intBitsToFloat(getIntElement(index));
}
@Override
public double getDoubleElement(int index) {
long lval =getLongElement(index);
return Double.longBitsToDouble(lval);
}
}
/**
* An array of references on the heap.
*
*/
class GCObjectArrayHeapDumpRecord extends LightHeapEntryRecord implements IGCObjectArrayHeapDumpRecord {
public GCObjectArrayHeapDumpRecord(short tag, IDataProvider provider) {
super(tag, provider);
}
@Override
public long getID() {
return readNumber(0, idSize);
}
@Override
public int getStackTraceNumber() {
return readInt(idSize);
}
@Override
public int getNumberOfElements() {
return readInt(idSize + 4);
}
@Override
public long getArrayClassObjectID() {
return readNumber(idSize + 8, idSize);
}
@Override
public long getElement(int index) {
return readNumber(idSize*2+8 + index * idSize, idSize);
}
@Override
public String toString() {
StringBuffer output = new StringBuffer(super.toString());
output.append("\nReferences: ");
for (int i=0; i < getNumberOfElements(); i++) {
output.append("0x").append(Long.toHexString(getElement(i))).append(' ');
}
return output.toString();
}
}
/**
* An ordinary object instance on the heap.
*
*/
class GCInstanceHeapDumpRecord extends LightHeapEntryRecord implements IGCInstanceHeapDumpRecord {
public GCInstanceHeapDumpRecord(short tag, IDataProvider provider) {
super(tag, provider);
}
public long getID() {
return readNumber(0, idSize);
}
public int getStackTraceNumber() {
return readInt(idSize);
}
public long getClassObjectID() {
return readNumber(idSize + 4, idSize);
}
public int getNumberOfValueBytes() {
return readInt((idSize * 2) + 4);
}
@Override
public byte getByteField(int offset) {
return (byte) readByte((idSize*2 + 8) + offset);
}
@Override
public char getCharField(int offset) {
return readChar((idSize*2 + 8) + offset);
}
@Override
public double getDoubleField(int offset) {
long lval = readLong((idSize*2 + 8) + offset);
//lval = (( 0x00000000ffffffffl & lval) << 32) | (((0xffffffff00000000l) & lval) >>>32);
return Double.longBitsToDouble(lval);
}
@Override
public float getFloatField(int offset) {
return Float.intBitsToFloat(readInt((idSize*2 + 8) + offset));
}
@Override
public long getIDField(int offset) {
return readNumber((idSize*2 + 8) + offset,idSize);
}
@Override
public int getIntegerField(int offset) {
return readInt((idSize*2 + 8) + offset);
}
@Override
public long getLongField(int offset) {
return readLong((idSize*2 + 8) + offset);
}
@Override
public short getShortField(int offset) {
return readShort((idSize*2 + 8) + offset);
}
@Override
public boolean getBooleanField(int offset) {
return !(getByteField(offset) == 0);
}
}
public class GCClassHeapDumpRecord extends LightHeapEntryRecord implements IGCClassHeapDumpRecord, IHeapObject {
private int staticFieldsOffset;
private int instFieldsOffset;
public GCClassHeapDumpRecord(byte tag, IDataProvider provider
, int staticFieldsOffset, int instFieldsOffset ) {
super(tag, provider);
this.staticFieldsOffset = staticFieldsOffset;
this.instFieldsOffset = instFieldsOffset;
}
public long getID() {
return readNumber(0, idSize);
}
public int getStackTraceNumber() {
return readInt(idSize);
}
public long getSuperClassObjectID() {
return readNumber(idSize + 4, idSize);
}
public long getClassLoaderObjectID() {
return readNumber((idSize * 2) + 4, idSize);
}
public long getSignersObjectID() {
return readNumber((idSize * 3) + 4, idSize);
}
public long getProtectionDomainObjectID() {
return readNumber((idSize * 4) + 4, idSize);
}
public int getInstanceSize() {
return readInt((idSize * 7) + 4);
}
public int getConstantPoolSize() {
return readShort((idSize * 7) + 8);
}
public ConstantPoolEntry[] getConstantPool() {
return readConstantPool((idSize * 7) + 10);
}
private ConstantPoolEntry[] readConstantPool(int offset) {
int entries = getConstantPoolSize();
ConstantPoolEntry[] pool = new ConstantPoolEntry[entries];
for (int i = 0; i < entries; i++) {
ConstantPoolEntry cpe = new ConstantPoolEntry();
cpe.cpIndex = readShort(offset);
offset += 2;
cpe.type = readByte(offset);
offset++;
int valueLength = sizes.getLengthFromType(cpe.type);
cpe.data = readBytes(offset, valueLength);
offset += valueLength;
pool[i] = cpe;
}
return pool;
}
public StaticFieldEntry[] getStaticFields() {
return readStaticFields(staticFieldsOffset);
}
private StaticFieldEntry[] readStaticFields(int offset) {
short staticFields = readShort(offset-2);
StaticFieldEntry[] fields = new StaticFieldEntry[staticFields];
for (int i = 0; i < staticFields; i++) {
long stringID = readNumber(offset,idSize);
offset += idSize;
short type = readByte(offset);
offset+=1;
int arrayLength = sizes.getLengthFromType(type);
long data = readNumber(offset, arrayLength);
offset += arrayLength;
fields[i] = new StaticFieldEntry(stringID, type, data);
}
return fields;
}
public InstanceFieldEntry[] getInstanceFields() {
return readInstanceFields(instFieldsOffset);
}
private InstanceFieldEntry[] readInstanceFields(int offset) {
short instFields = readShort(offset-2);
int fieldOffset = 0;
InstanceFieldEntry[] fields = new InstanceFieldEntry[instFields];
for (int i = 0; i < instFields; i++) {
long stringID = readNumber(offset,idSize);
offset += idSize;
short type = readByte(offset);
offset+=1;
fields[i] = new InstanceFieldEntry(stringID, type, fieldOffset);
fieldOffset += sizes.getLengthFromType(type);
}
offsetSize = fieldOffset;
return fields;
}
private int offsetSize=-1;
@Override
public int getOffsetSize() {
if(offsetSize == -1) {
getInstanceFields();
}
return offsetSize;
}
}
public void setSource(File source) {
this.source=source;
}
public File getSource() {
return source;
}
}