Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Johnson2018-10-30 13:08:00 -0400
committerAndrew Johnson2018-11-01 08:32:48 -0400
commit1988a54f149ccbdc792170d2ae9953f5f1de8bcb (patch)
tree4725e73c64bd1ffd32cdab3b7482ec20afa16c53
parent376ee81468a2ecf099259e19aa944c218b3c33c5 (diff)
downloadorg.eclipse.mat-1988a54f149ccbdc792170d2ae9953f5f1de8bcb.tar.gz
org.eclipse.mat-1988a54f149ccbdc792170d2ae9953f5f1de8bcb.tar.xz
org.eclipse.mat-1988a54f149ccbdc792170d2ae9953f5f1de8bcb.zip
[519274] Redacted Binary dump so as to protect privacy data
Add redact names option. Change-Id: I2555754fde494c3ae59f24879811f18755c9f0dc
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/HeapDumpInfoQuery.java7
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/Field.java5
-rw-r--r--plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/FieldDescriptor.java13
-rw-r--r--plugins/org.eclipse.mat.hprof/icons/export_hprof.gifbin0 -> 989 bytes
-rw-r--r--plugins/org.eclipse.mat.hprof/plugin.xml9
-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.java2387
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofIndexBuilder.java2
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofParserHandlerImpl.java31
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Messages.java16
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass1Parser.java32
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/Pass2Parser.java10
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/acquire/JMapHeapDumpProvider.java9
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties37
-rw-r--r--plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/messages.properties37
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/Messages.java2
-rw-r--r--plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotFactoryImpl.java48
-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/query/registry/ArgumentDescriptor.java3
-rw-r--r--plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/GeneralSnapshotTests.java46
-rw-r--r--plugins/org.eclipse.mat.ui.help/mimes/i-objectinspectorpin.pngbin0 -> 207 bytes
-rw-r--r--plugins/org.eclipse.mat.ui.help/mimes/message_warning.pngbin0 -> 662 bytes
-rw-r--r--plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.dita4
-rw-r--r--plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html300
-rw-r--r--plugins/org.eclipse.mat.ui.help/tasks/exportdump.dita281
-rw-r--r--plugins/org.eclipse.mat.ui.help/tasks/exportdump.html323
-rw-r--r--plugins/org.eclipse.mat.ui.help/toc.ditamap3
-rw-r--r--plugins/org.eclipse.mat.ui.help/toc.xml2
-rw-r--r--plugins/org.eclipse.mat.ui.help/welcome.dita1
-rw-r--r--plugins/org.eclipse.mat.ui.help/welcome.html2
-rw-r--r--plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/MemoryAnalyserPlugin.java4
-rw-r--r--plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/icon_labels.properties1
32 files changed, 3401 insertions, 218 deletions
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/HeapDumpInfoQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/HeapDumpInfoQuery.java
index a1f5fdbd..a70a8941 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/HeapDumpInfoQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/HeapDumpInfoQuery.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 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
@@ -27,7 +27,6 @@ import org.eclipse.mat.query.annotations.Category;
import org.eclipse.mat.query.annotations.CommandName;
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.query.results.ListResult;
-import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.SnapshotInfo;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
@@ -101,12 +100,10 @@ public class HeapDumpInfoQuery implements IQuery
}
@Argument
- public ISnapshot snapshot;
+ public SnapshotInfo info;
public IResult execute(IProgressListener listener) throws Exception
{
- SnapshotInfo info = snapshot.getSnapshotInfo();
-
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
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 419e1f1b..d7d57233 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,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG and others.
+ * Copyright (c) 2008, 2018 SAP AG and others.
* 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 - enhancements and fixes
*******************************************************************************/
package org.eclipse.mat.snapshot.model;
@@ -89,6 +90,6 @@ public final class Field extends FieldDescriptor implements Serializable
*/
public String toString()
{
- return type + " " + name + ": \t" + value; //$NON-NLS-1$//$NON-NLS-2$
+ return super.toString() + ": \t" + value; //$NON-NLS-1$
}
}
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/FieldDescriptor.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/FieldDescriptor.java
index 2fe5e3fc..1bbc06f6 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/FieldDescriptor.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/snapshot/model/FieldDescriptor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2010 SAP AG and others.
+ * Copyright (c) 2008, 2018 SAP AG and others.
* 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 - enhancements and fixes
*******************************************************************************/
package org.eclipse.mat.snapshot.model;
@@ -97,4 +98,14 @@ public class FieldDescriptor implements Serializable
String t = IPrimitiveArray.TYPE[type];
return t.substring(0, t.length() - 2);
}
+
+ /**
+ * A readable representation of the field descriptor.
+ * Do not rely on the format of the result.
+ * @return a description of this field descriptor.
+ */
+ public String toString()
+ {
+ return getVerboseSignature() + " " + name; //$NON-NLS-1$
+ }
}
diff --git a/plugins/org.eclipse.mat.hprof/icons/export_hprof.gif b/plugins/org.eclipse.mat.hprof/icons/export_hprof.gif
new file mode 100644
index 00000000..d125423c
--- /dev/null
+++ b/plugins/org.eclipse.mat.hprof/icons/export_hprof.gif
Binary files differ
diff --git a/plugins/org.eclipse.mat.hprof/plugin.xml b/plugins/org.eclipse.mat.hprof/plugin.xml
index 7ce4d428..bedfdd69 100644
--- a/plugins/org.eclipse.mat.hprof/plugin.xml
+++ b/plugins/org.eclipse.mat.hprof/plugin.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?> <!--
- Copyright (c) 2010 SAP AG.
+ Copyright (c) 2010, 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
@@ -8,6 +8,7 @@
Contributors:
SAP AG - initial API and implementation
+ Andrew Johnson/IBM Corporation - HPROF export
-->
<plugin>
@@ -52,5 +53,11 @@
<extension point="org.eclipse.core.runtime.preferences">
<initializer class="org.eclipse.mat.hprof.ui.PreferenceInitializer" />
</extension>
+ <extension
+ point="org.eclipse.mat.report.query">
+ <query
+ impl="org.eclipse.mat.hprof.ExportHprof">
+ </query>
+ </extension>
</plugin>
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 510181dd..2058ceb7 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
@@ -183,7 +183,7 @@ import org.eclipse.mat.util.SimpleMonitor.Listener;
case IObject.Type.LONG:
return in.readLong();
default:
- throw new IOException(MessageUtil.format(Messages.AbstractParser_Error_IllegalType, type));
+ throw new IOException(MessageUtil.format(Messages.AbstractParser_Error_IllegalType, type, in.position()));
}
}
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
new file mode 100644
index 00000000..ffd62576
--- /dev/null
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/ExportHprof.java
@@ -0,0 +1,2387 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andrew Johnson/IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+/**
+ * Create a HPROF format file from a snapshot, whatever the original dump format.
+ *
+ * Problems with current snapshot API:
+ * finding thread to GCroots linkage
+ * - have to presume a ROOT_THREAD_OBJECT and getOutboundReferences and use
+ * ThreadToLocalReference
+ * parsing stackframes to classes, methods
+ * - have to parse out string format
+ * converting class name in stackframe to class
+ * - could be multiple classes with the same name, information loss
+ * converting object and stack frame to GC root with type
+ * - If JNI local reference and Java local reference to same object in different frame, could get the type swapped.
+ *
+ */
+
+package org.eclipse.mat.hprof;
+
+import java.io.BufferedOutputStream;
+import java.io.Closeable;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+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.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.CommandName;
+import org.eclipse.mat.query.annotations.HelpUrl;
+import org.eclipse.mat.query.annotations.Icon;
+import org.eclipse.mat.snapshot.ISnapshot;
+import org.eclipse.mat.snapshot.SnapshotInfo;
+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.GCRootInfo.Type;
+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.IStackFrame;
+import org.eclipse.mat.snapshot.model.IThreadStack;
+import org.eclipse.mat.snapshot.model.NamedReference;
+import org.eclipse.mat.snapshot.model.ObjectReference;
+import org.eclipse.mat.snapshot.model.ThreadToLocalReference;
+import org.eclipse.mat.snapshot.query.IHeapObjectArgument;
+import org.eclipse.mat.snapshot.query.SnapshotQuery;
+import org.eclipse.mat.util.IProgressListener;
+import org.eclipse.mat.util.MessageUtil;
+import org.eclipse.mat.util.SilentProgressListener;
+
+@CommandName("export_hprof")
+@Icon("/icons/export_hprof.gif")
+@HelpUrl("/org.eclipse.mat.ui.help/tasks/exportdump.html")
+public class ExportHprof implements IQuery
+{
+ private static final Charset UTF8 = Charset.forName("UTF-8"); //$NON-NLS-1$
+
+ @Argument
+ public ISnapshot snapshot;
+
+ @Argument(advice = Advice.SAVE)
+ public File output;
+
+ public enum RedactType
+ {
+ NONE("none"), //$NON-NLS-1$
+ NAMES("names"), //$NON-NLS-1$
+ BASIC("basic"), //$NON-NLS-1$
+ FULL("full"); //$NON-NLS-1$
+ String type;
+
+ private RedactType(String s)
+ {
+ type = s;
+ }
+ }
+
+ @Argument(isMandatory = false)
+ public RedactType redact = RedactType.NONE;
+
+ @Argument(isMandatory = false, advice = Advice.SAVE, flag = "map")
+ public File mapFile;
+
+ @Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "skip")
+ public Pattern skipPattern = Pattern.compile("java\\..*|boolean|byte|char|short|int|long|float|double|void"); //$NON-NLS-1$
+
+ @Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "avoid")
+ public Pattern avoidPattern = Pattern.compile(Messages.ExportHprof_AvoidExample);
+
+ @Argument
+ public boolean undo;
+
+ @Argument(flag = Argument.UNFLAGGED, isMandatory = false)
+ public IHeapObjectArgument objects;
+
+ /** How big a heap dump segment can grow before it needs to be split */
+ private static final long MAX_SEGMENT = 0xffffffffL;
+
+ /** Strings to HPROF ID */
+ HashMap<String, Integer> stringToID = new HashMap<String, Integer>();
+ int nextStringID = 1;
+
+ /** Thread object ID to HPROF thread serial number */
+ HashMap<Integer, Integer> threadToSerial = new HashMap<Integer, Integer>();
+
+ /** Thread object ID to HPROF stack serial number */
+ HashMap<Integer, Integer> threadToStack = new HashMap<Integer, Integer>();
+
+ /** Whether to include this object in the dump */
+ BitField include;
+
+ /** Keep count for a final result */
+ int totalClasses;
+
+ /** Keep count for a final result */
+ int totalObjects;
+
+ /** Keep count for a final result */
+ int totalRoots;
+
+ /** Keep count for a final result */
+ int totalClassloaders;
+
+ /** Keep count for a final result */
+ long totalBytes;
+
+ /** keep track for totals */
+ SetInt classloaders = new SetInt();
+
+ /**
+ * Stream which discards the output.
+ */
+ static class NullStream extends OutputStream
+ {
+
+ @Override
+ public void write(int b) throws IOException
+ {
+ }
+
+ }
+
+ /**
+ * DataOutputStream which can have a long size.
+ */
+ static class DataOutputStream3 implements DataOutput, Closeable
+ {
+ final DataOutputStream2 out;
+ protected long written = 0;
+
+ public DataOutputStream3(OutputStream out)
+ {
+ this.out = new DataOutputStream2(out);
+ }
+
+ public void write(int b) throws IOException
+ {
+ out.write(b);
+ written += 1;
+ }
+
+ public void write(byte[] b) throws IOException
+ {
+ out.write(b);
+ written += b.length;
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ out.write(b, off, len);
+ written += len;
+ }
+
+ public void writeBoolean(boolean v) throws IOException
+ {
+ out.writeBoolean(v);
+ written += 1;
+ }
+
+ public void writeByte(int v) throws IOException
+ {
+ out.writeByte(v);
+ written += 1;
+ }
+
+ public void writeShort(int v) throws IOException
+ {
+ out.writeShort(v);
+ written += 2;
+ }
+
+ public void writeChar(int v) throws IOException
+ {
+ out.writeChar(v);
+ written += 2;
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ out.writeInt(v);
+ written += 4;
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ out.writeLong(v);
+ written += 8;
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ out.writeFloat(v);
+ written += 4;
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ out.writeDouble(v);
+ written += 8;
+ }
+
+ public void writeBytes(String s) throws IOException
+ {
+ out.writeBytes(s);
+ written += s.length();
+ }
+
+ public void writeChars(String s) throws IOException
+ {
+ out.writeChars(s);
+ written += 2 * s.length();
+ }
+
+ public void writeUTF(String s) throws IOException
+ {
+ out.reset();
+ int m1 = out.size();
+ out.writeUTF(s);
+ int m2 = out.size();
+ written += m2 - m1;
+ }
+
+ public long size()
+ {
+ return written;
+ }
+
+ public void close() throws IOException
+ {
+ out.close();
+ }
+ }
+
+ /**
+ * DataOutputStream which can have bytes written reset
+ */
+ static class DataOutputStream2 extends DataOutputStream {
+
+ public DataOutputStream2(OutputStream out)
+ {
+ super(out);
+ }
+ public void reset()
+ {
+ written = 0;
+ }
+ }
+
+ /** The size of the ID fields in the HPROF file */
+ int idsize = 8;
+
+ /** Progress monitor work per class for dumping objects */
+ private static final int WORK_OBJECT = 3;
+
+ Remap remap;
+
+ public IResult execute(IProgressListener listener) throws Exception
+ {
+ int ct = snapshot.getSnapshotInfo().getNumberOfClasses();
+
+ /*
+ * Stages and work items per class
+ * load classes 1 11%
+ * prepare classes 1 22%
+ * dump classes 1 33%
+ * prepare objects 3 67%
+ * dump objects 3 100%
+ * Won't work so well for objects argument.
+ */
+ int ct2 = initObjs();
+ if (ct2 <= 0)
+ ct2 = ct;
+ int totalWork = 3 * ct + 2 * WORK_OBJECT * ct2;
+ listener.beginTask(MessageUtil.format(Messages.ExportHprof_ExportTo, output.getName()), totalWork);
+
+ remap = new Remap(skipPattern, avoidPattern, redact == RedactType.NAMES, undo || mapFile == null);
+ remap.loadMapping(mapFile, undo);
+
+ long startTime;
+ DataOutputStream3 os = new DataOutputStream3(new BufferedOutputStream(new FileOutputStream(output), 1024 * 64));
+ try
+ {
+ os.writeBytes(AbstractParser.Version.JDK6.getLabel()+"\0"); //$NON-NLS-1$
+ idsize = snapshot.getSnapshotInfo().getIdentifierSize();
+ os.writeInt(idsize);
+ startTime = System.currentTimeMillis();
+ os.writeLong(startTime);
+
+ // os.writeByte(Constants.Record.HEAP_SUMMARY);
+
+ DataOutputStream3 os2 = new DataOutputStream3(new NullStream());
+
+ loadClasses(os, os2, startTime, listener);
+
+ // Keep track of new strings
+ int firstId = nextStringID;
+ long mark1 = os2.size();
+ listener.subTask(Messages.ExportHprof_PrepareClasses);
+ dumpClasses(os2, listener);
+ listener.subTask(Messages.ExportHprof_PrepareGCRoots);
+ gcRoots(os2);
+ dumpThreadRoots(os2);
+ long mark2 = os2.size();
+ long sizeseg1 = mark2 - mark1;
+
+ listener.subTask(Messages.ExportHprof_PrepareThreadStacks);
+ // Find all the strings
+ dumpThreadStacks(os2, startTime);
+
+ // Write out new Strings from the classes etc.
+ listener.subTask(Messages.ExportHprof_DumpStrings);
+ for (Map.Entry<String, Integer> e : stringToID.entrySet())
+ {
+ String ss = e.getKey();
+ int id = e.getValue();
+ if (id < firstId)
+ continue;
+ writeStringUTF(os, os2, startTime, ss, id);
+ }
+
+ listener.subTask(Messages.ExportHprof_DumpThreadStacks);
+ dumpThreadStacks(os, startTime);
+
+ int segnum = 1;
+ long seg1Start = os.size();
+ os.writeByte(Constants.Record.HEAP_DUMP_SEGMENT);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt((int)sizeseg1);
+
+ long markseg1a = os.size();
+ listener.subTask(MessageUtil.format(Messages.ExportHprof_DumpClasses, segnum));
+ dumpClasses(os, listener);
+ listener.subTask(MessageUtil.format(Messages.ExportHprof_DumpGCRoots, segnum));
+ gcRoots(os);
+ dumpThreadRoots(os);
+ long markseg1b = os.size();
+ long sizeseg1_a = markseg1b - markseg1a;
+
+ if (sizeseg1_a != sizeseg1)
+ {
+ listener.sendUserMessage(IProgressListener.Severity.WARNING,
+ MessageUtil.format(Messages.ExportHprof_SegmentSizeMismatch, segnum, sizeseg1, sizeseg1_a, Long.toHexString(seg1Start)), null);
+ }
+
+ /*
+ * Possibly multiple segments needed for objects
+ */
+ int st = 0;
+ do
+ {
+ ++segnum;
+
+ DataOutputStream3 os3 = new DataOutputStream3(new NullStream());
+ long m1 = os2.size();
+ listener.subTask(MessageUtil.format(Messages.ExportHprof_PrepareObjects, segnum));
+ int end = dumpObjects(os2, os3, st, Integer.MAX_VALUE, listener);
+ long m2 = os2.size();
+ long s2 = m2 - m1;
+ os3.close();
+
+ long sizel = s2;
+
+ long segStart = os.size();
+ if (sizel > 0xffffffffL)
+ {
+ // Too big, but carry on
+ listener.sendUserMessage(IProgressListener.Severity.WARNING,
+ MessageUtil.format(Messages.ExportHprof_SegmentTooLong, segnum, Long.toHexString(segStart), sizel), null);
+ }
+
+ os.writeByte(Constants.Record.HEAP_DUMP_SEGMENT);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt((int)sizel);
+
+ long checkmark1 = os.size();
+ listener.subTask(MessageUtil.format(Messages.ExportHprof_DumpObjects, segnum));
+ st = dumpObjects(os, os2, st, end, listener);
+ long checkmark2 = os.size();
+ long size2 = checkmark2 - checkmark1;
+ if (size2 != sizel)
+ {
+ listener.sendUserMessage(IProgressListener.Severity.WARNING,
+ MessageUtil.format(Messages.ExportHprof_SegmentSizeMismatch, segnum, sizel, size2, Long.toHexString(segStart)), null);
+ }
+ } while (st > 0);
+
+ os.writeByte(Constants.Record.HEAP_DUMP_END);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt(0);
+
+ os2.close();
+ }
+ finally
+ {
+ os.close();
+ }
+
+ String comments = MessageUtil.format(Messages.ExportHprof_RemapProperties, output.getName(), new File(snapshot.getSnapshotInfo().getPath()).getName());
+ remap.saveMapping(mapFile, undo, comments);
+
+
+ listener.done();
+
+ /*
+ * Report the result as a heap dump overview
+ */
+ int nclasses = totalClasses / 2;
+ int nobjects = totalObjects / 2;
+ int nroots = totalRoots / 2;
+ int nclassloaders = totalClassloaders / 2;
+ long nused = totalBytes / 2;
+
+ SnapshotQuery sq = SnapshotQuery.lookup("heap_dump_overview", snapshot); //$NON-NLS-1$
+ String name = output.getName();
+ int dot = name.lastIndexOf('.');
+ if (dot >= 0)
+ {
+ name = name.substring(0, dot);
+ }
+ String prefix = (new File(output.getParentFile(), name)).getPath();
+ SnapshotInfo si = new SnapshotInfo(output.getPath(), prefix, null, idsize, new Date(startTime), nobjects, nroots, nclasses, nclassloaders, nused);
+ String format = (new HprofContentDescriber()).getSupportedOptions()[0].getLocalName();
+ si.setProperty("$heapFormat", format); //$NON-NLS-1$
+ sq.setArgument("info", si); //$NON-NLS-1$
+ IResult ret = sq.execute(new SilentProgressListener(listener));
+ return ret;
+ }
+
+ /**
+ * Include an object in the output dump?
+ * @param obj
+ * @return
+ */
+ boolean includeObject(int obj)
+ {
+ return include == null || include.get(obj);
+ }
+
+ /**
+ * Set up the tests for whether to include objects.
+ */
+ int initObjs()
+ {
+ int n = 0;
+ if (objects != null)
+ {
+ include = new BitField(snapshot.getSnapshotInfo().getNumberOfObjects());
+ for (int ia[] : objects)
+ {
+ ++n;
+ for (int i : ia)
+ {
+ include.set(i);
+ }
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Dump the GC roots
+ *
+ * @param os
+ * @throws SnapshotException
+ * @throws IOException
+ */
+ private void gcRoots(DataOutput os) throws SnapshotException, IOException
+ {
+ int nextThreadSerial = 1;
+ for (int i : snapshot.getGCRoots())
+ {
+ int roots = 0;
+ GCRootInfo gi[] = snapshot.getGCRootInfo(i);
+ for (GCRootInfo gri : gi)
+ {
+ int tp;
+ long contextAddr;
+ switch (gri.getType())
+ {
+ case Type.BUSY_MONITOR:
+ tp = Constants.DumpSegment.ROOT_MONITOR_USED;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ ++roots;
+ break;
+ case Type.THREAD_OBJ:
+ tp = Constants.DumpSegment.ROOT_THREAD_OBJECT;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ os.writeInt(nextThreadSerial);
+ threadToSerial.put(gri.getObjectId(), nextThreadSerial);
+ // System.out.println("Thread "+gri.getObjectId()+"
+ // "+nextThreadSerial);
+ ++nextThreadSerial;
+ Integer stackserial = threadToStack.get(gri.getObjectId());
+ if (stackserial == null)
+ stackserial = -1;
+ os.writeInt(stackserial); // Stack trace
+ ++roots;
+ break;
+ case Type.JAVA_LOCAL:
+ contextAddr = gri.getContextAddress();
+ if (contextAddr == 0)
+ {
+ tp = Constants.DumpSegment.ROOT_JAVA_FRAME;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ os.writeInt(0); // thread serial
+ os.writeInt(-1); // stack frame number
+ ++roots;
+ }
+ break;
+ case Type.NATIVE_LOCAL:
+ contextAddr = gri.getContextAddress();
+ if (contextAddr == 0)
+ {
+ tp = Constants.DumpSegment.ROOT_JNI_GLOBAL;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ os.writeInt(0); // thread serial
+ os.writeInt(-1); // stack frame number
+ ++roots;
+ }
+ break;
+ case Type.NATIVE_STACK:
+ contextAddr = gri.getContextAddress();
+ if (contextAddr == 0)
+ {
+ tp = Constants.DumpSegment.ROOT_NATIVE_STACK;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ os.writeInt(0); // thread serial
+ ++roots;
+ }
+ break;
+ case Type.SYSTEM_CLASS:
+ tp = Constants.DumpSegment.ROOT_STICKY_CLASS;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ ++roots;
+ break;
+ case Type.THREAD_BLOCK:
+ contextAddr = gri.getContextAddress();
+ if (contextAddr == 0)
+ {
+ tp = Constants.DumpSegment.ROOT_THREAD_BLOCK;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ os.writeInt(0); // thread serial
+ ++roots;
+ }
+ break;
+ case Type.UNREACHABLE:
+ case Type.FINALIZABLE:
+ case Type.UNKNOWN:
+ default:
+ tp = Constants.DumpSegment.ROOT_UNKNOWN;
+ os.writeByte(tp);
+ writeID(os, gri.getObjectAddress());
+ ++roots;
+ break;
+ }
+ }
+ if (roots > 0)
+ {
+ ++totalRoots;
+ }
+ }
+ }
+
+ /**
+ * Class to hold results of parsing stack frames.
+ */
+ static class Frame
+ {
+ String clazz;
+ String method;
+ String signature;
+ String sourceFile;
+ int lineNumber;
+ public Frame(String clazz, String method, String signature, String sourceFile, int lineNumber)
+ {
+ this.clazz = clazz;
+ this.method = method;
+ this.signature = signature;
+ this.sourceFile = sourceFile;
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Parse lines such as:
+ * at org.eclipse.core.internal.jobs.Worker.run()V (Worker.java:55)
+ * at org.eclipse.swt.widgets.Display.sleep()Z (Display.java:4534)
+ * at org.eclipse.ui.internal.Workbench.lambda$3(Lorg/eclipse/swt/widgets/Display;Lorg/eclipse/ui/application/WorkbenchAdvisor;[I)V (Workbench.java:683) at
+ * at org.eclipse.swt.internal.win32.OS.WaitMessage()Z (Native Method)
+ * at org.eclipse.swt.internal.win32.OS.WaitMessage()Z (Compiled Code)
+ * at org.eclipse.swt.internal.win32.OS.WaitMessage()Z (Unknown source)
+ * at org.eclipse.swt.widgets.Display.sleep()Z (Display.java:4534(Compiled Code))
+ * at java.io.FileInputStream.readBytes([BII)I (Native Method)
+ * at java.io.FileInputStream.read([BII)I (FileInputStream.java:256)
+ * at java.io.BufferedInputStream.fill()V (BufferedInputStream.java:246)
+ * at java.io.BufferedInputStream.read()I (BufferedInputStream.java:265)
+ * at java.io.BufferedInputStream.read()I (BufferedInputStream.java:265(Compiled Code))
+ * at
+ * The last sometimes appears from DTFJ dumps in error.
+ *
+ * @param frame
+ * @return a new Frame object holding the parsed components
+ */
+ public static Frame parse(String frame)
+ {
+ String parts[] = frame.split("\\s+", 3); //$NON-NLS-1$
+ String mn = parts.length >= 2 ? parts[1] : ""; //$NON-NLS-1$
+ String source = parts.length >= 3 ? parts[2] : ""; //$NON-NLS-1$
+ int b = mn.indexOf('(');
+ String method;
+ String classname;
+ String sig;
+ if (b < 0)
+ {
+ b = mn.length();
+ }
+ int c = mn.lastIndexOf('.', b);
+ if (c >= 0)
+ {
+ classname = mn.substring(0, c);
+ }
+ else
+ {
+ classname = ""; //$NON-NLS-1$
+ }
+ method = mn.substring(c + 1, b);
+ sig = mn.substring(b);
+
+ if (source.startsWith("(")) //$NON-NLS-1$
+ source = source.substring(1);
+ if (source.endsWith(")")) //$NON-NLS-1$
+ source = source.substring(0, source.length() - 1);
+ int cl = source.indexOf(':');
+ String sourcefile;
+ int linenum = 0;
+ if (cl >= 0)
+ {
+ sourcefile = source.substring(0, cl);
+ int cn1 = cl + 1;
+ while (cn1 < source.length() && source.charAt(cn1) >= '0' && source.charAt(cn1) <= '9')
+ {
+ ++cn1;
+ }
+ if (cn1 > cl + 1)
+ {
+ linenum = Integer.parseInt(source.substring(cl + 1, cn1));
+ }
+ }
+ else
+ {
+ int br = source.indexOf('(');
+ if (br >= 0)
+ sourcefile = source.substring(0, br);
+ else
+ sourcefile = ""; //$NON-NLS-1$
+ if (source.contains("Compiled Code")) //$NON-NLS-1$
+ {
+ linenum = -2;
+ }
+ else if (source.contains("Native Method")) //$NON-NLS-1$
+ {
+ linenum = -3;
+ }
+ else
+ {
+ linenum = -1;
+ }
+ }
+ return new Frame(classname, method, sig, sourcefile, linenum);
+ }
+ }
+
+ /**
+ * Dump the thread stacks
+ * @param os the main output stream
+ * @param startTime
+ * @throws SnapshotException
+ * @throws IOException
+ */
+ private void dumpThreadStacks(DataOutput os, long startTime)
+ throws SnapshotException, IOException
+ {
+ int frameid = 1;
+ int serialid = 1;
+ // Find the threads
+ for (int i : snapshot.getGCRoots())
+ {
+ if (!includeObject(i))
+ continue;
+ for (GCRootInfo gr : snapshot.getGCRootInfo(i))
+ {
+ if (gr.getType() == GCRootInfo.Type.THREAD_OBJ)
+ {
+ IThreadStack its = snapshot.getThreadStack(i);
+ if (its == null)
+ continue;
+ // Find the stack frames
+ int firstframeid = frameid;
+ for (IStackFrame isf : its.getStackFrames())
+ {
+ String frametext = isf.getText();
+ Frame f = Frame.parse(frametext);
+ String classname = f.clazz;
+ String method = f.method;
+ String sig = f.signature;
+ String sourcefile = f.sourceFile;
+ method = remap.renameMethodName(classname, method, false);
+ sig = remap.renameSignature(sig);
+ sourcefile = remap.renameFileName(classname, sourcefile);
+ int linenum = f.lineNumber;
+ Collection<IClass> cls = snapshot.getClassesByName(classname, false);
+ int clsid;
+ if (cls == null || cls.isEmpty())
+ {
+ clsid = 0;
+ }
+ else
+ {
+ IClass cls1 = cls.iterator().next();
+ clsid = cls1.getObjectId();
+ }
+ os.writeByte(Constants.Record.STACK_FRAME);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt(4 * idsize + 2 * 4);
+ writeID(os, frameid);
+ writeString(os, method);
+ writeString(os, sig);
+ writeString(os, sourcefile);
+ os.writeInt(clsid);
+ os.writeInt(linenum);
+ ++frameid;
+ }
+ os.writeByte(Constants.Record.STACK_TRACE);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt(3 * 4 + (frameid - firstframeid) * idsize);
+ os.writeInt(serialid);
+ Integer prev = threadToStack.put(i, serialid);
+ //if (prev != null && prev != serialid)
+ // throw new IllegalStateException("thread " + i + "0x" + Long.toHexString(gr.getObjectAddress()) + " " + serialid + " != " + prev); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ ++serialid;
+ os.writeInt(threadToSerial.get(i));
+ os.writeInt(frameid - firstframeid);
+ for (int j = firstframeid; j < frameid; ++j)
+ {
+ writeID(os, j);
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Write out the body of a load class definition.
+ * @param os
+ * @param startTime
+ * @param cls
+ * @throws IOException
+ */
+ private void loadClassBody(DataOutput os, long startTime, IClass cls) throws IOException
+ {
+ os.writeInt(cls.getObjectId()); // Class serial id
+ writeID(os, cls.getObjectAddress());
+ os.writeInt(0); // stack trace serial
+ String classname = cls.getName();
+ classname = remap.renameClassName(classname);
+ writeString(os, classname);
+ }
+
+ /**
+ * Write out the whole load class, including the header and size
+ * @param os
+ * @param os2 Uses to measure the size of the body
+ * @param startTime
+ * @param cls
+ * @throws IOException
+ */
+ private void loadClass(DataOutput os, DataOutputStream3 os2, long startTime, IClass cls) throws IOException
+ {
+ int str = nextStringID;
+ long mark = os2.size();
+ loadClassBody(os2, startTime, cls);
+ long end = os2.size();
+ if (nextStringID != str)
+ {
+ String classname = cls.getName();
+ classname = remap.renameClassName(classname);
+ writeStringUTF(os, os2, startTime, classname, str);
+ }
+
+ loadClass(os, cls, startTime, (int) (end - mark));
+ }
+
+ /**
+ * Generate load class definitions for all the classes.
+ * @param os
+ * @param os2
+ * @param startTime
+ * @param listener
+ * @throws IOException
+ * @throws SnapshotException
+ */
+ private void loadClasses(DataOutput os, DataOutputStream3 os2, long startTime, IProgressListener listener)
+ throws IOException, SnapshotException
+ {
+ for (IClass cls : snapshot.getClasses())
+ {
+ if (includeObject(cls.getObjectId()))
+ {
+ loadClass(os, os2, startTime, cls);
+ }
+ listener.worked(1);
+ if (listener.isCanceled())
+ throw new OperationCanceledException();
+ }
+ return;
+ }
+
+ /**
+ * Write out a single string, as UTF-8.
+ * @param os
+ * @param os2
+ * @param startTime
+ * @param ss
+ * @param id
+ * @throws IOException
+ */
+ private void writeStringUTF(DataOutput os, DataOutputStream3 os2, long startTime, String ss, int id)
+ throws IOException
+ {
+ os.writeByte(Constants.Record.STRING_IN_UTF8);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ long mark = os2.size();
+ writeID(os2, id);
+ byte utf[] = ss.getBytes(UTF8);
+ os2.write(utf);
+ long reclen = os2.size() - mark;
+ os.writeInt((int)reclen);
+ writeID(os, id);
+ os.write(utf);
+ }
+
+ /**
+ * Dump all the classes into a heap dump segment.
+ * @param os
+ * @param listener
+ * @throws IOException
+ * @throws SnapshotException
+ */
+ private void dumpClasses(DataOutput os, IProgressListener listener) throws IOException, SnapshotException
+ {
+ for (IClass cls : snapshot.getClasses())
+ {
+ if (includeObject(cls.getObjectId()))
+ {
+ dumpClass(os, cls);
+ ++totalClasses;
+ }
+ listener.worked(1);
+ if (listener.isCanceled())
+ throw new OperationCanceledException();
+ }
+ // As well as actual used class loader types, include any classes extending the java classloader
+ for (IClass cls: snapshot.getClassesByName(IClass.JAVA_LANG_CLASSLOADER, true))
+ {
+ for (int i : cls.getObjectIds())
+ {
+ classloaders.add(i);
+ }
+ }
+ return;
+ }
+
+ private void loadClass(DataOutput os, IClass cls, long startTime, int len) throws IOException
+ {
+ os.writeByte(Constants.Record.LOAD_CLASS);
+ os.writeInt((int) (System.currentTimeMillis() - startTime));
+ os.writeInt(len);
+ loadClassBody(os, startTime, cls);
+ }
+
+ private void dumpClass(DataOutput os, IClass cls) throws IOException
+ {
+ os.writeByte(Constants.DumpSegment.CLASS_DUMP);
+ writeID(os, cls.getObjectAddress());
+ os.writeInt(0);
+ IClass sup = cls.getSuperClass();
+ writeID(os, sup != null ? sup.getObjectAddress() : 0);
+ 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
+ List<Field> statics = cls.getStaticFields();
+ os.writeShort((short) statics.size());
+ for (Field fld : statics)
+ {
+ String fieldName = fld.getName();
+ fieldName = remap.renameMethodName(cls.getName(), fieldName, true);
+ writeString(os, fieldName);
+ writeField(os, fld, true);
+ }
+ // Instance fields
+ List<FieldDescriptor> fields = cls.getFieldDescriptors();
+ os.writeShort((short) fields.size());
+ for (FieldDescriptor fld : fields)
+ {
+ String fieldName = fld.getName();
+ fieldName = remap.renameMethodName(cls.getName(), fieldName, false);
+ writeString(os, fieldName);
+ int ty = fld.getType();
+ switch (ty)
+ {
+ case IObject.Type.BOOLEAN:
+ os.writeByte(IObject.Type.BOOLEAN);
+ break;
+ case IObject.Type.BYTE:
+ os.writeByte(IObject.Type.BYTE);
+ break;
+ case IObject.Type.CHAR:
+ os.writeByte(IObject.Type.CHAR);
+ break;
+ case IObject.Type.SHORT:
+ os.writeByte(IObject.Type.SHORT);
+ break;
+ case IObject.Type.INT:
+ os.writeByte(IObject.Type.INT);
+ break;
+ case IObject.Type.FLOAT:
+ os.writeByte(IObject.Type.FLOAT);
+ break;
+ case IObject.Type.LONG:
+ os.writeByte(IObject.Type.LONG);
+ break;
+ case IObject.Type.DOUBLE:
+ os.writeByte(IObject.Type.DOUBLE);
+ break;
+ case IObject.Type.OBJECT:
+ os.writeByte(IObject.Type.OBJECT);
+ break;
+ default:
+ // Error
+ break;
+ }
+ }
+ // Check this
+ totalBytes += cls.getUsedHeapSize();
+ }
+
+ private void writeField(DataOutput os, Field fld, boolean addType) throws IOException
+ {
+ int ty = fld.getType();
+ switch (ty)
+ {
+ case IObject.Type.BOOLEAN:
+ if (addType)
+ os.writeByte(IObject.Type.BOOLEAN);
+ int booleanValue = redact == RedactType.FULL ? 0 : ((Boolean) fld.getValue()).booleanValue() ? 1 : 0;
+ os.writeByte(booleanValue);
+ break;
+ case IObject.Type.BYTE:
+ if (addType)
+ os.writeByte(IObject.Type.BYTE);
+ byte byteValue = redact != RedactType.NONE && redact != RedactType.NAMES ? 0 : ((Byte) fld.getValue()).byteValue();
+ os.writeByte(byteValue);
+ break;
+ case IObject.Type.CHAR:
+ if (addType)
+ os.writeByte(IObject.Type.CHAR);
+ char charValue = redact != RedactType.NONE && redact != RedactType.NAMES ? 0 : ((Character) fld.getValue()).charValue();
+ os.writeChar(charValue);
+ break;
+ case IObject.Type.SHORT:
+ if (addType)
+ os.writeByte(IObject.Type.SHORT);
+ short shortValue = redact == RedactType.FULL ? 0 : ((Short) fld.getValue()).shortValue();
+ os.writeShort(shortValue);
+ break;
+ case IObject.Type.INT:
+ if (addType)
+ os.writeByte(IObject.Type.INT);
+ int intValue = redact == RedactType.FULL ? 0 : ((Integer) fld.getValue()).intValue();
+ os.writeInt(intValue);
+ break;
+ case IObject.Type.FLOAT:
+ if (addType)
+ os.writeByte(IObject.Type.FLOAT);
+ float floatValue = redact == RedactType.FULL ? 0.0f : ((Float) fld.getValue()).floatValue();
+ os.writeFloat(floatValue);
+ break;
+ case IObject.Type.LONG:
+ if (addType)
+ os.writeByte(IObject.Type.LONG);
+ long longValue = redact == RedactType.FULL ? 0L : ((Long) fld.getValue()).longValue();
+ os.writeLong(longValue);
+ break;
+ case IObject.Type.DOUBLE:
+ if (addType)
+ os.writeByte(IObject.Type.DOUBLE);
+ double doubleValue = redact == RedactType.FULL ? 0.0 : ((Double) fld.getValue()).doubleValue();
+ os.writeDouble(doubleValue);
+ break;
+ case IObject.Type.OBJECT:
+ if (addType)
+ os.writeByte(IObject.Type.OBJECT);
+ ObjectReference value = (ObjectReference) fld.getValue();
+ if (value != null)
+ writeID(os, value.getObjectAddress());
+ else
+ writeID(os, 0);
+ break;
+ default:
+ // Error
+ break;
+ }
+ }
+
+ private void dumpThreadRoots(DataOutput os)
+ throws IOException, SnapshotException
+ {
+ for (IClass cls : snapshot.getClasses())
+ {
+ dumpThreadRoots(os, cls);
+ }
+ }
+
+ /**
+ * Dump objects from start to end (exclusive)
+ * @param os
+ * @param os2
+ * @param start Start object (inclusive)
+ * @param end End object (exclusive)
+ * @param listener
+ * @return next start position, or -1 for no more objects to do
+ * @throws IOException
+ * @throws SnapshotException
+ */
+ private int dumpObjects(DataOutputStream3 os, DataOutputStream3 os2, int start, int end, IProgressListener listener)
+ throws IOException, SnapshotException
+ {
+ int i = 0;
+ if (objects != null)
+ {
+ for (int objs[] : objects)
+ {
+ i = dumpObjects(os, os2, start, end, i, objs, listener);
+ if (i < 0)
+ return -i;
+ if (listener.isCanceled())
+ throw new OperationCanceledException();
+ }
+ }
+ else
+ {
+ for (IClass cls : snapshot.getClasses())
+ {
+ int objs[] = cls.getObjectIds();
+ i = dumpObjects(os, os2, start, end, i, objs, listener);
+ if (i < 0)
+ return -i;
+ if (listener.isCanceled())
+ throw new OperationCanceledException();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Dump objects from start to end (exclusive)
+ * @param os
+ * @param os2
+ * @param start Start object (inclusive)
+ * @param end End object (exclusive)
+ * @param i current position
+ * @param objs an array of the objects
+ * @param listener
+ * @return next start position, or -1 for no more objects to do
+ * @throws IOException
+ * @throws SnapshotException
+ */
+ private int dumpObjects(DataOutputStream3 os, DataOutputStream3 os2, int start, int end, int i,
+ int[] objs, IProgressListener listener) throws SnapshotException, IOException
+ {
+ int numberOfObjects = objs.length;
+ if (i + numberOfObjects <= start)
+ {
+ // Skipping class where none of the objects will be used
+ i += numberOfObjects;
+ // Try to keep the progress meter moving
+ if (i == start && numberOfObjects == 0 )
+ listener.worked(WORK_OBJECT);
+ }
+ else
+ {
+ int j = 0;
+ for (int o : objs)
+ {
+ if (i < start)
+ {
+ // skipping some initial objects
+ ++i;
+ }
+ 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))
+ {
+ // Success, enough room
+ ++totalObjects;
+ ++i;
+ ++j;
+ progress(numberOfObjects, j, listener);
+
+ if (i >= end && end >= 0 || os.size() > MAX_SEGMENT)
+ {
+ // Give up here if we have dumped all we should
+ // or we have overflowed
+ // Negative indicates return from caller too
+ return -i;
+ }
+ }
+ else
+ {
+ // No room for this object, so return and say so
+ // Negative indicates return from caller too
+ return -i;
+ }
+ }
+ }
+ // Try to keep the progress meter moving
+ if (numberOfObjects == 0)
+ listener.worked(WORK_OBJECT);
+ }
+ return i;
+ }
+
+ private void progress(int numberOfObjects, int j, IProgressListener listener)
+ {
+ // 1 : 1,1,1
+ // 2 : 1,2,2
+ // 3 : 1,2,3
+ // 4 : 2,3,4
+ // 5 : 2,4,5
+ // More rapid progress indicator
+ for (int k = 1; k <= WORK_OBJECT; ++k)
+ {
+ if (j == (k * numberOfObjects + WORK_OBJECT - 1) / WORK_OBJECT)
+ listener.worked(1);
+ }
+ }
+
+ /**
+ * Find the stack frame in which an object is referenced.
+ * Remove it from the list.
+ * @param id
+ * @param objid
+ * @return
+ */
+ public int findID(int id, int objid[][])
+ {
+ for (int i = 0; i < objid.length; ++i)
+ {
+ for (int j = 0; j < objid[i].length; ++j)
+ {
+ if (objid[i][j] == id)
+ {
+ // Remove from the list so the same object will be found
+ // in other frames
+ objid[i][j] = -1;
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Process local roots
+ * @param os
+ * @param g
+ * @param objs
+ * @throws IOException
+ */
+ private void processRoot(DataOutput os, GCRootInfo g, int objs[][]) throws IOException
+ {
+ if (!includeObject(g.getObjectId()))
+ return;
+ switch (g.getType())
+ {
+ case GCRootInfo.Type.NATIVE_LOCAL:
+ Integer serial = threadToSerial.get(g.getContextId());
+ if (serial != null)
+ {
+ os.writeByte(Constants.DumpSegment.ROOT_JNI_LOCAL);
+ writeID(os, g.getObjectAddress());
+ os.writeInt(serial);
+ os.writeInt(findID(g.getObjectId(), objs)); // stack frame
+ // number
+ }
+ break;
+ case GCRootInfo.Type.JAVA_LOCAL:
+ serial = threadToSerial.get(g.getContextId());
+ if (serial != null)
+ {
+ os.writeByte(Constants.DumpSegment.ROOT_JAVA_FRAME);
+ writeID(os, g.getObjectAddress());
+ os.writeInt(serial);
+ os.writeInt(findID(g.getObjectId(), objs)); // stack frame
+ // number
+ }
+ break;
+ case GCRootInfo.Type.NATIVE_STACK:
+ serial = threadToSerial.get(g.getContextId());
+ if (serial != null)
+ {
+ os.writeByte(Constants.DumpSegment.ROOT_NATIVE_STACK);
+ // System.out.println("Thread found "+g.getContextId()+"
+ // "+serial+" "+g.getContextAddress());
+ writeID(os, g.getObjectAddress());
+ os.writeInt(serial);
+ }
+ break;
+ case GCRootInfo.Type.THREAD_BLOCK:
+ serial = threadToSerial.get(g.getContextId());
+ if (serial != null)
+ {
+ os.writeByte(Constants.DumpSegment.ROOT_THREAD_BLOCK);
+ writeID(os, g.getObjectAddress());
+ os.writeInt(serial);
+ }
+ break;
+ case GCRootInfo.Type.JAVA_STACK_FRAME:
+ serial = threadToSerial.get(g.getContextId());
+ if (serial != null)
+ {
+ os.writeByte(Constants.DumpSegment.ROOT_JAVA_FRAME);
+ writeID(os, g.getObjectAddress());
+ os.writeInt(serial);
+ // Probably won't have a frame number??
+ os.writeInt(findID(g.getObjectId(), objs)); // stack frame
+ // number
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void dumpThreadRoots(DataOutput os, IClass cls) throws SnapshotException, IOException
+ {
+ for (int oid : cls.getObjectIds())
+ {
+ if (!includeObject(cls.getObjectId()))
+ {
+ // Skip the thread
+ continue;
+ }
+ GCRootInfo gp[] = snapshot.getGCRootInfo(oid);
+ if (gp != null)
+ {
+ for (GCRootInfo g : gp)
+ {
+ // System.out.println("Root
+ // "+GCRootInfo.getTypeAsString(g.getType())+"
+ // 0x"+Long.toHexString(g.getObjectAddress())+"
+ // 0x"+Long.toHexString(g.getContextAddress()));
+ switch (g.getType())
+ {
+ case GCRootInfo.Type.THREAD_OBJ:
+ IObject io = snapshot.getObject(oid);
+ IThreadStack its = snapshot.getThreadStack(oid);
+ int objs[][];
+ if (its != null)
+ {
+ IStackFrame fms[] = its.getStackFrames();
+ objs = new int[fms.length][];
+ for (int i = 0; i < fms.length; ++i)
+ {
+ objs[i] = fms[i].getLocalObjectsIds().clone();
+ }
+ } else {
+ objs = new int[0][0];
+ }
+ for (NamedReference nr : io.getOutboundReferences())
+ {
+ if (nr instanceof ThreadToLocalReference)
+ {
+ ThreadToLocalReference tlr = (ThreadToLocalReference) nr;
+ for (GCRootInfo g2 : tlr.getGcRootInfo())
+ {
+ processRoot(os, g2, objs);
+ }
+ }
+ }
+ break;
+ default:
+ processRoot(os, g, new int[0][0]);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Output all objects of a particular type.
+ * @param os the main out stream
+ * @param os2 a temporary length measuring stream
+ * @param cls the type of the object to dump
+ * @param io the object to dump
+ * @param check whether to check for segment overflow
+ * @throws IOException
+ * @throws SnapshotException
+ */
+ private boolean dumpObject(DataOutputStream3 os, DataOutputStream3 os2, IClass cls, IObject io, boolean check)
+ throws IOException, SnapshotException
+ {
+ if (io instanceof IInstance)
+ {
+ IInstance ii = (IInstance) io;
+ if (ii.getObjectAddress() == 0)
+ {
+ // skip Bootstrap class loader as it has no fields
+ if (classloaders.contains(io.getObjectId()))
+ ++totalClassloaders;
+ return true;
+ }
+ long mark1 = os2.size();
+ dumpInstance(os2, cls, ii);
+ long mark2 = os2.size();
+ long size = mark2 - mark1;
+
+ if (check && os.size() + 1L + idsize + 4 + idsize + 4 + size > MAX_SEGMENT)
+ {
+ // Overflow
+ return false;
+ }
+
+ os.writeByte(Constants.DumpSegment.INSTANCE_DUMP);
+ writeID(os, ii.getObjectAddress());
+ os.writeInt(0); // Stack trace
+ writeID(os, cls.getObjectAddress());
+ os.writeInt((int)size);
+ dumpInstance(os, cls, ii);
+ if (classloaders.contains(io.getObjectId()))
+ ++totalClassloaders;
+ totalBytes += cls.getHeapSizePerInstance();
+ }
+ else if (io instanceof IPrimitiveArray)
+ {
+ return dumpPrimitiveArray(os, io, check);
+ }
+ else if (io instanceof IObjectArray)
+ {
+ return dumpObjectArray(os, io, check);
+ } else {
+ // Classes are IObject but not necessarily IInstance
+ }
+ return true;
+ }
+
+ private boolean dumpObjectArray(DataOutputStream3 os, IObject io, boolean check) throws IOException
+ {
+ IObjectArray ii = (IObjectArray) io;
+
+ if (check && os.size() + 1L + idsize + 4 + 4 + ii.getLength() * idsize > MAX_SEGMENT)
+ {
+ // This object would overflow
+ return false;
+ }
+
+ os.writeByte(Constants.DumpSegment.OBJECT_ARRAY_DUMP);
+ writeID(os, ii.getObjectAddress());
+ os.writeInt(0); // Stack trace
+ os.writeInt(ii.getLength());
+ writeID(os, ii.getClazz().getObjectAddress());
+ long l[] = ii.getReferenceArray();
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ writeID(os, l[i]);
+ }
+ totalBytes += io.getUsedHeapSize();
+ return true;
+ }
+
+ private boolean dumpPrimitiveArray(DataOutputStream3 os, IObject io, 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;
+ }
+
+ os.writeByte(Constants.DumpSegment.PRIMITIVE_ARRAY_DUMP);
+ writeID(os, ii.getObjectAddress());
+ os.writeInt(0); // Stack trace
+ os.writeInt(ii.getLength());
+ os.writeByte(ii.getType());
+ // For safety, don't even read value for full redaction
+ Object a = redact == RedactType.FULL ? null : ii.getValueArray();
+ if (ii.getType() == IObject.Type.BOOLEAN)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ int booleanValue = redact == RedactType.FULL ? 0 : ((boolean[]) a)[i] ? 1 : 0;
+ os.writeByte(booleanValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.BYTE)
+ {
+ if (redact == RedactType.NAMES)
+ {
+ String s = new String((byte[])a, UTF8);
+ String newstr = remap.mapClass(s);
+ if (newstr == null)
+ {
+ newstr = remap.mapField(s);
+ }
+ if (newstr == null)
+ {
+ newstr = remap.mapSignature(s);
+ if (newstr != null)
+ System.out.println("Found "+s+" "+newstr);
+ }
+ if (newstr != null)
+ {
+ byte b[] = newstr.getBytes(UTF8);
+ if (b.length == ii.getLength())
+ {
+ // Mapped with exact length
+ a = b;
+ }
+ else
+ {
+ // Length problem, shouldn't happen
+ byte b2[] = new byte[ii.getLength()];
+ System.arraycopy(b, 0, b2, 0, Math.min(b.length, ii.getLength()));
+ a = b2;
+ }
+ }
+ }
+ else if (redact != RedactType.NONE)
+ {
+ a = new byte[ii.getLength()];
+ }
+ os.write((byte[]) a);
+ }
+ else if (ii.getType() == IObject.Type.SHORT)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ short shortValue = redact == RedactType.FULL ? 0 : ((short[]) a)[i];
+ os.writeShort(shortValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.CHAR)
+ {
+ if (redact == RedactType.NAMES)
+ {
+ String s = new String((char[])a);
+ String newstr = remap.mapClass(s);
+ if (newstr == null)
+ {
+ newstr = remap.mapField(s);
+ }
+ if (newstr == null)
+ {
+ newstr = remap.mapSignature(s);
+ }
+ if (newstr != null)
+ {
+ char b[] = newstr.toCharArray();
+ if (b.length == ii.getLength())
+ {
+ // Mapped with exact length
+ a = b;
+ }
+ else
+ {
+ // Length problem, shouldn't happen
+ char b2[] = new char[ii.getLength()];
+ System.arraycopy(b, 0, b2, 0, Math.min(b.length, ii.getLength()));
+ a = b2;
+ }
+ }
+ }
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ char shortValue = redact != RedactType.NONE && redact != RedactType.NAMES ? 0 : ((char[]) a)[i];
+ os.writeChar(shortValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.INT)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ int intValue = redact != RedactType.NONE && redact != RedactType.NAMES ? 0 : ((int[]) a)[i];
+ os.writeInt(intValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.LONG)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ long longValue = redact == RedactType.FULL ? 0L : ((long[]) a)[i];
+ os.writeLong(longValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.FLOAT)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ float floatValue = redact == RedactType.FULL ? 0.0f : ((float[]) a)[i];
+ os.writeFloat(floatValue);
+ }
+ }
+ else if (ii.getType() == IObject.Type.DOUBLE)
+ {
+ for (int i = 0; i < ii.getLength(); ++i)
+ {
+ double doubleValue = redact == RedactType.FULL ? 0.0 : ((double[]) a)[i];
+ os.writeDouble(doubleValue);
+ }
+ }
+ totalBytes += io.getUsedHeapSize();
+ return true;
+ }
+
+ /**
+ * Output a single plain object.
+ * @param os
+ * @param cls
+ * @param ii
+ * @throws IOException
+ */
+ private void dumpInstance(DataOutputStream3 os, IClass cls, IInstance ii) throws IOException
+ {
+ List<Field> allf = new ArrayList<Field>(ii.getFields());
+ for (IClass cls1 = cls; cls1 != null; cls1 = cls1.getSuperClass())
+ {
+ // Index by each descriptor
+ for (FieldDescriptor f : cls1.getFieldDescriptors())
+ {
+ boolean found = false;
+ // Find each field matching the descriptor
+ for (ListIterator<Field> it = allf.listIterator(); it.hasNext();)
+ {
+ Field fld = it.next();
+ if (f.getName().equals(fld.getName()))
+ {
+ it.remove(); // In case fields are defined in superclass
+ // too
+ found = true;
+ writeField(os, fld, false);
+ break;
+ }
+ }
+ if (!found)
+ throw new IllegalStateException("missing field value " + f); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Write an ID of an object as an appropriate size.
+ * @param os
+ * @param addr
+ * @throws IOException
+ */
+ private void writeID(DataOutput os, long addr) throws IOException
+ {
+ if (idsize == 4)
+ {
+ os.writeInt((int)addr);
+ }
+ else
+ {
+ os.writeLong(addr);
+ }
+ }
+
+ /**
+ * Write a string to the output stream as an ID.
+ * Add ID to the map if new, otherwise use the
+ * existing ID.
+ * Something else will have to output the string definition.
+ * @param os
+ * @param s
+ * @throws IOException
+ */
+ private void writeString(DataOutput os, String s) throws IOException
+ {
+ long id;
+ if (stringToID.containsKey(s))
+ {
+ id = stringToID.get(s);
+ }
+ else
+ {
+ id = nextStringID++;
+ stringToID.put(s, (int) id);
+ }
+ writeID(os, id);
+ }
+
+ /**
+ * Remaps class names.
+ * Separate class to isolate the generation of names from the actual
+ * contents of the snapshot.
+ *
+ */
+ public static class Remap {
+
+ Pattern skipPattern, avoidPattern;
+ boolean undo;
+ boolean matchFields;
+
+ int remapFail = 0;
+
+ /**
+ * Random number generator.
+ * Doesn't need to be particular secure as it just generates replacement names.
+ * The replacement name can't be remapped to the original name from the random number generator.
+ */
+ private Random rnd = new Random();
+
+ /**
+ * Create a remapper
+ * @param skipPattern Remap names unless they match this
+ * @param avoidPattern Avoid remapping to names which match this
+ * @param matchFields TODO
+ * @param undo Just use existing remappings
+ */
+ public Remap(Pattern skipPattern, Pattern avoidPattern, boolean matchFields, boolean undo)
+ {
+ this.skipPattern = skipPattern;
+ this.avoidPattern = avoidPattern;
+ this.matchFields = matchFields;
+ this.undo = undo;
+ }
+
+ /*
+ * The following section is for remapping class names to hide potential sensitive names.
+ */
+
+ /**
+ * Load the existing mapping table from a mapping properties file.
+ * Properties file format:
+ * original.package.Classname=new.package.Classname
+ * @param mapFile the Java format properties file
+ * @param undo whether to reverse the mappings contained in the file
+ * @throws IOException
+ */
+ public void loadMapping(File mapFile, boolean undo) throws IOException
+ {
+ if (mapFile != null && mapFile.canRead())
+ {
+ Properties p = new Properties();
+ FileReader rdr = new FileReader(mapFile);
+ try {
+ p.load(rdr);
+ }
+ finally
+ {
+ rdr.close();
+ }
+ for (Map.Entry<Object,Object> e : p.entrySet())
+ {
+ String key = e.getKey().toString();
+ String value = e.getValue().toString();
+ if (undo)
+ {
+ // Reverse the mapping
+ obfuscated.put(value, key);
+ used.add(key);
+ String fieldOld = fieldName(key);
+ String fieldNew = fieldName(value);
+ usedField.put(fieldNew, fieldOld);
+ }
+ else
+ {
+ // Mapping file shows old to new
+ obfuscated.put(key, value);
+ used.add(value);
+ String fieldOld = fieldName(key);
+ String fieldNew = fieldName(value);
+ usedField.put(fieldOld, fieldNew);
+ }
+ }
+ }
+ }
+
+ public void saveMapping(File mapFile, boolean undo, String comments) throws IOException
+ {
+ // Only save if we are not doing a reverse mapping, and it is okay to write to the file
+ if (!undo && mapFile != null && (mapFile.canWrite() || !mapFile.exists()))
+ {
+ // Sorted keys properties file
+ Properties p = new Properties()
+ {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Enumeration<Object> keys()
+ {
+ List<Object> list = Collections.list(super.keys());
+ Collections.sort(list, new Comparator<Object>()
+ {
+
+ public int compare(Object o1, Object o2)
+ {
+ return o1.toString().compareTo(o2.toString());
+ }
+
+ });
+ // Sorted, not sure about locking, but okay for this
+ // purpose
+ return Collections.enumeration(list);
+ }
+ };
+ for (Map.Entry<String,String> e : obfuscated.entrySet())
+ {
+ p.setProperty(e.getKey(), e.getValue());
+ }
+ FileWriter wrt = new FileWriter(mapFile);
+ try {
+ p.store(wrt, comments);
+ }
+ finally
+ {
+ wrt.close();
+ }
+ }
+ }
+
+ /**
+ * Is the class name one which should have a new name invented?
+ * @param cn
+ * @return
+ */
+ public boolean isRemapped(String cn)
+ {
+ return !undo && (skipPattern == null || !skipPattern.matcher(cn).matches());
+ }
+
+ /**
+ * Return the renamed version of a class
+ * @param cn
+ * @return null if not renamed
+ */
+ public String mapClass(String cn)
+ {
+ return obfuscated.get(cn);
+ }
+
+ /**
+ * Return the renamed version of a simple field/method
+ * @param cn
+ * @return null if not renamed
+ */
+ public String mapField(String cn)
+ {
+ return usedField.get(cn);
+ }
+
+ /**
+ * Return the renamed version of a method/type signature
+ * @param cn
+ * @return null if not renamed
+ */
+ public String mapSignature(String sig)
+ {
+ // Quick test for signature with class name
+ if (!sig.matches("\\p{Print}*L\\p{Print}+;\\p{Print}*")) //$NON-NLS-1$
+ return null;
+ // Split up around possible class names
+ String words[] = sig.split("[;<\\(\\)\\[\\]+*]+"); //$NON-NLS-1$
+ String wordsReplace[] = new String[words.length];
+ int found = 0;
+ for (int i = 0; i < words.length; ++i)
+ {
+ String w = words[i];
+ // Remove any simple types in front of the name
+ String w2 = w.replaceFirst("[VZBCIJFD]*", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ // True encoded class?
+ if (!w2.startsWith("L")) //$NON-NLS-1$
+ {
+ wordsReplace[i] = w;
+ continue;
+ }
+ w2 = w2.substring(1);
+ if (!obfuscated.containsKey(w2))
+ {
+ String w3 = w2.replace('/', '.');
+ if (!obfuscated.containsKey(w3))
+ {
+ wordsReplace[i] = w;
+ }
+ else
+ {
+ words[i] = "L" + w2; //$NON-NLS-1$
+ wordsReplace[i] = "L" + obfuscated.get(w3).replace('.', '/'); //$NON-NLS-1$
+ ++found;
+ }
+ }
+ else
+ {
+ words[i] = "L" + w2; //$NON-NLS-1$
+ wordsReplace[i] = "L" + obfuscated.get(w2); //$NON-NLS-1$
+ ++found;
+ }
+ }
+ if (found == 0)
+ {
+ // No changes
+ return null;
+ }
+ int j = 0;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < words.length; ++i)
+ {
+ int k = sig.indexOf(words[i], j);
+ if (k < 0)
+ return null;
+ sb.append(sig.substring(j, k));
+ sb.append(wordsReplace[i]);
+ j = k + words[i].length();
+ }
+ sb.append(sig.substring(j));
+ return sb.toString();
+ }
+
+ /**
+ * Whether to avoid generating this particular new name.
+ * @param cn
+ * @return
+ */
+ private boolean isAvoid(String cn)
+ {
+ return avoidPattern != null && avoidPattern.matcher(cn).matches();
+ }
+
+ /**
+ * java.lang.String: java.=rurl. java.lang.rurl.morl.
+ * java.lang.String=rurl.morl.Glaeck java.lang.Integer=rurl.morl.Wrirurl
+ * java.lang.String.split(Ljava.lang.String;)Ljava.lang.String;
+ */
+ private Map<String, String> obfuscated = new HashMap<String, String>();
+
+ /** Holds whether the new name has been used, to prevent conflicts */
+ private Set<String> used = new HashSet<String>();
+
+ /** Holds whether the field name has been mapped, so should be zeroed elsewhere */
+ private Map<String, String> usedField = new HashMap<String,String>();
+
+ /**
+ * Rename a file name based on a class name.
+ * @param classname
+ * @param filename
+ * @return
+ */
+ public String renameFileName(String classname, String filename)
+ {
+ String clsnw = renameClassName(classname);
+ String old = baseName(classname);
+ String nw = baseName(clsnw);
+ return filename.replaceFirst(old, nw);
+ }
+
+ /**
+ * Extract the last part of a class name.
+ * Up to a $ for an inner class,
+ * up to dot for an ordinary class
+ * @param classname
+ * @return
+ */
+ private String baseName(String classname)
+ {
+ int dot = classname.lastIndexOf('.');
+ int dol = classname.indexOf('$', dot);
+ if (dol < 0)
+ dol = classname.length();
+ String fn = classname.substring(dot + 1, dol);
+ return fn;
+ }
+
+ /**
+ * Rename a method signature.
+ * Extract the class names from the Lpack1.class1;II)VLpack2.class2;
+ * @param signature
+ * @return renamed signature
+ */
+ public String renameSignature(String signature)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < signature.length(); ++i)
+ {
+ char ch = signature.charAt(i);
+ sb.append(ch);
+ if (ch == 'L')
+ {
+ int semi = sb.indexOf(";", i); //$NON-NLS-1$
+ if (semi >= 0)
+ {
+ String cn = signature.substring(i + 1, semi);
+ String newcn = renameClassName(cn);
+ sb.append(newcn);
+ sb.append(";"); //$NON-NLS-1$
+ i = semi;
+ }
+ else
+ {
+ sb.append(signature.substring(i + 1));
+ break;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ // Avoid using = or : or # or ! as have to be escaped in properties files
+ private static final String methodSep = "@"; //$NON-NLS-1$
+
+ /**
+ * Generate a new method name.
+ * Remember it as package.class^method
+ *
+ * - ? Some unusual Javac generated methods:
+ * HistogramQuery$Grouping.values()
+ * HistogramQuery.$SWITCH_TABLE$org$eclipse$mat$inspections$HistogramQuery$Grouping()
+ *
+ * @param className
+ * @param method
+ * @param upper static field in upper case, else all lower case.
+ * @return
+ */
+ public String renameMethodName(String className, String method, boolean upper)
+ {
+ String mn = className + methodSep + method;
+ if (obfuscated.containsKey(mn))
+ {
+ String newmn = obfuscated.get(mn);
+ return newmn.substring(newmn.indexOf(methodSep) + 1);
+ }
+ if (!isRemapped(className))
+ return method;
+ String newcls = renameClassName(className);
+ String newmn = remap(mn, newcls + methodSep, method, "", true, upper); //$NON-NLS-1$
+ return newmn.substring(newmn.indexOf(methodSep) + 1);
+ }
+
+ private String fieldName(String classField)
+ {
+ String fns[] = classField.split(Pattern.quote(methodSep), 2);
+ if (fns.length >= 2)
+ {
+ return fns[1];
+ }
+ else
+ {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Renames a class.
+ * Break into component parts, reusing existing mapping for package if
+ * already used.
+ * Removes array suffixes and uses base class name.
+ * Translates inner classes with '$' piece by piece, reusing existing
+ * mapping of outer class.
+ *
+ * @param classname
+ * @return
+ */
+
+ public String renameClassName(String classname)
+ {
+ if (obfuscated.containsKey(classname))
+ return obfuscated.get(classname);
+ // Remap arrays preserving base class
+ if (classname.endsWith("[]")) //$NON-NLS-1$
+ {
+ String baseclassname = classname.replace("[]", ""); //$NON-NLS-1$//$NON-NLS-2$
+ return renameClassName(baseclassname) + classname.substring(baseclassname.length());
+ }
+ else if (classname.startsWith("[")) //$NON-NLS-1$
+ {
+ String baseclassname = classname.replace("[", ""); //$NON-NLS-1$//$NON-NLS-2$
+ return classname.substring(0, classname.length() - baseclassname.length()) + renameClassName(baseclassname);
+ }
+ // E.g. If only com.sun. is renamed, don't rename com.
+ if (!isRemapped(classname))
+ return classname;
+ String pack;
+ String newpack;
+ String cn;
+ String last = ""; //$NON-NLS-1$
+ if (classname.endsWith(".")) //$NON-NLS-1$
+ {
+ // Package name
+ last = "."; //$NON-NLS-1$
+ int i = classname.lastIndexOf('.', classname.length() - 2);
+ if (i >= 0)
+ {
+ pack = classname.substring(0, i + 1);
+ newpack = renameClassName(pack);
+ cn = classname.substring(i + 1, classname.length() - 1);
+ }
+ else
+ {
+ pack = ""; //$NON-NLS-1$
+ newpack = ""; //$NON-NLS-1$
+ cn = classname.substring(0, classname.length() - 1);
+ }
+ }
+ else
+ {
+ // Ordinary class name
+ int i = classname.lastIndexOf('.', classname.length() - 2);
+ int j = classname.lastIndexOf('$', classname.length() - 2);
+ if (j > i)
+ {
+ // Without $
+ pack = classname.substring(0, j);
+ newpack = renameClassName(pack) + "$"; //$NON-NLS-1$
+ cn = classname.substring(j + 1);
+ }
+ else if (i >= 0)
+ {
+ // With the dot
+ pack = classname.substring(0, i + 1);
+ newpack = renameClassName(pack);
+ cn = classname.substring(i + 1);
+ }
+ else
+ {
+ pack = ""; //$NON-NLS-1$
+ newpack = ""; //$NON-NLS-1$
+ cn = classname;
+ }
+ }
+
+ return remap(classname, newpack, cn, last, false, false);
+ }
+
+
+ /**
+ * Map a class name or package name to a new name
+ * @param classname the new fully qualified class name, include "." for just a package name
+ * @param newpack The new package name (already replaced), excluding cn
+ * @param cn The class name or last package component
+ * @param last The suffix to add onto the name ("." for package, "$" for inner class)
+ * @param field Is this a field name (do not title case)
+ * @param upper If a field name, then upper case
+ * @return
+ */
+ private String remap(String classname, String newpack, String cn, String last, boolean field, boolean upper)
+ {
+ /*
+ * 0 => 0-9 unchanged
+ * 0 + fields => field map
+ * 1-49 => word
+ * 50-99 => random lower case string
+ * 100-149 => random mixed case string
+ * 150-199 => random mixed case string, don't use avoid pattern
+ */
+ for (int k = 0; k < 200; ++k)
+ {
+ String np;
+ int ln = cn.length();
+ // Try not mapping numeric
+ if (k == 0 && cn.matches("[0-9]+")) //$NON-NLS-1$
+ {
+ np = cn;
+ }
+ else if (k == 0 && matchFields && field && usedField.containsKey(cn))
+ {
+ // Match field values across classes, keep the same names
+ np = usedField.get(cn);
+ }
+ else if (k < 50)
+ {
+ np = randomWordsLen(ln, field && upper);
+ }
+ else if (k < 100)
+ np = randomString(ln);
+ else
+ np = randomString2(ln);
+
+ if (field)
+ {
+ // otherwise lower/mixed
+ if (upper)
+ {
+ // static field in upper case
+ np = np.toUpperCase(Locale.ENGLISH);
+ }
+ }
+ else if (!last.equals(".")) //$NON-NLS-1$
+ {
+ // Class name in title case
+ np = titleCase(np);
+ }
+ else
+ {
+ // package in lower case
+ np = np.toLowerCase(Locale.ENGLISH);
+ }
+ String newcn = newpack + np + last;
+ // Check if already used, or could clash with the unmapped names
+ // Also avoid unwanted strings, unless we get desperate!
+ if (!used.contains(newcn) && isRemapped(newcn) && (k >= 150 || !isAvoid(newcn)))
+ {
+ // found new mapping
+ obfuscated.put(classname, newcn);
+ used.add(newcn);
+ if (field)
+ usedField.put(cn, np);
+ return newcn;
+ }
+ }
+ // Failed to find suitable mapping, record to avoid slow lookup later
+ // found new mapping
+ String newcn = newpack + cn + last;
+ obfuscated.put(classname, newcn);
+ used.add(newcn);
+ if (field)
+ usedField.put(cn, cn);
+ ++remapFail;
+ return newcn;
+ }
+
+ /**
+ * Title case a string.
+ * @param np lower/mixed case version
+ * @return Upper case first letter
+ */
+ private String titleCase(String np)
+ {
+ if (np.length() < 1)
+ return np;
+ np = Character.toTitleCase(np.charAt(0)) + np.substring(1);
+ return np;
+ }
+
+ /**
+ * Generate a random word of given length
+ * @param length the length in chars
+ * @param unders whether to separate words with underscore
+ * @return
+ */
+ private String randomWordsLen(int length, boolean unders)
+ {
+ final int minlen = 5;
+ // Capitals are hard to read, so shorten the words between underscores
+ final int maxlen = unders ? 11: 15;
+ if (length > maxlen)
+ {
+ // Split the word
+ int sp = minlen + rnd.nextInt(Math.min(maxlen, length - minlen - (unders ? 1 : 0)) + 1 - minlen);
+ String w = randomWordLen(sp);
+ if (unders)
+ return w = w + "_" + randomWordsLen(length - sp - 1, unders); //$NON-NLS-1$
+ else
+ return w + titleCase(randomWordsLen(length - sp, unders));
+ }
+ return randomWordLen(length);
+ }
+
+ /**
+ * Generate a random word
+ * @param length
+ * @return
+ */
+ private String randomWordLen(int length)
+ {
+ if (length < 3)
+ return randomString(length);
+ // Minimum number of parts
+ int p1 = (length - 2) / 4;
+ // Maximum number of parts
+ int p2 = (length - 1) / 2;
+ String ret;
+ do
+ {
+ // Decide whether to start with a vowel
+ boolean vowel = rnd.nextFloat() < 0.15f;
+ do
+ {
+ // Choose the number of parts
+ int pt = p1 + rnd.nextInt(p2 - p1 + 1);
+ ret = randomWord(pt);
+ }
+ while (ret.length() != (vowel ? length - 1 : length));
+ ret = addVowel(ret, vowel);
+ }
+ while (tryAgain(ret));
+ return ret;
+ }
+
+ /**
+ * Check whether the word is unsuitable.
+ * @param tocheck
+ * @return
+ */
+ private boolean tryAgain(String tocheck)
+ {
+ StringBuilder sb = new StringBuilder(tocheck);
+ String rev = sb.reverse().toString().toLowerCase(Locale.ENGLISH);
+ for (String ts : names4)
+ {
+ if (rev.indexOf(ts) >= 0)
+ return true;
+ }
+ return false;
+ }
+
+ /** Starting vowels */
+ @SuppressWarnings("nls")
+ private static final String names0[] = { "a", "e", "i", "o", "u" };
+
+ /** Starting / middle consonants */
+ @SuppressWarnings("nls")
+ private static final String names1[] = { "b", "bl", "br", "c", "cl", "cr", "d", "dr", "f", "fl", "fr", "g", "gl", "gr", "h", //$NON-NLS-1$
+ "j", "k", "kl", "kn", "kr", "kw", "l", "m", "n", "p", "pl", "pr", "qu", "r", "s", "sh", "st", "t", "th", //$NON-NLS-1$
+ "tr", "v", "w", "wr", "x", "y", "z" }; //$NON-NLS-1$
+
+ /** middle vowels */
+ @SuppressWarnings("nls")
+ private static final String names2[] = { "a", "ae", "ai", "e", "ea", "ee", "ei", "eo", "eu", "i", "ia", "ie", "io", "o", "oa", //$NON-NLS-1$
+ "oe", "oo", "ou", "u", "ua" }; //$NON-NLS-1$
+
+ /** endings */
+ @SuppressWarnings("nls")
+ private static final String names3[] = { "b", "ch", "ck", "d", "de", "ff", "g", "gh", "k", "l", "le", "ly", "m", "n", "nd", "ne", "ng", "nk", //$NON-NLS-1$
+ "p", "r", "rb", "rd", "re", "rf", "rk", "rl", "rm", "rn", "rp", "rt", "ry", "s", "sh", "st", "sy", //$NON-NLS-1$
+ "t", "te", "th", "ts", "ve", "w", "y", "z" }; //$NON-NLS-1$
+
+ /** excludes */
+ @SuppressWarnings("nls")
+ private static final String names4[] = {"tihs", "ssip", "kcuf", "tnuc", "kcoc", "stit", "knaw", "ggin"}; //$NON-NLS-1$
+
+ /**
+ * Perhaps add a vowel prefix to a string
+ * @param base
+ * @param add whether to add
+ * @return
+ */
+ private String addVowel(String base, boolean add)
+ {
+ if (add)
+ {
+ base = names0[rnd.nextInt(names0.length)] + base;
+ }
+ return base;
+ }
+
+ /**
+ * length varies from 2*parts+1 to 4*part+2
+ *
+ * @param parts
+ * @return random word
+ */
+ private String randomWord(int parts)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < parts; ++j)
+ {
+ sb.append(names1[rnd.nextInt(names1.length)]);
+ sb.append(names2[rnd.nextInt(names2.length)]);
+ }
+ sb.append(names3[rnd.nextInt(names3.length)]);
+ return sb.toString();
+ }
+
+ /**
+ * Random lower case string
+ *
+ * @param length
+ * @return random lower case string
+ */
+ private String randomString(int length)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; ++i)
+ {
+ sb.append((char) ('a' + rnd.nextInt(26)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Random mixed case string
+ *
+ * @param length
+ * @return random upper and lower case string
+ */
+ private String randomString2(int length)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; ++i)
+ {
+ sb.append((char) ('a' + rnd.nextInt(26) + (rnd.nextBoolean() ? 'A' - 'a' : 0)));
+ }
+ return sb.toString();
+ }
+
+ }
+}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofIndexBuilder.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofIndexBuilder.java
index a894fa08..dc9bee57 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofIndexBuilder.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/HprofIndexBuilder.java
@@ -67,7 +67,7 @@ public class HprofIndexBuilder implements IIndexBuilder
mon.beginTask(MessageUtil.format(Messages.HprofIndexBuilder_Scanning, new Object[] { file.getAbsolutePath() }),
(int) (file.length() / 1000));
Pass1Parser pass1 = new Pass1Parser(handler, mon, strictnessPreference);
- Serializable id = preliminary.getSnapshotInfo().getProperty("$runtimeId");
+ Serializable id = preliminary.getSnapshotInfo().getProperty("$runtimeId"); //$NON-NLS-1$
String dumpNrToRead;
if (id instanceof String)
{
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 db303949..f28b9749 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,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2016 SAP AG, IBM Corporation and others..
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -104,10 +104,7 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
}
// if necessary, create required classes not contained in the heap
- if (!requiredArrayClassIDs.isEmpty() || !requiredPrimitiveArrays.isEmpty() || !requiredClassIDs.isEmpty())
- {
- createRequiredFakeClasses();
- }
+ createRequiredFakeClasses();
// informational messages to the user
monitor.sendUserMessage(IProgressListener.Severity.INFO, MessageUtil.format(
@@ -238,7 +235,21 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
int clsid = 0;
// java.lang.Object for the superclass
List<ClassImpl>jlos = classesByName.get("java.lang.Object"); //$NON-NLS-1$
- long jlo = jlos.isEmpty() ? 0 : jlos.get(0).getObjectAddress();
+ 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); //$NON-NLS-1$
+ if (jlcs == null || jlcs.isEmpty())
+ {
+ while (identifiers.reverse(++nextObjectAddress) >= 0)
+ {}
+ IClass type = new ClassImpl(nextObjectAddress, cls, jlo, 0, new Field[0], new FieldDescriptor[0]);
+ ++clsid;
+ addClass((ClassImpl) type, -1);
+ }
+ }
// create required (fake) classes for arrays
if (!requiredArrayClassIDs.isEmpty())
@@ -256,7 +267,7 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
throw new SnapshotException(msg);
}
- arrayType = new ClassImpl(arrayClassID, "unknown-class-"+clsid+"[]", jlo, 0, new Field[0], //$NON-NLS-1$
+ arrayType = new ClassImpl(arrayClassID, "unknown-class-"+clsid+"[]", jlo, 0, new Field[0], //$NON-NLS-1$ //$NON-NLS-2$
new FieldDescriptor[0]);
++clsid;
addClass((ClassImpl) arrayType, -1);
@@ -310,16 +321,16 @@ public class HprofParserHandlerImpl implements IHprofParserHandler
int i;
for (i = 0; i < size / 4; ++i)
{
- fds[i] = new FieldDescriptor("unknown-field-"+i, IObject.Type.INT);
+ 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);
+ 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);
+ 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$
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 5080f3d9..320d5cc7 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2013 SAP AG and others.
+ * Copyright (c) 2010, 2018 SAP AG and others.
* 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
@@ -23,6 +23,20 @@ public class Messages extends NLS
public static String AbstractParser_Error_UnsupportedHPROFVersion;
public static String EnhancerRegistry_ErrorCreatingParser;
public static String EnhancerRegistry_ErrorCreatingRuntime;
+ public static String ExportHprof_AvoidExample;
+ public static String ExportHprof_ExportTo;
+ public static String ExportHprof_PrepareClasses;
+ public static String ExportHprof_PrepareGCRoots;
+ public static String ExportHprof_PrepareObjects;
+ public static String ExportHprof_PrepareThreadStacks;
+ public static String ExportHprof_DumpClasses;
+ public static String ExportHprof_DumpGCRoots;
+ public static String ExportHprof_DumpObjects;
+ public static String ExportHprof_DumpStrings;
+ public static String ExportHprof_DumpThreadStacks;
+ public static String ExportHprof_RemapProperties;
+ public static String ExportHprof_SegmentSizeMismatch;
+ public static String ExportHprof_SegmentTooLong;
public static String HprofIndexBuilder_ExtractingObjects;
public static String HprofIndexBuilder_Parsing;
public static String HprofIndexBuilder_Scanning;
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 c0fc796e..43ea228e 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2016 SAP AG, IBM Corporation and others.
+ * Copyright (c) 2008, 2018 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 v1.0
* which accompanies this distribution, and is available at
@@ -123,7 +123,7 @@ public class Pass1Parser extends AbstractParser
if (length < 0)
throw new SnapshotException(MessageUtil.format(Messages.Pass1Parser_Error_IllegalRecordLength,
- length, in.position(), record));
+ length, Long.toHexString(in.position() - 4), Integer.toHexString(record), Long.toHexString(curPos)));
if (curPos + length - 9 > fileSize)
{
@@ -132,12 +132,12 @@ public class Pass1Parser extends AbstractParser
case STRICTNESS_STOP:
throw new SnapshotException(Messages.HPROFStrictness_Stopped, new SnapshotException(
MessageUtil.format(Messages.Pass1Parser_Error_invalidHPROFFile, length,
- fileSize - curPos - 9)));
+ fileSize - curPos - 9, Integer.toHexString(record), Long.toHexString(curPos))));
case STRICTNESS_WARNING:
case STRICTNESS_PERMISSIVE:
monitor.sendUserMessage(Severity.WARNING,
MessageUtil.format(Messages.Pass1Parser_Error_invalidHPROFFile, length,
- fileSize - curPos - 9), null);
+ fileSize - curPos - 9, Integer.toHexString(record), Long.toHexString(curPos)), null);
break;
default:
throw new SnapshotException(Messages.HPROFStrictness_Unhandled_Preference);
@@ -149,8 +149,8 @@ public class Pass1Parser extends AbstractParser
case Constants.Record.STRING_IN_UTF8:
if (((int) (length - idSize) < 0))
throw new SnapshotException(MessageUtil.format(
- Messages.Pass1Parser_Error_IllegalRecordLength, length, in.position(),
- record));
+ Messages.Pass1Parser_Error_IllegalRecordLength, length, Long.toHexString(in.position() - 4),
+ Integer.toHexString(record), Long.toHexString(curPos)));
readString(length);
break;
case Constants.Record.LOAD_CLASS:
@@ -206,13 +206,13 @@ public class Pass1Parser extends AbstractParser
case STRICTNESS_STOP:
throw new SnapshotException(Messages.HPROFStrictness_Stopped, new SnapshotException(
MessageUtil.format(Messages.Pass1Parser_UnexpectedRecord,
- Integer.toHexString(record), length)));
+ Integer.toHexString(record), length, Long.toHexString(curPos))));
case STRICTNESS_WARNING:
case STRICTNESS_PERMISSIVE:
monitor.sendUserMessage(
Severity.WARNING,
MessageUtil.format(Messages.Pass1Parser_UnexpectedRecord,
- Integer.toHexString(record), length), null);
+ Integer.toHexString(record), length, Long.toHexString(curPos)), null);
in.skipBytes(length);
break;
default:
@@ -348,7 +348,7 @@ public class Pass1Parser extends AbstractParser
readGC(GCRootInfo.Type.SYSTEM_CLASS, 0);
break;
case Constants.DumpSegment.ROOT_THREAD_BLOCK:
- readGC(GCRootInfo.Type.THREAD_BLOCK, 4);
+ readGCWithThreadContext(GCRootInfo.Type.THREAD_BLOCK, false);
break;
case Constants.DumpSegment.ROOT_MONITOR_USED:
readGC(GCRootInfo.Type.BUSY_MONITOR, 0);
@@ -367,7 +367,7 @@ public class Pass1Parser extends AbstractParser
break;
default:
throw new SnapshotException(MessageUtil.format(Messages.Pass1Parser_Error_InvalidHeapDumpFile,
- segmentType, segmentStartPos));
+ Integer.toHexString(segmentType), Long.toHexString(segmentStartPos)));
}
segmentStartPos = in.position();
@@ -451,8 +451,10 @@ public class Pass1Parser extends AbstractParser
long superClassObjectId = readID();
long classLoaderObjectId = readID();
- // skip signers, protection domain, reserved ids (2), instance size
- in.skipBytes(this.idSize * 4 + 4);
+ // skip signers, protection domain, reserved ids (2)
+ in.skipBytes(this.idSize * 4);
+ // instance size
+ int instsize = in.readInt();
// constant pool: u2 ( u2 u1 value )*
int constantPoolSize = in.readUnsignedShort();
@@ -544,7 +546,7 @@ public class Pass1Parser extends AbstractParser
{
long address = readID();
handler.reportInstance(address, segmentStartPos);
- in.skipBytes(4);
+ in.skipBytes(4); // stack trace serial
long classID = readID();
int payload = in.readInt();
// check if class needs to be created
@@ -570,7 +572,7 @@ public class Pass1Parser extends AbstractParser
handler.reportInstance(address, segmentStartPos);
- in.skipBytes(4);
+ in.skipBytes(4); // stack trace serial
int size = in.readInt();
long arrayClassObjectID = readID();
@@ -612,7 +614,7 @@ public class Pass1Parser extends AbstractParser
return ""; //$NON-NLS-1$
String result = handler.getConstantPool().get(address);
- return result == null ? Messages.Pass1Parser_Error_UnresolvedName + Long.toHexString(address) : result;
+ return result == null ? MessageUtil.format(Messages.Pass1Parser_Error_UnresolvedName, Long.toHexString(address)) : result;
}
private void dumpThreads()
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 8182e479..7c99c651 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2016 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
@@ -168,7 +168,7 @@ public class Pass2Parser extends AbstractParser
break;
default:
throw new SnapshotException(MessageUtil.format(Messages.Pass1Parser_Error_InvalidHeapDumpFile,
- segmentType, segmentStartPos));
+ Integer.toHexString(segmentType), Long.toHexString(segmentStartPos)));
}
segmentStartPos = in.position();
}
@@ -236,7 +236,7 @@ public class Pass2Parser extends AbstractParser
boolean unknown = false;
for (IClass clazz : hierarchy)
{
- if (clazz.getName().startsWith("unknown-class"))
+ if (clazz.getName().startsWith("unknown-class")) //$NON-NLS-1$
{
unknown = true;
}
@@ -244,12 +244,12 @@ public class Pass2Parser extends AbstractParser
if (endPos >= in.position() && unknown && (strictnessPreference == HprofStrictness.STRICTNESS_WARNING || strictnessPreference == HprofStrictness.STRICTNESS_PERMISSIVE))
{
- monitor.sendUserMessage(Severity.WARNING, MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, segmentStartPos, endPos, in.position()), null);
+ 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);
in.skipBytes(endPos - in.position());
}
else
{
- throw new IOException(MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, segmentStartPos, endPos, in.position()));
+ throw new IOException(MessageUtil.format(Messages.Pass2Parser_Error_InsufficientBytesRead, thisClazz.getName(), Long.toHexString(id), Long.toHexString(segmentStartPos), Long.toHexString(endPos), Long.toHexString(in.position())));
}
}
diff --git a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/acquire/JMapHeapDumpProvider.java b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/acquire/JMapHeapDumpProvider.java
index 8afbfd46..d9fb0832 100644
--- a/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/acquire/JMapHeapDumpProvider.java
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/acquire/JMapHeapDumpProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009, 2010 SAP AG.
+ * Copyright (c) 2009, 2018 SAP AG.
* 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 - message fixes
*******************************************************************************/
package org.eclipse.mat.hprof.acquire;
@@ -29,6 +30,7 @@ import org.eclipse.mat.query.annotations.Argument.Advice;
import org.eclipse.mat.snapshot.acquire.IHeapDumpProvider;
import org.eclipse.mat.snapshot.acquire.VmInfo;
import org.eclipse.mat.util.IProgressListener;
+import org.eclipse.mat.util.MessageUtil;
import org.osgi.service.prefs.BackingStoreException;
@HelpUrl("/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html#task_acquiringheapdump__1")
@@ -102,13 +104,12 @@ public class JMapHeapDumpProvider implements IHeapDumpProvider
int exitCode = p.waitFor();
if (exitCode != 0)
{
- throw new SnapshotException(Messages.JMapHeapDumpProvider_ErrorCreatingDump + exitCode + "\r\n" + error.buf.toString()); //$NON-NLS-1$
+ throw new SnapshotException(MessageUtil.format(Messages.JMapHeapDumpProvider_ErrorCreatingDump, exitCode, error.buf.toString()));
}
if (!preferredLocation.exists())
{
- throw new SnapshotException(Messages.JMapHeapDumpProvider_HeapDumpNotCreated + exitCode + "\r\nstdout:\r\n" + output.buf.toString() //$NON-NLS-1$
- + "\r\nstderr:\r\n" + error.buf.toString()); //$NON-NLS-1$
+ throw new SnapshotException(MessageUtil.format(Messages.JMapHeapDumpProvider_HeapDumpNotCreated, exitCode, output.buf.toString(), error.buf.toString()));
}
}
catch (IOException ioe)
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
new file mode 100644
index 00000000..d88bd982
--- /dev/null
+++ b/plugins/org.eclipse.mat.hprof/src/org/eclipse/mat/hprof/annotations.properties
@@ -0,0 +1,37 @@
+###############################################################################
+# Copyright (c) 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
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Andrew Johnson/IBM Corporation - initial API and implementation
+###############################################################################
+ExportHprof.name = Export Snapshot
+ExportHprof.help = Exports the current snapshot as a new HPROF file.\n\
+This query can be used to convert a DTFJ core dump to HPROF format, or to redact sensitive data, or to \
+obfuscate class and method names in a dump. The mapping from the original names to the new obfuscated names \
+is stored in the mapping file. If several related snapshots are to be exported then the same mapping file \
+can be used for all, so that the exported HPROF files use the same obfuscated names for the same class in each file.
+ExportHprof.output.help = The file name of the new HPROF dump.
+ExportHprof.redact.help = Whether to redact certain field and array types.\n\
+NONE exports all field and array values unchanged.\n\
+NAMES obfuscates byte and char array contents to match obfuscated class and field names.\n\
+BASIC zeroes byte and char fields and arrays, and zeroes int arrays.\n\
+FULL zeroes all field and array values.\n\
+It is up to the user to determine whether these options are sufficient to remove all of the sensitive \
+data.
+ExportHprof.skipPattern.help = A regular expression specifying which class names not to obfuscate.\n\
+It is important to at least list 'java.lang.*' to avoid problems when parsing the new \
+HPROF file with Memory Analyzer.
+ExportHprof.avoidPattern.help = A regular expression matching names which should not be generated \
+by obfuscation.
+ExportHprof.mapFile.help = Specify this output file to enable obfuscation of class and field names.\n\
+It is a file in properties file format generated with mappings from old class names to new obfuscated names.\n\
+If present it is also read at the start to use existing mappings from previous exports of HPROF dumps.\n\
+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\
+could be used to remove unreachable objects retained by the 'keep unreachable objects' option.
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 b9ba6152..5b249866 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
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2010, 2016 SAP AG and others.
+# Copyright (c) 2010, 2018 SAP AG and others.
# 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
@@ -9,13 +9,28 @@
# SAP AG - initial API and implementation
# IBM Corporation - multiple snapshots
###############################################################################
-AbstractParser_Error_IllegalType=Illegal Type: {0}
+AbstractParser_Error_IllegalType=Illegal Type: {0} at position {1}
AbstractParser_Error_InvalidHPROFHeader=Invalid HPROF file header.
AbstractParser_Error_NotHeapDump=Not a HPROF heap dump
AbstractParser_Error_UnknownHPROFVersion=Unknown HPROF Version ({0})
AbstractParser_Error_UnsupportedHPROFVersion=Unsupported HPROF Version {0}
EnhancerRegistry_ErrorCreatingParser=Error creating parser for {0}
EnhancerRegistry_ErrorCreatingRuntime=Error creating runtime for {0}
+ExportHprof_ExportTo=Export to {0}
+# Language specific regular expression to create pronounceable words
+ExportHprof_AvoidExample=.*ae.*|.*oe.*|.*uu.*|.*kr.*|.*kw.*|.*[cC]ie.*|.*[^c]ei.*
+ExportHprof_PrepareClasses=Prepare classes
+ExportHprof_PrepareGCRoots=Prepare GC roots
+ExportHprof_PrepareThreadStacks=Prepare thread stacks
+ExportHprof_DumpStrings=Dump strings
+ExportHprof_DumpThreadStacks=Dump thread stacks
+ExportHprof_DumpClasses=Dump classes into heap dump segment {0}
+ExportHprof_DumpGCRoots=Dump GC roots into heap dump segment {0}
+ExportHprof_PrepareObjects=Prepare objects for heap dump segment {0}
+ExportHprof_DumpObjects=Dump objects into heap dump segment {0}
+ExportHprof_RemapProperties=Eclipse Memory Analyzer remap file version 1.0. Dump {0} exported from {1} with the following new names of classes.
+ExportHprof_SegmentSizeMismatch=Heap dump segment {0} expected size {1} actual size {2} at 0x{3}
+ExportHprof_SegmentTooLong=Heap dump segment {1} at 0x{1} is too long: {2}, continuing...
HprofIndexBuilder_ExtractingObjects=Extracting objects from {0}
HprofIndexBuilder_Parsing=Parsing {0}
HprofIndexBuilder_Scanning=Scanning {0}
@@ -28,32 +43,32 @@ HprofRandomAccessParser_Error_DuplicateClass=Duplicate class: {0}
HprofRandomAccessParser_Error_IllegalDumpSegment=Illegal dump segment {0}
HprofRandomAccessParser_Error_MissingClass=missing fake class {0}
HprofRandomAccessParser_Error_MissingFakeClass=missing fake class
-JMapHeapDumpProvider_ErrorCreatingDump=Error creating heap dump. jmap exit code =
-JMapHeapDumpProvider_HeapDumpNotCreated=Heap dump file was not created. jmap exit code =
+JMapHeapDumpProvider_ErrorCreatingDump=Error creating heap dump. jmap exit code {0}\r\n{1}
+JMapHeapDumpProvider_HeapDumpNotCreated=Heap dump file was not created. jmap exit code = {0}\r\nstdout:\r\n{1}\r\nstderr:\r\n{2}
JMapHeapDumpProvider_WaitForHeapDump=Waiting while the heap dump is written to the disk
JMapHeapDumpProvider_ListProcesses=Listing VMs using jps
LocalJavaProcessesUtils_ErrorGettingProcesses=Error getting list of processes
LocalJavaProcessesUtils_ErrorGettingProcessListJPS=Error getting Java processes list with 'jps'. Try to configure a JDK for the HPROF jmap provider
Pass1Parser_DetectedCompressedReferences=Detected compressed references, because with uncompressed 64-bit references the array at 0x{0} would overlap the array at 0x{1}
-Pass1Parser_Error_IllegalRecordLength=Illegal record length {0} at byte {1} for record type {2}
+Pass1Parser_Error_IllegalRecordLength=Illegal record length {0} at 0x{1} for record type 0x{2} at 0x{3}
Pass1Parser_Error_IllegalType=Illegal primitive object array type
-Pass1Parser_Error_InvalidHeapDumpFile=Error: Invalid heap dump file.\n Unsupported segment type {0} at position {1}
-Pass1Parser_Error_invalidHPROFFile=(Possibly) Invalid HPROF file: Expected to read another {0,number} bytes, but only {1,number} bytes are available.
+Pass1Parser_Error_InvalidHeapDumpFile=Error: Invalid heap dump file.\n Unsupported segment type 0x{0} at position 0x{1}
+Pass1Parser_Error_invalidHPROFFile=(Possibly) Invalid HPROF file: Expected to read another {0,number} bytes, but only {1,number} bytes are available for heap dump record 0x{2} at 0x{3}.
Pass1Parser_Error_SupportedDumps=Only 32bit and 64bit dumps are supported.
-Pass1Parser_Error_UnresolvedName=Unresolved Name 0x
+Pass1Parser_Error_UnresolvedName=Unresolved Name 0x{0}
Pass1Parser_Error_WritingThreadsInformation=Error writing threads information
Pass1Parser_Info_WroteThreadsTo=Wrote threads call stacks to {0}
Pass1Parser_Error_NoHeapDumpIndexFound=Parser found {0} HPROF dumps in file {1}. No heap dump index {2} found. See FAQ.
Pass1Parser_Info_UsingDumpIndex=Parser found {0} HPROF dumps in file {1}. Using dump index {2}. See FAQ.
Pass1Parser_UnexpectedEndPosition=Heap dump segment at 0x{0} size {1} ends at 0x{2} instead of 0x{3}
-Pass1Parser_UnexpectedRecord=Heap dump record 0x{0} size {1} is not a supported record type.
+Pass1Parser_UnexpectedRecord=Heap dump record 0x{0} size {1} at 0x{2} is not a supported record type.
Pass1Parser_GuessingLengthOverflow=Guessing that heap dump record 0x{0} at 0x{1} with length {2} is probably overflowed, updating to length {3}. See bug 404679.
Pass1Parser_HeapDumpCreated=Heap dump created at {0,time,long} {0,date,long}
Pass1Parser_HeapDumpsFound={0} heap dumps found
Pass2Parser_Error_HandleMustCreateFakeClassForName=handler must create fake class for {0}
Pass2Parser_Error_HandlerMustCreateFakeClassForAddress=handler must create fake class for 0x{0}
-Pass2Parser_Error_InsufficientBytesRead=Insufficient bytes read for instance at {0}, expected {1} read {2}
-HPROFPreferences_Description=Optional configuration of the HPROF heapdump parser.
+Pass2Parser_Error_InsufficientBytesRead=Insufficient bytes read for instance {0} @ 0x{1} at 0x{2}, expected to be at 0x{3} read to 0x{4}
+HPROFPreferences_Description=Optional configuration of the HPROF heap dump parser.
HPROFPreferences_Strictness=Parser Strictness: What to do when the dump doesn't match the specification
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.
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/Messages.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/Messages.java
index 772a5c42..a517c2a5 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/Messages.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/Messages.java
@@ -53,6 +53,8 @@ public class Messages extends NLS
public static String SnapshotFactoryImpl_Error_OpeningHeapDump;
public static String SnapshotFactoryImpl_Error_ReparsingHeapDump;
public static String SnapshotFactoryImpl_ErrorOpeningHeapDump;
+ public static String SnapshotFactoryImpl_GCRootContextIDDoesNotMatchAddress;
+ public static String SnapshotFactoryImpl_GCRootIDDoesNotMatchAddress;
public static String SnapshotFactoryImpl_GCRootIDDoesNotMatchIndex;
public static String SnapshotFactoryImpl_GCRootIDOutOfRange;
public static String SnapshotFactoryImpl_GCThreadIDOutOfRange;
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 c24466ca..594e0b08 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2013 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
@@ -453,6 +453,29 @@ public class SnapshotFactoryImpl implements SnapshotFactory.Implementation
listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
Messages.SnapshotFactoryImpl_GCRootIDDoesNotMatchIndex, objid, idx), null);
}
+ long objaddr = ifo.getObjectAddress();
+ int j = pidx.identifiers.reverse(objaddr);
+ if (j != idx)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootIDDoesNotMatchAddress, objid, Long.toHexString(objaddr)), null);
+ }
+ int ctxidx = ifo.getContextId();
+ long ctxaddr = ifo.getContextAddress();
+ if (ctxaddr != 0)
+ {
+ if (ctxidx < 0 || ctxidx >= maxIndex)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootIDOutOfRange, ctxidx, maxIndex), null);
+ }
+ int k = pidx.identifiers.reverse(ctxaddr);
+ if (k != ctxidx)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootContextIDDoesNotMatchAddress, objid, ctxidx, Long.toHexString(ctxaddr)), null);
+ }
+ }
}
}
}
@@ -485,6 +508,29 @@ public class SnapshotFactoryImpl implements SnapshotFactory.Implementation
Messages.SnapshotFactoryImpl_GCThreadRootIDDoesNotMatchIndex,
thrd, objid, idx), null);
}
+ long objaddr = ifo.getObjectAddress();
+ int j = pidx.identifiers.reverse(objaddr);
+ if (j != idx)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootIDDoesNotMatchAddress, objid, Long.toHexString(objaddr)), null);
+ }
+ int ctxidx = ifo.getContextId();
+ long ctxaddr = ifo.getContextAddress();
+ if (ctxaddr != 0)
+ {
+ if (ctxidx < 0 || ctxidx >= maxIndex)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootIDOutOfRange, ctxidx, maxIndex), null);
+ }
+ int k = pidx.identifiers.reverse(ctxaddr);
+ if (k != ctxidx)
+ {
+ listener.sendUserMessage(Severity.ERROR, MessageUtil.format(
+ Messages.SnapshotFactoryImpl_GCRootContextIDDoesNotMatchAddress, objid, ctxidx, Long.toHexString(ctxaddr)), 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 41ce2482..17809498 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
@@ -55,7 +55,9 @@ SnapshotFactoryImpl_ClassIndexNotEqualClassObjectID=Class index {0} address {1}
SnapshotFactoryImpl_ClassIndexAddressTypeIDNotEqualClassImplClassId=Class index {0} address {1} class id1 {2} 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}
SnapshotFactoryImpl_GCRootIDOutOfRange=GC root id {0} out of range [0,{1})
+SnapshotFactoryImpl_GCRootIDDoesNotMatchAddress=GC root info object id {0} does not match address 0x{1}
SnapshotFactoryImpl_GCRootIDDoesNotMatchIndex=GC root info object id {0} does not match index {1}
SnapshotFactoryImpl_GCThreadIDOutOfRange=GC root thread id {0} out of range [0,{1})
SnapshotFactoryImpl_GCThreadRootIDDoesNotMatchIndex=GC root thread id {0} info object id {1} does not match index {2}
diff --git a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/registry/ArgumentDescriptor.java b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/registry/ArgumentDescriptor.java
index ceb2ea7f..95dd5d1f 100644
--- a/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/registry/ArgumentDescriptor.java
+++ b/plugins/org.eclipse.mat.report/src/org/eclipse/mat/query/registry/ArgumentDescriptor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2011 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
@@ -214,6 +214,7 @@ public class ArgumentDescriptor implements IArgumentDescriptor
{
if (value instanceof ArgumentFactory)
{
+ buf.append(" ");
if (flag != null) buf.append("-").append(flag).append(" ");
((ArgumentFactory) value).appendUsage(buf);
return;
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 676b8d74..709ebe31 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010,2017 IBM Corporation.
+ * Copyright (c) 2010,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
@@ -14,6 +14,7 @@ package org.eclipse.mat.tests.snapshot;
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;
@@ -21,6 +22,7 @@ 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;
@@ -29,6 +31,7 @@ import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
@@ -37,6 +40,7 @@ import org.eclipse.mat.collect.SetInt;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.SnapshotFactory;
+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;
@@ -68,7 +72,7 @@ public class GeneralSnapshotTests
};
final Stacks stackInfo;
- @Parameters
+ @Parameters(name="{index}: Snapshot={0} options={1}")
public static Collection<Object[]> data()
{
return Arrays.asList(new Object[][] {
@@ -388,7 +392,43 @@ public class GeneralSnapshotTests
}
}
}
-
+
+ /**
+ * Test exporting as HPROF
+ * @throws SnapshotException
+ * @throws IOException
+ */
+ @Test
+ public void exportHPROF() throws SnapshotException, IOException
+ {
+ // Currently can't export PHD
+ assumeThat(snapshot.getSnapshotInfo().getProperty("$heapFormat"), not(equalTo((Serializable)"DTFJ-PHD")));
+ // Currently can't export methods as classes properly
+ assumeThat(hasMethods, equalTo(Methods.NONE));
+ // HPROF parser can't handle adding links from classloader to classes for javacore
+ File fn = new File(snapshot.getSnapshotInfo().getPrefix());
+ assumeThat(fn.getName(), not(containsString("javacore")));
+ File newSnapshotFile = File.createTempFile(fn.getName(), ".hprof");
+ try {
+ SnapshotQuery query = SnapshotQuery.parse("export_hprof -output "+newSnapshotFile.getPath(), snapshot);
+ IResult t = query.execute(new VoidProgressListener());
+ assertNotNull(t);
+ ISnapshot newSnapshot = SnapshotFactory.openSnapshot(newSnapshotFile, Collections.<String,String>emptyMap(), new VoidProgressListener());
+ try {
+ SnapshotInfo oldInfo = snapshot.getSnapshotInfo();
+ SnapshotInfo newInfo = newSnapshot.getSnapshotInfo();
+ 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());
+ } finally {
+ newSnapshot.dispose();
+ }
+ } finally {
+ newSnapshotFile.delete();
+ }
+ }
+
/**
* Test value of Strings
*/
diff --git a/plugins/org.eclipse.mat.ui.help/mimes/i-objectinspectorpin.png b/plugins/org.eclipse.mat.ui.help/mimes/i-objectinspectorpin.png
new file mode 100644
index 00000000..de37063c
--- /dev/null
+++ b/plugins/org.eclipse.mat.ui.help/mimes/i-objectinspectorpin.png
Binary files differ
diff --git a/plugins/org.eclipse.mat.ui.help/mimes/message_warning.png b/plugins/org.eclipse.mat.ui.help/mimes/message_warning.png
new file mode 100644
index 00000000..c5542c1a
--- /dev/null
+++ b/plugins/org.eclipse.mat.ui.help/mimes/message_warning.png
Binary files differ
diff --git a/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.dita b/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.dita
index 729bdc2d..1cfffcc0 100644
--- a/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.dita
+++ b/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.dita
@@ -112,8 +112,8 @@
The only significant downsides to system dumps over PHDs is that they are much larger, they usually take longer to produce,
they may be useless if they are manually taken in the middle of an exclusive event that manipulates the underlying Java heap
such as a garbage collection, and they sometimes require operating system configuration (
- <xref format="html" href="http://www.ibm.com/support/knowledgecenter/SSYKE2_7.1.0/com.ibm.java.lnx.71.doc/diag/problem_determination/linux_setup.html">Linux</xref>,
- <xref format="html" href="http://www.ibm.com/support/knowledgecenter/SSYKE2_7.1.0/com.ibm.java.aix.71.doc/diag/problem_determination/aix_setup_full_core.html">AIX</xref>)
+ <xref format="html" href="https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/diag/problem_determination/linux_setup.html">Linux</xref>,
+ <xref format="html" href="https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/diag/problem_determination/aix_setup_full_core.html">AIX</xref>)
to ensure non-truncation.
</p>
diff --git a/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html b/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html
index 77e8023c..7648c5d4 100644
--- a/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html
+++ b/plugins/org.eclipse.mat.ui.help/tasks/acquiringheapdump.html
@@ -129,8 +129,8 @@
The only significant downsides to system dumps over PHDs is that they are much larger, they usually take longer to produce,
they may be useless if they are manually taken in the middle of an exclusive event that manipulates the underlying Java heap
such as a garbage collection, and they sometimes require operating system configuration (
- <a class="xref" href="http://www.ibm.com/support/knowledgecenter/SSYKE2_7.1.0/com.ibm.java.lnx.71.doc/diag/problem_determination/linux_setup.html">Linux</a>,
- <a class="xref" href="http://www.ibm.com/support/knowledgecenter/SSYKE2_7.1.0/com.ibm.java.aix.71.doc/diag/problem_determination/aix_setup_full_core.html">AIX</a>)
+ <a class="xref" href="https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/diag/problem_determination/linux_setup.html">Linux</a>,
+ <a class="xref" href="https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/diag/problem_determination/aix_setup_full_core.html">AIX</a>)
to ensure non-truncation.
</p>
@@ -607,21 +607,21 @@
<thead class="thead" align="left">
<tr class="row">
- <th class="entry" valign="top" width="6.25%" id="d8167e575">Vendor</th>
+ <th class="entry" valign="top" width="6.25%" id="d8192e575">Vendor</th>
- <th class="entry" valign="top" width="12.5%" id="d8167e578">Release</th>
+ <th class="entry" valign="top" width="12.5%" id="d8192e578">Release</th>
- <th class="entry" colspan="3" valign="top" id="d8167e581">VM
+ <th class="entry" colspan="3" valign="top" id="d8192e581">VM
Parameter</th>
- <th class="entry" colspan="2" valign="top" id="d8167e584">Sun
+ <th class="entry" colspan="2" valign="top" id="d8192e584">Sun
Tools</th>
- <th class="entry" valign="top" width="6.25%" id="d8167e587">SAP Tool</th>
+ <th class="entry" valign="top" width="6.25%" id="d8192e587">SAP Tool</th>
- <th class="entry" valign="top" width="12.5%" id="d8167e591">Attach</th>
+ <th class="entry" valign="top" width="12.5%" id="d8192e591">Attach</th>
- <th class="entry" valign="top" width="12.5%" id="d8167e594">MAT</th>
+ <th class="entry" valign="top" width="12.5%" id="d8192e594">MAT</th>
</tr>
@@ -629,329 +629,329 @@
<tbody class="tbody">
<tr class="row">
- <td class="entry" valign="top" width="6.25%" headers="d8167e575 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e575 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">On out of memory</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">On out of memory</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">On Ctrl+Break</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">On Ctrl+Break</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Agent</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Agent</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">JMap</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">JMap</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">JConsole</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">JConsole</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">JVMMon</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">JVMMon</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">API</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">API</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">acquire heap dump</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">acquire heap dump</td>
</tr>
<tr class="row">
- <td class="entry" rowspan="3" valign="top" width="6.25%" headers="d8167e575 ">Sun, HP</td>
+ <td class="entry" rowspan="3" valign="top" width="6.25%" headers="d8192e575 ">Sun, HP</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.4.2_12</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.4.2_12</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 "> </td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 "> </td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">No</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.5.0_07</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.5.0_07</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes (Since 1.5.0_15)</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes (Since 1.5.0_15)</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">Yes (Only Solaris and Linux)</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">Yes (Only Solaris and Linux)</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 "> </td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes (Only Solaris and Linux)</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes (Only Solaris and Linux)</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.6.0_00</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.6.0_00</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" rowspan="2" valign="top" width="6.25%" headers="d8167e575 ">Oracle, OpenJDK, HP</td>
+ <td class="entry" rowspan="2" valign="top" width="6.25%" headers="d8192e575 ">Oracle, OpenJDK, HP</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.7.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.7.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.8.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.8.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="6.25%" headers="d8167e575 ">SAP</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e575 ">SAP</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">Any 1.5.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">Any 1.5.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">Yes (Only Solaris and Linux)</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">Yes (Only Solaris and Linux)</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 "> </td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 "> </td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">Yes</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 "> </td>
</tr>
<tr class="row">
- <td class="entry" rowspan="7" valign="top" width="6.25%" headers="d8167e575 ">IBM</td>
+ <td class="entry" rowspan="7" valign="top" width="6.25%" headers="d8192e575 ">IBM</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.4.2 SR12</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.4.2 SR12</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 "> </td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 "> </td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">No</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.5.0 SR8a</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.5.0 SR8a</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.ibm.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.ibm.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">No</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.6.0 SR2</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.6.0 SR2</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.ibm.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.ibm.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">No</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.6.0 SR6</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.6.0 SR6</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.ibm.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.ibm.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.7.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.7.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.ibm.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.ibm.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.8.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.8.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.ibm.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.ibm.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.8.0 SR5</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.8.0 SR5</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
<tr class="row">
- <td class="entry" valign="top" width="6.25%" headers="d8167e575 ">OpenJ9</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e575 ">OpenJ9</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e578 ">1.8.0</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e578 ">1.8.0</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e581 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e581 ">Yes</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e581 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e581 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e584 ">No</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e584 ">No</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e584 ">Yes (PHD only)</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e584 ">Yes (PHD only)</td>
- <td class="entry" valign="top" width="6.25%" headers="d8167e587 ">No</td>
+ <td class="entry" valign="top" width="6.25%" headers="d8192e587 ">No</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e591 ">com.sun.tools.attach</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e591 ">com.sun.tools.attach</td>
- <td class="entry" valign="top" width="12.5%" headers="d8167e594 ">Yes</td>
+ <td class="entry" valign="top" width="12.5%" headers="d8192e594 ">Yes</td>
</tr>
diff --git a/plugins/org.eclipse.mat.ui.help/tasks/exportdump.dita b/plugins/org.eclipse.mat.ui.help/tasks/exportdump.dita
new file mode 100644
index 00000000..980d744a
--- /dev/null
+++ b/plugins/org.eclipse.mat.ui.help/tasks/exportdump.dita
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 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
+ http://www.eclipse.org/legal/epl-v10.html
+
+ Contributors:
+ Andrew Johnson/IBM Corportation - initial API and implementation
+ -->
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "task.dtd" >
+<task id="task_exportdump" xml:lang="en-us">
+ <title>Export Heap Dump</title>
+ <prolog>
+ <copyright>
+ <copyryear year=""></copyryear>
+ <copyrholder>
+ Copyright (c) 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
+ http://www.eclipse.org/legal/epl-v10.html
+ </copyrholder>
+ </copyright>
+ </prolog>
+
+ <taskbody>
+ <context>
+ <p>A whole snapshot can be exported as a new HPROF format file by
+ using
+ the 'Export Snapshot' query.
+ This query can be used to convert a DTFJ core dump to HPROF format, or to redact sensitive data,
+ or to obfuscate class and method names in a dump.
+ </p>
+ <ul>
+ <li>
+ Choose the
+ <parmname>output</parmname>
+ file name, extension '.hprof'.
+ </li>
+ <li>
+ If required then certain data can be redacted from the output file,
+ to reduce the risk of passwords or other sensitive data being read
+ by
+ anyone analyzing the newly generated HPROF file. The
+ <parmname>redact</parmname>
+ option has
+ several choices:
+ <dl>
+ <dlentry>
+ <dt>NONE
+ </dt>
+ <dd>No redaction - all data is available in the new HPROF file.
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>NAMES
+ </dt>
+ <dd><apiname>char</apiname> and <apiname>byte</apiname> arrays which match a class or field name which is to be obfuscated get changed to the
+ obfuscated name.
+ An attempt is also made to obfuscate class names in <apiname>char</apiname> and <apiname>byte</apiname> arrays which are a Java method
+ or type signature.
+ This helps hide the true class and field names when obfuscation of class and field names is selected by the <parmname>map</parmname>
+ option, as sometimes class and field names are held in strings referenced by the <apiname>Class</apiname> object.
+ It is only approximate, as class and field names which are just part of a <apiname>char</apiname> or <apiname>byte</apiname> array
+ will not be changed.
+ All other field and array values are unchanged.
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>BASIC
+ </dt>
+ <dd>
+ <apiname>char</apiname>
+ arrays,
+ <apiname>byte</apiname>
+ arrays,
+ <apiname>int</apiname>
+ arrays,
+ <apiname>char</apiname>
+ fields and
+ <apiname>byte</apiname>
+ fields are redacted.
+ This removes some sensitive data, such as passwords and the majority
+ of
+ <apiname>BigInteger</apiname>
+ object contents,
+ which might hold private keys.
+ It leaves other data such as
+ <apiname>int</apiname>
+ fields, and
+ <apiname>boolean</apiname>
+ ,
+ <apiname>long</apiname>
+ ,
+ <apiname>float</apiname>
+ ,
+ <apiname>double</apiname>
+ fields and arrays.
+ which might also contain sensitive information such as personal ID
+ numbers or financial
+ information. Those fields might be useful however for solving some sorts of
+ problems.
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>FULL
+ </dt>
+ <dd>All fields and arrays are set to zero or false values, so removing
+ many examples of sensitive data.
+ Object reference fields and arrays are preserved, together with array
+ sizes, as these
+ are necessary to identify causes of out of memory errors. There is
+ a chance that numbers of,
+ sizes, and links between objects might hold sensitive information.
+ </dd>
+ </dlentry>
+ </dl>
+ <hazardstatement type="notice">
+ <messagepanel>
+ <typeofhazard>
+ Information disclosure
+ </typeofhazard>
+ <consequence>
+ Incorrect use of these options may leave sensitive data in the new dump.
+ If this dump is then given to an unauthorised person the
+ sensitive data may
+ be extracted and disclosed.
+ </consequence>
+ <howtoavoid>
+ Review the correct use of these options.
+ </howtoavoid>
+ <howtoavoid>
+ Examine the newly generated HPROF file, for example with Memory
+ Analyzer, to
+ confirm that no sensitive data is visible.
+ </howtoavoid>
+ </messagepanel>
+ <hazardsymbol href="../mimes/message_warning.png" />
+ </hazardstatement>
+ </li>
+ <li>
+ <p>Class names, field names and method names can hold sensitive
+ information about the
+ nature of the application. The <parmname>map</parmname> option allows the names to be
+ mapped to obfuscated names,
+ hiding the true nature of the application when the new dump is inspected.
+ Consider using at least <cmdname>redact=NAMES</cmdname> or
+ <cmdname>redact=BASIC</cmdname> option as well to hide class, field and method names held in
+ <apiname>byte</apiname> and <apiname>char</apiname> arrays.
+ </p>
+ <p>
+ The
+ <parmname>map</parmname>
+ option gives a file into which will be stored
+ the mapping from original class name to new class name.
+ This allows the results of analysis of the new HPROF file to be
+ manually converted back
+ to class names of the original file.
+ Any existing mappings in the file are read at the start and are used,
+ and the full list of old and any new mappings are output at the end.
+ Also if several related
+ snapshots are to be exported
+ then the same mapping file should be used so that all the new HPROF
+ files have the same new class names for the same old class names.
+ If this option is omitted, no obfuscation will be done.
+ </p>
+ <p>
+ It is normally better to retain the names of
+ Java API classes, or at least those of the '<apiname>java.lang.</apiname>' package, as all
+ applications use them,
+ and having the true names available makes the analysis of the new
+ HPROF file a little easier.
+ The
+ <parmname>skip</parmname>
+ option allows this to be controlled.
+ </p>
+ <p>
+ The
+ <parmname>exclude</parmname>
+ option allows certain phrases or words to be excluded from the
+ generated names.
+ This might be useful if the generated names might otherwise include
+ unpronouncable or unsuitable characer
+ sequences.
+ </p>
+ <p>
+ The
+ <parmname>undo</parmname>
+ option allows the mapping operation to be reversed if
+ the original snapshot file has been lost and only the
+ mapped HPROF file and the mapping file are available.
+ </p>
+ <hazardstatement type="notice">
+ <messagepanel>
+ <typeofhazard>
+ Information disclosure
+ </typeofhazard>
+ <consequence>
+ Incorrect use of these options may leave sensitive information about the
+ application design in the new dump.
+ If this dump is then given to an unauthorised person, sensitive
+ information about the application may
+ be extracted and disclosed.
+ </consequence>
+ <howtoavoid>
+ Review the correct use of these options.
+ </howtoavoid>
+ <howtoavoid>
+ Examine the newly generated HPROF file, for example with Memory
+ Analyzer, to
+ confirm that no sensitive data is visible.
+ </howtoavoid>
+ <howtoavoid>
+ Do not pass the mapping file to an unauthorised person as it contains
+ the true names of classes, fields and methods of the application.
+ </howtoavoid>
+ </messagepanel>
+ <hazardsymbol href="../mimes/message_warning.png" />
+ </hazardstatement>
+ <hazardstatement type="notice">
+ <messagepanel>
+ <typeofhazard>
+ Offensive content
+ </typeofhazard>
+ <consequence>
+ As the class, method and field names are randomly generated there is the
+ possibility of offensive words occuring in the names.
+ </consequence>
+ <howtoavoid>
+ Use the <parmname>avoid</parmname> option to filter out words.
+ </howtoavoid>
+ <howtoavoid>
+ Examine the generated mapping file to confirm that no offensive words
+ have been generated. If they have, delete the mapping file and
+ export the dump again.
+ </howtoavoid>
+ </messagepanel>
+ <hazardsymbol href="../mimes/message_warning.png" />
+ </hazardstatement>
+ </li>
+ <li>
+ <p>
+ Normally the whole snapshot is exported. Sometimes it might be
+ useful to export
+ a subset of all the objects. For example if the original dump was
+ parsed with the
+ <parmname>keep unreachable objects</parmname>
+ option the unreachable objects will be retained by some created
+ dummy
+ <xref href="../concepts/gcroots.dita"><parmname>UNREACHABLE</parmname></xref>
+ roots. It might be desired to create a new smaller HPROF file without
+ these objects.
+ </p>
+ <p>
+ To create a HPROF file without the unreachable objects, either
+ reparse without the
+ <xref href="configure_mat.dita"><parmname>keep unreachable objects</parmname></xref>
+ option then export the entire snapshot, or else run the
+ <cmdname>GC Roots</cmdname>
+ query, then
+ select all roots except the
+ <parmname>UNREACHABLE</parmname>
+ roots, find the retained set of those other
+ roots, then select all the objects and use the context menu to export
+ the snapshot for the selected objects.
+ </p>
+ <p>
+ Use this option with care - a dump exported without many objects
+ such as classes, or class loaders,
+ or objects from the
+ '<apiname>java.lang.</apiname>'
+ package may have broken links and be hard to
+ interpret.
+ </p>
+ </li>
+ </ul>
+ </context>
+ </taskbody>
+</task> \ No newline at end of file
diff --git a/plugins/org.eclipse.mat.ui.help/tasks/exportdump.html b/plugins/org.eclipse.mat.ui.help/tasks/exportdump.html
new file mode 100644
index 00000000..25048a02
--- /dev/null
+++ b/plugins/org.eclipse.mat.ui.help/tasks/exportdump.html
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xml:lang="en-us" lang="en-us">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="DC.Type" content="task"/>
+<meta name="DC.Title" content="Export Heap Dump"/>
+<meta name="copyright" content="Copyright (c) 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 http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
+<meta name="DC.Rights.Owner" content="Copyright (c) 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 http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
+<meta name="DC.Format" content="XHTML"/>
+<meta name="DC.Identifier" content="task_exportdump"/>
+<meta name="DC.Language" content="en-us"/>
+<link rel="stylesheet" type="text/css" href="../styles/commonltr.css"/>
+<title>Export Heap Dump</title>
+</head>
+<body id="task_exportdump">
+
+
+ <h1 class="title topictitle1">Export Heap Dump</h1>
+
+
+
+ <div class="body taskbody">
+ <div class="section context">
+ <p class="p">A whole snapshot can be exported as a new HPROF format file by
+ using
+ the 'Export Snapshot' query.
+ This query can be used to convert a DTFJ core dump to HPROF format, or to redact sensitive data,
+ or to obfuscate class and method names in a dump.
+ </p>
+
+ <ul class="ul">
+ <li class="li">
+ Choose the
+ <span class="keyword parmname">output</span>
+ file name, extension '.hprof'.
+ </li>
+
+ <li class="li">
+ If required then certain data can be redacted from the output file,
+ to reduce the risk of passwords or other sensitive data being read
+ by
+ anyone analyzing the newly generated HPROF file. The
+ <span class="keyword parmname">redact</span>
+ option has
+ several choices:
+ <dl class="dl">
+
+ <dt class="dt dlterm">NONE
+ </dt>
+
+ <dd class="dd">No redaction - all data is available in the new HPROF file.
+ </dd>
+
+
+
+ <dt class="dt dlterm">NAMES
+ </dt>
+
+ <dd class="dd"><span class="keyword apiname">char</span> and <span class="keyword apiname">byte</span> arrays which match a class or field name which is to be obfuscated get changed to the
+ obfuscated name.
+ An attempt is also made to obfuscate class names in <span class="keyword apiname">char</span> and <span class="keyword apiname">byte</span> arrays which are a Java method
+ or type signature.
+ This helps hide the true class and field names when obfuscation of class and field names is selected by the <span class="keyword parmname">map</span>
+ option, as sometimes class and field names are held in strings referenced by the <span class="keyword apiname">Class</span> object.
+ It is only approximate, as class and field names which are just part of a <span class="keyword apiname">char</span> or <span class="keyword apiname">byte</span> array
+ will not be changed.
+ All other field and array values are unchanged.
+ </dd>
+
+
+
+ <dt class="dt dlterm">BASIC
+ </dt>
+
+ <dd class="dd">
+ <span class="keyword apiname">char</span>
+ arrays,
+ <span class="keyword apiname">byte</span>
+ arrays,
+ <span class="keyword apiname">int</span>
+ arrays,
+ <span class="keyword apiname">char</span>
+ fields and
+ <span class="keyword apiname">byte</span>
+ fields are redacted.
+ This removes some sensitive data, such as passwords and the majority
+ of
+ <span class="keyword apiname">BigInteger</span>
+ object contents,
+ which might hold private keys.
+ It leaves other data such as
+ <span class="keyword apiname">int</span>
+ fields, and
+ <span class="keyword apiname">boolean</span>
+ ,
+ <span class="keyword apiname">long</span>
+ ,
+ <span class="keyword apiname">float</span>
+ ,
+ <span class="keyword apiname">double</span>
+ fields and arrays.
+ which might also contain sensitive information such as personal ID
+ numbers or financial
+ information. Those fields might be useful however for solving some sorts of
+ problems.
+ </dd>
+
+
+
+ <dt class="dt dlterm">FULL
+ </dt>
+
+ <dd class="dd">All fields and arrays are set to zero or false values, so removing
+ many examples of sensitive data.
+ Object reference fields and arrays are preserved, together with array
+ sizes, as these
+ are necessary to identify causes of out of memory errors. There is
+ a chance that numbers of,
+ sizes, and links between objects might hold sensitive information.
+ </dd>
+
+
+ </dl>
+
+ <div class="note hazardstatement note"><span class="notetitle">Note:</span>
+ <ul class="ul messagepanel">
+ <li class="li typeofhazard">
+ Information disclosure
+ </li>
+
+ <li class="li consequence">
+ Incorrect use of these options may leave sensitive data in the new dump.
+ If this dump is then given to an unauthorised person the
+ sensitive data may
+ be extracted and disclosed.
+ </li>
+
+ <li class="li howtoavoid">
+ Review the correct use of these options.
+ </li>
+
+ <li class="li howtoavoid">
+ Examine the newly generated HPROF file, for example with Memory
+ Analyzer, to
+ confirm that no sensitive data is visible.
+ </li>
+
+ </ul>
+
+ <img class="image hazardsymbol" src="../mimes/message_warning.png"/>
+ </div>
+
+ </li>
+
+ <li class="li">
+ <p class="p">Class names, field names and method names can hold sensitive
+ information about the
+ nature of the application. The <span class="keyword parmname">map</span> option allows the names to be
+ mapped to obfuscated names,
+ hiding the true nature of the application when the new dump is inspected.
+ Consider using at least <span class="keyword cmdname">redact=NAMES</span> or
+ <span class="keyword cmdname">redact=BASIC</span> option as well to hide class, field and method names held in
+ <span class="keyword apiname">byte</span> and <span class="keyword apiname">char</span> arrays.
+ </p>
+
+ <p class="p">
+ The
+ <span class="keyword parmname">map</span>
+ option gives a file into which will be stored
+ the mapping from original class name to new class name.
+ This allows the results of analysis of the new HPROF file to be
+ manually converted back
+ to class names of the original file.
+ Any existing mappings in the file are read at the start and are used,
+ and the full list of old and any new mappings are output at the end.
+ Also if several related
+ snapshots are to be exported
+ then the same mapping file should be used so that all the new HPROF
+ files have the same new class names for the same old class names.
+ If this option is omitted, no obfuscation will be done.
+ </p>
+
+ <p class="p">
+ It is normally better to retain the names of
+ Java API classes, or at least those of the '<span class="keyword apiname">java.lang.</span>' package, as all
+ applications use them,
+ and having the true names available makes the analysis of the new
+ HPROF file a little easier.
+ The
+ <span class="keyword parmname">skip</span>
+ option allows this to be controlled.
+ </p>
+
+ <p class="p">
+ The
+ <span class="keyword parmname">exclude</span>
+ option allows certain phrases or words to be excluded from the
+ generated names.
+ This might be useful if the generated names might otherwise include
+ unpronouncable or unsuitable characer
+ sequences.
+ </p>
+
+ <p class="p">
+ The
+ <span class="keyword parmname">undo</span>
+ option allows the mapping operation to be reversed if
+ the original snapshot file has been lost and only the
+ mapped HPROF file and the mapping file are available.
+ </p>
+
+ <div class="note hazardstatement note"><span class="notetitle">Note:</span>
+ <ul class="ul messagepanel">
+ <li class="li typeofhazard">
+ Information disclosure
+ </li>
+
+ <li class="li consequence">
+ Incorrect use of these options may leave sensitive information about the
+ application design in the new dump.
+ If this dump is then given to an unauthorised person, sensitive
+ information about the application may
+ be extracted and disclosed.
+ </li>
+
+ <li class="li howtoavoid">
+ Review the correct use of these options.
+ </li>
+
+ <li class="li howtoavoid">
+ Examine the newly generated HPROF file, for example with Memory
+ Analyzer, to
+ confirm that no sensitive data is visible.
+ </li>
+
+ <li class="li howtoavoid">
+ Do not pass the mapping file to an unauthorised person as it contains
+ the true names of classes, fields and methods of the application.
+ </li>
+
+ </ul>
+
+ <img class="image hazardsymbol" src="../mimes/message_warning.png"/>
+ </div>
+
+ <div class="note hazardstatement note"><span class="notetitle">Note:</span>
+ <ul class="ul messagepanel">
+ <li class="li typeofhazard">
+ Offensive content
+ </li>
+
+ <li class="li consequence">
+ As the class, method and field names are randomly generated there is the
+ possibility of offensive words occuring in the names.
+ </li>
+
+ <li class="li howtoavoid">
+ Use the <span class="keyword parmname">avoid</span> option to filter out words.
+ </li>
+
+ <li class="li howtoavoid">
+ Examine the generated mapping file to confirm that no offensive words
+ have been generated. If they have, delete the mapping file and
+ export the dump again.
+ </li>
+
+ </ul>
+
+ <img class="image hazardsymbol" src="../mimes/message_warning.png"/>
+ </div>
+
+ </li>
+
+ <li class="li">
+ <p class="p">
+ Normally the whole snapshot is exported. Sometimes it might be
+ useful to export
+ a subset of all the objects. For example if the original dump was
+ parsed with the
+ <span class="keyword parmname">keep unreachable objects</span>
+ option the unreachable objects will be retained by some created
+ dummy
+ <a class="xref" href="../concepts/gcroots.html"><span class="keyword parmname">UNREACHABLE</span></a>
+ roots. It might be desired to create a new smaller HPROF file without
+ these objects.
+ </p>
+
+ <p class="p">
+ To create a HPROF file without the unreachable objects, either
+ reparse without the
+ <a class="xref" href="configure_mat.html"><span class="keyword parmname">keep unreachable objects</span></a>
+ option then export the entire snapshot, or else run the
+ <span class="keyword cmdname">GC Roots</span>
+ query, then
+ select all roots except the
+ <span class="keyword parmname">UNREACHABLE</span>
+ roots, find the retained set of those other
+ roots, then select all the objects and use the context menu to export
+ the snapshot for the selected objects.
+ </p>
+
+ <p class="p">
+ Use this option with care - a dump exported without many objects
+ such as classes, or class loaders,
+ or objects from the
+ '<span class="keyword apiname">java.lang.</span>'
+ package may have broken links and be hard to
+ interpret.
+ </p>
+
+ </li>
+
+ </ul>
+
+ </div>
+
+ </div>
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.mat.ui.help/toc.ditamap b/plugins/org.eclipse.mat.ui.help/toc.ditamap
index 4f6fcb29..f6c2c36d 100644
--- a/plugins/org.eclipse.mat.ui.help/toc.ditamap
+++ b/plugins/org.eclipse.mat.ui.help/toc.ditamap
@@ -25,7 +25,8 @@
<topicref href="tasks/analyzingfinalizer.dita" type="task" />
<topicref href="tasks/bundleregistry.dita" type="task" />
<topicref href="tasks/comparingdata.dita" type="task" />
- <topicref href="tasks/exportdata.dita" type="task" />
+ <topicref href="tasks/exportdata.dita" type="task" />
+ <topicref href="tasks/exportdump.dita" type="task" />
<topicref href="tasks/configure_mat.dita" type="task" />
</topichead>
<topichead navtitle="Reference">
diff --git a/plugins/org.eclipse.mat.ui.help/toc.xml b/plugins/org.eclipse.mat.ui.help/toc.xml
index 1388f456..124ea6a7 100644
--- a/plugins/org.eclipse.mat.ui.help/toc.xml
+++ b/plugins/org.eclipse.mat.ui.help/toc.xml
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<?NLS TYPE="org.eclipse.help.toc"?>
-<toc label="Memory Analyzer" topic="welcome.html"><topic label="Introduction" href="welcome.html"/><topic label="Getting Started"><topic label="Basic Tutorial" href="gettingstarted/basictutorial.html"/></topic><topic label="Concepts"><topic label="Heap Dump" href="concepts/heapdump.html"/><topic label="Shallow vs. Retained Heap" href="concepts/shallowretainedheap.html"/><topic label="Dominator Tree" href="concepts/dominatortree.html"/><topic label="Garbage Collection Roots" href="concepts/gcroots.html"/></topic><topic label="Tasks"><topic label="Acquiring Heap Dumps" href="tasks/acquiringheapdump.html"/><topic label="Installing IBM DTFJ feature for IBM dumps" href="tasks/installDTFJ.html"/><topic label="Running Leak Suspect Report" href="tasks/runningleaksuspectreport.html"/><topic label="List the Biggest Objects" href="tasks/listbiggestobjects.html"/><topic label="Finding Responsible Objects" href="tasks/findingresponsibleobjects.html"/><topic label="Querying Heap Objects (OQL)" href="tasks/queryingheapobjects.html"/><topic label="Analyze Class Loader" href="tasks/analyzingclassloader.html"/><topic label="Analyzing Threads" href="tasks/analyzingthreads.html"/><topic label="Analyzing Java Collection Usage" href="tasks/analyzingjavacollectionusage.html"/><topic label="Analyzing Finalizer" href="tasks/analyzingfinalizer.html"/><topic label="Eclipse Equinox Bundle Registry" href="tasks/bundleregistry.html"/><topic label="Comparing Objects" href="tasks/comparingdata.html"/><topic label="Export Data" href="tasks/exportdata.html"/><topic label="Memory Analyzer Configuration" href="tasks/configure_mat.html"/></topic><topic label="Reference"><topic label="Inspections"><topic label="Leak Identification"><topic label="Component Report" href="reference/inspections/component_report.html"/></topic><topic label="Immediate Dominators" href="reference/inspections/immediate_dominators.html"/><topic label="Group by Value" href="reference/inspections/group_by_value.html"/><topic label="Path to GC Roots" href="reference/inspections/path_to_gc_roots.html"/><topic label="Retained Set" href="reference/inspections/retained_set.html"/><topic label="Top Consumers" href="reference/inspections/top_consumers.html"/></topic><topic label="Query Matrix" href="reference/querymatrix.html"><topic label="Finding Memory Leak" href="reference/findingmemoryleak.html"/><topic label="Analyzing Memory Consumption" href="reference/analyzingmemoryconsumption.html"/></topic><topic label="OQL Syntax" href="reference/oqlsyntax.html"><topic label="SELECT Clause" href="reference/oqlsyntaxselect.html"/><topic label="FROM Clause" href="reference/oqlsyntaxfrom.html"/><topic label="WHERE Clause" href="reference/oqlsyntaxwhere.html"/><topic label="Property Accessors" href="reference/propertyaccessors.html"/><topic label="BNF for the Object Query Language" href="reference/bnfofoql.html"/></topic><topic label="Selecting Queries" href="reference/selectingqueries.html"/><topic label="Icons Assist" href="reference/iconassist.html"/><topic label="Tips and Tricks" href="reference/tipsandtricks.html"/><topic label="Extending Memory Analyzer" href="reference/extendingmat.html"/><topic label="API Reference" href="doc/index.html"/></topic><topic label="New and Noteworthy" href="noteworthy.html"/><topic label="Legal" href="legal.html"/></toc> \ No newline at end of file
+<toc label="Memory Analyzer" topic="welcome.html"><topic label="Introduction" href="welcome.html"/><topic label="Getting Started"><topic label="Basic Tutorial" href="gettingstarted/basictutorial.html"/></topic><topic label="Concepts"><topic label="Heap Dump" href="concepts/heapdump.html"/><topic label="Shallow vs. Retained Heap" href="concepts/shallowretainedheap.html"/><topic label="Dominator Tree" href="concepts/dominatortree.html"/><topic label="Garbage Collection Roots" href="concepts/gcroots.html"/></topic><topic label="Tasks"><topic label="Acquiring Heap Dumps" href="tasks/acquiringheapdump.html"/><topic label="Installing IBM DTFJ feature for IBM dumps" href="tasks/installDTFJ.html"/><topic label="Running Leak Suspect Report" href="tasks/runningleaksuspectreport.html"/><topic label="List the Biggest Objects" href="tasks/listbiggestobjects.html"/><topic label="Finding Responsible Objects" href="tasks/findingresponsibleobjects.html"/><topic label="Querying Heap Objects (OQL)" href="tasks/queryingheapobjects.html"/><topic label="Analyze Class Loader" href="tasks/analyzingclassloader.html"/><topic label="Analyzing Threads" href="tasks/analyzingthreads.html"/><topic label="Analyzing Java Collection Usage" href="tasks/analyzingjavacollectionusage.html"/><topic label="Analyzing Finalizer" href="tasks/analyzingfinalizer.html"/><topic label="Eclipse Equinox Bundle Registry" href="tasks/bundleregistry.html"/><topic label="Comparing Objects" href="tasks/comparingdata.html"/><topic label="Export Data" href="tasks/exportdata.html"/><topic label="Export Heap Dump" href="tasks/exportdump.html"/><topic label="Memory Analyzer Configuration" href="tasks/configure_mat.html"/></topic><topic label="Reference"><topic label="Inspections"><topic label="Leak Identification"><topic label="Component Report" href="reference/inspections/component_report.html"/></topic><topic label="Immediate Dominators" href="reference/inspections/immediate_dominators.html"/><topic label="Group by Value" href="reference/inspections/group_by_value.html"/><topic label="Path to GC Roots" href="reference/inspections/path_to_gc_roots.html"/><topic label="Retained Set" href="reference/inspections/retained_set.html"/><topic label="Top Consumers" href="reference/inspections/top_consumers.html"/></topic><topic label="Query Matrix" href="reference/querymatrix.html"><topic label="Finding Memory Leak" href="reference/findingmemoryleak.html"/><topic label="Analyzing Memory Consumption" href="reference/analyzingmemoryconsumption.html"/></topic><topic label="OQL Syntax" href="reference/oqlsyntax.html"><topic label="SELECT Clause" href="reference/oqlsyntaxselect.html"/><topic label="FROM Clause" href="reference/oqlsyntaxfrom.html"/><topic label="WHERE Clause" href="reference/oqlsyntaxwhere.html"/><topic label="Property Accessors" href="reference/propertyaccessors.html"/><topic label="BNF for the Object Query Language" href="reference/bnfofoql.html"/></topic><topic label="Selecting Queries" href="reference/selectingqueries.html"/><topic label="Icons Assist" href="reference/iconassist.html"/><topic label="Tips and Tricks" href="reference/tipsandtricks.html"/><topic label="Extending Memory Analyzer" href="reference/extendingmat.html"/><topic label="API Reference" href="doc/index.html"/></topic><topic label="New and Noteworthy" href="noteworthy.html"/><topic label="Legal" href="legal.html"/></toc> \ No newline at end of file
diff --git a/plugins/org.eclipse.mat.ui.help/welcome.dita b/plugins/org.eclipse.mat.ui.help/welcome.dita
index c0a6851b..a73310b4 100644
--- a/plugins/org.eclipse.mat.ui.help/welcome.dita
+++ b/plugins/org.eclipse.mat.ui.help/welcome.dita
@@ -70,6 +70,7 @@
<p><xref href="tasks/bundleregistry.dita" /></p>
<p><xref href="tasks/comparingdata.dita" /></p>
<p><xref href="tasks/exportdata.dita" /></p>
+ <p><xref href="tasks/exportdump.dita" /></p>
<p><xref href="tasks/configure_mat.dita" /></p>
</section>
diff --git a/plugins/org.eclipse.mat.ui.help/welcome.html b/plugins/org.eclipse.mat.ui.help/welcome.html
index 5e9f7976..7ab88084 100644
--- a/plugins/org.eclipse.mat.ui.help/welcome.html
+++ b/plugins/org.eclipse.mat.ui.help/welcome.html
@@ -87,6 +87,8 @@
<p class="p"><a class="xref" href="tasks/exportdata.html">Export Data</a></p>
+ <p class="p"><a class="xref" href="tasks/exportdump.html">Export Heap Dump</a></p>
+
<p class="p"><a class="xref" href="tasks/configure_mat.html">Memory Analyzer Configuration</a></p>
</div>
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/MemoryAnalyserPlugin.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/MemoryAnalyserPlugin.java
index c04c5b1e..c3fe9c51 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/MemoryAnalyserPlugin.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/MemoryAnalyserPlugin.java
@@ -454,7 +454,7 @@ public class MemoryAnalyserPlugin extends AbstractUIPlugin
final String[] iconDirs = { // UI draws icons from several locations
// Assume the following two top-level icons directories relative to
// current directory.
- "../org.eclipse.mat.api/META-INF/icons", "../org.eclipse.mat.ui/icons", "../org.eclipse.mat.jdt/icons" };
+ "../org.eclipse.mat.api/META-INF/icons", "../org.eclipse.mat.ui/icons", "../org.eclipse.mat.hprof/icons", "../org.eclipse.mat.jdt/icons" };
try
// Trap any Exceptions at the outermost level.
{
@@ -462,7 +462,7 @@ public class MemoryAnalyserPlugin extends AbstractUIPlugin
// reproducible.
Map<String, String> iconMap = new TreeMap<String, String>();
for (String iconDir : iconDirs) // For each /icons directory
- // (currently 3).
+ // (currently 4).
{
File iconDirFile = new File(iconDir);
if (iconDirFile.isDirectory()) // Check input is valid directory
diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/icon_labels.properties b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/icon_labels.properties
index 811f6006..77a60347 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/icon_labels.properties
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/icon_labels.properties
@@ -40,6 +40,7 @@ IconLabel-expert=expert
IconLabel-explore=explore
IconLabel-export=export
IconLabel-export_csv=export csv
+IconLabel-export_hprof=export hprof
IconLabel-export_html=export html
IconLabel-export_txt=export txt
IconLabel-fill_arguments_wiz=fill arguments wiz

Back to the top