Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Johnson2018-11-05 12:04:26 -0500
committerAndrew Johnson2018-11-05 16:37:53 -0500
commit3861e17c4b5da489aa04aab2dc2b2faeb2b4d607 (patch)
tree27a98ffcfee4065c46367ac287e8942eb55442ef
parent6b01120808e6eb3446d7b4f66cfcefbe869f5086 (diff)
downloadorg.eclipse.mat-3861e17c4b5da489aa04aab2dc2b2faeb2b4d607.tar.gz
org.eclipse.mat-3861e17c4b5da489aa04aab2dc2b2faeb2b4d607.tar.xz
org.eclipse.mat-3861e17c4b5da489aa04aab2dc2b2faeb2b4d607.zip
[519274] Redacted Binary dump so as to protect privacy data
Remove spaces etc. Change-Id: I0f853afd74bff53a64c95bec8ce973dcf791f4fe
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java90
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java28
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java25
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java36
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties2
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java16
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/messages.properties2
-rw-r--r--plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayIntCompressed.java8
-rw-r--r--plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayLongCompressed.java8
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/collect/CompressedArraysTest.java147
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java6
11 files changed, 330 insertions, 38 deletions
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 f9ecef19..77eff24c 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
@@ -598,7 +598,9 @@ public class ExportHprof implements IQuery
case Type.NATIVE_STATIC:
case Type.NATIVE_STACK:
contextAddr = gri.getContextAddress();
- if (contextAddr == 0)
+ // Currently DTFJ parser generates some roots with context address == object address
+ // when no context address
+ if (contextAddr == 0 || contextAddr == gri.getObjectAddress())
{
tp = Constants.DumpSegment.ROOT_JNI_GLOBAL;
os.writeByte(tp);
@@ -1189,10 +1191,8 @@ public class ExportHprof implements IQuery
else
{
// Use these objects
- IObject io = snapshot.getObject(o);
- IClass cls = io.getClazz();
// check for overflow if there is an unlimited end and this not the first object
- if (dumpObject(os, os2, cls, io, i > start && end == Integer.MAX_VALUE))
+ if (this.dumpObject(os, os2, snapshot.getObject(o), i > start && end == Integer.MAX_VALUE))
{
// Success, enough room
++totalObjects;
@@ -1413,7 +1413,7 @@ public class ExportHprof implements IQuery
* @throws IOException
* @throws SnapshotException
*/
- private boolean dumpObject(DataOutputStream3 os, DataOutputStream3 os2, IClass cls, IObject io, boolean check)
+ private boolean dumpObject(DataOutputStream3 os, DataOutputStream3 os2, IObject io, boolean check)
throws IOException, SnapshotException
{
if (io instanceof IInstance)
@@ -1426,6 +1426,7 @@ public class ExportHprof implements IQuery
++totalClassloaders;
return true;
}
+ IClass cls = io.getClazz();
long mark1 = os2.size();
dumpInstance(os2, cls, ii);
long mark2 = os2.size();
@@ -1449,21 +1450,80 @@ public class ExportHprof implements IQuery
}
else if (io instanceof IPrimitiveArray)
{
- return dumpPrimitiveArray(os, io, check);
+ return dumpPrimitiveArray(os, (IPrimitiveArray)io, check);
}
else if (io instanceof IObjectArray)
{
- return dumpObjectArray(os, io, check);
- } else {
+ return dumpObjectArray(os, (IObjectArray)io, check);
+ } else if (io instanceof IClass){
// Classes are IObject but not necessarily IInstance
+ return dumpClassObject(os, (IClass)io, check);
}
return true;
}
- private boolean dumpObjectArray(DataOutputStream3 os, IObject io, boolean check) throws IOException
+ /**
+ * Sometimes it might be desireable 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.
+ * CLASS_DUMP only has constant pool, declared fields and static fields - but not fields
+ * declared by java.lang.Class.
+ * @param os
+ * @param io
+ * @param check
+ * @return
+ * @throws IOException
+ */
+ private boolean dumpClassObject(DataOutputStream3 os, IClass io, boolean check) throws IOException
{
- IObjectArray ii = (IObjectArray) io;
+ 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))
+ {
+ // Most types are of class java.lang.Class, and don't need this extra information.
+ return true;
+ }
+ // Classes are IObject but not necessarily IInstance
+ int size = (int)cls.getHeapSizePerInstance();
+ int size2 = 0;
+ for (IClass cls2 = cls; cls2 != null; cls2 = cls2.getSuperClass())
+ {
+ for (FieldDescriptor fd : cls2.getFieldDescriptors()) {
+ int se;
+ 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;
+ }
+ }
+ size = size2;
+ if (check && os.size() + 1L + idsize + 4 + idsize + 4 + size > MAX_SEGMENT)
+ {
+ // Overflow
+ return false;
+ }
+ os.writeByte(Constants.DumpSegment.INSTANCE_DUMP);
+ writeID(os, io.getObjectAddress());
+ os.writeInt(UNKNOWN_STACK_TRACE_SERIAL); // stack trace serial number
+ writeID(os, cls.getObjectAddress());
+ os.writeInt((int)size);
+ os.write(new byte[size]);
+ return true;
+ }
+
+ private boolean dumpObjectArray(DataOutputStream3 os, IObjectArray ii, boolean check) throws IOException
+ {
if (check && os.size() + 1L + idsize + 4 + 4 + ii.getLength() * idsize > MAX_SEGMENT)
{
// This object would overflow
@@ -1480,14 +1540,12 @@ public class ExportHprof implements IQuery
{
writeID(os, l[i]);
}
- totalBytes += io.getUsedHeapSize();
+ totalBytes += ii.getUsedHeapSize();
return true;
}
- private boolean dumpPrimitiveArray(DataOutputStream3 os, IObject io, boolean check) throws IOException
+ private boolean dumpPrimitiveArray(DataOutputStream3 os, IPrimitiveArray ii, boolean check) throws IOException
{
- IPrimitiveArray ii = (IPrimitiveArray) io;
-
if (check && os.size() + 1L + idsize + 4 + 4 + 1 + ii.getLength() * (1L << (ii.getType() & 3) ) > MAX_SEGMENT)
{
return false;
@@ -1624,7 +1682,7 @@ public class ExportHprof implements IQuery
os.writeDouble(doubleValue);
}
}
- totalBytes += io.getUsedHeapSize();
+ totalBytes += ii.getUsedHeapSize();
return true;
}
@@ -2075,7 +2133,7 @@ public class ExportHprof implements IQuery
* mapping of outer class.
*
* @param classname
- * @return
+ * @return the renamed class name, or the original name if no renaming is to be done for this class
*/
public String renameClassName(String classname)
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 f28b9749..15498763 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
@@ -75,6 +75,8 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
private int pointerSize;
// The alignment between successive objects
private int objectAlign;
+ // New size of classes including per-instance fields
+ private static final boolean NEWCLASSSIZE = false;
// //////////////////////////////////////////////////////////////
// lifecycle
@@ -156,9 +158,15 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
clazz.setClassLoaderIndex(identifiers.reverse(0));
}
- // add class instance
- clazz.setClassInstance(javaLangClass);
- javaLangClass.addInstance(clazz.getUsedHeapSize());
+ // 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
+ clazz.setUsedHeapSize(clazz.getUsedHeapSize() + clazz.getClazz().getHeapSizePerInstance());
+ }
+ clazz.getClazz().addInstance(clazz.getUsedHeapSize());
// resolve super class
ClassImpl superclass = lookupClass(clazz.getSuperClassAddress());
@@ -240,7 +248,7 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
String clss[] = {ClassImpl.JAVA_LANG_CLASS, ClassImpl.JAVA_LANG_CLASSLOADER};
for (String cls : clss)
{
- List<ClassImpl>jlcs = classesByName.get(cls); //$NON-NLS-1$
+ List<ClassImpl>jlcs = classesByName.get(cls);
if (jlcs == null || jlcs.isEmpty())
{
while (identifiers.reverse(++nextObjectAddress) >= 0)
@@ -531,13 +539,13 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
{
if (referrer != 0)
{
- HashMapLongObject localAddressToRootInfo = threadAddressToLocals.get(referrer);
+ HashMapLongObject<List<XGCRootInfo>> localAddressToRootInfo = threadAddressToLocals.get(referrer);
if (localAddressToRootInfo == null)
{
- localAddressToRootInfo = new HashMapLongObject();
+ localAddressToRootInfo = new HashMapLongObject<List<XGCRootInfo>>();
threadAddressToLocals.put(referrer, localAddressToRootInfo);
}
- List<XGCRootInfo> gcRootInfo = (List<XGCRootInfo>) localAddressToRootInfo.get(id);
+ List<XGCRootInfo> gcRootInfo = localAddressToRootInfo.get(id);
if (gcRootInfo == null)
{
gcRootInfo = new ArrayList<XGCRootInfo>(1);
@@ -596,7 +604,11 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
public void reportInstance(long id, long filePosition)
{
- this.identifiers.add(id);
+ // Check for INSTANCE_DUMP for an existing class object
+ if (!classesByAddress.containsKey(id))
+ {
+ this.identifiers.add(id);
+ }
}
public void reportRequiredObjectArray(long arrayClassID)
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 69ef3ab3..3985f77f 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
@@ -156,6 +156,9 @@ public class Pass1Parser extends AbstractParser
case Constants.Record.LOAD_CLASS:
readLoadClass();
break;
+ case Constants.Record.UNLOAD_CLASS:
+ readUnloadClass();
+ break;
case Constants.Record.STACK_FRAME:
readStackFrame(length);
break;
@@ -191,7 +194,6 @@ public class Pass1Parser extends AbstractParser
currentDumpNr++;
in.skipBytes(length);
break;
- case Constants.Record.UNLOAD_CLASS:
case Constants.Record.ALLOC_SITES:
case Constants.Record.HEAP_SUMMARY:
case Constants.Record.START_THREAD:
@@ -279,6 +281,14 @@ public class Pass1Parser extends AbstractParser
classSerNum2id.put(classSerNum, classID);
}
+ private void readUnloadClass() throws IOException
+ {
+ long classSerNum = readUnsignedInt(); // used in stacks frames
+ long classID = classSerNum2id.get(classSerNum);
+ // class2name only holds active classes
+ class2name.remove(classID);
+ }
+
private void readStackFrame(long length) throws IOException
{
long frameId = readID();
@@ -532,6 +542,8 @@ public class Pass1Parser extends AbstractParser
}
ClassImpl clazz = new ClassImpl(address, className, superClassObjectId, classLoaderObjectId, statics, fields);
+ // This will be replaced by a size calculated from the field sizes
+ clazz.setHeapSizePerInstance(instsize);
handler.addClass(clazz, segmentStartPos);
// Just in case the superclass is missing
@@ -553,6 +565,17 @@ public class Pass1Parser extends AbstractParser
IClass instanceType = handler.lookupClass(classID);
if (instanceType == null)
handler.reportRequiredClass(classID, payload);
+ else
+ {
+ // If this is a instance record for a class
+ IClass instanceCls = handler.lookupClass(address);
+ if (instanceCls instanceof ClassImpl)
+ {
+ // Set the type here
+ ClassImpl instClsImpl = (ClassImpl)instanceCls;
+ instClsImpl.setClassInstance((ClassImpl) instanceType);
+ }
+ }
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 7c99c651..d337d640 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
@@ -40,6 +40,8 @@ public class Pass2Parser extends AbstractParser
{
private IHprofParserHandler handler;
private SimpleMonitor.Listener monitor;
+ // New size of classes including per-instance fields
+ private static final boolean NEWCLASSSIZE = false;
public Pass2Parser(IHprofParserHandler handler, SimpleMonitor.Listener monitor,
HprofPreferences.HprofStrictness strictnessPreference)
@@ -207,10 +209,38 @@ public class Pass2Parser extends AbstractParser
List<IClass> hierarchy = handler.resolveClassHierarchy(classID);
ClassImpl thisClazz = (ClassImpl) hierarchy.get(0);
- HeapObject heapObject = new HeapObject(handler.mapAddressToId(id), id, thisClazz,
- thisClazz.getHeapSizePerInstance());
+ HeapObject heapObject;
- heapObject.references.add(thisClazz.getObjectAddress());
+ IClass objcl = handler.lookupClass(id);
+ 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
+ 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);
+ // Heap size of each class type object is individual as have statics
+ heapObject = new HeapObject(handler.mapAddressToId(id), id, thisClazz,
+ objcls.getUsedHeapSize());
+ // and extract the class references
+ heapObject.references.addAll(objcls.getReferences());
+ }
+ else
+ {
+ heapObject = new HeapObject(handler.mapAddressToId(id), id, thisClazz,
+ thisClazz.getHeapSizePerInstance());
+ heapObject.references.add(thisClazz.getObjectAddress());
+ }
// extract outgoing references
for (IClass clazz : hierarchy)
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 d88bd982..c1709b30 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
@@ -33,5 +33,5 @@ If present it is also read at the start to use existing mappings from previous e
It is then updated with any new mappings.
ExportHprof.undo.help = Whether to reverse the obfuscation process using the supplied \
mapping file.
-ExportHprof.objects.help = Only export selected objects. See the help for how this\n\
+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.
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 594e0b08..6a819925 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
@@ -233,8 +233,20 @@ public class SnapshotFactoryImpl implements SnapshotFactory.Implementation
indexBuilder.clean(purgedMapping, listener);
SnapshotImpl snapshot = builder.create(parser, listener);
-
- snapshot.calculateDominatorTree(listener);
+ boolean done = false;
+ try
+ {
+ snapshot.calculateDominatorTree(listener);
+ done = true;
+ }
+ finally
+ {
+ if (!done)
+ {
+ // Error in dominator tree, so close the index files
+ snapshot.dispose();
+ }
+ }
return snapshot;
}
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 17809498..59f6e67b 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
@@ -52,7 +52,7 @@ SnapshotFactoryImpl_InvalidFirstOutbound=Object at index {0} address {1} type {2
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}
-SnapshotFactoryImpl_ClassIndexAddressTypeIDNotEqualClassImplClassId=Class index {0} address {1} class id1 {2} ClassImpl class id {3} name {4}
+SnapshotFactoryImpl_ClassIndexAddressTypeIDNotEqualClassImplClassId=Class index {0} address {1} class id1 {2} different from ClassImpl class id {3} name {4}
SnapshotFactoryImpl_ClassIndexAddressNoLoaderID=Class index {0} address {1} clsId {2} no loader id {3} address {4} class name {5}
SnapshotFactoryImpl_ObjectsFoundButClassesHadObjectsAndClassesInTotal={0} objects found but {1} classes had {2} objects and class objects in total
SnapshotFactoryImpl_GCRootContextIDDoesNotMatchAddress=GC root info object id {0} context id {1} does not match address 0x{2}
diff --git a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayIntCompressed.java b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayIntCompressed.java
index 8e75a40d..1e9c9ed7 100644
--- a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayIntCompressed.java
+++ b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayIntCompressed.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG.
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson/IBM Corporation - allow changing of entries
*******************************************************************************/
package org.eclipse.mat.collect;
@@ -140,6 +141,8 @@ public class ArrayIntCompressed
if (off > 0x8)
{
off -= 0x8;
+ // Mask off old data
+ data[idx] &= ~((1 << varyingBits - off) - 1);
data[idx++] |= (byte) (value >>> off);
while (off > 0x8)
{
@@ -147,6 +150,9 @@ public class ArrayIntCompressed
data[idx++] = (byte) (value >>> off);
}
}
+ // Mask off old data
+ // Shift 2 by varyingBits - 1 to avoid problem with varying bits == 32
+ data[idx] &= ~(((2 << varyingBits - 1) - 1) << (0x8 - off));
data[idx] |= (byte) (value << (0x8 - off));
}
diff --git a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayLongCompressed.java b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayLongCompressed.java
index 49d9320a..9698a308 100644
--- a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayLongCompressed.java
+++ b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/collect/ArrayLongCompressed.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG.
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson/IBM Corporation - allow changing of entries
*******************************************************************************/
package org.eclipse.mat.collect;
@@ -140,6 +141,8 @@ public class ArrayLongCompressed
if (off > 0x8)
{
off -= 0x8;
+ // Mask off old data
+ data[idx] &= ~((1L << varyingBits - off) - 1);
data[idx++] |= (byte) (value >>> off);
while (off > 0x8)
{
@@ -147,6 +150,9 @@ public class ArrayLongCompressed
data[idx++] = (byte) (value >>> off);
}
}
+ // Mask off old data
+ // Shift 2L by varyingBits - 1 to avoid problem with varying bits == 64
+ data[idx] &= ~(((2L << varyingBits - 1) - 1) << (0x8 - off));
data[idx] |= (byte) (value << (0x8 - off));
}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/collect/CompressedArraysTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/collect/CompressedArraysTest.java
index 074374e2..b1921869 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/collect/CompressedArraysTest.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/collect/CompressedArraysTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008 SAP AG.
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -7,9 +7,12 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Andrew Johnson/IBM Corporation - test array overwrites, and different leading and trailing zeroes
*******************************************************************************/
package org.eclipse.mat.tests.collect;
+import static org.junit.Assert.assertEquals;
+
import java.util.Random;
import org.eclipse.mat.collect.ArrayIntCompressed;
@@ -134,4 +137,146 @@ public class CompressedArraysTest
}
}
}
+
+ /**
+ * Test that we can overwrite values in the Array.
+ * Also test every combination of leading and trailing zeroes.
+ */
+ @Test
+ public void testIntArrayCompressedOverwrite()
+ {
+ Random rand = new Random(SEED);
+ int INTS = 100;
+ for (int i = 1; i <= INTS; i++)
+ {
+ // size in bits
+ for (int j1 = 1; j1 <= Integer.SIZE; j1++)
+ {
+ // trailing clear bits
+ for (int j2 = 0; j1 + j2 <= Integer.SIZE; j2++)
+ {
+ int[] ints = new int[i];
+ int order[] = new int[i];
+ for (int k = 0; k < ints.length; k++)
+ {
+ ints[k] = (rand.nextInt() >>> Integer.SIZE - j1) << j2;
+ order[k] = k;
+ }
+ // Shuffle order
+ for (int k = 0; k < order.length; ++k)
+ {
+ int l = rand.nextInt(order.length - k);
+ int t = order[k];
+ order[k] = order[l];
+ order[l] = t;
+ }
+
+ ArrayIntCompressed array = new ArrayIntCompressed(ints.length, Integer.SIZE - j1 - j2, j2);
+
+ // Random fill
+ for (int k = 0; k < order.length; ++k)
+ {
+ array.set(k, (rand.nextInt() >>> Integer.SIZE - j1) << j2);
+ }
+
+ String msg = "i=" + i + " j1=" + j1 + " j2=" + j2;
+ // Fill final values in random order
+ for (int k = 0; k < order.length; ++k)
+ {
+ int kx = order[k];
+ int prev = kx - 1 >= 0 ? array.get(kx - 1) : 0;
+ int next = kx + 1 < order.length ? array.get(kx + 1) : 0;
+ array.set(kx, ints[kx]);
+ int prev1 = kx - 1 >= 0 ? array.get(kx - 1) : 0;
+ int val = array.get(kx);
+ int next1 = kx + 1 < order.length ? array.get(kx + 1) : 0;
+ assertEquals(msg+" kx=" + kx, ints[kx], val);
+ assertEquals(msg+" prev kx=" + kx, prev, prev1);
+ assertEquals(msg+" next kx=" + kx, next, next1);
+ }
+ byte[] bytes = array.toByteArray();
+ int expectedLength = 2 + (j1 * ints.length + 7) / 8;
+ assertEquals(msg+" expected compressed length", expectedLength, bytes.length);
+ ArrayIntCompressed array2 = new ArrayIntCompressed(bytes);
+ int[] ints2 = new int[ints.length];
+ for (int k = 0; k < ints.length; k++)
+ {
+ ints2[k] = array2.get(k);
+ }
+ Assert.assertArrayEquals(msg, ints, ints2);
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that we can overwrite values in the Array.
+ * Also test every combination of leading and trailing zeroes.
+ */
+ @Test
+ public void testLongArrayCompressedOverwrite()
+ {
+ Random rand = new Random(SEED);
+ int LONGS = 100;
+ for (int i = 1; i <= LONGS; i++)
+ {
+ // size in bits
+ for (int j1 = 1; j1 <= Long.SIZE; j1++)
+ {
+ // trailing clear bits
+ for (int j2 = 0; j1 + j2 <= Long.SIZE; j2++)
+ {
+ long[] longs = new long[i];
+ int order[] = new int[i];
+ for (int k = 0; k < longs.length; k++)
+ {
+ longs[k] = (rand.nextLong() >>> Long.SIZE - j1) << j2;
+ order[k] = k;
+ }
+ // Shuffle order
+ for (int k = 0; k < order.length; ++k)
+ {
+ int l = rand.nextInt(order.length - k);
+ int t = order[k];
+ order[k] = order[l];
+ order[l] = t;
+ }
+
+ ArrayLongCompressed array = new ArrayLongCompressed(longs.length, Long.SIZE - j1 - j2, j2);
+
+ // Random fill
+ for (int k = 0; k < order.length; ++k)
+ {
+ array.set(k, (rand.nextLong() >>> Long.SIZE - j1) << j2);
+ }
+
+ String msg = "i=" + i + " j1=" + j1 + " j2=" + j2;
+ // Fill final values in random order
+ for (int k = 0; k < order.length; ++k)
+ {
+ int kx = order[k];
+ long prev = kx - 1 >= 0 ? array.get(kx - 1) : 0;
+ long next = kx + 1 < order.length ? array.get(kx + 1) : 0;
+ array.set(kx, longs[kx]);
+ long prev1 = kx - 1 >= 0 ? array.get(kx - 1) : 0;
+ long val = array.get(kx);
+ long next1 = kx + 1 < order.length ? array.get(kx + 1) : 0;
+ assertEquals(msg+" kx=" + kx, longs[kx], val);
+ assertEquals(msg+" prev kx=" + kx, prev, prev1);
+ assertEquals(msg+" next kx=" + kx, next, next1);
+ }
+ byte[] bytes = array.toByteArray();
+ int expectedLength = 2 + (j1 * longs.length + 7) / 8;
+ assertEquals(msg+" expected compressed length", expectedLength, bytes.length);
+ ArrayLongCompressed array2 = new ArrayLongCompressed(bytes);
+ long[] longs2 = new long[longs.length];
+ for (int k = 0; k < longs.length; k++)
+ {
+ longs2[k] = array2.get(k);
+ }
+ Assert.assertArrayEquals(msg, longs, longs2);
+ }
+ }
+ }
+ }
}
diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java
index cc03d34e..655ccc89 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/OQLTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2017 SAP AG and IBM Corporation.
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -875,7 +875,7 @@ public class OQLTest
@Test
public void testComplex1() throws SnapshotException {
int res[] = (int[])execute("SELECT OBJECTS r from OBJECTS ${snapshot}.@GCRoots r "
- + " WHERE (SELECT s FROM OBJECTS ${snapshot}.getGCRootInfo(r) s WHERE s.@type = 128) != null");
+ + " WHERE (SELECT s FROM OBJECTS ${snapshot}.getGCRootInfo(r) s WHERE s.@type = 8) != null");
assertEquals(23, res.length);
}
@@ -886,7 +886,7 @@ public class OQLTest
@Test
public void testComplex2() throws SnapshotException {
int res[] = (int[])execute("SELECT OBJECTS r from OBJECTS ${snapshot}.@GCRoots r "
- + " WHERE (SELECT s FROM dummy s UNION (SELECT s FROM OBJECTS ${snapshot}.getGCRootInfo(r) s WHERE s.@type = 128)) != null");
+ + " WHERE (SELECT s FROM dummy s UNION (SELECT s FROM OBJECTS ${snapshot}.getGCRootInfo(r) s WHERE s.@type = 8)) != null");
assertEquals(23, res.length);
}

Back to the top