Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Johnson2018-11-13 16:55:55 -0500
committerAndrew Johnson2018-11-14 08:25:43 -0500
commitfe86e66a1f76b427411df3077bfff86e37f7e085 (patch)
tree4d7b766cb67ff92b6b09d9a443546bb5eed4faf6
parent3861e17c4b5da489aa04aab2dc2b2faeb2b4d607 (diff)
downloadorg.eclipse.mat-fe86e66a1f76b427411df3077bfff86e37f7e085.tar.gz
org.eclipse.mat-fe86e66a1f76b427411df3077bfff86e37f7e085.tar.xz
org.eclipse.mat-fe86e66a1f76b427411df3077bfff86e37f7e085.zip
[519274] Redacted Binary or PHD dump so as to protect privacy data
Allow export of classes also as object instances. Change-Id: I6aa96ad69846b6f5747f58c27481d02424e2c42b
-rw-r--r--plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java5
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/AbstractParser.java2
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java183
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java97
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java1
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java99
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java41
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties1
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties1
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HPROFPreferencePage.java5
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HprofPreferences.java13
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/PreferenceInitializer.java3
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java3
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties2
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java31
15 files changed, 425 insertions, 62 deletions
diff --git a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
index a85372c9..16833d75 100644
--- a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
+++ b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
@@ -1699,6 +1699,7 @@ public class DTFJIndexBuilder implements IIndexBuilder
boolean foundFields = false;
for (JavaClass j2 : allClasses)
{
+ if (listener.isCanceled()) { throw new IProgressListener.OperationCanceledException(); }
// Don't do java.lang.Class twice
if (j2.equals(clsJavaLangClass))
continue;
@@ -2475,6 +2476,7 @@ public class DTFJIndexBuilder implements IIndexBuilder
out.print(" at "); //$NON-NLS-1$
if (next instanceof CorruptData)
{
+ // Consider whether we should generate a stack frame line with just " at " when there is corrupt data.
continue;
}
JavaStackFrame jsf = (JavaStackFrame) next;
@@ -7175,7 +7177,8 @@ public class DTFJIndexBuilder implements IIndexBuilder
}
// Add constant pool entries as pseudo fields
- int cpindex = 0;
+ // Constant pool index starts at 1: see JVM spec ClassFile structure
+ int cpindex = 1;
Iterator<?> f1;
try
{
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/AbstractParser.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/AbstractParser.java
index 2058ceb7..b31c157d 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/AbstractParser.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/AbstractParser.java
@@ -195,7 +195,7 @@ import org.eclipse.mat.util.SimpleMonitor.Listener;
protected void skipValue(int type) throws IOException
{
- if (type == 2)
+ if (type == IObject.Type.OBJECT)
in.skipBytes(idSize);
else
in.skipBytes(IPrimitiveArray.ELEMENT_SIZE[type]);
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java
index 77eff24c..1254ded4 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java
@@ -52,6 +52,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.OperationCanceledException;
@@ -59,6 +60,7 @@ import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.BitField;
import org.eclipse.mat.collect.SetInt;
import org.eclipse.mat.hprof.AbstractParser.Constants;
+import org.eclipse.mat.hprof.ui.HprofPreferences;
import org.eclipse.mat.query.IQuery;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.annotations.Argument;
@@ -139,6 +141,10 @@ public class ExportHprof implements IQuery
@Argument(flag = Argument.UNFLAGGED, isMandatory = false)
public IHeapObjectArgument objects;
+ /** Dump class fields as a instance dump */
+ @Argument(isMandatory = false, flag = "classInstance")
+ public boolean classesAsInstances = false;
+
/** How big a heap dump segment can grow before it needs to be split */
private static final long MAX_SEGMENT = 0xffffffffL;
@@ -318,6 +324,9 @@ public class ExportHprof implements IQuery
/** Progress monitor work per class for dumping objects */
private static final int WORK_OBJECT = 3;
+ /** Ready for new way of reading classes */
+ private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
+
Remap remap;
public IResult execute(IProgressListener listener) throws Exception
@@ -972,18 +981,122 @@ public class ExportHprof implements IQuery
writeID(os, cls.getClassLoaderAddress());
// Remember the type of the loader as a possible type for all class loaders
classloaders.add(cls.getClassLoaderId());
- writeID(os, 0); // signers
- writeID(os, 0); // protection domain
- writeID(os, 0); // reserved
- writeID(os, 0); // reserved
- os.writeInt((int) cls.getHeapSizePerInstance());
- os.writeShort(0); // constant pool
- // Static fields
+ // Calculate constant pool
List<Field> statics = cls.getStaticFields();
- os.writeShort((short) statics.size());
+ boolean skip[] = new boolean[statics.size()];
+ long signersId = 0;
+ long protectionDomainId = 0;
+ long reserved1Id = 0;
+ long reserved2Id = 0;
+ // count constant pool
+ int skipfields = 0;
+ int cpsize = 0;
+ Pattern cpName = Pattern.compile("constant pool\\[(\\d+)\\]"); //$NON-NLS-1$
+ int idx = 0;
+ for (Field fld : statics)
+ {
+ String fieldName = fld.getName();
+ if (NEWCLASSSIZE && cpName.matcher(fieldName).matches())
+ {
+ ++cpsize;
+ ++skipfields;
+ skip[idx] = true;
+ }
+ if (NEWCLASSSIZE && fld.getType() == IObject.Type.OBJECT)
+ {
+ if (fieldName.equals("<signers>")) //$NON-NLS-1$
+ {
+ ++skipfields;
+ skip[idx] = true;
+ if (fld.getValue() instanceof IObject)
+ signersId = ((IObject)fld.getValue()).getObjectAddress();
+ }
+ if (fieldName.equals("<protectionDomain>")) //$NON-NLS-1$
+ {
+ ++skipfields;
+ skip[idx] = true;
+ if (fld.getValue() instanceof IObject)
+ protectionDomainId = ((IObject)fld.getValue()).getObjectAddress();
+ }
+ if (fieldName.equals("<reserved1>")) //$NON-NLS-1$
+ {
+ ++skipfields;
+ skip[idx] = true;
+ if (fld.getValue() instanceof IObject)
+ reserved1Id = ((IObject)fld.getValue()).getObjectAddress();
+ }
+ if (fieldName.equals("<reserved2>")) //$NON-NLS-1$
+ {
+ ++skipfields;
+ skip[idx] = true;
+ if (fld.getValue() instanceof IObject)
+ reserved2Id = ((IObject)fld.getValue()).getObjectAddress();
+ }
+ }
+ ++idx;
+ }
+ // Ignore static fields which will be dumped as class fields
+ if (classesAsInstances)
+ {
+ for (IClass cls1 = cls.getClazz(); cls1 != null; cls1 = cls1.getSuperClass())
+ {
+ for (FieldDescriptor fd : cls1.getFieldDescriptors())
+ {
+ int idx2 = 0;
+ for (Field fld : cls.getStaticFields())
+ {
+ if (!skip[idx2] && fd.getType() == fld.getType() && fld.getName().equals("<"+fd.getName()+">")) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ ++skipfields;
+ skip[idx2] = true;
+ break;
+ }
+ ++idx2;
+ }
+ }
+ }
+ }
+ writeID(os, signersId); // signers
+ writeID(os, protectionDomainId); // protection domain
+ writeID(os, reserved1Id); // reserved
+ writeID(os, reserved2Id); // reserved
+ // Calculate the HPROF instance size as the size of an instance dump record data
+ // not what this snapshot has as the whole instance size: cls.getHeapSizePerInstance();
+ int hprofInstanceSize = 0;
+ for (IClass cls1 = cls; cls1 != null; cls1 = cls1.getSuperClass())
+ {
+ for (FieldDescriptor fld : cls1.getFieldDescriptors())
+ {
+ int type = fld.getType();
+ if (type == IObject.Type.OBJECT)
+ hprofInstanceSize += idsize;
+ else
+ hprofInstanceSize += IPrimitiveArray.ELEMENT_SIZE[type];
+ }
+ }
+ os.writeInt(hprofInstanceSize);
+ // write constant pool
+ os.writeShort(cpsize); // constant pool
+ for (Field fld : statics)
+ {
+ String fieldName = fld.getName();
+ Matcher matcher = cpName.matcher(fieldName);
+ if (NEWCLASSSIZE && matcher.matches())
+ {
+ String id = matcher.group(1);
+ int cpidx = Integer.parseInt(id);
+ os.writeShort((short)cpidx);
+ writeField(os, fld, true);
+ }
+ }
+ // Static fields
+ os.writeShort((short) (statics.size() - skipfields));
+ idx = 0;
for (Field fld : statics)
{
String fieldName = fld.getName();
+ if (skip[idx++])
+ continue;
fieldName = remap.renameMethodName(cls.getName(), fieldName, true);
writeString(os, fieldName);
writeField(os, fld, true);
@@ -1192,7 +1305,7 @@ public class ExportHprof implements IQuery
{
// Use these objects
// check for overflow if there is an unlimited end and this not the first object
- if (this.dumpObject(os, os2, snapshot.getObject(o), i > start && end == Integer.MAX_VALUE))
+ if (dumpObject(os, os2, snapshot.getObject(o), i > start && end == Integer.MAX_VALUE))
{
// Success, enough room
++totalObjects;
@@ -1463,7 +1576,7 @@ public class ExportHprof implements IQuery
}
/**
- * Sometimes it might be desireable to dump an IClass also as an instance.
+ * Sometimes it might be desirable to dump an IClass also as an instance.
* This is likely to be incompatible with other HPROF tools, but might be necessary to indicate
* the type of an object is something other than java.lang.Class,
* or to output per instance fields declared in java.lang.Class for other classes.
@@ -1477,15 +1590,11 @@ public class ExportHprof implements IQuery
*/
private boolean dumpClassObject(DataOutputStream3 os, IClass io, boolean check) throws IOException
{
- IClass cls = io.getClazz();
- String cn = cls.getName();
- String rnc = remap.mapClass(cn);
- String newclsname = rnc != null ? rnc : cn;
- if (IClass.JAVA_LANG_CLASS.equals(newclsname))
+ if (!classesAsInstances)
{
- // Most types are of class java.lang.Class, and don't need this extra information.
return true;
}
+ IClass cls = io.getClazz();
// Classes are IObject but not necessarily IInstance
int size = (int)cls.getHeapSizePerInstance();
int size2 = 0;
@@ -1496,14 +1605,13 @@ public class ExportHprof implements IQuery
switch (fd.getType())
{
case IObject.Type.OBJECT:
- // TODO - retrieve per instance java.lang.Class fields
se = idsize;
break;
default:
se = IPrimitiveArray.ELEMENT_SIZE[fd.getType()];
break;
}
- size2 += se;
+ size2 += se;
}
}
size = size2;
@@ -1518,7 +1626,42 @@ public class ExportHprof implements IQuery
os.writeInt(UNKNOWN_STACK_TRACE_SERIAL); // stack trace serial number
writeID(os, cls.getObjectAddress());
os.writeInt((int)size);
- os.write(new byte[size]);
+ // Dump the actual fields
+ Field ss[] = cls.getStaticFields().toArray(new Field[0]);
+ for (IClass cls2 = cls; cls2 != null; cls2 = cls2.getSuperClass())
+ {
+ for (FieldDescriptor fd : cls2.getFieldDescriptors()) {
+ int fix = 0;
+ for (fix = 0; fix < ss.length; ++fix)
+ {
+ Field fs = ss[fix];
+ if (fs == null)
+ continue;
+ // match by type and pseudo-reference name
+ if (fs.getType() == fd.getType() && fs.getName().equals("<"+fd.getName()+">")) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ writeField(os, fs, false);
+ ss[fix] = null;
+ break;
+ }
+ }
+ if (fix >= ss.length)
+ {
+ // Not found
+ int se;
+ switch (fd.getType())
+ {
+ case IObject.Type.OBJECT:
+ se = idsize;
+ break;
+ default:
+ se = IPrimitiveArray.ELEMENT_SIZE[fd.getType()];
+ break;
+ }
+ os.write(new byte[se]);
+ }
+ }
+ }
return true;
}
@@ -1579,8 +1722,6 @@ public class ExportHprof implements IQuery
if (newstr == null)
{
newstr = remap.mapSignature(s);
- if (newstr != null)
- System.out.println("Found "+s+" "+newstr);
}
if (newstr != null)
{
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 15498763..f4e80408 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
@@ -25,6 +25,8 @@ import java.util.Set;
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.hprof.ui.HprofPreferences;
import org.eclipse.mat.collect.IteratorLong;
import org.eclipse.mat.parser.IPreliminaryIndex;
import org.eclipse.mat.parser.index.IIndexReader.IOne2LongIndex;
@@ -76,7 +78,7 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
// The alignment between successive objects
private int objectAlign;
// New size of classes including per-instance fields
- private static final boolean NEWCLASSSIZE = false;
+ private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
// //////////////////////////////////////////////////////////////
// lifecycle
@@ -112,6 +114,9 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
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
@@ -158,15 +163,25 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
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)
+ 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
{
- // Recalculate the clazz heap size based on also java.lang.Class fields
- clazz.setUsedHeapSize(clazz.getUsedHeapSize() + clazz.getClazz().getHeapSizePerInstance());
+ // References for classes with instance dump records will be generated in pass2
+ skipLogRefs = true;
}
- clazz.getClazz().addInstance(clazz.getUsedHeapSize());
// resolve super class
ClassImpl superclass = lookupClass(clazz.getSuperClassAddress());
@@ -175,7 +190,8 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
object2classId.set(clazz.getObjectId(), clazz.getClazz().getObjectId());
- outbound.log(identifiers, clazz.getObjectId(), clazz.getReferences());
+ if (!skipLogRefs)
+ outbound.log(identifiers, clazz.getObjectId(), clazz.getReferences());
}
// report dependencies for system class loader
@@ -191,6 +207,71 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
}
/**
+ * 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 bby 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).
*/
@@ -254,7 +335,6 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
while (identifiers.reverse(++nextObjectAddress) >= 0)
{}
IClass type = new ClassImpl(nextObjectAddress, cls, jlo, 0, new Field[0], new FieldDescriptor[0]);
- ++clsid;
addClass((ClassImpl) type, -1);
}
}
@@ -391,7 +471,7 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
private int sizeOf(FieldDescriptor field)
{
int type = field.getType();
- if (type == 2)
+ if (type == IObject.Type.OBJECT)
return refSize;
return IPrimitiveArray.ELEMENT_SIZE[type];
@@ -534,7 +614,6 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
}
}
- @SuppressWarnings("unchecked")
public void addGCRoot(long id, long referrer, int rootType)
{
if (referrer != 0)
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
index 320d5cc7..c3d60a89 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java
@@ -81,6 +81,7 @@ public class Messages extends NLS
public static String HPROFPreferences_Strictness_Permissive;
public static String HPROFStrictness_Unhandled_Preference;
public static String HPROFStrictness_Stopped;
+ public static String HPROFPreferences_Additional_Class_References;
static
{
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java
index 3985f77f..695b7c88 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java
@@ -28,6 +28,7 @@ import java.util.regex.Pattern;
import org.eclipse.core.runtime.Platform;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.HashMapLongObject;
+import org.eclipse.mat.collect.HashMapLongObject.Entry;
import org.eclipse.mat.hprof.ui.HprofPreferences;
import org.eclipse.mat.parser.io.PositionInputStream;
import org.eclipse.mat.parser.model.ClassImpl;
@@ -36,7 +37,10 @@ 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.IObject.Type;
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.IProgressListener.Severity;
import org.eclipse.mat.util.MessageUtil;
@@ -47,11 +51,15 @@ public class Pass1Parser extends AbstractParser
private static final Pattern PATTERN_OBJ_ARRAY = Pattern.compile("^(\\[+)L(.*);$"); //$NON-NLS-1$
private static final Pattern PATTERN_PRIMITIVE_ARRAY = Pattern.compile("^(\\[+)(.)$"); //$NON-NLS-1$
+ // New size of classes including per-instance fields
+ private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
+
private HashMapLongObject<String> class2name = new HashMapLongObject<String>();
private HashMapLongObject<Long> thread2id = new HashMapLongObject<Long>();
private HashMapLongObject<StackFrame> id2frame = new HashMapLongObject<StackFrame>();
private HashMapLongObject<StackTrace> serNum2stackTrace = new HashMapLongObject<StackTrace>();
private HashMapLongObject<Long> classSerNum2id = new HashMapLongObject<Long>();
+ private HashMapLongObject<Long> class2type = new HashMapLongObject<Long>();
private HashMapLongObject<List<JavaLocal>> thread2locals = new HashMapLongObject<List<JavaLocal>>();
private IHprofParserHandler handler;
private SimpleMonitor.Listener monitor;
@@ -273,7 +281,7 @@ public class Pass1Parser extends AbstractParser
{
long classSerNum = readUnsignedInt(); // used in stacks frames
long classID = readID();
- in.skipBytes(4);
+ in.skipBytes(4); // stack trace
long nameID = readID();
String className = getStringConstant(nameID).replace('/', '.');
@@ -461,22 +469,41 @@ public class Pass1Parser extends AbstractParser
long superClassObjectId = readID();
long classLoaderObjectId = readID();
- // skip signers, protection domain, reserved ids (2)
- in.skipBytes(this.idSize * 4);
+ // read signers, protection domain, reserved ids (2)
+ long signersId = readID();
+ long protectionDomainId = readID();
+ long reserved1Id = readID();
+ long reserved2Id = readID();
+ int extraDummyStatics = 0;
+ if (NEWCLASSSIZE)
+ {
+ // Always add signers / protectionDomain
+ extraDummyStatics += 2;
+ if (reserved1Id != 0)
+ ++extraDummyStatics;
+ if (reserved2Id != 0)
+ ++extraDummyStatics;
+ }
+
// instance size
int instsize = in.readInt();
// constant pool: u2 ( u2 u1 value )*
int constantPoolSize = in.readUnsignedShort();
+ Field[] constantPool = new Field[constantPoolSize];
for (int ii = 0; ii < constantPoolSize; ii++)
{
- in.skipBytes(2); // index
- skipValue(); // value
+ int index = in.readUnsignedShort(); // index
+ String name = "<constant pool["+index+"]>"; //$NON-NLS-1$ //$NON-NLS-2$
+ byte type = in.readByte();
+
+ Object value = readValue(null, type);
+ constantPool[ii] = new Field(name, type, value);
}
// static fields: u2 num ( name ID, u1 type, value)
int numStaticFields = in.readUnsignedShort();
- Field[] statics = new Field[numStaticFields];
+ Field[] statics = new Field[numStaticFields + extraDummyStatics];
for (int ii = 0; ii < numStaticFields; ii++)
{
@@ -489,6 +516,21 @@ public class Pass1Parser extends AbstractParser
statics[ii] = new Field(name, type, value);
}
+ if (NEWCLASSSIZE)
+ {
+ int si = numStaticFields;
+ statics[si++] = new Field("<signers>", Type.OBJECT, signersId == 0 ? null : new ObjectReference(null, signersId)); //$NON-NLS-1$
+ statics[si++] = new Field("<protectionDomain>", Type.OBJECT, protectionDomainId == 0 ? null : new ObjectReference(null, protectionDomainId)); //$NON-NLS-1$
+ if (reserved1Id != 0)
+ statics[si++] = new Field("<reserved1>", Type.OBJECT, reserved1Id == 0 ? null : new ObjectReference(null, reserved1Id)); //$NON-NLS-1$
+ if (reserved2Id != 0)
+ statics[si++] = new Field("<reserved2>", Type.OBJECT, reserved2Id == 0 ? null : new ObjectReference(null, reserved2Id)); //$NON-NLS-1$
+ Field all[] = new Field[statics.length + constantPool.length];
+ System.arraycopy(statics, 0, all, 0, statics.length);
+ System.arraycopy(constantPool, 0, all, statics.length, constantPool.length);
+ statics = all;
+ }
+
// instance fields: u2 num ( name ID, u1 type )
int numInstanceFields = in.readUnsignedShort();
FieldDescriptor[] fields = new FieldDescriptor[numInstanceFields];
@@ -549,8 +591,45 @@ public class Pass1Parser extends AbstractParser
// Just in case the superclass is missing
if (superClassObjectId != 0 && handler.lookupClass(superClassObjectId) == null)
{
+ // 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
- handler.reportRequiredClass(superClassObjectId, Integer.MAX_VALUE);
+ handler.reportRequiredClass(superClassObjectId, supersize);
+ }
+
+ // Check / set types of classes
+ if (class2type.containsKey(address))
+ {
+ // Already seen an instance dump for class type
+ long typeId = class2type.get(address);
+ IClass type = handler.lookupClass(typeId);
+ if (type instanceof ClassImpl)
+ {
+ clazz.setClassInstance((ClassImpl)type);
+ }
+ }
+ for (Iterator<Entry<Long>>it = class2type.entries(); it.hasNext(); )
+ {
+ Entry<Long>e = it.next();
+ if (e.getValue() == address)
+ {
+ // Existing class has this class as its type
+ IClass base = handler.lookupClass(e.getKey());
+ if (base instanceof ClassImpl)
+ {
+ ClassImpl baseCls = (ClassImpl)base;
+ baseCls.setClassInstance(clazz);
+ }
+ }
}
}
@@ -576,6 +655,12 @@ public class Pass1Parser extends AbstractParser
instClsImpl.setClassInstance((ClassImpl) instanceType);
}
}
+ // Is this actually a class?
+ if (class2name.containsKey(address))
+ {
+ // record its type
+ class2type.put(address, classID);
+ }
in.skipBytes(payload);
}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java
index d337d640..41ec2ed0 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java
@@ -23,14 +23,16 @@ import org.eclipse.mat.hprof.ui.HprofPreferences;
import org.eclipse.mat.hprof.ui.HprofPreferences.HprofStrictness;
import org.eclipse.mat.parser.io.PositionInputStream;
import org.eclipse.mat.parser.model.ClassImpl;
+import org.eclipse.mat.snapshot.model.Field;
import org.eclipse.mat.snapshot.model.FieldDescriptor;
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.IProgressListener.Severity;
import org.eclipse.mat.util.MessageUtil;
import org.eclipse.mat.util.SimpleMonitor;
-import org.eclipse.mat.util.IProgressListener.Severity;
/**
* Parser used to read the hprof formatted heap dump
@@ -212,23 +214,14 @@ public class Pass2Parser extends AbstractParser
HeapObject heapObject;
IClass objcl = handler.lookupClass(id);
+ 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
- // So fix the type
+ // The true type has already been set in PassParser1 and beforePass2()
ClassImpl objcls = (ClassImpl) objcl;
- // Remove size from old type (java.lang.Class)
- objcls.getClazz().removeInstance(objcls.getUsedHeapSize());
- // Set the new size, allowing for the fields in the new type, see also beforePass2()
- if (NEWCLASSSIZE)
- {
- // We are calculating class instance sizes of static fields + fields of java.lang.Class (or the new type)
- long perInstDelta = thisClazz.getHeapSizePerInstance()- objcls.getClazz().getHeapSizePerInstance();
- objcls.setUsedHeapSize(objcls.getUsedHeapSize() + perInstDelta);
- }
- // Set the type of the new clazz
- objcls.setClassInstance(thisClazz);
+ statics = objcls.getStaticFields().toArray(statics);
// Heap size of each class type object is individual as have statics
heapObject = new HeapObject(handler.mapAddressToId(id), id, thisClazz,
objcls.getUsedHeapSize());
@@ -248,15 +241,35 @@ public class Pass2Parser extends AbstractParser
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 = readID();
if (refId != 0)
+ {
heapObject.references.add(refId);
+ if (stField != null)
+ {
+ stField.setValue(new ObjectReference(null, refId));
+ }
+ }
}
else
{
- skipValue(type);
+ Object value = readValue(null, type);
+ if (stField != null)
+ stField.setValue(value);
}
}
}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties
index c1709b30..6c004fb7 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties
@@ -35,3 +35,4 @@ ExportHprof.undo.help = Whether to reverse the obfuscation process using the sup
mapping file.
ExportHprof.objects.help = Only export selected objects. See the help for how this \
could be used to remove unreachable objects retained by the 'keep unreachable objects' option.
+ExportHprof.classInst = Also export classes as object instances. Experimental, for class fields and types of classes.
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
index 5b249866..cdb8702d 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties
@@ -73,5 +73,6 @@ HPROFPreferences_Strictness=Parser Strictness: What to do when the dump doesn't
HPROFPreferences_Strictness_Stop=Strict: Do not finish loading the dump and throw an error. For one exception to this strictness, see bug 404679.
HPROFPreferences_Strictness_Warning=Warning: Continue parsing and loading the dump and raise a warning to the Error Log.
HPROFPreferences_Strictness_Permissive=Permissive: Raise a warning and try to fix the potential problem.
+HPROFPreferences_Additional_Class_References=Include additional references from class objects found in a HPROF file.
HPROFStrictness_Unhandled_Preference=The parser does not know how to handle the current strictness preference in some situations.
HPROFStrictness_Stopped=The HPROF parser encountered a violation of the HPROF specification that it could not safely handle. This could be due to file truncation or a bug in the JVM. Please consider filing a bug at eclipse.org. To continue parsing the dump anyway, you can use -DhprofStrictnessWarning=true or set the strictness mode under Preferences > HPROF Parser > Parser Strictness. See the inner exception for details.
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HPROFPreferencePage.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HPROFPreferencePage.java
index 74b0a17a..4a521d6c 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HPROFPreferencePage.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HPROFPreferencePage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011,2013 IBM Corporation.
+ * Copyright (c) 2011,2018 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.mat.hprof.ui;
+import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.RadioGroupFieldEditor;
@@ -67,6 +68,8 @@ public class HPROFPreferencePage extends FieldEditorPreferencePage implements IW
// PreferenceConstants.HprofStrictness.STRICTNESS_PERMISSIVE
// .toString() }
}, getFieldEditorParent(), true));
+ addField(new BooleanFieldEditor(HprofPreferences.ADDITIONAL_CLASS_REFERENCES, Messages.HPROFPreferences_Additional_Class_References,
+ getFieldEditorParent()));
}
/**
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HprofPreferences.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HprofPreferences.java
index 8ed62589..f4cb1d76 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HprofPreferences.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/HprofPreferences.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011 IBM Corporation.
+ * Copyright (c) 2011, 2018 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -24,6 +24,9 @@ public class HprofPreferences
/** Default strictness for preferences and value parsing */
public static final HprofStrictness DEFAULT_STRICTNESS = HprofStrictness.STRICTNESS_STOP;
+ /** Additional references for classes */
+ public static final String ADDITIONAL_CLASS_REFERENCES = "hprofAddClassRefs"; //$NON-NLS-1$
+
/**
* Return the currently selected preference for strictness. This first
* checks the preference store, and then checks for any -D$(STRICTNESS)=true
@@ -36,7 +39,7 @@ public class HprofPreferences
{
HprofPreferences.HprofStrictness strictnessPreference = HprofPreferences.HprofStrictness.parse(Platform
.getPreferencesService().getString(HprofPlugin.getDefault().getBundle().getSymbolicName(),
- HprofPreferences.STRICTNESS_PREF, "", null));
+ HprofPreferences.STRICTNESS_PREF, "", null)); //$NON-NLS-1$
// Check if the user overrides on the command line
for (HprofStrictness strictness : HprofStrictness.values())
@@ -114,4 +117,10 @@ public class HprofPreferences
return DEFAULT_STRICTNESS;
}
}
+
+ public static boolean useAdditionalClassReferences()
+ {
+ return Platform.getPreferencesService().getBoolean(HprofPlugin.getDefault().getBundle().getSymbolicName(),
+ HprofPreferences.ADDITIONAL_CLASS_REFERENCES, false, null);
+ }
}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/PreferenceInitializer.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/PreferenceInitializer.java
index 98e98801..323e7190 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/PreferenceInitializer.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ui/PreferenceInitializer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011,2013 IBM Corporation.
+ * Copyright (c) 2011,2018 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -26,6 +26,7 @@ public class PreferenceInitializer extends AbstractPreferenceInitializer
{
IPreferenceStore store = (IPreferenceStore)HprofPlugin.getDefault().getPreferenceStore();
store.setDefault(HprofPreferences.STRICTNESS_PREF, HprofPreferences.DEFAULT_STRICTNESS.toString());
+ store.setDefault(HprofPreferences.ADDITIONAL_CLASS_REFERENCES, Boolean.FALSE);
}
catch (LinkageError e)
{
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java
index 6a819925..c92dcec4 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java
@@ -383,8 +383,9 @@ public class SnapshotFactoryImpl implements SnapshotFactory.Implementation
if (outs[0] != clsId) {
long address = outs[0] >= 0 && outs[0] < maxIndex ? pidx.identifiers.get(outs[0]) : -1;
String desc = objDesc(pidx, i);
+ long clsAddress = clsId >= 0 && outs[0] < maxIndex ? pidx.identifiers.get(clsId) : -1;
listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
- Messages.SnapshotFactoryImpl_InvalidFirstOutbound, i, format(addr), desc, outs[0], format(address), clsId), null);
+ Messages.SnapshotFactoryImpl_InvalidFirstOutbound, i, format(addr), desc, outs[0], format(address), clsId, format(clsAddress)), null);
}
}
}
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties
index 59f6e67b..11f70fe6 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties
@@ -48,7 +48,7 @@ SnapshotFactoryImpl_IndexAddressFoundAtOtherID=Index {0} address {1} found at in
SnapshotFactoryImpl_ClassIDNotFound=Class id not found for index {0} address {1}, class id {2}
SnapshotFactoryImpl_ClassImplNotFound=ClassImpl not found for index {0} address {1} class id {2}
SnapshotFactoryImpl_IndexAddressNegativeArraySize=Index {0} address {1} negative size {2} type name {3}
-SnapshotFactoryImpl_InvalidFirstOutbound=Object at index {0} address {1} type {2} has first outbound index {3} address {4} which is not its class index {5}
+SnapshotFactoryImpl_InvalidFirstOutbound=Object at index {0} address {1} type {2} has first outbound index {3} address {4} which is not its class index {5} address {6}
SnapshotFactoryImpl_InvalidOutbound=Object at index {0} address {1} type {2} has outbounds[{3}] with an invalid index {4}
SnapshotFactoryImpl_ClassIndexAddressNotEqualClassObjectAddress=Class index {0} address {1} not equal to class object address {2} name {3}
SnapshotFactoryImpl_ClassIndexNotEqualClassObjectID=Class index {0} address {1} not equal to class object id {2} name {3}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java
index 709ebe31..2337e7f7 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java
@@ -11,10 +11,10 @@
*******************************************************************************/
package org.eclipse.mat.tests.snapshot;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.either;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
@@ -44,6 +43,7 @@ import org.eclipse.mat.snapshot.SnapshotInfo;
import org.eclipse.mat.snapshot.model.GCRootInfo;
import org.eclipse.mat.snapshot.model.GCRootInfo.Type;
import org.eclipse.mat.snapshot.model.IClass;
+import org.eclipse.mat.snapshot.model.IClassLoader;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IStackFrame;
import org.eclipse.mat.snapshot.model.IThreadStack;
@@ -420,7 +420,32 @@ public class GeneralSnapshotTests
assertEquals("Classes", oldInfo.getNumberOfClasses(), newInfo.getNumberOfClasses());
assertEquals("Objects", oldInfo.getNumberOfObjects(), newInfo.getNumberOfObjects());
assertEquals("Classloaders", oldInfo.getNumberOfClassLoaders(), newInfo.getNumberOfClassLoaders());
- assertEquals("GC Roots", oldInfo.getNumberOfGCRoots(), newInfo.getNumberOfGCRoots());
+ // Check number of (non-array) system classes not marked as GC root
+ IObject boot = snapshot.getObject(0);
+ int bootcls = 0;
+ int systemclsroot = 0;
+ if (boot instanceof IClassLoader)
+ {
+ IClassLoader bootldr = ((IClassLoader)boot);
+ if (bootldr.getObjectAddress() == 0)
+ {
+ for (IClass cl : bootldr.getDefinedClasses())
+ {
+ if (cl.isArrayType())
+ continue;
+ ++bootcls;
+ GCRootInfo g[] = snapshot.getGCRootInfo(cl.getObjectId());
+ if (g != null && g.length >= 1)
+ {
+ // The class is a root for some reason
+ ++systemclsroot;
+ }
+ }
+ }
+ }
+ // Parsing new HPROF will make all classes loaded by boot loader as GC roots, so adjust the expected total
+ // Only seems to apply for IBM 1.4.2 SDFF dumps with 'double', 'long' classes not as system class roots
+ assertEquals("GC Roots", oldInfo.getNumberOfGCRoots() + (bootcls - systemclsroot), newInfo.getNumberOfGCRoots());
} finally {
newSnapshot.dispose();
}

Back to the top