Skip to content

Instantly share code, notes, and snippets.

@voronaam
Created December 4, 2015 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voronaam/0747c013895d0191e8f2 to your computer and use it in GitHub Desktop.
Save voronaam/0747c013895d0191e8f2 to your computer and use it in GitHub Desktop.
A quick implementation of the object dump query for Eclipse Memory Analizer (MAT)
package org.eclipse.mat.ui.snapshot.actions;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
import org.eclipse.mat.snapshot.model.IObjectArray;
import org.eclipse.mat.snapshot.model.FieldDescriptor;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.query.IContextObject;
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.Argument.Advice;
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IPrimitiveArray;
import org.eclipse.mat.ui.Messages;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
@Icon("/icons/copy.gif")
public class SaveObjectToFile implements IQuery
{
@Argument
public ISnapshot snapshot;
@Argument
public List<IContextObject> objects;
@Argument(advice = Advice.SAVE)
public File file;
@Argument(isMandatory = false)
public int depth = 5;
@Argument
public Display display;
private final HashSet<IObject> visited = new HashSet<IObject>();
public int currentDepth = 0;
public IResult execute(IProgressListener listener) throws Exception
{
checkIfFileExists();
if (objects.size() > 1)
writeStringData();
else if (objects.size() == 1)
writeRawData();
// let the UI ignore this query
throw new IProgressListener.OperationCanceledException();
}
private void checkIfFileExists()
{
if (file.exists())
{
try
{
// message box will popup in background...
Thread.sleep(500);
}
catch (InterruptedException ignore)
{}
final boolean[] goAhead = new boolean[1];
display.syncExec(new Runnable()
{
public void run()
{
MessageBox box = new MessageBox(display.getActiveShell(), //
SWT.YES | SWT.NO);
box.setText(Messages.SaveValueAsQuery_Overwrite);
box.setMessage(MessageUtil.format(Messages.SaveValueAsQuery_FileExists, file.getAbsolutePath()));
int retValue = box.open();
goAhead[0] = retValue == SWT.YES;
}
});
if (!goAhead[0])
throw new IProgressListener.OperationCanceledException();
}
}
private void writeStringData() throws Exception
{
FileOutputStream out = new FileOutputStream(file);
try
{
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, System
.getProperty("file.encoding")))); //$NON-NLS-1$
try
{
boolean isFirst = true;
for (IContextObject object : objects)
{
if (object.getObjectId() < 0)
continue;
if (!isFirst)
writer.append("\n---\n");
IObject subject = snapshot.getObject(object.getObjectId());
writeFullData(writer, subject);
isFirst = false;
}
writer.flush();
}
finally
{
writer.close();
}
}
finally
{
out.close();
}
}
private void offset(PrintWriter writer, int extra) {
for (int i=0; i<currentDepth * 4 + extra; i++) {
writer.append(' ');
}
}
private void writeFullData(PrintWriter writer, IObject subject) throws SnapshotException {
offset(writer, 0);
ExportInfo info = ExportInfo.of(subject);
if (info == null)
{
String name = subject.getClassSpecificName();
writer.append(name != null ? name : subject.getTechnicalName());
writer.append("\n");
if (currentDepth < depth && !visited.contains(subject)) {
visited.add(subject);
currentDepth++;
IClass clazz = subject.getClazz();
while (clazz != null) {
for(FieldDescriptor field: clazz.getFieldDescriptors()) {
offset(writer, 2);
writer.append(field.getName() + " = ");
Object sub = subject.resolveValue(field.getName());
if (sub == null) {
writer.append("null");
} else if (sub instanceof IObjectArray) {
IObjectArray arr = (IObjectArray)sub;
writer.append("[");
for(long addr: arr.getReferenceArray()) {
if (addr == 0) continue;
IObject member = subject.getSnapshot().getObject(subject.getSnapshot().mapAddressToId(addr));
if (member == null) {
writer.append("<error>");
} else {
writeFullData(writer, member);
}
}
offset(writer, 2);
writer.append("]");
} else if (sub instanceof IObject) {
writeFullData(writer, (IObject)sub);
} else {
writer.append("" + sub);
}
writer.append("\n");
}
clazz = clazz.getSuperClass();
}
currentDepth--;
}
}
else
{
IPrimitiveArray charArray = info.getCharArray();
final int length = charArray.getLength();
final int end = info.getOffset() + info.getCount();
int offset = info.getOffset();
while (offset < end)
{
int read = Math.min(4092, length - offset);
char[] array = (char[]) charArray.getValueArray(offset, read);
writer.append(new String(array));
offset += read;
}
writer.append("\n");
}
}
private void writeRawData() throws Exception
{
IContextObject obj = objects.get(0);
if (obj.getObjectId() < 0)
return;
IObject object = snapshot.getObject(obj.getObjectId());
if (!(object instanceof IPrimitiveArray))
{
writeStringData();
return;
}
IPrimitiveArray array = (IPrimitiveArray) object;
FileOutputStream out = new FileOutputStream(file);
try
{
DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(out));
try
{
int size = array.getLength();
int offset = 0;
while (offset < size)
{
int read = Math.min(4092, size - offset);
Object valueArray = array.getValueArray(offset, read);
switch (array.getType())
{
case IObject.Type.BOOLEAN:
{
boolean[] a = (boolean[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeBoolean(a[ii]);
break;
}
case IObject.Type.BYTE:
{
byte[] a = (byte[]) valueArray;
writer.write(a);
break;
}
case IObject.Type.CHAR:
{
char[] a = (char[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeChar(a[ii]);
break;
}
case IObject.Type.DOUBLE:
{
double[] a = (double[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeDouble(a[ii]);
break;
}
case IObject.Type.FLOAT:
{
float[] a = (float[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeFloat(a[ii]);
break;
}
case IObject.Type.INT:
{
int[] a = (int[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeInt(a[ii]);
break;
}
case IObject.Type.LONG:
{
long[] a = (long[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeLong(a[ii]);
break;
}
case IObject.Type.SHORT:
{
short[] a = (short[]) valueArray;
for (int ii = 0; ii < a.length; ii++)
writer.writeShort(a[ii]);
break;
}
default:
throw new SnapshotException(MessageUtil.format(
Messages.SaveValueAsQuery_UnrecognizedPrimitiveArrayType, array.getType()));
}
offset += read;
}
writer.flush();
}
finally
{
writer.close();
}
}
finally
{
out.close();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment