Bug 573598 Display unindexed objects for dumps with discarded objects
Initial code drop. Use ObjectReference to look for unindexed objects.
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=573598
Change-Id: Id78ae91db739dbd1d781a036c33233c3d11490dc
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/HashEntriesQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/HashEntriesQuery.java
index 01431be..5c48ace 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/HashEntriesQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/HashEntriesQuery.java
@@ -355,17 +355,30 @@
for (Map.Entry<IObject, IObject> me : map)
{
Entry e;
+ int keyId,valueId;
+ try
+ {
+ keyId = (me.getKey() != null) ? me.getKey().getObjectId() : -1;
+ }
+ catch (RuntimeException e1)
+ {
+ keyId = -1;
+ }
+ try
+ {
+ valueId = (me.getValue() != null) ? me.getValue().getObjectId() : -1;
+ }
+ catch (RuntimeException e1)
+ {
+ valueId = -1;
+ }
if (me instanceof IObject)
{
IObject meObject = (IObject) me;
- int keyId = (me.getKey() != null) ? me.getKey().getObjectId() : -1;
- int valueId = (me.getValue() != null) ? me.getValue().getObjectId() : -1;
e = new Entry(obj.getObjectId(), obj.getDisplayName(), keyId, valueId);
}
else
{
- int keyId = (me.getKey() != null) ? me.getKey().getObjectId() : -1;
- int valueId = (me.getValue() != null) ? me.getValue().getObjectId() : -1;
e = new Entry(objectId, obj.getDisplayName(), keyId, valueId);
}
hashEntries1.add(e);
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/MapCollisionRatioQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/MapCollisionRatioQuery.java
index 83985d7..ce532c4 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/MapCollisionRatioQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/MapCollisionRatioQuery.java
@@ -16,6 +16,7 @@
import java.util.Arrays;
+import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.HashMapIntLong;
import org.eclipse.mat.collect.HashMapIntObject;
import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils;
@@ -167,7 +168,7 @@
}
}
}
- catch (RuntimeException e)
+ catch (RuntimeException | SnapshotException e)
{
int classId = obj.getClazz().getObjectId();
if (!exceptions.containsKey(classId))
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/PrimitiveArraysWithAConstantValueQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/PrimitiveArraysWithAConstantValueQuery.java
index 2d2faf0..dc59b05 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/PrimitiveArraysWithAConstantValueQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/collections/PrimitiveArraysWithAConstantValueQuery.java
@@ -14,6 +14,7 @@
*******************************************************************************/
package org.eclipse.mat.inspections.collections;
+import java.lang.reflect.Array;
import java.util.Arrays;
import org.eclipse.mat.collect.HashMapIntObject;
@@ -118,17 +119,36 @@
int length = array.getLength();
if (length > 1)
{
+ // Read in chunks as DTFJ is slow for single reads
+ final int BUFSIZE = 16 * 1024;
boolean allSame = true;
- Object value0 = array.getValueAt(0);
- for (int i = 1; i < length; i++)
+ Object value0 = null;
+ for (int i = 0; allSame && i < length;)
{
- Object valueAt = array.getValueAt(i);
- if (valueAt.equals(value0))
- continue;
+ int buflen = Math.min(length - i, BUFSIZE);
+ Object o = array.getValueArray(i, buflen);
+ int j;
+ if (i == 0)
+ {
+ value0 = Array.get(objectIds, 0);
+ j = 1;
+ }
else
{
- allSame = false;
- break;
+ j = 0;
+ }
+ int actlen = Array.getLength(o);
+ i += actlen;
+ for (;j < actlen; ++j)
+ {
+ Object valueAt = Array.get(o, j);
+ if (valueAt.equals(value0))
+ continue;
+ else
+ {
+ allSame = false;
+ break;
+ }
}
}
if (allSame)
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/collectionextract/MapCollectionExtractorBase.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/collectionextract/MapCollectionExtractorBase.java
index 1728258..90ad26c 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/collectionextract/MapCollectionExtractorBase.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/collectionextract/MapCollectionExtractorBase.java
@@ -1,130 +1,141 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2019 SAP AG, IBM Corporation and others
- * 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
- * IBM Corporation - enhancements and fixes
- * James Livingston - expose collection utils as API
- *******************************************************************************/
-package org.eclipse.mat.internal.collectionextract;
-
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-import org.eclipse.mat.SnapshotException;
-import org.eclipse.mat.inspections.collectionextract.IMapExtractor;
-import org.eclipse.mat.snapshot.ISnapshot;
-import org.eclipse.mat.snapshot.model.IObject;
-import org.eclipse.mat.snapshot.model.IObjectArray;
-
-public abstract class MapCollectionExtractorBase implements IMapExtractor
-{
- protected final String keyField;
- protected final String valueField;
-
- public MapCollectionExtractorBase(String keyField, String valueField)
- {
- this.keyField = keyField;
- this.valueField = valueField;
- }
-
- public boolean hasCapacity()
- {
- return hasExtractableArray();
- }
-
- public Integer getCapacity(IObject coll) throws SnapshotException
- {
- IObjectArray table = extractEntries(coll);
- return (table == null) ? null : table.getLength();
- }
-
- public Iterator<Entry<IObject, IObject>> extractMapEntries(IObject coll)
- {
- try
- {
- return new MapEntryIterator(coll.getSnapshot(), coll, extractEntryIds(coll));
- }
- catch (SnapshotException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private class MapEntryIterator implements Iterator<Entry<IObject, IObject>>
- {
- private final ISnapshot snapshot;
- private final int[] ids;
- private int idx;
- private IObject coll;
-
- public MapEntryIterator(ISnapshot snapshot, IObject coll, int[] ids)
- {
- this.snapshot = snapshot;
- this.ids = ids;
- this.idx = 0;
- this.coll = coll;
- }
-
- public boolean hasNext()
- {
- return idx < ids.length;
- }
-
- public Entry<IObject, IObject> next()
- {
- try
- {
- IObject obj = snapshot.getObject(ids[idx++]);
- return new EntryObject(obj, getEntryKey(obj), getEntryValue(obj));
- }
- catch (SnapshotException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- public void remove()
- {
- throw new UnsupportedOperationException();
- }
- }
-
- protected IObject getEntryKey(IObject obj)
- {
- try
- {
- IObject ret = (IObject) obj.resolveValue(keyField);
- if (ret != null)
- {
- return ret;
- }
- else
- {
- return null;
- }
- }
- catch (SnapshotException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- protected IObject getEntryValue(IObject obj)
- {
- try
- {
- return (IObject) obj.resolveValue(valueField);
- }
- catch (SnapshotException e)
- {
- throw new RuntimeException(e);
- }
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others
+ * 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
+ * IBM Corporation - enhancements and fixes
+ * James Livingston - expose collection utils as API
+ *******************************************************************************/
+package org.eclipse.mat.internal.collectionextract;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.inspections.collectionextract.IMapExtractor;
+import org.eclipse.mat.snapshot.ISnapshot;
+import org.eclipse.mat.snapshot.model.IObject;
+import org.eclipse.mat.snapshot.model.IObjectArray;
+
+public abstract class MapCollectionExtractorBase implements IMapExtractor
+{
+ protected final String keyField;
+ protected final String valueField;
+
+ public MapCollectionExtractorBase(String keyField, String valueField)
+ {
+ this.keyField = keyField;
+ this.valueField = valueField;
+ }
+
+ public boolean hasCapacity()
+ {
+ return hasExtractableArray();
+ }
+
+ public Integer getCapacity(IObject coll) throws SnapshotException
+ {
+ IObjectArray table = extractEntries(coll);
+ return (table == null) ? null : table.getLength();
+ }
+
+ public Iterator<Entry<IObject, IObject>> extractMapEntries(IObject coll)
+ {
+ try
+ {
+ return new MapEntryIterator(coll.getSnapshot(), coll, extractEntryIds(coll));
+ }
+ catch (SnapshotException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private class MapEntryIterator implements Iterator<Entry<IObject, IObject>>
+ {
+ private final ISnapshot snapshot;
+ private final int[] ids;
+ private int idx;
+ private IObject coll;
+
+ public MapEntryIterator(ISnapshot snapshot, IObject coll, int[] ids)
+ {
+ this.snapshot = snapshot;
+ this.ids = ids;
+ this.idx = 0;
+ this.coll = coll;
+ }
+
+ public boolean hasNext()
+ {
+ return idx < ids.length;
+ }
+
+ public Entry<IObject, IObject> next()
+ {
+ try
+ {
+ IObject obj = snapshot.getObject(ids[idx++]);
+ return new EntryObject(obj, null, null) {
+ public IObject getKey()
+ {
+ return getEntryKey(obj);
+ }
+ public IObject getValue()
+ {
+ return getEntryValue(obj);
+ }
+ };
+ }
+ catch (SnapshotException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected IObject getEntryKey(IObject obj)
+ {
+ try
+ {
+ IObject ret = (IObject) obj.resolveValue(keyField);
+ if (ret != null)
+ {
+ return ret;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ catch (SnapshotException e)
+ {
+ //throw new RuntimeException(e);
+ return null;
+ }
+ }
+
+ protected IObject getEntryValue(IObject obj)
+ {
+ try
+ {
+ return (IObject) obj.resolveValue(valueField);
+ }
+ catch (SnapshotException e)
+ {
+ //throw new RuntimeException(e);
+ return null;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/Field.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/Field.java
index bec5864..baa6094 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/Field.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/Field.java
@@ -1,10 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2008, 2018 SAP AG and others.
+ * Copyright (c) 2008, 2021 SAP AG and others.
* 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -29,16 +29,18 @@
* @param name the name of the field
* @param type the type {@link IObject.Type}
* @param value
- * value is one of
- * ObjectReference - for an object field
- * Byte - for a byte field
- * Short - for a short field
- * Integer - for an int field
- * Long - for a long field
- * Boolean - for a boolean field
- * Char - for a char field
- * Float - for a float field
- * Double - for a double field
+ * value is one of
+ * <dl>
+ * <dt>{@link ObjectReference}</dt><dd>for an object field</dd>
+ * <dt>{@link Byte}</dt><dd>for a byte field</dd>
+ * <dt>{@link Short}</dt><dd>for a short field</dd>
+ * <dt>{@link Integer}</dt><dd>for an int field</dd>
+ * <dt>{@link Long}</dt><dd>for a long field</dd>
+ * <dt>{@link Boolean}</dt><dd>for a boolean field</dd>
+ * <dt>{@link Character}</dt><dd>for a char field</dd>
+ * <dt>{@link Float}</dt><dd>for a float field</dd>
+ * <dt>{@link Double}</dt><dd>for a double field</dd>
+ * </dl>
*/
public Field(String name, int type, Object value)
{
@@ -49,15 +51,17 @@
/**
* Gets the value of the field.
* @return
- * ObjectReference - for an object field
- * Byte - for a byte field
- * Short - for a short field
- * Integer - for an int field
- * Long - for a long field
- * Boolean - for a boolean field
- * Char - for a char field
- * Float - for a float field
- * Double - for a double field
+ * <dl>
+ * <dt>{@link ObjectReference}</dt><dd>for an object field</dd>
+ * <dt>{@link Byte}</dt><dd>for a byte field</dd>
+ * <dt>{@link Short}</dt><dd>for a short field</dd>
+ * <dt>{@link Integer}</dt><dd>for an int field</dd>
+ * <dt>{@link Long}</dt><dd>for a long field</dd>
+ * <dt>{@link Boolean}</dt><dd>for a boolean field</dd>
+ * <dt>{@link Character}</dt><dd>for a char field</dd>
+ * <dt>{@link Float}</dt><dd>for a float field</dd>
+ * <dt>{@link Double}</dt><dd>for a double field</dd>
+ * </dl>
*/
public Object getValue()
{
@@ -70,15 +74,18 @@
* Currently this is used after deserializing static fields
* to change the object reference to one having a link to the current snapshot.
* @param object
- * ObjectReference - for an object field
- * Byte - for a byte field
- * Short - for a short field
- * Integer - for an int field
- * Long - for a long field
- * Boolean - for a boolean field
- * Char - for a char field
- * Float - for a float field
- * Double - for a double field
+ * object is one of
+ * <dl>
+ * <dt>{@link ObjectReference}</dt><dd>for an object field</dd>
+ * <dt>{@link Byte}</dt><dd>for a byte field</dd>
+ * <dt>{@link Short}</dt><dd>for a short field</dd>
+ * <dt>{@link Integer}</dt><dd>for an int field</dd>
+ * <dt>{@link Long}</dt><dd>for a long field</dd>
+ * <dt>{@link Boolean}</dt><dd>for a boolean field</dd>
+ * <dt>{@link Character}</dt><dd>for a char field</dd>
+ * <dt>{@link Float}</dt><dd>for a float field</dd>
+ * <dt>{@link Double}</dt><dd>for a double field</dd>
+ * </dl>
*/
public void setValue(Object object)
{
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/ObjectReference.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/ObjectReference.java
index 762a9dc..bbb2578 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/ObjectReference.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/ObjectReference.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG and others.
+ * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others.
* 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
@@ -9,6 +9,7 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson (IBM Corporation) - lookup by address
*******************************************************************************/
package org.eclipse.mat.snapshot.model;
@@ -65,7 +66,25 @@
*/
public IObject getObject() throws SnapshotException
{
- return snapshot.getObject(getObjectId());
+ int objectId;
+ try
+ {
+ objectId = getObjectId();
+ }
+ catch (SnapshotException e)
+ {
+ ObjectReference proxy = snapshot.getSnapshotAddons(ObjectReference.class);
+ if (proxy != null)
+ {
+ proxy.address = getObjectAddress();
+ return proxy.getObject();
+ }
+ else
+ {
+ throw e;
+ }
+ }
+ return snapshot.getObject(objectId);
}
/**
diff --git a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJHeapObjectReader.java b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJHeapObjectReader.java
index 1023c72..e8b97e0 100644
--- a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJHeapObjectReader.java
+++ b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJHeapObjectReader.java
@@ -1,10 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2009,2018 IBM Corporation.
+ * Copyright (c) 2009,2021 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -24,6 +24,7 @@
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.dtfj.DTFJIndexBuilder.RuntimeInfo;
import org.eclipse.mat.parser.IObjectReader;
+import org.eclipse.mat.parser.model.AbstractObjectImpl;
import org.eclipse.mat.parser.model.ClassImpl;
import org.eclipse.mat.parser.model.ClassLoaderImpl;
import org.eclipse.mat.parser.model.InstanceImpl;
@@ -74,6 +75,8 @@
private File file;
/** All the key DTFJ data */
private RuntimeInfo dtfjInfo;
+ /** the snapshot */
+ private ISnapshot snapshot;
/**
* whether to give up and throw an exception if reading object data fails,
* or whether to carry on getting more data
@@ -88,6 +91,42 @@
{
// Close the dump
DTFJIndexBuilder.DumpCache.releaseDump(file, dtfjInfo, true);
+ snapshot = null;
+ }
+
+ /**
+ * Used as a proxy for org.eclipse.mat.parser to read objects
+ * via address.
+ */
+ class ObjectAddressReference extends ObjectReference
+ {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ * The address will be filled in by {@link ObjectReference}.
+ */
+ public ObjectAddressReference()
+ {
+ super(snapshot, 0);
+ }
+
+ /**
+ * Create an IObject from the address.
+ */
+ public IObject getObject() throws SnapshotException
+ {
+ try
+ {
+ IObject o = DTFJHeapObjectReader.this.read(getObjectAddress(), -1, snapshot);
+ ((AbstractObjectImpl)o).setSnapshot(snapshot);
+ return o;
+ }
+ catch (IOException e)
+ {
+ throw new SnapshotException(e);
+ }
+ }
}
/**
@@ -132,6 +171,10 @@
{
return addon.cast(factory);
}
+ else if (addon.isAssignableFrom(ObjectAddressReference.class))
+ {
+ return addon.cast(new ObjectAddressReference());
+ }
else
{
return null;
@@ -147,6 +190,7 @@
public void open(ISnapshot snapshot) throws IOException, SnapshotException
{
file = new File(snapshot.getSnapshotInfo().getPath());
+ this.snapshot = snapshot;
SnapshotInfo snapinfo = snapshot.getSnapshotInfo();
RuntimeInfo info = DTFJIndexBuilder.DumpCache.getDump(file, snapinfo.getProperty("$heapFormat")); //$NON-NLS-1$
Serializable runtimeId = snapinfo.getProperty(DTFJIndexBuilder.RUNTIME_ID_KEY);
@@ -162,12 +206,17 @@
public IObject read(int objectId, ISnapshot snapshot) throws SnapshotException, IOException
{
long addr = snapshot.mapIdToAddress(objectId);
+ return read(addr, objectId, snapshot);
+ }
+
+ IObject read(long addr, int objectId, ISnapshot snapshot) throws SnapshotException, IOException
+ {
try
{
// DTFJ is not thread safe, but MAT is multi-threaded
synchronized (dtfjInfo.getImage())
{
- if (getExtraInfo)
+ if (getExtraInfo && objectId != -1)
{
// See if the class looks like a method
ClassImpl cls = (ClassImpl) snapshot.getClassOf(objectId);
@@ -181,7 +230,7 @@
}
JavaObject jo = getJavaObjectByAddress0(addr);
IObject inst;
- if (snapshot.isArray(objectId))
+ if (objectId == -1 ? jo.isArray() : snapshot.isArray(objectId))
{
inst = createArray(snapshot, objectId, addr, jo);
}
@@ -469,7 +518,7 @@
private InstanceImpl createObject(ISnapshot snapshot, int objectId, long addr, JavaObject jo)
throws CorruptDataException, MemoryAccessException, SnapshotException
{
- ClassImpl cls = (ClassImpl) snapshot.getClassOf(objectId);
+ ClassImpl cls = findClass(snapshot, objectId, jo, addr);
/*
* Optimization - don't bother going to dump if there are no fields to
* be found, and find the number of fields in advance if we do.
@@ -543,6 +592,52 @@
}
/**
+ * Find the {@link ClassImpl} which is the type of the object.
+ * @param snapshot the snapshot
+ * @param objectId for normal reads this is the object ID, -1 if unknown
+ * @param jo The DTFJ {@link JavaObject}
+ * @param addr the address of the object, for error messages
+ * @return the ClassImpl for the object, to allow the IOject to be constructed.
+ * @throws CorruptDataException
+ * @throws SnapshotException
+ */
+ private ClassImpl findClass(ISnapshot snapshot, int objectId, JavaObject jo, long addr)
+ throws CorruptDataException, SnapshotException
+ {
+ ClassImpl cls;
+ if (objectId == -1)
+ {
+ JavaClass jc = jo.getJavaClass();
+ if (jc != null)
+ {
+ JavaObject jco = jc.getObject();
+ long caddr;
+ if (jco != null)
+ caddr = jco.getID().getAddress();
+ else
+ caddr = jc.getID().getAddress();
+ int clsId = snapshot.mapAddressToId(caddr);
+ IObject cls1 = snapshot.getObject(clsId);
+ if (cls1 instanceof ClassImpl)
+ cls = (ClassImpl) cls1;
+ else
+ throw new SnapshotException(MessageFormat.format(
+ Messages.DTFJHeapObjectReader_ErrorReadingObjectAtIndex, objectId, format(addr)));
+ }
+ else
+ {
+ throw new SnapshotException(MessageFormat.format(
+ Messages.DTFJHeapObjectReader_ErrorReadingObjectAtIndex, objectId, format(addr)));
+ }
+ }
+ else
+ {
+ cls = (ClassImpl) snapshot.getClassOf(objectId);
+ }
+ return cls;
+ }
+
+ /**
* Avoid problems with corrupt dumps
* @param jc
* @return
@@ -622,7 +717,7 @@
private IObject createArray(ISnapshot snapshot, int objectId, long addr, JavaObject jo)
throws CorruptDataException, MemoryAccessException, SnapshotException
{
- ClassImpl cls = (ClassImpl) snapshot.getClassOf(objectId);
+ ClassImpl cls = findClass(snapshot, objectId, jo, addr);
int offset = 0;
int length = jo.getArraySize();
// System.out.println("Array length "+length);
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofHeapObjectReader.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofHeapObjectReader.java
index 8e67c90..a754b74 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofHeapObjectReader.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofHeapObjectReader.java
@@ -1,296 +1,301 @@
-/*******************************************************************************
- * 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));
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2008, 2021 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$
+ public static final String HPROF_HEAP_START = "hprof.heap.start"; //$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, o2hprof);
+ }
+
+ /**
+ * 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;
+ }
+ if (addon.isAssignableFrom(HprofRandomAccessParser.ObjectAddressReference.class))
+ {
+ return addon.cast(new HprofRandomAccessParser.ObjectAddressReference(snapshot, hprofDump, o2hprof, Long.MIN_VALUE));
+ }
+ 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));
+ }
+
+}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java
index 814381d..782001a 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java
@@ -1,1168 +1,1176 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others.
- * 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 - bug fix for missing classes
- * Netflix (Jason Koch) - refactors for increased performance and concurrency
- *******************************************************************************/
-package org.eclipse.mat.hprof;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
-
-import org.eclipse.mat.SnapshotException;
-import org.eclipse.mat.collect.HashMapIntObject;
-import org.eclipse.mat.collect.HashMapLongObject;
-import org.eclipse.mat.collect.HashMapLongObject.Entry;
-import org.eclipse.mat.collect.IteratorLong;
-import org.eclipse.mat.hprof.describer.Version;
-import org.eclipse.mat.hprof.ui.HprofPreferences;
-import org.eclipse.mat.parser.IPreliminaryIndex;
-import org.eclipse.mat.parser.index.IIndexReader.IOne2LongIndex;
-import org.eclipse.mat.parser.index.IndexManager.Index;
-import org.eclipse.mat.parser.index.IndexWriter;
-import org.eclipse.mat.parser.index.IndexWriter.IntArray1NWriter;
-import org.eclipse.mat.parser.index.IndexWriter.IntIndexCollector;
-import org.eclipse.mat.parser.index.IndexWriter.LongIndexCollector;
-import org.eclipse.mat.parser.index.IndexWriter.LongIndexStreamer;
-import org.eclipse.mat.parser.model.ClassImpl;
-import org.eclipse.mat.parser.model.PrimitiveArrayImpl;
-import org.eclipse.mat.parser.model.XGCRootInfo;
-import org.eclipse.mat.parser.model.XSnapshotInfo;
-import org.eclipse.mat.snapshot.UnreachableObjectsHistogram;
-import org.eclipse.mat.snapshot.model.Field;
-import org.eclipse.mat.snapshot.model.FieldDescriptor;
-import org.eclipse.mat.snapshot.model.GCRootInfo;
-import org.eclipse.mat.snapshot.model.IClass;
-import org.eclipse.mat.snapshot.model.IObject;
-import org.eclipse.mat.snapshot.model.IPrimitiveArray;
-import org.eclipse.mat.snapshot.model.ObjectReference;
-import org.eclipse.mat.util.IProgressListener;
-import org.eclipse.mat.util.MessageUtil;
-
-public class HprofParserHandlerImpl implements IHprofParserHandler
-{
- // private String prefix;
- private Version version;
-
- private XSnapshotInfo info = new XSnapshotInfo();
-
- private Map<String, List<ClassImpl>> classesByName = new HashMap<String, List<ClassImpl>>();
- private HashMapLongObject<ClassImpl> classesByAddress = new HashMapLongObject<ClassImpl>();
-
- private HashMapLongObject<List<XGCRootInfo>> gcRoots = new HashMapLongObject<List<XGCRootInfo>>(200);
-
- private IndexWriter.Identifier identifiers = null;
- private IntArray1NWriter outbound = null;
- private IntIndexCollector object2classId = null;
- private LongIndexCollector object2position = null;
- private IndexWriter.SizeIndexCollectorUncompressed array2size = null;
-
- private HashMap<Long, Boolean> requiredArrayClassIDs = new HashMap<Long, Boolean>();
- private HashMap<Long, Integer> requiredClassIDs = new HashMap<Long, Integer>();
- private IClass[] primitiveArrays = new IClass[IPrimitiveArray.TYPE.length];
- private boolean[] requiredPrimitiveArrays = new boolean[IPrimitiveArray.COMPONENT_TYPE.length];
-
- private HashMapLongObject<HashMapLongObject<List<XGCRootInfo>>> threadAddressToLocals = new HashMapLongObject<HashMapLongObject<List<XGCRootInfo>>>();
-
- /** Keep track of numbers and size of discarded objects */
- private ConcurrentHashMap<Integer, ClassImpl> discardedObjectsByClass = new ConcurrentHashMap<Integer, ClassImpl>();
-
- // The size of (possibly compressed) references in the heap
- private int refSize;
- // The size of uncompressed pointers in the object headers in the heap
- private int pointerSize;
- // The alignment between successive objects
- private int objectAlign;
- // New size of classes including per-instance fields
- private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
- // Largest offset into HPROF file
- private long maxFilePosition = 0;
-
- /** Which class instances to possibly discard */
- private Pattern discardPattern = Pattern.compile("char\\[\\]|java\\.lang\\.String"); //$NON-NLS-1$
- /** How often to discard */
- private double discardRatio = 0.0;
- /** Select which group of objects are discarded */
- private double discardOffset = 0.0;
- /** Random number seed for choosing discards */
- private long discardSeed = 1;
- /** Random number generator to choose what to discard */
- private Random rand = new Random(discardSeed);
-
- // //////////////////////////////////////////////////////////////
- // lifecycle
- // //////////////////////////////////////////////////////////////
-
- public void beforePass1(XSnapshotInfo snapshotInfo) throws IOException
- {
- this.info = snapshotInfo;
- this.identifiers = new IndexWriter.Identifier();
- if (info.getProperty("discard_ratio") instanceof Integer) //$NON-NLS-1$
- {
- discardRatio = (Integer)info.getProperty("discard_ratio") / 100.0; //$NON-NLS-1$
- if (info.getProperty("discard_offset") instanceof Integer) //$NON-NLS-1$
- {
- discardOffset = (Integer)info.getProperty("discard_offset") / 100.0; //$NON-NLS-1$
- }
- else
- {
- info.setProperty("discard_offset", (int)Math.round(discardOffset * 100)); //$NON-NLS-1$
- }
- if (info.getProperty("discard_seed") instanceof Integer) //$NON-NLS-1$
- {
- discardSeed = (Integer)info.getProperty("discard_seed"); //$NON-NLS-1$
- }
- else
- {
- info.setProperty("discard_seed", discardSeed); //$NON-NLS-1$
- }
- rand = new Random(discardSeed);
- if (info.getProperty("discard_pattern") instanceof String) //$NON-NLS-1$
- {
- discardPattern = Pattern.compile((String)info.getProperty("discard_pattern")); //$NON-NLS-1$
- }
- else
- {
- info.setProperty("discard_pattern", discardPattern.toString()); //$NON-NLS-1$
- }
- }
- }
-
- public void beforePass2(IProgressListener monitor) throws IOException, SnapshotException
- {
- // add dummy address for system class loader object
- identifiers.add(0);
-
- // sort and assign preliminary object ids
- identifiers.sort();
-
- // See what the actual object alignment is
- calculateAlignment();
-
- // Set property to show if compressed oops are used on x64 bit dumps
- if (pointerSize == 8) // if x64 bit dump
- {
- info.setProperty("$useCompressedOops", refSize == 4); //$NON-NLS-1$
- }
-
- // if necessary, create required classes not contained in the heap
- createRequiredFakeClasses();
-
- // informational messages to the user
- monitor.sendUserMessage(IProgressListener.Severity.INFO, MessageUtil.format(
- Messages.HprofParserHandlerImpl_HeapContainsObjects, info.getPath(), identifiers.size()), null);
-
- // if instance dumps for classes are present, then fix up the classes
- addTypesAndDummyStatics();
-
- int maxClassId = 0;
-
- // calculate instance size for all classes
- for (Iterator<?> e = classesByAddress.values(); e.hasNext();)
- {
- ClassImpl clazz = (ClassImpl) e.next();
- int index = identifiers.reverse(clazz.getObjectAddress());
- clazz.setObjectId(index);
-
- maxClassId = Math.max(maxClassId, index);
-
- clazz.setHeapSizePerInstance(calculateInstanceSize(clazz));
- clazz.setUsedHeapSize(calculateClassSize(clazz));
- }
-
- // create index writers
- outbound = new IntArray1NWriter(this.identifiers.size(), Index.OUTBOUND.getFile(info.getPrefix()
- + "temp."));//$NON-NLS-1$
- object2classId = new IntIndexCollector(this.identifiers.size(), IndexWriter
- .mostSignificantBit(maxClassId));
- object2position = new LongIndexCollector(this.identifiers.size(), IndexWriter
- .mostSignificantBit(maxFilePosition));
- array2size = new IndexWriter.SizeIndexCollectorUncompressed(this.identifiers.size());
-
- // java.lang.Class needs some special treatment so that object2classId
- // is written correctly
- List<ClassImpl> javaLangClasses = classesByName.get(ClassImpl.JAVA_LANG_CLASS);
- ClassImpl javaLangClass = javaLangClasses.get(0);
- javaLangClass.setObjectId(identifiers.reverse(javaLangClass.getObjectAddress()));
-
- // log references for classes
- for (Iterator<?> e = classesByAddress.values(); e.hasNext();)
- {
- ClassImpl clazz = (ClassImpl) e.next();
- clazz.setSuperClassIndex(identifiers.reverse(clazz.getSuperClassAddress()));
- clazz.setClassLoaderIndex(identifiers.reverse(clazz.getClassLoaderAddress()));
-
- // [INFO] in newer jdk hprof files, the boot class loader
- // has an address other than 0. The class loader instances
- // is still not contained in the hprof file
- if (clazz.getClassLoaderId() < 0)
- {
- clazz.setClassLoaderAddress(0);
- clazz.setClassLoaderIndex(identifiers.reverse(0));
- }
-
- boolean skipLogRefs = false;
- // add class instance - if not set by pass1 from an instance_dump for the class
- if (clazz.getClazz() == null)
- {
- clazz.setClassInstance(javaLangClass);
- if (NEWCLASSSIZE)
- {
- // Recalculate the clazz heap size based on also java.lang.Class fields
- // No need to do this for classes of other types as
- // these have object instance records with fields converted to statics
- clazz.setUsedHeapSize(clazz.getUsedHeapSize() + clazz.getClazz().getHeapSizePerInstance());
- }
- clazz.getClazz().addInstance(clazz.getUsedHeapSize());
- }
- else
- {
- // References for classes with instance dump records will be generated in pass2
- skipLogRefs = true;
- }
-
- // resolve super class
- ClassImpl superclass = lookupClass(clazz.getSuperClassAddress());
- if (superclass != null)
- superclass.addSubClass(clazz);
-
- object2classId.set(clazz.getObjectId(), clazz.getClazz().getObjectId());
-
- if (!skipLogRefs)
- outbound.log(identifiers, clazz.getObjectId(), clazz.getReferences());
- }
-
- // report dependencies for system class loader
- // (if no classes use this class loader, cleanup garbage will remove it
- // again)
- ClassImpl classLoaderClass = this.classesByName.get(IClass.JAVA_LANG_CLASSLOADER).get(0);
- HeapObject heapObject = new HeapObject(0, classLoaderClass, classLoaderClass
- .getHeapSizePerInstance());
- heapObject.references.add(classLoaderClass.getObjectAddress());
- this.addObject(heapObject, true);
-
- }
-
- /**
- * Possible HPROF extension:
- * classes also with instance dump records.
- * The classes could be of a type other than java.lang.Class
- * There could also be per-instance fields defined by their type.
- * Those values can be made accessible to MAT by creating pseudo-static fields.
- */
- private void addTypesAndDummyStatics()
- {
- // Set type (and size?) for classes with object instances
- // These will have had the type set by Pass1Parser
- for (Iterator<Entry<ClassImpl>> it = classesByAddress.entries(); it.hasNext();)
- {
- Entry<ClassImpl> e = it.next();
- ClassImpl cl = e.getValue();
- ClassImpl type = cl.getClazz();
- if (type != null)
- {
- List<FieldDescriptor> newStatics = new ArrayList<FieldDescriptor>(cl.getStaticFields());
- List<IClass> icls = resolveClassHierarchy(type.getClassAddress());
- for (IClass tcl : icls)
- {
- for (FieldDescriptor fd : tcl.getFieldDescriptors())
- {
- // Create pseudo-static field
- Field st = new Field("<" + fd.getName() + ">", fd.getType(), null); //$NON-NLS-1$ //$NON-NLS-2$
- newStatics.add(st);
- }
- }
- if (newStatics.size() != cl.getStaticFields().size())
- {
- ClassImpl newcl = new ClassImpl(cl.getObjectAddress(), cl.getName(), cl.getSuperClassAddress(),
- cl.getClassLoaderAddress(), newStatics.toArray(new Field[newStatics.size()]),
- cl.getFieldDescriptors().toArray(new FieldDescriptor[0]));
- newcl.setClassInstance(type);
- // Fix up the existing lookups
- classesByAddress.put(e.getKey(), newcl);
- List<ClassImpl>nms = classesByName.get(cl.getName());
- for (int i = 0; i < nms.size(); ++i)
- {
- if (nms.get(i) == cl)
- nms.set(i, newcl);
- }
- }
- }
- }
-
- // Set type to new type with the dummy static fields
- for (Iterator<Entry<ClassImpl>> it = classesByAddress.entries(); it.hasNext();)
- {
- Entry<ClassImpl> e = it.next();
- ClassImpl cl = e.getValue();
- ClassImpl type = cl.getClazz();
- if (type != null)
- {
- ClassImpl type2 = classesByAddress.get(type.getObjectAddress());
- // Actual object test, not equality as we need to maintain linkage to the new class
- if (type != type2)
- {
- cl.setClassInstance(type2);
- }
- }
- }
- }
-
- /**
- * Calculate possible restrictions on object alignment by finding the GCD of differences
- * between object addresses (ignoring address 0).
- */
- private void calculateAlignment()
- {
- // Minimum alignment of 8 bytes
- final int minAlign = 8;
- // Maximum alignment of 256 bytes
- final int maxAlign = 256;
- long prev = 0;
- long align = 0;
- for (IteratorLong it = identifiers.iterator(); it.hasNext(); )
- {
- long next = it.next();
- if (next == 0)
- continue;
- long diff = next - prev;
- prev = next;
- if (next == diff)
- continue;
- if (align == 0)
- {
- align = diff;
- }
- else
- {
- long mx = Math.max(align, diff);
- long mn = Math.min(align, diff);
- long d = mx % mn;
- while (d != 0) {
- mx = mn;
- mn = d;
- d = mx % mn;
- }
- align = mn;
- // Minimum alignment
- if (align <= minAlign)
- break;
- }
- }
- // Sanitise the alignment
- objectAlign = Math.max((int)Math.min(align, maxAlign), minAlign);
- }
-
- private void createRequiredFakeClasses() throws IOException, SnapshotException
- {
- // we know: system class loader has object address 0
- long nextObjectAddress = 0;
- // For generating the fake class names
- int clsid = 0;
- // java.lang.Object for the superclass
- List<ClassImpl>jlos = classesByName.get("java.lang.Object"); //$NON-NLS-1$
- long jlo = jlos == null || jlos.isEmpty() ? 0 : jlos.get(0).getObjectAddress();
- // Fake java.lang.Class, java.lang.ClassLoader for later
- String clss[] = {ClassImpl.JAVA_LANG_CLASS, ClassImpl.JAVA_LANG_CLASSLOADER};
- for (String cls : clss)
- {
- List<ClassImpl>jlcs = classesByName.get(cls);
- if (jlcs == null || jlcs.isEmpty())
- {
- while (identifiers.reverse(++nextObjectAddress) >= 0)
- {}
- IClass type = new ClassImpl(nextObjectAddress, cls, jlo, 0, new Field[0], new FieldDescriptor[0]);
- addFakeClass((ClassImpl) type, -1);
- }
- }
-
- // create required (fake) classes for arrays
- if (!requiredArrayClassIDs.isEmpty())
- {
- for (long arrayClassID : requiredArrayClassIDs.keySet())
- {
- IClass arrayType = lookupClass(arrayClassID);
- if (arrayType == null)
- {
- int objectId = identifiers.reverse(arrayClassID);
- if (objectId >= 0)
- {
- String msg = MessageUtil.format(Messages.HprofParserHandlerImpl_Error_ExpectedClassSegment,
- Long.toHexString(arrayClassID));
- throw new SnapshotException(msg);
- }
-
- arrayType = new ClassImpl(arrayClassID, "unknown-class-"+clsid+"[]", jlo, 0, new Field[0], //$NON-NLS-1$ //$NON-NLS-2$
- new FieldDescriptor[0]);
- ++clsid;
- addFakeClass((ClassImpl) arrayType, -1);
- }
- }
- }
- requiredArrayClassIDs = null;
-
- for(int arrayType = 0; arrayType < requiredPrimitiveArrays.length; arrayType++)
- {
- if (requiredPrimitiveArrays[arrayType])
- {
- String name = IPrimitiveArray.TYPE[arrayType];
- IClass clazz = lookupClassByName(name, true);
- if (clazz == null)
- {
- while (identifiers.reverse(++nextObjectAddress) >= 0)
- {}
-
- clazz = new ClassImpl(nextObjectAddress, name, jlo, 0, new Field[0], new FieldDescriptor[0]);
- addFakeClass((ClassImpl) clazz, -1);
- }
- primitiveArrays[arrayType] = clazz;
- }
- }
-
- // create required (fake) classes for objects
- if (!requiredClassIDs.isEmpty())
- {
- for (Map.Entry<Long, Integer> e : requiredClassIDs.entrySet())
- {
- long classID = e.getKey();
- IClass type = lookupClass(classID);
- if (type == null)
- {
- int objectId = identifiers.reverse(classID);
- if (objectId >= 0)
- {
- String msg = MessageUtil.format(Messages.HprofParserHandlerImpl_Error_ExpectedClassSegment,
- Long.toHexString(classID));
- throw new SnapshotException(msg);
- }
- // Create some dummy fields
- int size = e.getValue();
- // Special value for missing superclass
- if (size >= Integer.MAX_VALUE)
- size = 0;
- int nfields = size / 4 + Integer.bitCount(size % 4);
- FieldDescriptor fds[] = new FieldDescriptor[nfields];
- int i;
- for (i = 0; i < size / 4; ++i)
- {
- fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.INT); //$NON-NLS-1$
- }
- if ((size & 2) != 0)
- {
- fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.SHORT); //$NON-NLS-1$
- ++i;
- }
- if ((size & 1) != 0)
- {
- fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.BYTE); //$NON-NLS-1$
- ++i;
- }
- type = new ClassImpl(classID, "unknown-class-"+clsid, jlo, 0, new Field[0], fds); //$NON-NLS-1$
- ++clsid;
- addFakeClass((ClassImpl) type, -1);
- }
- }
- }
- requiredClassIDs = null;
-
- identifiers.sort();
- }
-
- private int calculateInstanceSize(ClassImpl clazz)
- {
- if (!clazz.isArrayType())
- {
- return alignUpToX(calculateSizeRecursive(clazz), objectAlign);
- }
- else
- {
- // use the referenceSize only to pass the proper ID size
- // arrays calculate the rest themselves.
- return refSize;
- }
- }
-
- private int calculateSizeRecursive(ClassImpl clazz)
- {
- if (clazz.getSuperClassAddress() == 0)
- {
- return pointerSize + refSize;
- }
- ClassImpl superClass = classesByAddress.get(clazz.getSuperClassAddress());
- int ownFieldsSize = 0;
- for (FieldDescriptor field : clazz.getFieldDescriptors())
- ownFieldsSize += sizeOf(field);
-
- return alignUpToX(ownFieldsSize + calculateSizeRecursive(superClass), refSize);
- }
-
- private int calculateClassSize(ClassImpl clazz)
- {
- int staticFieldsSize = 0;
- for (Field field : clazz.getStaticFields())
- staticFieldsSize += sizeOf(field);
- return alignUpToX(staticFieldsSize, objectAlign);
- }
-
- private int sizeOf(FieldDescriptor field)
- {
- int type = field.getType();
- if (type == IObject.Type.OBJECT)
- return refSize;
-
- return IPrimitiveArray.ELEMENT_SIZE[type];
- }
-
- private int alignUpToX(int n, int x)
- {
- int r = n % x;
- return r == 0 ? n : n + x - r;
- }
-
- private long alignUpToX(long n, int x)
- {
- long r = n % x;
- return r == 0 ? n : n + x - r;
- }
-
- public IOne2LongIndex fillIn(IPreliminaryIndex index, IProgressListener listener) throws IOException
- {
- /*
- * System classes should be marked appropriately in the HPROF file.
- * lambda classes should not be marked as system GC roots.
- */
- boolean foundSystemClasses = false;
- for (Iterator<List<XGCRootInfo>>it = gcRoots.values(); it.hasNext() && !foundSystemClasses; )
- {
- for (XGCRootInfo x : it.next())
- {
- if (x.getType() == GCRootInfo.Type.SYSTEM_CLASS)
- {
- foundSystemClasses = true;
- break;
- }
- }
- }
- if (!foundSystemClasses)
- {
- // Probably not needed anymore.
- // ensure all classes loaded by the system class loaders are marked as
- // GCRoots
- //
- // For some dumps produced with jmap 1.5_xx this is not the case, and
- // it may happen that the super classes of some classes are missing
- // Array classes, e.g. java.lang.String[][] are not explicitly
- // marked. They are also not marked as "system class" in the non-jmap
- // heap dumps
- ClassImpl[] allClasses = classesByAddress.getAllValues(new ClassImpl[0]);
- for (ClassImpl clazz : allClasses)
- {
- if (clazz.getClassLoaderAddress() == 0 && !clazz.isArrayType()
- && !gcRoots.containsKey(clazz.getObjectAddress()))
- {
- addGCRoot(clazz.getObjectAddress(), 0, GCRootInfo.Type.SYSTEM_CLASS);
- }
- }
- }
-
- // classes model
- HashMapIntObject<ClassImpl> classesById = new HashMapIntObject<ClassImpl>(classesByAddress.size());
- for (Iterator<ClassImpl> iter = classesByAddress.values(); iter.hasNext();)
- {
- ClassImpl clazz = iter.next();
- classesById.put(clazz.getObjectId(), clazz);
- }
- index.setClassesById(classesById);
-
- // Create Histogram of discarded objects
- long discardedObjects = 0;
- long discardedSize = 0;
- List<UnreachableObjectsHistogram.Record> records = new ArrayList<UnreachableObjectsHistogram.Record>();
- for(ClassImpl clazz : discardedObjectsByClass.values()) {
- records.add(new UnreachableObjectsHistogram.Record(
- clazz.getName(),
- clazz.getObjectAddress(),
- clazz.getNumberOfObjects(),
- clazz.getTotalSize()));
- discardedObjects += clazz.getNumberOfObjects();
- discardedSize += clazz.getTotalSize();
- }
- if (discardedObjects > 0)
- {
- UnreachableObjectsHistogram deadObjectHistogram = new UnreachableObjectsHistogram(records);
- info.setProperty(UnreachableObjectsHistogram.class.getName(), deadObjectHistogram);
- listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(
- Messages.HprofParserHandlerImpl_DiscardedObjects,
- discardedObjects, discardedSize, discardRatio, discardPattern), null);
- }
-
- index.setGcRoots(map2ids(gcRoots));
-
- HashMapIntObject<HashMapIntObject<List<XGCRootInfo>>> thread2objects2roots = new HashMapIntObject<HashMapIntObject<List<XGCRootInfo>>>();
- for (Iterator<HashMapLongObject.Entry<HashMapLongObject<List<XGCRootInfo>>>> iter = threadAddressToLocals
- .entries(); iter.hasNext();)
- {
- HashMapLongObject.Entry<HashMapLongObject<List<XGCRootInfo>>> entry = iter.next();
- int threadId = identifiers.reverse(entry.getKey());
- if (threadId >= 0)
- {
- HashMapIntObject<List<XGCRootInfo>> objects2roots = map2ids(entry.getValue());
- if (!objects2roots.isEmpty())
- thread2objects2roots.put(threadId, objects2roots);
- }
- }
- index.setThread2objects2roots(thread2objects2roots);
-
- index.setIdentifiers((new LongIndexStreamer()).writeTo(Index.IDENTIFIER.getFile(info.getPrefix() + "temp."), identifiers.iterator())); //$NON-NLS-1$);
-
- index.setArray2size(array2size.writeTo(Index.A2SIZE.getFile(info.getPrefix() + "temp."))); //$NON-NLS-1$
-
- index.setObject2classId(object2classId.writeTo(Index.O2CLASS.getFile(info.getPrefix() + "temp."))); //$NON-NLS-1$
-
- index.setOutbound(outbound.flush());
-
- return object2position.writeTo(new File(info.getPrefix() + "temp.o2hprof.index")); //$NON-NLS-1$
- }
-
- private HashMapIntObject<List<XGCRootInfo>> map2ids(HashMapLongObject<List<XGCRootInfo>> source)
- {
- HashMapIntObject<List<XGCRootInfo>> sink = new HashMapIntObject<List<XGCRootInfo>>();
- for (Iterator<HashMapLongObject.Entry<List<XGCRootInfo>>> iter = source.entries(); iter.hasNext();)
- {
- HashMapLongObject.Entry<List<XGCRootInfo>> entry = iter.next();
- int idx = identifiers.reverse(entry.getKey());
- if (idx >= 0)
- {
- // sometimes it happens that there is no object for an
- // address reported as a GC root. It's not clear why
- for (Iterator<XGCRootInfo> roots = entry.getValue().iterator(); roots.hasNext();)
- {
- XGCRootInfo root = roots.next();
- root.setObjectId(idx);
- if (root.getContextAddress() != 0)
- {
- int contextId = identifiers.reverse(root.getContextAddress());
- if (contextId < 0)
- roots.remove();
- else
- root.setContextId(contextId);
- }
- }
- sink.put(idx, entry.getValue());
- }
- }
- return sink;
- }
-
- public void cancel()
- {
- if (outbound != null)
- outbound.cancel();
-
- }
-
- // //////////////////////////////////////////////////////////////
- // report parsed entities
- // //////////////////////////////////////////////////////////////
-
- public void addProperty(String name, String value) throws IOException
- {
- if (IHprofParserHandler.VERSION.equals(name))
- {
- version = Version.valueOf(value);
- info.setProperty(HprofHeapObjectReader.VERSION_PROPERTY, version.name());
- }
- else if (IHprofParserHandler.IDENTIFIER_SIZE.equals(name))
- {
- int idSize = Integer.parseInt(value);
- info.setIdentifierSize(idSize);
- pointerSize = idSize;
- refSize = idSize;
- }
- else if (IHprofParserHandler.CREATION_DATE.equals(name))
- {
- info.setCreationDate(new Date(Long.parseLong(value)));
- }
- else if (IHprofParserHandler.REFERENCE_SIZE.equals(name))
- {
- refSize = Integer.parseInt(value);
- }
- else if (IHprofParserHandler.STREAM_LENGTH.equals(name))
- {
- long length = Long.parseLong(value);
- info.setProperty(HprofHeapObjectReader.HPROF_LENGTH_PROPERTY, length);
- }
- }
-
- public void addGCRoot(long id, long referrer, int rootType)
- {
- if (referrer != 0)
- {
- HashMapLongObject<List<XGCRootInfo>> localAddressToRootInfo = threadAddressToLocals.get(referrer);
- if (localAddressToRootInfo == null)
- {
- localAddressToRootInfo = new HashMapLongObject<List<XGCRootInfo>>();
- threadAddressToLocals.put(referrer, localAddressToRootInfo);
- }
- List<XGCRootInfo> gcRootInfo = localAddressToRootInfo.get(id);
- if (gcRootInfo == null)
- {
- gcRootInfo = new ArrayList<XGCRootInfo>(1);
- localAddressToRootInfo.put(id, gcRootInfo);
- }
- gcRootInfo.add(new XGCRootInfo(id, referrer, rootType));
- return; // do not add the object as GC root
- }
-
- List<XGCRootInfo> r = gcRoots.get(id);
- if (r == null)
- gcRoots.put(id, r = new ArrayList<XGCRootInfo>(3));
- r.add(new XGCRootInfo(id, referrer, rootType));
- }
-
- private void addFakeClass(ClassImpl clazz, long filePosition) throws IOException
- {
- this.identifiers.add(clazz.getObjectAddress());
- this.classesByAddress.put(clazz.getObjectAddress(), clazz);
-
- List<ClassImpl> list = classesByName.get(clazz.getName());
- if (list == null)
- classesByName.put(clazz.getName(), list = new ArrayList<ClassImpl>());
- list.add(clazz);
- }
-
- public void addClass(ClassImpl clazz, long filePosition, int idSize, int instsize) throws IOException
- {
- this.identifiers.add(clazz.getObjectAddress());
- this.classesByAddress.put(clazz.getObjectAddress(), clazz);
-
- List<ClassImpl> list = classesByName.get(clazz.getName());
- if (list == null)
- classesByName.put(clazz.getName(), list = new ArrayList<ClassImpl>());
- list.add(clazz);
-
- if (clazz.getSuperClassAddress() != 0) {
- // Try to calculate how big the superclass should be
- int ownFieldsSize = 0;
- for (FieldDescriptor field : clazz.getFieldDescriptors())
- {
- int type = field.getType();
- if (type == IObject.Type.OBJECT)
- ownFieldsSize += idSize;
- else
- ownFieldsSize += IPrimitiveArray.ELEMENT_SIZE[type];
- }
- int supersize = Math.max(instsize - ownFieldsSize, 0);
- // A real size of an instance will override this
- reportRequiredClass(clazz.getSuperClassAddress(), supersize, false);
- }
- }
-
- private void prepareHeapObject(HeapObject object) throws IOException
- {
- if (object.isPrimitiveArray)
- {
- byte elementType = (byte) object.classIdOrElementType;
- ClassImpl clazz = (ClassImpl) lookupPrimitiveArrayClassByType(elementType);
- object.usedHeapSize = getPrimitiveArrayHeapSize(elementType, object.arraySize);
- object.references.add(clazz.getObjectAddress());
- object.clazz = clazz;
- }
-
- if (object.isObjectArray)
- {
- long arrayClassObjectID = object.classIdOrElementType;
- ClassImpl arrayType = (ClassImpl) lookupClass(arrayClassObjectID);
- if (arrayType == null)
- throw new RuntimeException(MessageUtil.format(
- Messages.Pass2Parser_Error_HandlerMustCreateFakeClassForAddress,
- Long.toHexString(arrayClassObjectID)));
-
- object.usedHeapSize = getObjectArrayHeapSize(arrayType, object.arraySize);
- object.references.add(arrayType.getObjectAddress());
- long[] ids = object.ids;
- for (int ii = 0; ii < object.arraySize; ii++)
- {
- if (ids[ii] != 0)
- object.references.add(ids[ii]);
- }
- object.clazz = arrayType;
- // References now transfered, so free some space
- ids = null;
- object.ids = null;
- }
-
- if (!object.isObjectArray && !object.isPrimitiveArray)
- {
- long classID = object.classIdOrElementType;
- List<IClass> hierarchy = resolveClassHierarchy(classID);
- ByteArrayPositionInputStream in = new ByteArrayPositionInputStream(object.instanceData, object.idSize);
-
- ClassImpl thisClazz = (ClassImpl) hierarchy.get(0);
-
- IClass objcl = lookupClass(object.objectAddress);
- Field statics[] = new Field[0];
- if (objcl instanceof ClassImpl)
- {
- // An INSTANCE_DUMP record for a class type
- // This clazz is perhaps of different actual type, not java.lang.Class
- // The true type has already been set in PassParser1 and beforePass2()
- ClassImpl objcls = (ClassImpl) objcl;
- statics = objcls.getStaticFields().toArray(statics);
- // Heap size of each class type object is individual as have statics
- object.clazz = thisClazz;
- object.usedHeapSize = objcls.getUsedHeapSize();
- // and extract the class references
- object.references.addAll(objcls.getReferences());
- }
- else
- {
- object.clazz = thisClazz;
- object.usedHeapSize = thisClazz.getHeapSizePerInstance();
- object.references.add(thisClazz.getObjectAddress());
- }
-
- // extract outgoing references
- int pos = 0;
- for (IClass clazz : hierarchy)
- {
- for (FieldDescriptor field : clazz.getFieldDescriptors())
- {
- int type = field.getType();
- // Find match for pseudo-statics
- Field stField = null;
- for (int stidx = 0; stidx < statics.length; ++stidx)
- {
- if (statics[stidx] != null && statics[stidx].getType() == type && statics[stidx].getName().equals("<"+field.getName()+">")) { //$NON-NLS-1$ //$NON-NLS-2$
- // Found a field
- stField = statics[stidx];
- // Don't use this twice.
- statics[stidx] = null;
- break;
- }
- }
- if (type == IObject.Type.OBJECT)
- {
- long refId = in.readID(object.idSize);
- pos += object.idSize;
- if (refId != 0)
- {
- object.references.add(refId);
- if (stField != null)
- {
- stField.setValue(new ObjectReference(null, refId));
- }
- }
- }
- else
- {
- Object value = AbstractParser.readValue(in, null, type, object.idSize);
- if (stField != null)
- stField.setValue(value);
- }
- }
- }
-
- if (pos != object.instanceData.length)
- {
- boolean unknown = false;
- for (IClass clazz : hierarchy)
- {
- if (clazz.getName().startsWith("unknown-class")) //$NON-NLS-1$
- {
- unknown = true;
- }
- }
-
- // TODO get the strictness settings across from eclipse
-// if (unknown && (strictnessPreference == HprofStrictness.STRICTNESS_WARNING || strictnessPreference == HprofStrictness.STRICTNESS_PERMISSIVE))
-// {
-// //monitor.sendUserMessage(Severity.WARNING, MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, thisClazz.getName(), Long.toHexString(id), Long.toHexString(segmentStartPos), Long.toHexString(endPos), Long.toHexString(in.position())), null);
-// }
-// else
-// {
-// throw new IOException(MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, thisClazz.getName(), Long.toHexString(id), Long.toHexString(segmentStartPos), Long.toHexString(endPos), Long.toHexString(in.position())));
-// }
- }
- }
-
- }
- public void addObject(HeapObject object) throws IOException
- {
- addObject(object, false);
- }
-
- private void addObject(HeapObject object, boolean prepared) throws IOException
- {
- if (!prepared)
- prepareHeapObject(object);
-
- // this may be called from multiple threads
- // so, each function called inside here needs to be threadsafe
- // it will not do to simply synchronize here as we need
- // better concurrency than that
-
- int index = mapAddressToId(object.objectAddress);
- if (index < 0)
- {
- // Discarded object
- ClassImpl cls = discardedObjectsByClass.get(object.clazz.getObjectId());
- if (cls == null)
- {
- cls = new ClassImpl(object.clazz.getObjectAddress(),
- object.clazz.getName(),
- object.clazz.getSuperClassAddress(),
- object.clazz.getClassLoaderAddress(),
- new Field[0],
- new FieldDescriptor[0]);
- cls.setHeapSizePerInstance(object.clazz.getHeapSizePerInstance());
- ClassImpl clsOld = discardedObjectsByClass.putIfAbsent(object.clazz.getObjectId(), cls);
- if (clsOld != null)
- cls = clsOld;
- }
- /*
- * Keep count of discards
- * @TODO consider overflow as we count discard >Integer.MAX_VALUE
- */
- cls.addInstance(object.usedHeapSize);
- return;
- }
-
- // check if some thread to local variables references have to be added
- HashMapLongObject<List<XGCRootInfo>> localVars = threadAddressToLocals.get(object.objectAddress);
- if (localVars != null)
- {
- IteratorLong e = localVars.keys();
- while (e.hasNext())
- {
- object.references.add(e.next());
- }
- }
-
- // log references
- outbound.log(identifiers, index, object.references);
-
- int classIndex = object.clazz.getObjectId();
- object.clazz.addInstance(object.usedHeapSize);
-
- // log address
- object2classId.set(index, classIndex);
- object2position.set(index, object.filePosition);
-
- // log array size
- if (object.isPrimitiveArray || object.isObjectArray)
- array2size.set(index, object.usedHeapSize);
- }
-
- /**
- * Randomly choose whether to discard
- * @return
- */
- private boolean discard()
- {
- if (discardRatio <= 0.0)
- return false;
- double d = rand.nextDouble();
- double top = discardRatio + discardOffset;
- /*
- * Wrap around the range.
- * [dddddddd..] 0.8,0.0
- * [..dddddddd] 0.8,0.2
- * [dd..dddddd] 0.8,0.4
- */
- return (d < top && d >= discardOffset) || d < top - 1.0;
- }
-
- /**
- * Choose to discard also based on object type
- *
- * @param classId the HPROF class Id
- * @return
- */
- private boolean discard(long classId)
- {
- if (!discard())
- return false;
- ClassImpl cls = lookupClass(classId);
- if (cls != null && discardPattern.matcher(cls.getName()).matches())
- {
- return true;
- }
- return false;
- }
-
- /**
- * Adjust the last seen file position.
- * Used to see how many bits are needed for the index.
- * @param filePosition
- */
- private void reportFilePosition(long filePosition)
- {
- if (filePosition > maxFilePosition)
- maxFilePosition = filePosition;
- }
-
- private void reportInstance(long id, long filePosition)
- {
- this.identifiers.add(id);
- reportFilePosition(filePosition);
- }
-
- public void reportInstanceWithClass(long id, long filePosition, long classID, int size)
- {
- if (discard(classID))
- {
- // Skip
- reportFilePosition(filePosition);
- return;
- }
- reportInstance(id, filePosition);
- reportRequiredClass(classID, size, true);
- }
-
- public void reportInstanceOfObjectArray(long id, long filePosition, long arrayClassID)
- {
- if (discard(arrayClassID))
- {
- // Skip
- reportFilePosition(filePosition);
- return;
- }
- reportInstance(id, filePosition);
- reportRequiredObjectArray(arrayClassID);
- }
-
- public void reportInstanceOfPrimitiveArray(long id, long filePosition, int arrayType)
- {
- if (discard() && discardPattern.matcher(IPrimitiveArray.TYPE[arrayType]).matches())
- {
- // Skip
- reportFilePosition(filePosition);
- reportRequiredPrimitiveArray(arrayType);
- return;
- }
- reportInstance(id, filePosition);
- reportRequiredPrimitiveArray(arrayType);
- }
-
- private void reportRequiredObjectArray(long arrayClassID)
- {
- requiredArrayClassIDs.putIfAbsent(arrayClassID, true);
- }
-
- private void reportRequiredPrimitiveArray(int arrayType)
- {
- requiredPrimitiveArrays[arrayType] = true;
- }
-
- private void reportRequiredClass(long classID, int size, boolean sizeKnown)
- {
- if (sizeKnown)
- {
- requiredClassIDs.put(classID, size);
- }
- else
- {
- requiredClassIDs.putIfAbsent(classID, size);
- }
- }
-
- // //////////////////////////////////////////////////////////////
- // lookup heap infos
- // //////////////////////////////////////////////////////////////
-
- public int getIdentifierSize()
- {
- return info.getIdentifierSize();
- }
-
- public ClassImpl lookupClass(long classId)
- {
- return classesByAddress.get(classId);
- }
-
- public IClass lookupPrimitiveArrayClassByType(byte elementType)
- {
- return primitiveArrays[elementType];
- }
-
- public IClass lookupClassByName(String name, boolean failOnMultipleInstances)
- {
- List<ClassImpl> list = classesByName.get(name);
- if (list == null)
- return null;
- if (failOnMultipleInstances && list.size() != 1)
- throw new RuntimeException(MessageUtil.format(
- Messages.HprofParserHandlerImpl_Error_MultipleClassInstancesExist, name));
- return list.get(0);
- }
-
- public IClass lookupClassByIndex(int objIndex)
- {
- return lookupClass(this.identifiers.get(objIndex));
- }
-
- ConcurrentHashMap<Long, List<IClass>> classHierarchyCache = new ConcurrentHashMap<Long, List<IClass>>();
- public List<IClass> resolveClassHierarchy(long classId)
- {
- List<IClass> cached = classHierarchyCache.get(classId);
- if (cached != null)
- {
- return cached;
- }
-
- List<IClass> answer = new ArrayList<IClass>();
-
- ClassImpl clazz = classesByAddress.get(classId);
- answer.add(clazz);
-
- while (clazz.hasSuperClass())
- {
- clazz = classesByAddress.get(clazz.getSuperClassAddress());
- answer.add(clazz);
- }
-
- classHierarchyCache.put(classId, answer);
- return answer;
- }
-
- public int mapAddressToId(long address)
- {
- return this.identifiers.reverse(address);
- }
-
- public XSnapshotInfo getSnapshotInfo()
- {
- return info;
- }
-
- public long getObjectArrayHeapSize(ClassImpl arrayType, int size)
- {
- long usedHeapSize = alignUpToX(pointerSize + refSize + 4 + size * arrayType.getHeapSizePerInstance(), objectAlign);
- return usedHeapSize;
- }
-
- public long getPrimitiveArrayHeapSize(byte elementType, int size)
- {
- long usedHeapSize = alignUpToX(alignUpToX(pointerSize + refSize + 4, refSize) + size * (long)PrimitiveArrayImpl.ELEMENT_SIZE[(int) elementType], objectAlign);
- return usedHeapSize;
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others.
+ * 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 - bug fix for missing classes
+ * Netflix (Jason Koch) - refactors for increased performance and concurrency
+ *******************************************************************************/
+package org.eclipse.mat.hprof;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.collect.HashMapIntObject;
+import org.eclipse.mat.collect.HashMapLongObject;
+import org.eclipse.mat.collect.HashMapLongObject.Entry;
+import org.eclipse.mat.collect.IteratorLong;
+import org.eclipse.mat.hprof.describer.Version;
+import org.eclipse.mat.hprof.ui.HprofPreferences;
+import org.eclipse.mat.parser.IPreliminaryIndex;
+import org.eclipse.mat.parser.index.IIndexReader.IOne2LongIndex;
+import org.eclipse.mat.parser.index.IndexManager.Index;
+import org.eclipse.mat.parser.index.IndexWriter;
+import org.eclipse.mat.parser.index.IndexWriter.IntArray1NWriter;
+import org.eclipse.mat.parser.index.IndexWriter.IntIndexCollector;
+import org.eclipse.mat.parser.index.IndexWriter.LongIndexCollector;
+import org.eclipse.mat.parser.index.IndexWriter.LongIndexStreamer;
+import org.eclipse.mat.parser.model.ClassImpl;
+import org.eclipse.mat.parser.model.PrimitiveArrayImpl;
+import org.eclipse.mat.parser.model.XGCRootInfo;
+import org.eclipse.mat.parser.model.XSnapshotInfo;
+import org.eclipse.mat.snapshot.UnreachableObjectsHistogram;
+import org.eclipse.mat.snapshot.model.Field;
+import org.eclipse.mat.snapshot.model.FieldDescriptor;
+import org.eclipse.mat.snapshot.model.GCRootInfo;
+import org.eclipse.mat.snapshot.model.IClass;
+import org.eclipse.mat.snapshot.model.IObject;
+import org.eclipse.mat.snapshot.model.IPrimitiveArray;
+import org.eclipse.mat.snapshot.model.ObjectReference;
+import org.eclipse.mat.util.IProgressListener;
+import org.eclipse.mat.util.MessageUtil;
+
+public class HprofParserHandlerImpl implements IHprofParserHandler
+{
+ // private String prefix;
+ private Version version;
+
+ private XSnapshotInfo info = new XSnapshotInfo();
+
+ private Map<String, List<ClassImpl>> classesByName = new HashMap<String, List<ClassImpl>>();
+ private HashMapLongObject<ClassImpl> classesByAddress = new HashMapLongObject<ClassImpl>();
+
+ private HashMapLongObject<List<XGCRootInfo>> gcRoots = new HashMapLongObject<List<XGCRootInfo>>(200);
+
+ private IndexWriter.Identifier identifiers = null;
+ private IntArray1NWriter outbound = null;
+ private IntIndexCollector object2classId = null;
+ private LongIndexCollector object2position = null;
+ private IndexWriter.SizeIndexCollectorUncompressed array2size = null;
+
+ private HashMap<Long, Boolean> requiredArrayClassIDs = new HashMap<Long, Boolean>();
+ private HashMap<Long, Integer> requiredClassIDs = new HashMap<Long, Integer>();
+ private IClass[] primitiveArrays = new IClass[IPrimitiveArray.TYPE.length];
+ private boolean[] requiredPrimitiveArrays = new boolean[IPrimitiveArray.COMPONENT_TYPE.length];
+
+ private HashMapLongObject<HashMapLongObject<List<XGCRootInfo>>> threadAddressToLocals = new HashMapLongObject<HashMapLongObject<List<XGCRootInfo>>>();
+
+ /** Keep track of numbers and size of discarded objects */
+ private ConcurrentHashMap<Integer, ClassImpl> discardedObjectsByClass = new ConcurrentHashMap<Integer, ClassImpl>();
+
+ // The size of (possibly compressed) references in the heap
+ private int refSize;
+ // The size of uncompressed pointers in the object headers in the heap
+ private int pointerSize;
+ // The alignment between successive objects
+ private int objectAlign;
+ // New size of classes including per-instance fields
+ private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
+ // Largest offset into HPROF file
+ private long maxFilePosition = 0;
+
+ /** Which class instances to possibly discard */
+ private Pattern discardPattern = Pattern.compile("char\\[\\]|java\\.lang\\.String"); //$NON-NLS-1$
+ /** How often to discard */
+ private double discardRatio = 0.0;
+ /** Select which group of objects are discarded */
+ private double discardOffset = 0.0;
+ /** Random number seed for choosing discards */
+ private long discardSeed = 1;
+ /** Random number generator to choose what to discard */
+ private Random rand = new Random(discardSeed);
+
+ // //////////////////////////////////////////////////////////////
+ // lifecycle
+ // //////////////////////////////////////////////////////////////
+
+ public void beforePass1(XSnapshotInfo snapshotInfo) throws IOException
+ {
+ this.info = snapshotInfo;
+ this.identifiers = new IndexWriter.Identifier();
+ if (info.getProperty("discard_ratio") instanceof Integer) //$NON-NLS-1$
+ {
+ discardRatio = (Integer)info.getProperty("discard_ratio") / 100.0; //$NON-NLS-1$
+ if (info.getProperty("discard_offset") instanceof Integer) //$NON-NLS-1$
+ {
+ discardOffset = (Integer)info.getProperty("discard_offset") / 100.0; //$NON-NLS-1$
+ }
+ else
+ {
+ info.setProperty("discard_offset", (int)Math.round(discardOffset * 100)); //$NON-NLS-1$
+ }
+ if (info.getProperty("discard_seed") instanceof Integer) //$NON-NLS-1$
+ {
+ discardSeed = (Integer)info.getProperty("discard_seed"); //$NON-NLS-1$
+ }
+ else
+ {
+ info.setProperty("discard_seed", discardSeed); //$NON-NLS-1$
+ }
+ rand = new Random(discardSeed);
+ if (info.getProperty("discard_pattern") instanceof String) //$NON-NLS-1$
+ {
+ discardPattern = Pattern.compile((String)info.getProperty("discard_pattern")); //$NON-NLS-1$
+ }
+ else
+ {
+ info.setProperty("discard_pattern", discardPattern.toString()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ public void beforePass2(IProgressListener monitor) throws IOException, SnapshotException
+ {
+ // add dummy address for system class loader object
+ identifiers.add(0);
+
+ // sort and assign preliminary object ids
+ identifiers.sort();
+
+ // See what the actual object alignment is
+ calculateAlignment();
+
+ // Set property to show if compressed oops are used on x64 bit dumps
+ if (pointerSize == 8) // if x64 bit dump
+ {
+ info.setProperty("$useCompressedOops", refSize == 4); //$NON-NLS-1$
+ }
+
+ // if necessary, create required classes not contained in the heap
+ createRequiredFakeClasses();
+
+ // informational messages to the user
+ monitor.sendUserMessage(IProgressListener.Severity.INFO, MessageUtil.format(
+ Messages.HprofParserHandlerImpl_HeapContainsObjects, info.getPath(), identifiers.size()), null);
+
+ // if instance dumps for classes are present, then fix up the classes
+ addTypesAndDummyStatics();
+
+ int maxClassId = 0;
+
+ // calculate instance size for all classes
+ for (Iterator<?> e = classesByAddress.values(); e.hasNext();)
+ {
+ ClassImpl clazz = (ClassImpl) e.next();
+ int index = identifiers.reverse(clazz.getObjectAddress());
+ clazz.setObjectId(index);
+
+ maxClassId = Math.max(maxClassId, index);
+
+ clazz.setHeapSizePerInstance(calculateInstanceSize(clazz));
+ clazz.setUsedHeapSize(calculateClassSize(clazz));
+ }
+
+ // create index writers
+ outbound = new IntArray1NWriter(this.identifiers.size(), Index.OUTBOUND.getFile(info.getPrefix()
+ + "temp."));//$NON-NLS-1$
+ object2classId = new IntIndexCollector(this.identifiers.size(), IndexWriter
+ .mostSignificantBit(maxClassId));
+ object2position = new LongIndexCollector(this.identifiers.size(), IndexWriter
+ .mostSignificantBit(maxFilePosition));
+ array2size = new IndexWriter.SizeIndexCollectorUncompressed(this.identifiers.size());
+
+ // java.lang.Class needs some special treatment so that object2classId
+ // is written correctly
+ List<ClassImpl> javaLangClasses = classesByName.get(ClassImpl.JAVA_LANG_CLASS);
+ ClassImpl javaLangClass = javaLangClasses.get(0);
+ javaLangClass.setObjectId(identifiers.reverse(javaLangClass.getObjectAddress()));
+
+ // log references for classes
+ for (Iterator<?> e = classesByAddress.values(); e.hasNext();)
+ {
+ ClassImpl clazz = (ClassImpl) e.next();
+ clazz.setSuperClassIndex(identifiers.reverse(clazz.getSuperClassAddress()));
+ clazz.setClassLoaderIndex(identifiers.reverse(clazz.getClassLoaderAddress()));
+
+ // [INFO] in newer jdk hprof files, the boot class loader
+ // has an address other than 0. The class loader instances
+ // is still not contained in the hprof file
+ if (clazz.getClassLoaderId() < 0)
+ {
+ clazz.setClassLoaderAddress(0);
+ clazz.setClassLoaderIndex(identifiers.reverse(0));
+ }
+
+ boolean skipLogRefs = false;
+ // add class instance - if not set by pass1 from an instance_dump for the class
+ if (clazz.getClazz() == null)
+ {
+ clazz.setClassInstance(javaLangClass);
+ if (NEWCLASSSIZE)
+ {
+ // Recalculate the clazz heap size based on also java.lang.Class fields
+ // No need to do this for classes of other types as
+ // these have object instance records with fields converted to statics
+ clazz.setUsedHeapSize(clazz.getUsedHeapSize() + clazz.getClazz().getHeapSizePerInstance());
+ }
+ clazz.getClazz().addInstance(clazz.getUsedHeapSize());
+ }
+ else
+ {
+ // References for classes with instance dump records will be generated in pass2
+ skipLogRefs = true;
+ }
+
+ // resolve super class
+ ClassImpl superclass = lookupClass(clazz.getSuperClassAddress());
+ if (superclass != null)
+ superclass.addSubClass(clazz);
+
+ object2classId.set(clazz.getObjectId(), clazz.getClazz().getObjectId());
+
+ if (!skipLogRefs)
+ outbound.log(identifiers, clazz.getObjectId(), clazz.getReferences());
+ }
+
+ // report dependencies for system class loader
+ // (if no classes use this class loader, cleanup garbage will remove it
+ // again)
+ ClassImpl classLoaderClass = this.classesByName.get(IClass.JAVA_LANG_CLASSLOADER).get(0);
+ HeapObject heapObject = new HeapObject(0, classLoaderClass, classLoaderClass
+ .getHeapSizePerInstance());
+ heapObject.references.add(classLoaderClass.getObjectAddress());
+ this.addObject(heapObject, true);
+
+ }
+
+ /**
+ * Possible HPROF extension:
+ * classes also with instance dump records.
+ * The classes could be of a type other than java.lang.Class
+ * There could also be per-instance fields defined by their type.
+ * Those values can be made accessible to MAT by creating pseudo-static fields.
+ */
+ private void addTypesAndDummyStatics()
+ {
+ // Set type (and size?) for classes with object instances
+ // These will have had the type set by Pass1Parser
+ for (Iterator<Entry<ClassImpl>> it = classesByAddress.entries(); it.hasNext();)
+ {
+ Entry<ClassImpl> e = it.next();
+ ClassImpl cl = e.getValue();
+ ClassImpl type = cl.getClazz();
+ if (type != null)
+ {
+ List<FieldDescriptor> newStatics = new ArrayList<FieldDescriptor>(cl.getStaticFields());
+ List<IClass> icls = resolveClassHierarchy(type.getClassAddress());
+ for (IClass tcl : icls)
+ {
+ for (FieldDescriptor fd : tcl.getFieldDescriptors())
+ {
+ // Create pseudo-static field
+ Field st = new Field("<" + fd.getName() + ">", fd.getType(), null); //$NON-NLS-1$ //$NON-NLS-2$
+ newStatics.add(st);
+ }
+ }
+ if (newStatics.size() != cl.getStaticFields().size())
+ {
+ ClassImpl newcl = new ClassImpl(cl.getObjectAddress(), cl.getName(), cl.getSuperClassAddress(),
+ cl.getClassLoaderAddress(), newStatics.toArray(new Field[newStatics.size()]),
+ cl.getFieldDescriptors().toArray(new FieldDescriptor[0]));
+ newcl.setClassInstance(type);
+ // Fix up the existing lookups
+ classesByAddress.put(e.getKey(), newcl);
+ List<ClassImpl>nms = classesByName.get(cl.getName());
+ for (int i = 0; i < nms.size(); ++i)
+ {
+ if (nms.get(i) == cl)
+ nms.set(i, newcl);
+ }
+ }
+ }
+ }
+
+ // Set type to new type with the dummy static fields
+ for (Iterator<Entry<ClassImpl>> it = classesByAddress.entries(); it.hasNext();)
+ {
+ Entry<ClassImpl> e = it.next();
+ ClassImpl cl = e.getValue();
+ ClassImpl type = cl.getClazz();
+ if (type != null)
+ {
+ ClassImpl type2 = classesByAddress.get(type.getObjectAddress());
+ // Actual object test, not equality as we need to maintain linkage to the new class
+ if (type != type2)
+ {
+ cl.setClassInstance(type2);
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculate possible restrictions on object alignment by finding the GCD of differences
+ * between object addresses (ignoring address 0).
+ */
+ private void calculateAlignment()
+ {
+ // Minimum alignment of 8 bytes
+ final int minAlign = 8;
+ // Maximum alignment of 256 bytes
+ final int maxAlign = 256;
+ long prev = 0;
+ long align = 0;
+ for (IteratorLong it = identifiers.iterator(); it.hasNext(); )
+ {
+ long next = it.next();
+ if (next == 0)
+ continue;
+ long diff = next - prev;
+ prev = next;
+ if (next == diff)
+ continue;
+ if (align == 0)
+ {
+ align = diff;
+ }
+ else
+ {
+ long mx = Math.max(align, diff);
+ long mn = Math.min(align, diff);
+ long d = mx % mn;
+ while (d != 0) {
+ mx = mn;
+ mn = d;
+ d = mx % mn;
+ }
+ align = mn;
+ // Minimum alignment
+ if (align <= minAlign)
+ break;
+ }
+ }
+ // Sanitise the alignment
+ objectAlign = Math.max((int)Math.min(align, maxAlign), minAlign);
+ }
+
+ private void createRequiredFakeClasses() throws IOException, SnapshotException
+ {
+ // we know: system class loader has object address 0
+ long nextObjectAddress = 0;
+ // For generating the fake class names
+ int clsid = 0;
+ // java.lang.Object for the superclass
+ List<ClassImpl>jlos = classesByName.get("java.lang.Object"); //$NON-NLS-1$
+ long jlo = jlos == null || jlos.isEmpty() ? 0 : jlos.get(0).getObjectAddress();
+ // Fake java.lang.Class, java.lang.ClassLoader for later
+ String clss[] = {ClassImpl.JAVA_LANG_CLASS, ClassImpl.JAVA_LANG_CLASSLOADER};
+ for (String cls : clss)
+ {
+ List<ClassImpl>jlcs = classesByName.get(cls);
+ if (jlcs == null || jlcs.isEmpty())
+ {
+ while (identifiers.reverse(++nextObjectAddress) >= 0)
+ {}
+ IClass type = new ClassImpl(nextObjectAddress, cls, jlo, 0, new Field[0], new FieldDescriptor[0]);
+ addFakeClass((ClassImpl) type, -1);
+ }
+ }
+
+ // create required (fake) classes for arrays
+ if (!requiredArrayClassIDs.isEmpty())
+ {
+ for (long arrayClassID : requiredArrayClassIDs.keySet())
+ {
+ IClass arrayType = lookupClass(arrayClassID);
+ if (arrayType == null)
+ {
+ int objectId = identifiers.reverse(arrayClassID);
+ if (objectId >= 0)
+ {
+ String msg = MessageUtil.format(Messages.HprofParserHandlerImpl_Error_ExpectedClassSegment,
+ Long.toHexString(arrayClassID));
+ throw new SnapshotException(msg);
+ }
+
+ arrayType = new ClassImpl(arrayClassID, "unknown-class-"+clsid+"[]", jlo, 0, new Field[0], //$NON-NLS-1$ //$NON-NLS-2$
+ new FieldDescriptor[0]);
+ ++clsid;
+ addFakeClass((ClassImpl) arrayType, -1);
+ }
+ }
+ }
+ requiredArrayClassIDs = null;
+
+ for(int arrayType = 0; arrayType < requiredPrimitiveArrays.length; arrayType++)
+ {
+ if (requiredPrimitiveArrays[arrayType])
+ {
+ String name = IPrimitiveArray.TYPE[arrayType];
+ IClass clazz = lookupClassByName(name, true);
+ if (clazz == null)
+ {
+ while (identifiers.reverse(++nextObjectAddress) >= 0)
+ {}
+
+ clazz = new ClassImpl(nextObjectAddress, name, jlo, 0, new Field[0], new FieldDescriptor[0]);
+ addFakeClass((ClassImpl) clazz, -1);
+ }
+ primitiveArrays[arrayType] = clazz;
+ }
+ }
+
+ // create required (fake) classes for objects
+ if (!requiredClassIDs.isEmpty())
+ {
+ for (Map.Entry<Long, Integer> e : requiredClassIDs.entrySet())
+ {
+ long classID = e.getKey();
+ IClass type = lookupClass(classID);
+ if (type == null)
+ {
+ int objectId = identifiers.reverse(classID);
+ if (objectId >= 0)
+ {
+ String msg = MessageUtil.format(Messages.HprofParserHandlerImpl_Error_ExpectedClassSegment,
+ Long.toHexString(classID));
+ throw new SnapshotException(msg);
+ }
+ // Create some dummy fields
+ int size = e.getValue();
+ // Special value for missing superclass
+ if (size >= Integer.MAX_VALUE)
+ size = 0;
+ int nfields = size / 4 + Integer.bitCount(size % 4);
+ FieldDescriptor fds[] = new FieldDescriptor[nfields];
+ int i;
+ for (i = 0; i < size / 4; ++i)
+ {
+ fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.INT); //$NON-NLS-1$
+ }
+ if ((size & 2) != 0)
+ {
+ fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.SHORT); //$NON-NLS-1$
+ ++i;
+ }
+ if ((size & 1) != 0)
+ {
+ fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.BYTE); //$NON-NLS-1$
+ ++i;
+ }
+ type = new ClassImpl(classID, "unknown-class-"+clsid, jlo, 0, new Field[0], fds); //$NON-NLS-1$
+ ++clsid;
+ addFakeClass((ClassImpl) type, -1);
+ }
+ }
+ }
+ requiredClassIDs = null;
+
+ identifiers.sort();
+ }
+
+ private int calculateInstanceSize(ClassImpl clazz)
+ {
+ if (!clazz.isArrayType())
+ {
+ return alignUpToX(calculateSizeRecursive(clazz), objectAlign);
+ }
+ else
+ {
+ // use the referenceSize only to pass the proper ID size
+ // arrays calculate the rest themselves.
+ return refSize;
+ }
+ }
+
+ private int calculateSizeRecursive(ClassImpl clazz)
+ {
+ if (clazz.getSuperClassAddress() == 0)
+ {
+ return pointerSize + refSize;
+ }
+ ClassImpl superClass = classesByAddress.get(clazz.getSuperClassAddress());
+ int ownFieldsSize = 0;
+ for (FieldDescriptor field : clazz.getFieldDescriptors())
+ ownFieldsSize += sizeOf(field);
+
+ return alignUpToX(ownFieldsSize + calculateSizeRecursive(superClass), refSize);
+ }
+
+ private int calculateClassSize(ClassImpl clazz)
+ {
+ int staticFieldsSize = 0;
+ for (Field field : clazz.getStaticFields())
+ staticFieldsSize += sizeOf(field);
+ return alignUpToX(staticFieldsSize, objectAlign);
+ }
+
+ private int sizeOf(FieldDescriptor field)
+ {
+ int type = field.getType();
+ if (type == IObject.Type.OBJECT)
+ return refSize;
+
+ return IPrimitiveArray.ELEMENT_SIZE[type];
+ }
+
+ private int alignUpToX(int n, int x)
+ {
+ int r = n % x;
+ return r == 0 ? n : n + x - r;
+ }
+
+ private long alignUpToX(long n, int x)
+ {
+ long r = n % x;
+ return r == 0 ? n : n + x - r;
+ }
+
+ public IOne2LongIndex fillIn(IPreliminaryIndex index, IProgressListener listener) throws IOException
+ {
+ /*
+ * System classes should be marked appropriately in the HPROF file.
+ * lambda classes should not be marked as system GC roots.
+ */
+ boolean foundSystemClasses = false;
+ for (Iterator<List<XGCRootInfo>>it = gcRoots.values(); it.hasNext() && !foundSystemClasses; )
+ {
+ for (XGCRootInfo x : it.next())
+ {
+ if (x.getType() == GCRootInfo.Type.SYSTEM_CLASS)
+ {
+ foundSystemClasses = true;
+ break;
+ }
+ }
+ }
+ if (!foundSystemClasses)
+ {
+ // Probably not needed anymore.
+ // ensure all classes loaded by the system class loaders are marked as
+ // GCRoots
+ //
+ // For some dumps produced with jmap 1.5_xx this is not the case, and
+ // it may happen that the super classes of some classes are missing
+ // Array classes, e.g. java.lang.String[][] are not explicitly
+ // marked. They are also not marked as "system class" in the non-jmap
+ // heap dumps
+ ClassImpl[] allClasses = classesByAddress.getAllValues(new ClassImpl[0]);
+ for (ClassImpl clazz : allClasses)
+ {
+ if (clazz.getClassLoaderAddress() == 0 && !clazz.isArrayType()
+ && !gcRoots.containsKey(clazz.getObjectAddress()))
+ {
+ addGCRoot(clazz.getObjectAddress(), 0, GCRootInfo.Type.SYSTEM_CLASS);
+ }
+ }
+ }
+
+ // classes model
+ HashMapIntObject<ClassImpl> classesById = new HashMapIntObject<ClassImpl>(classesByAddress.size());
+ for (Iterator<ClassImpl> iter = classesByAddress.values(); iter.hasNext();)
+ {
+ ClassImpl clazz = iter.next();
+ classesById.put(clazz.getObjectId(), clazz);
+ }
+ index.setClassesById(classesById);
+
+ // Create Histogram of discarded objects
+ long discardedObjects = 0;
+ long discardedSize = 0;
+ List<UnreachableObjectsHistogram.Record> records = new ArrayList<UnreachableObjectsHistogram.Record>();
+ for(ClassImpl clazz : discardedObjectsByClass.values()) {
+ records.add(new UnreachableObjectsHistogram.Record(
+ clazz.getName(),
+ clazz.getObjectAddress(),
+ clazz.getNumberOfObjects(),
+ clazz.getTotalSize()));
+ discardedObjects += clazz.getNumberOfObjects();
+ discardedSize += clazz.getTotalSize();
+ }
+ if (discardedObjects > 0)
+ {
+ UnreachableObjectsHistogram deadObjectHistogram = new UnreachableObjectsHistogram(records);
+ info.setProperty(UnreachableObjectsHistogram.class.getName(), deadObjectHistogram);
+ listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(
+ Messages.HprofParserHandlerImpl_DiscardedObjects,
+ discardedObjects, discardedSize, discardRatio, discardPattern), null);
+ }
+
+ index.setGcRoots(map2ids(gcRoots));
+
+ HashMapIntObject<HashMapIntObject<List<XGCRootInfo>>> thread2objects2roots = new HashMapIntObject<HashMapIntObject<List<XGCRootInfo>>>();
+ for (Iterator<HashMapLongObject.Entry<HashMapLongObject<List<XGCRootInfo>>>> iter = threadAddressToLocals
+ .entries(); iter.hasNext();)
+ {
+ HashMapLongObject.Entry<HashMapLongObject<List<XGCRootInfo>>> entry = iter.next();
+ int threadId = identifiers.reverse(entry.getKey());
+ if (threadId >= 0)
+ {
+ HashMapIntObject<List<XGCRootInfo>> objects2roots = map2ids(entry.getValue());
+ if (!objects2roots.isEmpty())
+ thread2objects2roots.put(threadId, objects2roots);
+ }
+ }
+ index.setThread2objects2roots(thread2objects2roots);
+
+ index.setIdentifiers((new LongIndexStreamer()).writeTo(Index.IDENTIFIER.getFile(info.getPrefix() + "temp."), identifiers.iterator())); //$NON-NLS-1$);
+
+ index.setArray2size(array2size.writeTo(Index.A2SIZE.getFile(info.getPrefix() + "temp."))); //$NON-NLS-1$
+
+ index.setObject2classId(object2classId.writeTo(Index.O2CLASS.getFile(info.getPrefix() + "temp."))); //$NON-NLS-1$
+
+ index.setOutbound(outbound.flush());
+
+ return object2position.writeTo(new File(info.getPrefix() + "temp.o2hprof.index")); //$NON-NLS-1$
+ }
+
+ private HashMapIntObject<List<XGCRootInfo>> map2ids(HashMapLongObject<List<XGCRootInfo>> source)
+ {
+ HashMapIntObject<List<XGCRootInfo>> sink = new HashMapIntObject<List<XGCRootInfo>>();
+ for (Iterator<HashMapLongObject.Entry<List<XGCRootInfo>>> iter = source.entries(); iter.hasNext();)
+ {
+ HashMapLongObject.Entry<List<XGCRootInfo>> entry = iter.next();
+ int idx = identifiers.reverse(entry.getKey());
+ if (idx >= 0)
+ {
+ // sometimes it happens that there is no object for an
+ // address reported as a GC root. It's not clear why
+ for (Iterator<XGCRootInfo> roots = entry.getValue().iterator(); roots.hasNext();)
+ {
+ XGCRootInfo root = roots.next();
+ root.setObjectId(idx);
+ if (root.getContextAddress() != 0)
+ {
+ int contextId = identifiers.reverse(root.getContextAddress());
+ if (contextId < 0)
+ roots.remove();
+ else
+ root.setContextId(contextId);
+ }
+ }
+ sink.put(idx, entry.getValue());
+ }
+ }
+ return sink;
+ }
+
+ public void cancel()
+ {
+ if (outbound != null)
+ outbound.cancel();
+
+ }
+
+ // //////////////////////////////////////////////////////////////
+ // report parsed entities
+ // //////////////////////////////////////////////////////////////
+
+ public void addProperty(String name, String value) throws IOException
+ {
+ if (IHprofParserHandler.VERSION.equals(name))
+ {
+ version = Version.valueOf(value);
+ info.setProperty(HprofHeapObjectReader.VERSION_PROPERTY, version.name());
+ }
+ else if (IHprofParserHandler.IDENTIFIER_SIZE.equals(name))
+ {
+ int idSize = Integer.parseInt(value);
+ info.setIdentifierSize(idSize);
+ pointerSize = idSize;
+ refSize = idSize;
+ }
+ else if (IHprofParserHandler.CREATION_DATE.equals(name))
+ {
+ info.setCreationDate(new Date(Long.parseLong(value)));
+ }
+ else if (IHprofParserHandler.REFERENCE_SIZE.equals(name))
+ {
+ refSize = Integer.parseInt(value);
+ }
+ else if (IHprofParserHandler.STREAM_LENGTH.equals(name))
+ {
+ long length = Long.parseLong(value);
+ info.setProperty(HprofHeapObjectReader.HPROF_LENGTH_PROPERTY, length);
+ }
+ else if (IHprofParserHandler.HEAP_POSITION.equals(name))
+ {
+ long pos = Long.parseLong(value);
+ info.setProperty(HprofHeapObjectReader.HPROF_HEAP_START, pos);
+ }
+ }
+
+ public void addGCRoot(long id, long referrer, int rootType)
+ {
+ if (referrer != 0)
+ {
+ HashMapLongObject<List<XGCRootInfo>> localAddressToRootInfo = threadAddressToLocals.get(referrer);
+ if (localAddressToRootInfo == null)
+ {
+ localAddressToRootInfo = new HashMapLongObject<List<XGCRootInfo>>();
+ threadAddressToLocals.put(referrer, localAddressToRootInfo);
+ }
+ List<XGCRootInfo> gcRootInfo = localAddressToRootInfo.get(id);
+ if (gcRootInfo == null)
+ {
+ gcRootInfo = new ArrayList<XGCRootInfo>(1);
+ localAddressToRootInfo.put(id, gcRootInfo);
+ }
+ gcRootInfo.add(new XGCRootInfo(id, referrer, rootType));
+ return; // do not add the object as GC root
+ }
+
+ List<XGCRootInfo> r = gcRoots.get(id);
+ if (r == null)
+ gcRoots.put(id, r = new ArrayList<XGCRootInfo>(3));
+ r.add(new XGCRootInfo(id, referrer, rootType));
+ }
+
+ private void addFakeClass(ClassImpl clazz, long filePosition) throws IOException
+ {
+ this.identifiers.add(clazz.getObjectAddress());
+ this.classesByAddress.put(clazz.getObjectAddress(), clazz);
+
+ List<ClassImpl> list = classesByName.get(clazz.getName());
+ if (list == null)
+ classesByName.put(clazz.getName(), list = new ArrayList<ClassImpl>());
+ list.add(clazz);
+ }
+
+ public void addClass(ClassImpl clazz, long filePosition, int idSize, int instsize) throws IOException
+ {
+ this.identifiers.add(clazz.getObjectAddress());
+ this.classesByAddress.put(clazz.getObjectAddress(), clazz);
+
+ List<ClassImpl> list = classesByName.get(clazz.getName());
+ if (list == null)
+ classesByName.put(clazz.getName(), list = new ArrayList<ClassImpl>());
+ list.add(clazz);
+
+ if (clazz.getSuperClassAddress() != 0) {
+ // Try to calculate how big the superclass should be
+ int ownFieldsSize = 0;
+ for (FieldDescriptor field : clazz.getFieldDescriptors())
+ {
+ int type = field.getType();
+ if (type == IObject.Type.OBJECT)
+ ownFieldsSize += idSize;
+ else
+ ownFieldsSize += IPrimitiveArray.ELEMENT_SIZE[type];
+ }
+ int supersize = Math.max(instsize - ownFieldsSize, 0);
+ // A real size of an instance will override this
+ reportRequiredClass(clazz.getSuperClassAddress(), supersize, false);
+ }
+ }
+
+ private void prepareHeapObject(HeapObject object) throws IOException
+ {
+ if (object.isPrimitiveArray)
+ {
+ byte elementType = (byte) object.classIdOrElementType;
+ ClassImpl clazz = (ClassImpl) lookupPrimitiveArrayClassByType(elementType);
+ object.usedHeapSize = getPrimitiveArrayHeapSize(elementType, object.arraySize);
+ object.references.add(clazz.getObjectAddress());
+ object.clazz = clazz;
+ }
+
+ if (object.isObjectArray)
+ {
+ long arrayClassObjectID = object.classIdOrElementType;
+ ClassImpl arrayType = (ClassImpl) lookupClass(arrayClassObjectID);
+ if (arrayType == null)
+ throw new RuntimeException(MessageUtil.format(
+ Messages.Pass2Parser_Error_HandlerMustCreateFakeClassForAddress,
+ Long.toHexString(arrayClassObjectID)));
+
+ object.usedHeapSize = getObjectArrayHeapSize(arrayType, object.arraySize);
+ object.references.add(arrayType.getObjectAddress());
+ long[] ids = object.ids;
+ for (int ii = 0; ii < object.arraySize; ii++)
+ {
+ if (ids[ii] != 0)
+ object.references.add(ids[ii]);
+ }
+ object.clazz = arrayType;
+ // References now transfered, so free some space
+ ids = null;
+ object.ids = null;
+ }
+
+ if (!object.isObjectArray && !object.isPrimitiveArray)
+ {
+ long classID = object.classIdOrElementType;
+ List<IClass> hierarchy = resolveClassHierarchy(classID);
+ ByteArrayPositionInputStream in = new ByteArrayPositionInputStream(object.instanceData, object.idSize);
+
+ ClassImpl thisClazz = (ClassImpl) hierarchy.get(0);
+
+ IClass objcl = lookupClass(object.objectAddress);
+ Field statics[] = new Field[0];
+ if (objcl instanceof ClassImpl)
+ {
+ // An INSTANCE_DUMP record for a class type
+ // This clazz is perhaps of different actual type, not java.lang.Class
+ // The true type has already been set in PassParser1 and beforePass2()
+ ClassImpl objcls = (ClassImpl) objcl;
+ statics = objcls.getStaticFields().toArray(statics);
+ // Heap size of each class type object is individual as have statics
+ object.clazz = thisClazz;
+ object.usedHeapSize = objcls.getUsedHeapSize();
+ // and extract the class references
+ object.references.addAll(objcls.getReferences());
+ }
+ else
+ {
+ object.clazz = thisClazz;
+ object.usedHeapSize = thisClazz.getHeapSizePerInstance();
+ object.references.add(thisClazz.getObjectAddress());
+ }
+
+ // extract outgoing references
+ int pos = 0;
+ for (IClass clazz : hierarchy)
+ {
+ for (FieldDescriptor field : clazz.getFieldDescriptors())
+ {
+ int type = field.getType();
+ // Find match for pseudo-statics
+ Field stField = null;
+ for (int stidx = 0; stidx < statics.length; ++stidx)
+ {
+ if (statics[stidx] != null && statics[stidx].getType() == type && statics[stidx].getName().equals("<"+field.getName()+">")) { //$NON-NLS-1$ //$NON-NLS-2$
+ // Found a field
+ stField = statics[stidx];
+ // Don't use this twice.
+ statics[stidx] = null;
+ break;
+ }
+ }
+ if (type == IObject.Type.OBJECT)
+ {
+ long refId = in.readID(object.idSize);
+ pos += object.idSize;
+ if (refId != 0)
+ {
+ object.references.add(refId);
+ if (stField != null)
+ {
+ stField.setValue(new ObjectReference(null, refId));
+ }
+ }
+ }
+ else
+ {
+ Object value = AbstractParser.readValue(in, null, type, object.idSize);
+ if (stField != null)
+ stField.setValue(value);
+ }
+ }
+ }
+
+ if (pos != object.instanceData.length)
+ {
+ boolean unknown = false;
+ for (IClass clazz : hierarchy)
+ {
+ if (clazz.getName().startsWith("unknown-class")) //$NON-NLS-1$
+ {
+ unknown = true;
+ }
+ }
+
+ // TODO get the strictness settings across from eclipse
+// if (unknown && (strictnessPreference == HprofStrictness.STRICTNESS_WARNING || strictnessPreference == HprofStrictness.STRICTNESS_PERMISSIVE))
+// {
+// //monitor.sendUserMessage(Severity.WARNING, MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, thisClazz.getName(), Long.toHexString(id), Long.toHexString(segmentStartPos), Long.toHexString(endPos), Long.toHexString(in.position())), null);
+// }
+// else
+// {
+// throw new IOException(MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, thisClazz.getName(), Long.toHexString(id), Long.toHexString(segmentStartPos), Long.toHexString(endPos), Long.toHexString(in.position())));
+// }
+ }
+ }
+
+ }
+ public void addObject(HeapObject object) throws IOException
+ {
+ addObject(object, false);
+ }
+
+ private void addObject(HeapObject object, boolean prepared) throws IOException
+ {
+ if (!prepared)
+ prepareHeapObject(object);
+
+ // this may be called from multiple threads
+ // so, each function called inside here needs to be threadsafe
+ // it will not do to simply synchronize here as we need
+ // better concurrency than that
+
+ int index = mapAddressToId(object.objectAddress);
+ if (index < 0)
+ {
+ // Discarded object
+ ClassImpl cls = discardedObjectsByClass.get(object.clazz.getObjectId());
+ if (cls == null)
+ {
+ cls = new ClassImpl(object.clazz.getObjectAddress(),
+ object.clazz.getName(),
+ object.clazz.getSuperClassAddress(),
+ object.clazz.getClassLoaderAddress(),
+ new Field[0],
+ new FieldDescriptor[0]);
+ cls.setHeapSizePerInstance(object.clazz.getHeapSizePerInstance());
+ ClassImpl clsOld = discardedObjectsByClass.putIfAbsent(object.clazz.getObjectId(), cls);
+ if (clsOld != null)
+ cls = clsOld;
+ }
+ /*
+ * Keep count of discards
+ * @TODO consider overflow as we count discard >Integer.MAX_VALUE
+ */
+ cls.addInstance(object.usedHeapSize);
+ return;
+ }
+
+ // check if some thread to local variables references have to be added
+ HashMapLongObject<List<XGCRootInfo>> localVars = threadAddressToLocals.get(object.objectAddress);
+ if (localVars != null)
+ {
+ IteratorLong e = localVars.keys();
+ while (e.hasNext())
+ {
+ object.references.add(e.next());
+ }
+ }
+
+ // log references
+ outbound.log(identifiers, index, object.references);
+
+ int classIndex = object.clazz.getObjectId();
+ object.clazz.addInstance(object.usedHeapSize);
+
+ // log address
+ object2classId.set(index, classIndex);
+ object2position.set(index, object.filePosition);
+
+ // log array size
+ if (object.isPrimitiveArray || object.isObjectArray)
+ array2size.set(index, object.usedHeapSize);
+ }
+
+ /**
+ * Randomly choose whether to discard
+ * @return
+ */
+ private boolean discard()
+ {
+ if (discardRatio <= 0.0)
+ return false;
+ // Always accept the first object to give a start to the heap
+ if (identifiers.size() == 0)
+ return false;
+ double d = rand.nextDouble();
+ double top = discardRatio + discardOffset;
+ /*
+ * Wrap around the range.
+ * [dddddddd..] 0.8,0.0
+ * [..dddddddd] 0.8,0.2
+ * [dd..dddddd] 0.8,0.4
+ */
+ return (d < top && d >= discardOffset) || d < top - 1.0;
+ }
+
+ /**
+ * Choose to discard also based on object type
+ *
+ * @param classId the HPROF class Id
+ * @return
+ */
+ private boolean discard(long classId)
+ {
+ if (!discard())
+ return false;
+ ClassImpl cls = lookupClass(classId);
+ if (cls != null && discardPattern.matcher(cls.getName()).matches())
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adjust the last seen file position.
+ * Used to see how many bits are needed for the index.
+ * @param filePosition
+ */
+ private void reportFilePosition(long filePosition)
+ {
+ if (filePosition > maxFilePosition)
+ maxFilePosition = filePosition;
+ }
+
+ private void reportInstance(long id, long filePosition)
+ {
+ this.identifiers.add(id);
+ reportFilePosition(filePosition);
+ }
+
+ public void reportInstanceWithClass(long id, long filePosition, long classID, int size)
+ {
+ if (discard(classID))
+ {
+ // Skip
+ reportFilePosition(filePosition);
+ return;
+ }
+ reportInstance(id, filePosition);
+ reportRequiredClass(classID, size, true);
+ }
+
+ public void reportInstanceOfObjectArray(long id, long filePosition, long arrayClassID)
+ {
+ if (discard(arrayClassID))
+ {
+ // Skip
+ reportFilePosition(filePosition);
+ return;
+ }
+ reportInstance(id, filePosition);
+ reportRequiredObjectArray(arrayClassID);
+ }
+
+ public void reportInstanceOfPrimitiveArray(long id, long filePosition, int arrayType)
+ {
+ if (discard() && discardPattern.matcher(IPrimitiveArray.TYPE[arrayType]).matches())
+ {
+ // Skip
+ reportFilePosition(filePosition);
+ reportRequiredPrimitiveArray(arrayType);
+ return;
+ }
+ reportInstance(id, filePosition);
+ reportRequiredPrimitiveArray(arrayType);
+ }
+
+ private void reportRequiredObjectArray(long arrayClassID)
+ {
+ requiredArrayClassIDs.putIfAbsent(arrayClassID, true);
+ }
+
+ private void reportRequiredPrimitiveArray(int arrayType)
+ {
+ requiredPrimitiveArrays[arrayType] = true;
+ }
+
+ private void reportRequiredClass(long classID, int size, boolean sizeKnown)
+ {
+ if (sizeKnown)
+ {
+ requiredClassIDs.put(classID, size);
+ }
+ else
+ {
+ requiredClassIDs.putIfAbsent(classID, size);
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////
+ // lookup heap infos
+ // //////////////////////////////////////////////////////////////
+
+ public int getIdentifierSize()
+ {
+ return info.getIdentifierSize();
+ }
+
+ public ClassImpl lookupClass(long classId)
+ {
+ return classesByAddress.get(classId);
+ }
+
+ public IClass lookupPrimitiveArrayClassByType(byte elementType)
+ {
+ return primitiveArrays[elementType];
+ }
+
+ public IClass lookupClassByName(String name, boolean failOnMultipleInstances)
+ {
+ List<ClassImpl> list = classesByName.get(name);
+ if (list == null)
+ return null;
+ if (failOnMultipleInstances && list.size() != 1)
+ throw new RuntimeException(MessageUtil.format(
+ Messages.HprofParserHandlerImpl_Error_MultipleClassInstancesExist, name));
+ return list.get(0);
+ }
+
+ public IClass lookupClassByIndex(int objIndex)
+ {
+ return lookupClass(this.identifiers.get(objIndex));
+ }
+
+ ConcurrentHashMap<Long, List<IClass>> classHierarchyCache = new ConcurrentHashMap<Long, List<IClass>>();
+ public List<IClass> resolveClassHierarchy(long classId)
+ {
+ List<IClass> cached = classHierarchyCache.get(classId);
+ if (cached != null)
+ {
+ return cached;
+ }
+
+ List<IClass> answer = new ArrayList<IClass>();
+
+ ClassImpl clazz = classesByAddress.get(classId);
+ answer.add(clazz);
+
+ while (clazz.hasSuperClass())
+ {
+ clazz = classesByAddress.get(clazz.getSuperClassAddress());
+ answer.add(clazz);
+ }
+
+ classHierarchyCache.put(classId, answer);
+ return answer;
+ }
+
+ public int mapAddressToId(long address)
+ {
+ return this.identifiers.reverse(address);
+ }
+
+ public XSnapshotInfo getSnapshotInfo()
+ {
+ return info;
+ }
+
+ public long getObjectArrayHeapSize(ClassImpl arrayType, int size)
+ {
+ long usedHeapSize = alignUpToX(pointerSize + refSize + 4 + size * arrayType.getHeapSizePerInstance(), objectAlign);
+ return usedHeapSize;
+ }
+
+ public long getPrimitiveArrayHeapSize(byte elementType, int size)
+ {
+ long usedHeapSize = alignUpToX(alignUpToX(pointerSize + refSize + 4, refSize) + size * (long)PrimitiveArrayImpl.ELEMENT_SIZE[(int) elementType], objectAlign);
+ return usedHeapSize;
+ }
+
+}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofRandomAccessParser.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofRandomAccessParser.java
index 8534350..b5af743 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofRandomAccessParser.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofRandomAccessParser.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2020 SAP AG and others.
+ * Copyright (c) 2008, 2021 SAP AG, Netflix, IBM Corporation and others.
* 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
@@ -14,6 +14,7 @@
*******************************************************************************/
package org.eclipse.mat.hprof;
+import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@@ -22,9 +23,12 @@
import java.util.List;
import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.hprof.AbstractParser.Constants.Record;
import org.eclipse.mat.hprof.describer.Version;
import org.eclipse.mat.hprof.ui.HprofPreferences;
+import org.eclipse.mat.parser.index.IIndexReader.IOne2LongIndex;
import org.eclipse.mat.parser.io.BufferedRandomAccessInputStream;
+import org.eclipse.mat.parser.model.AbstractObjectImpl;
import org.eclipse.mat.parser.model.ClassImpl;
import org.eclipse.mat.parser.model.ClassLoaderImpl;
import org.eclipse.mat.parser.model.InstanceImpl;
@@ -37,6 +41,7 @@
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IPrimitiveArray;
+import org.eclipse.mat.snapshot.model.ObjectReference;
import org.eclipse.mat.util.MessageUtil;
public class HprofRandomAccessParser extends AbstractParser
@@ -80,10 +85,14 @@
in.close();
}
- public synchronized IObject read(int objectId, long position, ISnapshot dump) throws IOException, SnapshotException
+ public synchronized IObject read(int objectId, long position, ISnapshot dump, IOne2LongIndex o2hprof) throws IOException, SnapshotException
{
in.seek(position);
int segmentType = in.readUnsignedByte();
+ if (objectId == -1)
+ {
+ segmentType = skipRecords(segmentType);
+ }
switch (segmentType)
{
case Constants.DumpSegment.INSTANCE_DUMP:
@@ -114,14 +123,192 @@
return answer;
}
+ /**
+ * A reference to an object via its address
+ * which can handle the address not being in the index.
+ */
+ static class ObjectAddressReference extends ObjectReference
+ {
+ private static final long serialVersionUID = 1L;
+ transient IOne2LongIndex o2hprof;
+ transient ISnapshot snapshot;
+ transient HprofRandomAccessParser parser;
+ /**
+ * Construct an {@link ObjectAddressReference} which can be used to retrieve an
+ * object by address even if it has not been indexed.
+ * Used as a proxy by {@link ObjectReference} which can change the {@link ObjectReference#address}
+ * private field.
+ * @param snapshot the snapshot
+ * @param parser the HPROF parser used to build the IObject if required
+ * @param o2hprof the index from object ID to HPROF file position
+ * @param address the address of the object
+ */
+ public ObjectAddressReference(ISnapshot snapshot, HprofRandomAccessParser parser, IOne2LongIndex o2hprof, long address)
+ {
+ super(snapshot, address);
+ this.snapshot = snapshot;
+ this.o2hprof = o2hprof;
+ this.parser = parser;
+ }
+ @Override
+ public int getObjectId() throws SnapshotException
+ {
+ try
+ {
+ int id = super.getObjectId();
+ return id;
+ }
+ catch (SnapshotException e)
+ {
+ return -1;
+ }
+ }
+ /**
+ * Actually construct an IObject.
+ * First try constructing by object ID.
+ * If that fails then construct by address.
+ * Find the indexed objects before and after this.
+ * Parse the HPROF file between these two points looking for an
+ * object of the correct address.
+ */
+ @Override
+ public IObject getObject() throws SnapshotException
+ {
+ SnapshotException e1 = null;
+ try
+ {
+ int id = super.getObjectId();
+ if (id >= 0)
+ {
+ IObject o = super.getObject();
+ return o;
+ }
+ }
+ catch (SnapshotException e)
+ {
+ e1 = e;
+ }
+ // Find the object IDs before and after this object
+ int low = 0;
+ int high = o2hprof.size() - 1;
+ for (;low + 1 < high;)
+ {
+ int mid = (low + high) >>> 1;
+ long midAddr = snapshot.mapIdToAddress(mid);
+ if (getObjectAddress() < midAddr)
+ {
+ high = mid;
+ }
+ else if (getObjectAddress() > midAddr)
+ {
+ low = mid;
+ }
+ else
+ {
+ low = mid;
+ high = mid;
+ }
+ }
+ // Some objects don't have a position (classes?), so find valid file positions
+ long pos = 0;
+ while ((pos = o2hprof.get(low)) == 0 && low > 0)
+ --low;
+ if (pos == 0)
+ {
+ // There might be discarded objects before the first
+ Long lpos = (Long)snapshot.getSnapshotInfo().getProperty(HprofHeapObjectReader.HPROF_HEAP_START);
+ if (lpos != null)
+ pos = lpos;
+ else while ((pos = o2hprof.get(low)) == 0 && low < o2hprof.size() - 1)
+ ++low;
+ }
+ long posHigh = pos;
+ while (high >= 0 && (posHigh = o2hprof.get(high)) == 0 && high < o2hprof.size() - 1)
+ ++high;
+ if (posHigh == 0)
+ {
+ // There might be discarded objects after the last
+ Long olen = (Long)snapshot.getSnapshotInfo().getProperty(HprofHeapObjectReader.HPROF_LENGTH_PROPERTY);
+ posHigh = (olen != null) ? olen : Long.MAX_VALUE;
+ }
+ // Reparse the HPROF file looking for an object of the right address
+ try
+ {
+ do
+ {
+ IObject o;
+ synchronized (parser)
+ {
+ // Need final position without interference
+ o = parser.read(-1, pos, snapshot, o2hprof);
+ pos = parser.in.position();
+ }
+ if (o.getObjectAddress() == getObjectAddress())
+ {
+ // Class was discarded from the snapshot
+ if (o.getClazz() == null)
+ throw new IOException();
+ ((AbstractObjectImpl)o).setSnapshot(snapshot);
+ return o;
+ }
+ // Gone past the object address we want
+ if (o.getObjectAddress() > getObjectAddress())
+ break;
+ }
+ while (pos < posHigh);
+ }
+ catch (IOException e2)
+ {
+ if (e2 instanceof EOFException)
+ throw e1;
+ throw new SnapshotException(e2);
+ }
+ // Not found, so throw the original exception
+ throw e1;
+ }
+ }
+
private IObject readInstanceDump(int objectId, ISnapshot dump) throws IOException, SnapshotException
{
long address = in.readID(idSize);
- if (in.skipBytes(8 + idSize) != 8 + idSize)
- throw new IOException();
+ IClass oclazz;
+ if (objectId >= 0)
+ {
+ // Skip serial number, class ID, length
+ if (in.skipBytes(8 + idSize) != 8 + idSize)
+ throw new IOException();
+ oclazz = dump.getClassOf(objectId);
+ }
+ else
+ {
+ // skip serial number
+ if (in.skipBytes(4) != 4)
+ throw new IOException();
+ // class ID
+ long classAddr = in.readID(idSize);
+ // length
+ long len = in.readUnsignedInt();
+ try
+ {
+ int classID = dump.mapAddressToId(classAddr);
+ IObject io = dump.getObject(classID);
+ if (io instanceof IClass)
+ oclazz = (IClass)io;
+ else
+ throw new IOException();
+ }
+ catch (SnapshotException e)
+ {
+ // move to end of object
+ if (in.skipBytes(len) != len)
+ throw new IOException();
+ // Invalid object, but might be good enough for skipping over
+ return new InstanceImpl(objectId, address, null, null);
+ }
+ }
// check if we need to defer reading the class
- List<IClass> hierarchy = resolveClassHierarchy(dump, dump.getClassOf(objectId));
+ List<IClass> hierarchy = resolveClassHierarchy(dump, oclazz);
if (hierarchy == null)
{
throw new IOException(Messages.HprofRandomAccessParser_Error_DumpIncomplete);
@@ -156,15 +343,36 @@
in.skipBytes(4);
int size = in.readInt();
+ long len = (long)size * idSize;
long arrayClassObjectID = in.readID(idSize);
- IClass arrayType = (IClass) dump.getObject(dump.mapAddressToId(arrayClassObjectID));
+ int typeId;
+ if (objectId == -1)
+ {
+ try
+ {
+ typeId = dump.mapAddressToId(arrayClassObjectID);
+ }
+ catch (SnapshotException e)
+ {
+ ObjectArrayImpl array = new ObjectArrayImpl(objectId, id, null, size);
+ // Move to end of object
+ if (in.skipBytes(len) != len)
+ throw new IOException();
+ return array;
+ }
+ }
+ else
+ {
+ typeId = dump.mapAddressToId(arrayClassObjectID);
+ }
+ IClass arrayType = (IClass) dump.getObject(typeId);
if (arrayType == null)
throw new RuntimeException(Messages.HprofRandomAccessParser_Error_MissingFakeClass);
Object content = null;
- if ((long)size * idSize < LAZY_LOADING_LIMIT)
+ if (len < LAZY_LOADING_LIMIT)
{
long[] data = new long[size];
for (int ii = 0; ii < data.length; ii++)
@@ -174,6 +382,12 @@
else
{
content = new ArrayDescription.Offline(false, in.position(), 0, size);
+ if (objectId == -1)
+ {
+ // Move to end of object
+ if (in.skipBytes(len) != len)
+ throw new IOException();
+ }
}
ObjectArrayImpl array = new ObjectArrayImpl(objectId, id, (ClassImpl) arrayType, size);
@@ -205,6 +419,12 @@
else
{
content = new ArrayDescription.Offline(true, in.position(), elementSize, arraySize);
+ if (objectId == -1)
+ {
+ // Move to end of object
+ if (in.skipBytes(len) != len)
+ throw new IOException();
+ }
}
// lookup class by name
@@ -212,7 +432,15 @@
String name = IPrimitiveArray.TYPE[(int) elementType];
Collection<IClass> classes = dump.getClassesByName(name, false);
if (classes == null || classes.isEmpty())
+ {
+ if (objectId == -1)
+ {
+ // Return a dummy array which can be ignored
+ PrimitiveArrayImpl array = new PrimitiveArrayImpl(objectId, id, (ClassImpl) clazz, arraySize, (int) elementType);
+ return array;
+ }
throw new IOException(MessageUtil.format(Messages.HprofRandomAccessParser_Error_MissingClass, name));
+ }
else if (classes.size() > 1)
throw new IOException(MessageUtil.format(Messages.HprofRandomAccessParser_Error_DuplicateClass, name));
else
@@ -247,4 +475,74 @@
in.readFully(data);
return data;
}
+
+ private int skipRecords(int segmentType) throws IOException
+ {
+ boolean again = true;
+ do
+ {
+ int skip = -1;
+ switch (segmentType)
+ {
+ case Record.HEAP_DUMP_SEGMENT:
+ skip = 8;
+ break;
+ case Constants.DumpSegment.ROOT_UNKNOWN:
+ case Constants.DumpSegment.ROOT_STICKY_CLASS:
+ case Constants.DumpSegment.ROOT_MONITOR_USED:
+ skip = idSize;
+ break;
+ case Constants.DumpSegment.ROOT_JNI_GLOBAL:
+ skip = idSize * 2;
+ break;
+ case Constants.DumpSegment.ROOT_JNI_LOCAL:
+ case Constants.DumpSegment.ROOT_JAVA_FRAME:
+ case Constants.DumpSegment.ROOT_THREAD_OBJECT:
+ skip = idSize + 8;
+ break;
+ case Constants.DumpSegment.ROOT_NATIVE_STACK:
+ case Constants.DumpSegment.ROOT_THREAD_BLOCK:
+ skip = idSize + 4;
+ break;
+ case Constants.DumpSegment.CLASS_DUMP:
+ skipClassDump();
+ // Already skipped enough, so just reread
+ skip = 0;
+ break;
+ default:
+ again = false;
+ break;
+ }
+ if (skip >= 0)
+ {
+ // Skip over new segment header etc.
+ in.skipBytes(skip);
+ segmentType = in.readUnsignedByte();
+ }
+ }
+ while (again);
+ return segmentType;
+ }
+
+ private void skipClassDump() throws IOException
+ {
+ in.skipBytes(7 * idSize + 8);
+
+ int constantPoolSize = in.readUnsignedShort();
+ for (int ii = 0; ii < constantPoolSize; ii++)
+ {
+ in.skipBytes(2);
+ skipValue(in);
+ }
+
+ int numStaticFields = in.readUnsignedShort();
+ for (int i = 0; i < numStaticFields; i++)
+ {
+ in.skipBytes(idSize);
+ skipValue(in);
+ }
+
+ int numInstanceFields = in.readUnsignedShort();
+ in.skipBytes((idSize + 1) * numInstanceFields);
+ }
}
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/IObjectReader.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/IObjectReader.java
index 48050aa..b5a0f33 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/IObjectReader.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/IObjectReader.java
@@ -1,87 +1,88 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG and others.
- * 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
- *******************************************************************************/
-package org.eclipse.mat.parser;
-
-import java.io.IOException;
-
-import org.eclipse.mat.SnapshotException;
-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;
-
-/**
- * Part of the parser which retrieves detailed information about an object
- */
-public interface IObjectReader
-{
- /**
- * Open the dump file associated with the snapshot
- * @param snapshot
- * @throws SnapshotException
- * @throws IOException
- */
- void open(ISnapshot snapshot) //
- throws SnapshotException, IOException;
-
- /**
- * Get detailed information about an object
- * @param objectId the object id
- * @param snapshot the snapshot
- * @return an IObject such as {@link org.eclipse.mat.parser.model.InstanceImpl}, {@link org.eclipse.mat.parser.model.ObjectArrayImpl}, {@link org.eclipse.mat.parser.model.PrimitiveArrayImpl}, {@link org.eclipse.mat.parser.model.ClassLoaderImpl}
- * @throws SnapshotException
- * @throws IOException
- */
- IObject read(int objectId, ISnapshot snapshot) //
- throws SnapshotException, IOException;
-
- /**
- * Get detailed information about a primitive array
- * @param array the array
- * @param offset where in the array to start
- * @param length how much to read
- * @return a byte[], short[], int[], long[], boolean[], char[], float[], double[]
- * @throws IOException
- * @throws SnapshotException
- */
- Object readPrimitiveArrayContent(PrimitiveArrayImpl array, int offset, int length) //
- throws IOException, SnapshotException;
-
- /**
- * Get detailed information about a object array
- * @param array
- * @param offset where in the array to start
- * @param length how much to read
- * @return an array of object addresses, with 0 for nulls
- * @throws IOException
- * @throws SnapshotException
- */
- long[] readObjectArrayContent(ObjectArrayImpl array, int offset, int length) //
- throws IOException, SnapshotException;
-
- /**
- * Get additional information about the snapshot
- * @param addon type of the additional information
- * @return the additional information
- * @throws SnapshotException
- */
- <A> A getAddon(Class<A> addon) //
- throws SnapshotException;
-
- /**
- * tidy up when snapshot no longer required
- * @throws IOException
- */
- void close() throws IOException;
-
-}
+/*******************************************************************************
+ * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others.
+ * 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) - Javadoc
+ *******************************************************************************/
+package org.eclipse.mat.parser;
+
+import java.io.IOException;
+
+import org.eclipse.mat.SnapshotException;
+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;
+
+/**
+ * Part of the parser which retrieves detailed information about an object
+ */
+public interface IObjectReader
+{
+ /**
+ * Open the dump file associated with the snapshot
+ * @param snapshot the snapshot
+ * @throws SnapshotException some other problem
+ * @throws IOException an IO problem, or corrupt indexes or unexpected data in the dump
+ */
+ void open(ISnapshot snapshot) //
+ throws SnapshotException, IOException;
+
+ /**
+ * Get detailed information about an object
+ * @param objectId the object id
+ * @param snapshot the snapshot
+ * @return an IObject such as {@link org.eclipse.mat.parser.model.InstanceImpl}, {@link org.eclipse.mat.parser.model.ObjectArrayImpl}, {@link org.eclipse.mat.parser.model.PrimitiveArrayImpl}, {@link org.eclipse.mat.parser.model.ClassLoaderImpl}
+ * @throws SnapshotException some other problem such as where the object is incompatible with the snapshot
+ * @throws IOException an IO problem or unexpected data in the dump
+ */
+ IObject read(int objectId, ISnapshot snapshot) //
+ throws SnapshotException, IOException;
+
+ /**
+ * Get detailed information about a primitive array
+ * @param array the array
+ * @param offset where in the array to start
+ * @param length how much to read
+ * @return a byte[], short[], int[], long[], boolean[], char[], float[], double[]
+ * @throws SnapshotException some other problem such as where the object is incompatible with the snapshot
+ * @throws IOException an IO problem or unexpected data in the dump
+ */
+ Object readPrimitiveArrayContent(PrimitiveArrayImpl array, int offset, int length) //
+ throws IOException, SnapshotException;
+
+ /**
+ * Get detailed information about a object array
+ * @param array
+ * @param offset where in the array to start
+ * @param length how much to read
+ * @return an array of object addresses, with 0 for nulls
+ * @throws SnapshotException some other problem such as where the object is incompatible with the snapshot
+ * @throws IOException an IO problem or unexpected data in the dump
+ */
+ long[] readObjectArrayContent(ObjectArrayImpl array, int offset, int length) //
+ throws IOException, SnapshotException;
+
+ /**
+ * Get additional information about the snapshot
+ * @param addon type of the additional information
+ * @return the additional information
+ * @throws SnapshotException
+ */
+ <A> A getAddon(Class<A> addon) //
+ throws SnapshotException;
+
+ /**
+ * tidy up when snapshot no longer required
+ * @throws IOException should not normally occur
+ */
+ void close() throws IOException;
+
+}
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/AbstractObjectImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/AbstractObjectImpl.java
index 0bf716a..535559b 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/AbstractObjectImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/AbstractObjectImpl.java
@@ -1,10 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2008, 2020 SAP AG, IBM Corporation and others.
+ * Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others.
* 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -140,7 +140,20 @@
{
try
{
- return source.getRetainedHeapSize(getObjectId());
+ int objId;
+ try
+ {
+ objId = getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof SnapshotException)
+ return 0;
+ else
+ throw e;
+ }
+ return source.getRetainedHeapSize(objId);
}
catch (SnapshotException e)
{
@@ -290,7 +303,20 @@
public GCRootInfo[] getGCRootInfo() throws SnapshotException
{
- return source.getGCRootInfo(getObjectId());
+ int objId;
+ try
+ {
+ objId = getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof SnapshotException)
+ throw (SnapshotException)cause;
+ else
+ throw e;
+ }
+ return source.getGCRootInfo(objId);
}
@Override
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/InstanceImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/InstanceImpl.java
index 40aee4e..1d594b7 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/InstanceImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/InstanceImpl.java
@@ -151,9 +151,25 @@
@Override
public long getUsedHeapSize()
{
- try {
- return getSnapshot().getHeapSize(getObjectId());
- } catch (SnapshotException e) {
+ try
+ {
+ int objId;
+ try
+ {
+ objId = getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof SnapshotException)
+ throw (SnapshotException) cause;
+ else
+ throw e;
+ }
+ return getSnapshot().getHeapSize(objId);
+ }
+ catch (SnapshotException e)
+ {
return classInstance.getHeapSizePerInstance();
}
}
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/ObjectArrayImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/ObjectArrayImpl.java
index e0d27eb..55840b8 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/ObjectArrayImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/ObjectArrayImpl.java
@@ -51,9 +51,25 @@
@Override
public long getUsedHeapSize()
{
- try {
- return getSnapshot().getHeapSize(getObjectId());
- } catch (SnapshotException e) {
+ try
+ {
+ int objId;
+ try
+ {
+ objId = getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof SnapshotException)
+ throw (SnapshotException) cause;
+ else
+ throw e;
+ }
+ return getSnapshot().getHeapSize(objId);
+ }
+ catch (SnapshotException e)
+ {
return doGetUsedHeapSize(classInstance, getLength());
}
}
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/PrimitiveArrayImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/PrimitiveArrayImpl.java
index 9cd19e6..f24c6e6 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/PrimitiveArrayImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/model/PrimitiveArrayImpl.java
@@ -128,9 +128,25 @@
@Override
public long getUsedHeapSize()
{
- try {
- return getSnapshot().getHeapSize(getObjectId());
- } catch (SnapshotException e) {
+ try
+ {
+ int objId;
+ try
+ {
+ objId = getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof SnapshotException)
+ throw (SnapshotException) cause;
+ else
+ throw e;
+ }
+ return getSnapshot().getHeapSize(objId);
+ }
+ catch (SnapshotException e)
+ {
return doGetUsedHeapSize(classInstance, getLength(), type);
}
}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/CreateCollectionDump.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/CreateCollectionDump.java
index 145dde9..ac18398 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/CreateCollectionDump.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/CreateCollectionDump.java
@@ -115,7 +115,7 @@
}
}
if (!found)
- System.out.println("Missing entry set class " + s2.getClass());
+ System.out.println("Missing entry set " + s2.getClass());
}
}
}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/TestUnreachableObjects.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/TestUnreachableObjects.java
index 95679bb..e7e66b3 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/TestUnreachableObjects.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/TestUnreachableObjects.java
@@ -1,10 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
+ * Copyright (c) 2008, 2021 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -13,17 +13,31 @@
*******************************************************************************/
package org.eclipse.mat.tests.snapshot;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.assertTrue;
+
import java.util.HashMap;
import java.util.Map;
import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.query.IResult;
import org.eclipse.mat.snapshot.ClassHistogramRecord;
import org.eclipse.mat.snapshot.Histogram;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.UnreachableObjectsHistogram;
+import org.eclipse.mat.snapshot.model.IClass;
+import org.eclipse.mat.snapshot.model.IObject;
+import org.eclipse.mat.snapshot.model.IObjectArray;
+import org.eclipse.mat.snapshot.model.NamedReference;
+import org.eclipse.mat.snapshot.query.SnapshotQuery;
import org.eclipse.mat.tests.TestSnapshots;
+import org.eclipse.mat.tests.snapshot.GeneralSnapshotTests.CheckedProgressListener;
import org.eclipse.mat.util.VoidProgressListener;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ErrorCollector;
public class TestUnreachableObjects
{
@@ -64,6 +78,43 @@
compareDiscard(TestSnapshots.IBM_JDK6_32BIT_SYSTEM);
}
+ @Test
+ public void testDiscardReportSunJDK5_64() throws SnapshotException
+ {
+ discardComponentReport(TestSnapshots.SUN_JDK5_64BIT);
+ }
+
+ @Test
+ public void testDiscardReportSunJDK6_32() throws SnapshotException
+ {
+ discardComponentReport(TestSnapshots.SUN_JDK6_32BIT);
+ }
+
+ @Test
+ public void testDiscardReportIBMJDK6_32_SYSTEM() throws SnapshotException
+ {
+ discardComponentReport(TestSnapshots.IBM_JDK6_32BIT_SYSTEM);
+ }
+
+
+ @Test
+ public void testDiscardStringsSunJDK5_64() throws SnapshotException
+ {
+ discardStringsValue(TestSnapshots.SUN_JDK5_64BIT);
+ }
+
+ @Test
+ public void testDiscardStringsSunJDK6_32() throws SnapshotException
+ {
+ discardStringsValue(TestSnapshots.SUN_JDK6_32BIT);
+ }
+
+ @Test
+ public void testDiscardStringsIBMJDK6_32_SYSTEM() throws SnapshotException
+ {
+ discardStringsValue(TestSnapshots.IBM_JDK6_32BIT_SYSTEM);
+ }
+
// IBM PHD files do not have accurate roots, so this test won't work for IBM_JDK6_32BIT_HEAP
private void compareUnreachable(String snapshotName) throws SnapshotException
@@ -85,7 +136,7 @@
options.put("keep_unreachable_objects", "true");
ISnapshot unreachables = TestSnapshots.getSnapshot(snapshotName, options, true);
-
+
Map<String, String> options2 = new HashMap<String, String>();
options2.put("keep_unreachable_objects", "true");
options2.put("discard_ratio", "60");
@@ -99,6 +150,81 @@
classic.dispose();
}
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+ private void discardComponentReport(String snapshotName) throws SnapshotException
+ {
+ Map<String, String> options2 = new HashMap<String, String>();
+ options2.put("keep_unreachable_objects", "false");
+ options2.put("discard_ratio", "60");
+ options2.put("discard_pattern", "char\\[\\]|java\\.lang\\.String");
+ options2.put("discard_offset", "80");
+ options2.put("discard_seed", "2");
+ ISnapshot snapshot = TestSnapshots.getSnapshot(snapshotName, options2, true);
+ SnapshotQuery query = SnapshotQuery.lookup("component_report_top", snapshot);
+ query.setArgument("aggressive", true);
+ IResult result = query.execute(new CheckedProgressListener(collector));
+ assertTrue(result != null);
+ // Tidy up these pristine snapshots early
+ snapshot.dispose();
+ }
+
+ private void discardStringsValue(String snapshotName) throws SnapshotException
+ {
+ Map<String, String> options2 = new HashMap<String, String>();
+ options2.put("keep_unreachable_objects", "false");
+ options2.put("discard_ratio", "60");
+ options2.put("discard_pattern", "char\\[\\]|java\\.lang\\.String");
+ options2.put("discard_offset", "80");
+ options2.put("discard_seed", "2");
+ ISnapshot snapshot = TestSnapshots.getSnapshot(snapshotName, options2, true);
+ int len = 0;
+ int bad = 0;
+ int unindexed = 0;
+ for (IClass cls : snapshot.getClassesByName("java.lang.String[]", false))
+ {
+ for (int id : cls.getObjectIds())
+ {
+ IObjectArray ia = (IObjectArray) snapshot.getObject(id);
+ for (NamedReference ref : ia.getOutboundReferences())
+ {
+ try
+ {
+ IObject s = ref.getObject();
+ String v = s.getClassSpecificName();
+ if (v != null)
+ {
+ len += v.length();
+ if (v.length() > 0)
+ {
+ try
+ {
+ int id1 = s.getObjectId();
+ if (id1 < 0)
+ ++unindexed;
+ }
+ catch (RuntimeException e)
+ {
+ if (e.getCause() instanceof SnapshotException)
+ ++unindexed;
+ }
+ }
+ }
+ }
+ catch (SnapshotException e)
+ {
+ ++bad;
+ }
+ }
+ }
+ }
+ assertThat("Strings should be readable", len, greaterThanOrEqualTo(800));
+ assertThat("Not too many bad reads", bad, lessThanOrEqualTo(300));
+ assertThat("Unindexed objects have resolved", unindexed, greaterThanOrEqualTo(15));
+ // Tidy up these pristine snapshots early
+ snapshot.dispose();
+ }
+
private void compare(ISnapshot unreachables, ISnapshot classic) throws SnapshotException
{
Histogram fullHistogram = unreachables.getHistogram(new VoidProgressListener());
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/actions/CopyActions.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/actions/CopyActions.java
index 87b297c..878ee3b 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/actions/CopyActions.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/actions/CopyActions.java
@@ -17,11 +17,13 @@
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.query.IContextObject;
+import org.eclipse.mat.query.IContextObjectSet;
import org.eclipse.mat.query.IQuery;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.annotations.Argument;
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.snapshot.ISnapshot;
+import org.eclipse.mat.snapshot.OQL;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.ui.Messages;
@@ -61,6 +63,10 @@
appendValue(buf, object);
}
+ else
+ {
+ appendValue(buf, argument);
+ }
listener.worked(1);
if (listener.isCanceled())
break;
@@ -88,6 +94,9 @@
}
protected abstract void appendValue(StringBuilder buf, IObject object) throws SnapshotException;
+ protected void appendValue(StringBuilder buf, IContextObject ctx) throws SnapshotException
+ {}
+
// //////////////////////////////////////////////////////////////
// several copy actions
@@ -100,6 +109,29 @@
{
buf.append("0x").append(Long.toHexString(object.getObjectAddress()));//$NON-NLS-1$
}
+ protected void appendValue(StringBuilder buf, IContextObject ctx) throws SnapshotException
+ {
+ if (ctx instanceof IContextObjectSet)
+ {
+ IContextObjectSet ctxs = (IContextObjectSet)ctx;
+ String oql = ctxs.getOQL();
+ if (oql != null && ctxs.getObjectIds().length == 0)
+ {
+ String dummy = OQL.forAddress(0x0);
+ if (oql.startsWith(dummy.substring(0, dummy.length() - 3)))
+ {
+ // Special OQL indicating unindexed object
+ String addr = oql.substring(dummy.length() - 3);
+ if (addr.matches("0[xX][0-9A-Fa-f]+")) //$NON-NLS-1$
+ {
+ if (buf.length() > 0)
+ buf.append(System.lineSeparator());
+ buf.append(addr);
+ }
+ }
+ }
+ }
+ }
}
@Icon("/icons/copy.gif")
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/FieldsLabelProvider.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/FieldsLabelProvider.java
index f83144a..a17e458 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/FieldsLabelProvider.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/FieldsLabelProvider.java
@@ -1,10 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG.
+ * Copyright (c) 2008, 2021 SAP AG.
* 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -20,6 +20,7 @@
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.Field;
import org.eclipse.mat.snapshot.model.IObject;
+import org.eclipse.mat.snapshot.model.ObjectReference;
import org.eclipse.mat.ui.MemoryAnalyserPlugin;
import org.eclipse.mat.ui.snapshot.views.inspector.FieldsContentProvider.MoreNode;
import org.eclipse.swt.SWT;
@@ -99,7 +100,9 @@
ISnapshot snapshot = this.inspectorView.snapshot;
if (snapshot != null)
{
- IObject object = snapshot.getObject(snapshot.mapAddressToId(objectAddress));
+ // Could work for unindexed objects
+ ObjectReference ref = new ObjectReference(snapshot, objectAddress);
+ IObject object = ref.getObject();
String text = object.getClassSpecificName();
if (text == null)
text = object.getTechnicalName();
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java
index 225a490..be2064b 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG.
+ * Copyright (c) 2008, 2021 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
@@ -9,13 +9,16 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson (IBM) - handle missing objects
*******************************************************************************/
package org.eclipse.mat.ui.snapshot.views.inspector;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.query.ContextProvider;
import org.eclipse.mat.query.IContextObject;
+import org.eclipse.mat.query.IContextObjectSet;
import org.eclipse.mat.snapshot.ISnapshot;
+import org.eclipse.mat.snapshot.OQL;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.ui.snapshot.views.inspector.InspectorView.BaseNode;
@@ -58,6 +61,28 @@
return node.objectId;
}
};
+ if (node.addr != Long.MIN_VALUE)
+ return new IContextObjectSet()
+ {
+
+ @Override
+ public int getObjectId()
+ {
+ return -1;
+ }
+
+ @Override
+ public int[] getObjectIds()
+ {
+ return new int[0];
+ }
+
+ @Override
+ public String getOQL()
+ {
+ return OQL.forAddress(node.addr);
+ }
+ };
}
else if (row instanceof IClass)
{
@@ -75,6 +100,32 @@
}
catch (SnapshotException e)
{
+ if (row instanceof NamedReferenceNode)
+ {
+ NamedReferenceNode node = (NamedReferenceNode) row;
+ return new IContextObjectSet() {
+
+ @Override
+ public int getObjectId()
+ {
+ return -1;
+ }
+
+ @Override
+ public int[] getObjectIds()
+ {
+ return new int[0];
+ }
+
+ @Override
+ public String getOQL()
+ {
+ // Used when the address does not convert to an object ID,
+ // such as when objects are discarded.
+ return OQL.forAddress(node.objectAddress);
+ }
+ };
+ }
throw new RuntimeException(e);
}
}
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java
index a59d411..6cf83f5 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java
@@ -50,13 +50,16 @@
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.query.IContextObject;
+import org.eclipse.mat.query.IContextObjectSet;
import org.eclipse.mat.snapshot.ISnapshot;
+import org.eclipse.mat.snapshot.OQL;
import org.eclipse.mat.snapshot.model.GCRootInfo;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IInstance;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IObjectArray;
import org.eclipse.mat.snapshot.model.IPrimitiveArray;
+import org.eclipse.mat.snapshot.model.ObjectReference;
import org.eclipse.mat.ui.MemoryAnalyserPlugin;
import org.eclipse.mat.ui.Messages;
import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter;
@@ -123,21 +126,43 @@
/* package */static class BaseNode
{
int objectId;
+ long addr;
public BaseNode(int objectId)
{
+ this(objectId, 0);
+ }
+ public BaseNode(int objectId, long addr)
+ {
this.objectId = objectId;
+ this.addr = addr;
+ }
+
+ static int objectID(IObject object)
+ {
+ if (object == null)
+ return -1;
+ try
+ {
+ return object.getObjectId();
+ }
+ catch (RuntimeException e)
+ {
+ if (e.getCause() instanceof SnapshotException)
+ return -1;
+ throw e;
+ }
}
}
- private class ObjectNode extends BaseNode
+ private static class ObjectNode extends BaseNode
{
String label;
int imageType;
public ObjectNode(IObject object)
{
- super(object.getObjectId());
+ super(objectID(object), object.getObjectAddress());
this.label = object.getTechnicalName();
this.imageType = ImageHelper.getType(object);
}
@@ -209,19 +234,19 @@
}
}
- public class InfoItem extends BaseNode
+ public static class InfoItem extends BaseNode
{
private ImageDescriptor descriptor;
private String text;
public InfoItem(ImageDescriptor descriptor, String text)
{
- this(-1, descriptor, text);
+ this(null, descriptor, text);
}
- public InfoItem(int objectId, ImageDescriptor descriptor, String text)
+ public InfoItem(IObject object, ImageDescriptor descriptor, String text)
{
- super(objectId);
+ super(objectID(object), object != null ? object.getObjectAddress() : 0);
this.descriptor = descriptor;
this.text = text;
}
@@ -814,7 +839,21 @@
objectSet = (IContextObject) object;
}
- if (objectSet == null || objectSet.getObjectId() < 0)
+ long addr = 0;
+ if (objectSet instanceof IContextObjectSet)
+ {
+ String oql = ((IContextObjectSet)objectSet).getOQL();
+ if (oql != null && ((IContextObjectSet)objectSet).getObjectIds().length == 0)
+ {
+ String addr0 = OQL.forAddress(0);
+ addr0 = addr0.substring(0, addr0.length() - 1);
+ if (oql.startsWith(addr0.substring(0, addr0.length())) && oql.substring(addr0.length() - 2).matches("0[xX][0-9a-fA-F]+")) //$NON-NLS-1$
+ {
+ addr = Long.parseUnsignedLong(oql.substring(addr0.length()), 16);
+ }
+ }
+ }
+ if (objectSet == null || (objectSet.getObjectId() < 0 && addr == 0))
{
clearInput();
}
@@ -828,8 +867,17 @@
{
int current = ((Integer) data).intValue();
if (current == objectId)
- return;
+ {
+ Object addr0 = topTableViewer.getData("address"); //$NON-NLS-1$
+ if (addr0 != null)
+ {
+ long currentAddr = ((Long) addr0).longValue();
+ if (currentAddr == addr)
+ return;
+ }
+ }
}
+ final long objectAddress = addr;
final ISnapshot savedSnapshot = snapshot;
@@ -844,7 +892,11 @@
if (snapshot == null || savedSnapshot != snapshot)
return Status.OK_STATUS;
- final IObject object = savedSnapshot.getObject(objectId);
+ final IObject object;
+ if (objectId >= 0)
+ object = savedSnapshot.getObject(objectId);
+ else
+ object = (new ObjectReference(savedSnapshot, objectAddress)).getObject();
// prepare object info
final List<Object> classInfos = prepareClassInfo(object);
@@ -864,6 +916,7 @@
{
topTableViewer.setInput(classInfos);
topTableViewer.setData("input", objectId);//$NON-NLS-1$
+ topTableViewer.setData("address", object.getObjectAddress());//$NON-NLS-1$
staticsTable.setInput(staticFields);
attributesTable.setInput(attributeFields);
updateVisualViewer(toShow);
@@ -1021,7 +1074,7 @@
{
List<Object> details = new ArrayList<Object>();
- details.add(new InfoItem(object.getObjectId(), MemoryAnalyserPlugin
+ details.add(new InfoItem(object, MemoryAnalyserPlugin
.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.ID), "0x"//$NON-NLS-1$
+ Long.toHexString(object.getObjectAddress())));
@@ -1031,7 +1084,7 @@
int p = className.lastIndexOf('.');
if (p < 0) // primitive
{
- InfoItem item = new InfoItem(object.getObjectId(), ImageHelper.getImageDescriptor(ImageHelper
+ InfoItem item = new InfoItem(object, ImageHelper.getImageDescriptor(ImageHelper
.getType(object)), className);
details.add(item);
details.add(new InfoItem(MemoryAnalyserPlugin
@@ -1039,7 +1092,7 @@
}
else
{
- details.add(new InfoItem(object.getObjectId(), ImageHelper.getImageDescriptor(ImageHelper
+ details.add(new InfoItem(object, ImageHelper.getImageDescriptor(ImageHelper
.getType(object)), className.substring(p + 1)));
details.add(new InfoItem(MemoryAnalyserPlugin
.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.PACKAGE), className
@@ -1053,7 +1106,7 @@
if (superClass != null)
{
- details.add(new InfoItem(superClass.getObjectId(), MemoryAnalyserPlugin
+ details.add(new InfoItem(superClass, MemoryAnalyserPlugin
.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SUPERCLASS), superClass
.getName()));
}
@@ -1071,7 +1124,15 @@
.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SIZE), MessageUtil.format(
Messages.InspectorView_retainedSize, object.getRetainedHeapSize())));
- GCRootInfo[] gc = object.getGCRootInfo();
+ GCRootInfo[] gc;
+ try
+ {
+ gc = object.getGCRootInfo();
+ }
+ catch (SnapshotException e)
+ {
+ gc = null;
+ }
details.add(gc != null ? (Object) gc : new InfoItem(MemoryAnalyserPlugin
.getImageDescriptor(ImageHelper.Decorations.GC_ROOT),
Messages.InspectorView_noGCRoot));
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java
index a429672..e3d26d6 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java
@@ -1,14 +1,15 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG.
+ * Copyright (c) 2008, 2021 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/
- *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson - read discarded objects by address
*******************************************************************************/
package org.eclipse.mat.ui.snapshot.views.inspector;
@@ -34,6 +35,7 @@
private WeakReference<O> array;
private int objectId;
+ private long objectAddr;
protected List<Object> cache = new ArrayList<Object>();
@@ -44,7 +46,7 @@
this.snapshot = new WeakReference<ISnapshot>(object.getSnapshot());
this.array = new WeakReference<O>(object);
- this.objectId = object.getObjectId();
+ this.objectAddr = object.getObjectAddress();
}
}
@@ -73,7 +75,8 @@
try
{
- object = (O) snapshot.getObject(objectId);
+ ObjectReference ref = new ObjectReference(snapshot, objectAddr);
+ object = (O) ref.getObject();
this.array = new WeakReference<O>(object);
}
catch (SnapshotException e)