blob: 8e67c90935c5b989d5912cb5f4a1ff1756b6a0c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP AG - initial API and implementation
* Andrew Johnson (IBM Corporation) - additional properties
*******************************************************************************/
package org.eclipse.mat.hprof;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.hprof.describer.Version;
import org.eclipse.mat.hprof.extension.IRuntimeEnhancer;
import org.eclipse.mat.hprof.ui.HprofPreferences;
import org.eclipse.mat.parser.IObjectReader;
import org.eclipse.mat.parser.index.IIndexReader;
import org.eclipse.mat.parser.index.IndexReader;
import org.eclipse.mat.parser.model.AbstractArrayImpl;
import org.eclipse.mat.parser.model.ObjectArrayImpl;
import org.eclipse.mat.parser.model.PrimitiveArrayImpl;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IPrimitiveArray;
public class HprofHeapObjectReader implements IObjectReader
{
public static final String VERSION_PROPERTY = "hprof.version"; //$NON-NLS-1$
public static final String HPROF_LENGTH_PROPERTY = "hprof.length"; //$NON-NLS-1$
private ISnapshot snapshot;
private HprofRandomAccessParser hprofDump;
private IIndexReader.IOne2LongIndex o2hprof;
private List<IRuntimeEnhancer> enhancers;
public void open(ISnapshot snapshot) throws IOException
{
this.snapshot = snapshot;
Version version = Version.valueOf((String) snapshot.getSnapshotInfo()
.getProperty(VERSION_PROPERTY));
HprofPreferences.HprofStrictness strictnessPreference = HprofPreferences.getCurrentStrictness();
Long olen = (Long)snapshot.getSnapshotInfo().getProperty(HPROF_LENGTH_PROPERTY);
long len = (olen != null) ? olen : -1;
this.hprofDump = new HprofRandomAccessParser(new File(snapshot.getSnapshotInfo().getPath()), //
version, //
snapshot.getSnapshotInfo().getIdentifierSize(), len, strictnessPreference);
this.o2hprof = new IndexReader.LongIndexReader(new File(snapshot.getSnapshotInfo().getPrefix()
+ "o2hprof.index")); //$NON-NLS-1$
this.enhancers = new ArrayList<IRuntimeEnhancer>();
for (EnhancerRegistry.Enhancer enhancer : EnhancerRegistry.instance().delegates())
{
IRuntimeEnhancer runtime = enhancer.runtime();
if (runtime != null)
this.enhancers.add(runtime);
}
}
public long[] readObjectArrayContent(ObjectArrayImpl array, int offset, int length) throws IOException,
SnapshotException
{
Object info = array.getInfo();
if (info instanceof ArrayDescription.Offline)
{
ArrayDescription.Offline description = (ArrayDescription.Offline) info;
long[] answer = (long[]) description.getLazyReadContent();
if (answer == null)
{
answer = hprofDump.readObjectArray(description, offset, length);
// save content if fully read...
if (offset == 0 && length == array.getLength())
description.setLazyReadContent(answer);
return answer;
}
else
{
return (long[]) fragment(array, answer, offset, length);
}
}
else if (info instanceof long[])
{
return (long[]) fragment(array, info, offset, length);
}
else
{
throw new IllegalArgumentException();
}
}
public Object readPrimitiveArrayContent(PrimitiveArrayImpl array, int offset, int length) throws IOException,
SnapshotException
{
Object info = array.getInfo();
if (info instanceof ArrayDescription.Offline)
{
ArrayDescription.Offline description = (ArrayDescription.Offline) info;
Object content = description.getLazyReadContent();
if (content == null)
{
content = convert(array, hprofDump.readPrimitiveArray(description, offset, length));
// save content if fully read...
if (offset == 0 && length == array.getLength())
description.setLazyReadContent(content);
return content;
}
else
{
return fragment(array, content, offset, length);
}
}
else if (info instanceof ArrayDescription.Raw)
{
ArrayDescription.Raw description = (ArrayDescription.Raw) info;
Object content = convert(array, description.getContent());
array.setInfo(content);
return fragment(array, content, offset, length);
}
else
{
return fragment(array, info, offset, length);
}
}
private Object convert(PrimitiveArrayImpl array, byte[] content)
{
int type = array.getType();
if (type == IObject.Type.BYTE)
return content;
int elementSize = IPrimitiveArray.ELEMENT_SIZE[type];
int length = content.length / elementSize;
Object answer = Array.newInstance(IPrimitiveArray.COMPONENT_TYPE[type], length);
int index = 0;
for (int ii = 0; ii < content.length; ii += elementSize)
{
switch (type)
{
case IObject.Type.BOOLEAN:
Array.set(answer, index, content[ii] != 0);
break;
case IObject.Type.CHAR:
Array.set(answer, index, readChar(content, ii));
break;
case IObject.Type.FLOAT:
Array.set(answer, index, readFloat(content, ii));
break;
case IObject.Type.DOUBLE:
Array.set(answer, index, readDouble(content, ii));
break;
case IObject.Type.SHORT:
Array.set(answer, index, readShort(content, ii));
break;
case IObject.Type.INT:
Array.set(answer, index, readInt(content, ii));
break;
case IObject.Type.LONG:
Array.set(answer, index, readLong(content, ii));
break;
}
index++;
}
return answer;
}
private Object fragment(AbstractArrayImpl array, Object content, int offset, int length)
{
if (offset == 0 && length == array.getLength())
return content;
Object answer = Array.newInstance(content.getClass().getComponentType(), length);
System.arraycopy(content, offset, answer, 0, length);
return answer;
}
public IObject read(int objectId, ISnapshot snapshot) throws SnapshotException, IOException
{
long filePosition = o2hprof.get(objectId);
return hprofDump.read(objectId, filePosition, snapshot);
}
/**
* Returns extra data to be provided by
* {@link ISnapshot#getSnapshotAddons(Class addon)}. Also can be returned
* via {@link org.eclipse.mat.query.annotations.Argument}.
*
* @see org.eclipse.mat.parser.IObjectReader#getAddon(Class)
* @param addon
* the type of the extra data required from the dump.
* HprofHeapObjectReader can be extended using an
* {@link IRuntimeEnhancer} extension to return extra data.
* @return the extra data
*/
public <A> A getAddon(Class<A> addon) throws SnapshotException
{
for (IRuntimeEnhancer enhancer : enhancers)
{
A answer = enhancer.getAddon(snapshot, addon);
if (answer != null)
return answer;
}
return null;
}
public void close() throws IOException
{
try
{
hprofDump.close();
}
catch (IOException ignore)
{}
try
{
o2hprof.close();
}
catch (IOException ignore)
{}
}
// //////////////////////////////////////////////////////////////
// conversion routines
// //////////////////////////////////////////////////////////////
private short readShort(byte[] data, int offset)
{
int b1 = (data[offset] & 0xff);
int b2 = (data[offset + 1] & 0xff);
return (short) ((b1 << 8) + b2);
}
private char readChar(byte[] data, int offset)
{
int b1 = (data[offset] & 0xff);
int b2 = (data[offset + 1] & 0xff);
return (char) ((b1 << 8) + b2);
}
private int readInt(byte[] data, int offset)
{
int ch1 = data[offset] & 0xff;
int ch2 = data[offset + 1] & 0xff;
int ch3 = data[offset + 2] & 0xff;
int ch4 = data[offset + 3] & 0xff;
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
private float readFloat(byte[] data, int offset)
{
return Float.intBitsToFloat(readInt(data, offset));
}
private long readLong(byte[] data, int offset)
{
return ((((long) data[offset] & 0xff) << 56) + //
((long) (data[offset + 1] & 0xff) << 48) + //
((long) (data[offset + 2] & 0xff) << 40) + //
((long) (data[offset + 3] & 0xff) << 32) + //
((long) (data[offset + 4] & 0xff) << 24) + //
((data[offset + 5] & 0xff) << 16) + //
((data[offset + 6] & 0xff) << 8) + //
((data[offset + 7] & 0xff) << 0));
}
private double readDouble(byte[] data, int offset)
{
return Double.longBitsToDouble(readLong(data, offset));
}
}