aboutsummaryrefslogtreecommitdiffstats
path: root/ddms
diff options
context:
space:
mode:
authorSiva Velusamy <vsiva@google.com>2011-10-06 09:35:33 -0700
committerSiva Velusamy <vsiva@google.com>2011-10-06 11:34:32 -0700
commited552915f84f9f6acb5c34f3b9e413d0b1534af3 (patch)
tree630f60f046506aba733f1fa41d04421a0b07c829 /ddms
parent9a8de7350e1f8fb3a99c62c769e448d8e277f8fb (diff)
downloadsdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.zip
sdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.tar.gz
sdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.tar.bz2
Add support for importing saved heap data.
Change-Id: I8552af1754f5093dcdb156f7f81e3beef776835a
Diffstat (limited to 'ddms')
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/NativeAllocationInfo.java66
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapDataImporter.java216
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java135
-rw-r--r--ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/heap/NativeHeapDataImporterTest.java74
4 files changed, 458 insertions, 33 deletions
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/NativeAllocationInfo.java b/ddms/libs/ddmlib/src/com/android/ddmlib/NativeAllocationInfo.java
index 6730c8c..385ce0d 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/NativeAllocationInfo.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/NativeAllocationInfo.java
@@ -30,6 +30,13 @@ import java.util.regex.Pattern;
* storage for resolved stack trace, this is merely for convenience.
*/
public final class NativeAllocationInfo {
+ /* Keywords used as delimiters in the string representation of a NativeAllocationInfo */
+ public static final String END_STACKTRACE_KW = "EndStacktrace";
+ public static final String BEGIN_STACKTRACE_KW = "BeginStacktrace:";
+ public static final String TOTAL_SIZE_KW = "TotalSize:";
+ public static final String SIZE_KW = "Size:";
+ public static final String ALLOCATIONS_KW = "Allocations:";
+
/* constants for flag bits */
private static final int FLAG_ZYGOTE_CHILD = (1<<31);
private static final int FLAG_MASK = (FLAG_ZYGOTE_CHILD);
@@ -66,7 +73,7 @@ public final class NativeAllocationInfo {
* @param size The size of the allocations.
* @param allocations the allocation count
*/
- NativeAllocationInfo(int size, int allocations) {
+ public NativeAllocationInfo(int size, int allocations) {
this.mSize = size & ~FLAG_MASK;
this.mIsZygoteChild = ((size & FLAG_ZYGOTE_CHILD) != 0);
this.mAllocations = allocations;
@@ -76,7 +83,7 @@ public final class NativeAllocationInfo {
* Adds a stack call address for this allocation.
* @param address The address to add.
*/
- void addStackCallAddress(long address) {
+ public void addStackCallAddress(long address) {
mStackCallAddresses.add(address);
}
@@ -209,40 +216,41 @@ public final class NativeAllocationInfo {
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
- buffer.append("Allocations: ");
+ buffer.append(ALLOCATIONS_KW);
+ buffer.append(' ');
buffer.append(mAllocations);
- buffer.append("\n"); //$NON-NLS-1$
+ buffer.append('\n');
- buffer.append("Size: ");
+ buffer.append(SIZE_KW);
+ buffer.append(' ');
buffer.append(mSize);
- buffer.append("\n"); //$NON-NLS-1$
+ buffer.append('\n');
- buffer.append("Total Size: ");
+ buffer.append(TOTAL_SIZE_KW);
+ buffer.append(' ');
buffer.append(mSize * mAllocations);
- buffer.append("\n"); //$NON-NLS-1$
-
- Iterator<Long> addrIterator = mStackCallAddresses.iterator();
-
- if (mResolvedStackCall == null) {
- return buffer.toString();
- }
+ buffer.append('\n');
+
+ if (mResolvedStackCall != null) {
+ buffer.append(BEGIN_STACKTRACE_KW);
+ buffer.append('\n');
+ for (NativeStackCallInfo source : mResolvedStackCall) {
+ long addr = source.getAddress();
+ if (addr == 0) {
+ continue;
+ }
- Iterator<NativeStackCallInfo> sourceIterator = mResolvedStackCall.iterator();
-
- while (sourceIterator.hasNext()) {
- long addr = addrIterator.next();
- NativeStackCallInfo source = sourceIterator.next();
- if (addr == 0)
- continue;
-
- if (source.getLineNumber() != -1) {
- buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d\n", addr,
- source.getLibraryName(), source.getMethodName(),
- source.getSourceFile(), source.getLineNumber()));
- } else {
- buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s\n", addr,
- source.getLibraryName(), source.getMethodName(), source.getSourceFile()));
+ if (source.getLineNumber() != -1) {
+ buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d\n", addr,
+ source.getLibraryName(), source.getMethodName(),
+ source.getSourceFile(), source.getLineNumber()));
+ } else {
+ buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s\n", addr,
+ source.getLibraryName(), source.getMethodName(), source.getSourceFile()));
+ }
}
+ buffer.append(END_STACKTRACE_KW);
+ buffer.append('\n');
}
return buffer.toString();
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapDataImporter.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapDataImporter.java
new file mode 100644
index 0000000..9c53b18
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapDataImporter.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmuilib.heap;
+
+import com.android.ddmlib.NativeAllocationInfo;
+import com.android.ddmlib.NativeStackCallInfo;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.InputMismatchException;
+import java.util.List;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+public class NativeHeapDataImporter implements IRunnableWithProgress {
+ private LineNumberReader mReader;
+ private int mStartLineNumber;
+ private int mEndLineNumber;
+
+ private NativeHeapSnapshot mSnapshot;
+
+ public NativeHeapDataImporter(Reader stream) {
+ mReader = new LineNumberReader(stream);
+ mReader.setLineNumber(1); // start numbering at 1
+ }
+
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ monitor.beginTask("Importing Heap Data", IProgressMonitor.UNKNOWN);
+
+ List<NativeAllocationInfo> allocations = new ArrayList<NativeAllocationInfo>();
+ try {
+ while (true) {
+ String line;
+ StringBuilder sb = new StringBuilder();
+
+ // read in a sequence of lines corresponding to a single NativeAllocationInfo
+ mStartLineNumber = mReader.getLineNumber();
+ while ((line = mReader.readLine()) != null) {
+ if (line.trim().length() == 0) {
+ // each block of allocations end with an empty line
+ break;
+ }
+
+ sb.append(line);
+ sb.append('\n');
+ }
+ mEndLineNumber = mReader.getLineNumber();
+
+ // parse those lines into a NativeAllocationInfo object
+ String allocationBlock = sb.toString();
+ if (allocationBlock.trim().length() > 0) {
+ allocations.add(getNativeAllocation(allocationBlock));
+ }
+
+ if (line == null) { // EOF
+ break;
+ }
+ }
+ } catch (Exception e) {
+ if (e.getMessage() == null) {
+ e = new RuntimeException(genericErrorMessage("Unexpected Parse error"));
+ }
+ throw new InvocationTargetException(e);
+ } finally {
+ try {
+ mReader.close();
+ } catch (IOException e) {
+ // we can ignore this exception
+ }
+ monitor.done();
+ }
+
+ mSnapshot = new NativeHeapSnapshot(allocations);
+ }
+
+ /** Parse a single native allocation dump. This is the complement of
+ * {@link NativeAllocationInfo#toString()}.
+ *
+ * An allocation is of the following form:
+ * Allocations: 1
+ * Size: 344748
+ * Total Size: 344748
+ * BeginStackTrace:
+ * 40069bd8 /lib/libc_malloc_leak.so --- get_backtrace --- /libc/bionic/malloc_leak.c:258
+ * 40069dd8 /lib/libc_malloc_leak.so --- leak_calloc --- /libc/bionic/malloc_leak.c:576
+ * 40069bd8 /lib/libc_malloc_leak.so --- 40069bd8 ---
+ * 40069dd8 /lib/libc_malloc_leak.so --- 40069dd8 ---
+ * EndStackTrace
+ * Note that in the above stack trace, the last two lines are examples where the address
+ * was not resolved.
+ *
+ * @param block a string of lines corresponding to a single {@code NativeAllocationInfo}
+ * @return parse the input and return the corresponding {@link NativeAllocationInfo}
+ * @throws InputMismatchException if there are any parse errors
+ */
+ private NativeAllocationInfo getNativeAllocation(String block) {
+ Scanner sc = new Scanner(block);
+
+ String kw = sc.next();
+ if (!NativeAllocationInfo.ALLOCATIONS_KW.equals(kw)) {
+ throw new InputMismatchException(
+ expectedKeywordErrorMessage(NativeAllocationInfo.ALLOCATIONS_KW, kw));
+ }
+
+ int allocations = sc.nextInt();
+
+ kw = sc.next();
+ if (!NativeAllocationInfo.SIZE_KW.equals(kw)) {
+ throw new InputMismatchException(
+ expectedKeywordErrorMessage(NativeAllocationInfo.SIZE_KW, kw));
+ }
+
+ int size = sc.nextInt();
+
+ kw = sc.next();
+ if (!NativeAllocationInfo.TOTAL_SIZE_KW.equals(kw)) {
+ throw new InputMismatchException(
+ expectedKeywordErrorMessage(NativeAllocationInfo.TOTAL_SIZE_KW, kw));
+ }
+
+ int totalSize = sc.nextInt();
+ if (totalSize != size * allocations) {
+ throw new InputMismatchException(
+ genericErrorMessage("Total Size does not match size * # of allocations"));
+ }
+
+ NativeAllocationInfo info = new NativeAllocationInfo(size, allocations);
+
+ kw = sc.next();
+ if (!NativeAllocationInfo.BEGIN_STACKTRACE_KW.equals(kw)) {
+ throw new InputMismatchException(
+ expectedKeywordErrorMessage(NativeAllocationInfo.BEGIN_STACKTRACE_KW, kw));
+ }
+
+ List<NativeStackCallInfo> stackInfo = new ArrayList<NativeStackCallInfo>();
+ Pattern endTracePattern = Pattern.compile(NativeAllocationInfo.END_STACKTRACE_KW);
+
+ while (true) {
+ long address = sc.nextLong(16);
+ info.addStackCallAddress(address);
+
+ String library = sc.next();
+ sc.next(); // ignore "---"
+ String method = scanTillSeparator(sc, "---");
+
+ String filename = "";
+ if (!isUnresolved(method, address)) {
+ filename = sc.next();
+ }
+
+ stackInfo.add(new NativeStackCallInfo(address, library, method, filename));
+
+ if (sc.hasNext(endTracePattern)) {
+ break;
+ }
+ }
+
+ info.setResolvedStackCall(stackInfo);
+ return info;
+ }
+
+ private String scanTillSeparator(Scanner sc, String separator) {
+ StringBuilder sb = new StringBuilder();
+
+ while (true) {
+ String token = sc.next();
+ if (token.equals(separator)) {
+ break;
+ }
+
+ sb.append(token);
+ }
+
+ return sb.toString();
+ }
+
+ private boolean isUnresolved(String method, long address) {
+ // a method is unresolved if it is just the hex representation of the address
+ return Long.toString(address, 16).equals(method);
+ }
+
+ private String genericErrorMessage(String message) {
+ return String.format("%1$s between lines %2$d and %3$d",
+ message, mStartLineNumber, mEndLineNumber);
+ }
+
+ private String expectedKeywordErrorMessage(String expected, String actual) {
+ return String.format("Expected keyword '%1$s', saw '%2$s' between lines %3$d to %4$d.",
+ expected, actual, mStartLineNumber, mEndLineNumber);
+ }
+
+ public NativeHeapSnapshot getImportedSnapshot() {
+ return mSnapshot;
+ }
+}
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
index 6af195c..5bbd487 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
@@ -29,6 +29,7 @@ import com.android.ddmuilib.TableHelper;
import com.android.ddmuilib.ITableFocusListener.IFocusedTableActivator;
import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
@@ -57,6 +58,7 @@ import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
@@ -64,9 +66,14 @@ import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -86,6 +93,7 @@ public class NativeHeapPanel extends BaseHeapPanel {
private static final String GROUPBY_IMAGE = "groupby.png";
private static final String SNAPSHOT_HEAP_BUTTON_TEXT = "Snapshot Current Native Heap Usage";
+ private static final String LOAD_HEAP_DATA_BUTTON_TEXT = "Import Heap Data";
private static final String SYMBOL_SEARCH_PATH_LABEL_TEXT = "Symbol Search Path:";
private static final String SYMBOL_SEARCH_PATH_TEXT_MESSAGE =
"List of colon separated paths to search for symbol debug information. See tooltip for examples.";
@@ -98,6 +106,7 @@ public class NativeHeapPanel extends BaseHeapPanel {
private static final String PREFS_GROUP_BY_LIBRARY = "nativeheap.grouby.library";
private static final String PREFS_SYMBOL_SEARCH_PATH = "nativeheap.search.path";
private static final String PREFS_SASH_HEIGHT_PERCENT = "nativeheap.sash.percent";
+ private static final String PREFS_LAST_IMPORTED_HEAPPATH = "nativeheap.last.import.path";
private IPreferenceStore mPrefStore;
private List<NativeHeapSnapshot> mNativeHeapSnapshots;
@@ -108,7 +117,10 @@ public class NativeHeapPanel extends BaseHeapPanel {
// The list is filled lazily on demand.
private List<NativeHeapSnapshot> mDiffSnapshots;
+ private Map<Integer, List<NativeHeapSnapshot>> mImportedSnapshotsPerPid;
+
private Button mSnapshotHeapButton;
+ private Button mLoadHeapDataButton;
private Text mSymbolSearchPathText;
private Combo mSnapshotIndexCombo;
private Label mMemoryAllocatedText;
@@ -135,6 +147,7 @@ public class NativeHeapPanel extends BaseHeapPanel {
mNativeHeapSnapshots = new ArrayList<NativeHeapSnapshot>();
mDiffSnapshots = new ArrayList<NativeHeapSnapshot>();
+ mImportedSnapshotsPerPid = new HashMap<Integer, List<NativeHeapSnapshot>>();
}
/** {@inheritDoc} */
@@ -188,12 +201,22 @@ public class NativeHeapPanel extends BaseHeapPanel {
public void clientSelected() {
Client c = getCurrentClient();
- mSnapshotHeapButton.setEnabled(c != null);
-
mNativeHeapSnapshots = new ArrayList<NativeHeapSnapshot>();
mDiffSnapshots = new ArrayList<NativeHeapSnapshot>();
if (c != null) {
+ mSnapshotHeapButton.setEnabled(true);
+ mLoadHeapDataButton.setEnabled(true);
+
+ List<NativeHeapSnapshot> importedSnapshots = mImportedSnapshotsPerPid.get(
+ c.getClientData().getPid());
+ if (importedSnapshots != null) {
+ for (NativeHeapSnapshot n : importedSnapshots) {
+ mNativeHeapSnapshots.add(n);
+ mDiffSnapshots.add(null);
+ }
+ }
+
List<NativeAllocationInfo> allocations = c.getClientData().getNativeAllocationList();
allocations = shallowCloneList(allocations);
@@ -201,6 +224,9 @@ public class NativeHeapPanel extends BaseHeapPanel {
mNativeHeapSnapshots.add(new NativeHeapSnapshot(allocations));
mDiffSnapshots.add(null); // filled in lazily on demand
}
+ } else {
+ mSnapshotHeapButton.setEnabled(false);
+ mLoadHeapDataButton.setEnabled(false);
}
updateDisplay();
@@ -339,7 +365,7 @@ public class NativeHeapPanel extends BaseHeapPanel {
c.setLayout(new GridLayout(3, false));
c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- createGetHeapDataButton(c);
+ createGetHeapDataSection(c);
Label l = new Label(c, SWT.SEPARATOR | SWT.VERTICAL);
l.setLayoutData(new GridData(GridData.FILL_VERTICAL));
@@ -347,7 +373,19 @@ public class NativeHeapPanel extends BaseHeapPanel {
createDisplaySection(c);
}
- private void createGetHeapDataButton(Composite parent) {
+ private void createGetHeapDataSection(Composite parent) {
+ Composite c = new Composite(parent, SWT.NONE);
+ c.setLayout(new GridLayout(1, false));
+
+ createTakeHeapSnapshotButton(c);
+
+ Label l = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL);
+ l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ createLoadHeapDataButton(c);
+ }
+
+ private void createTakeHeapSnapshotButton(Composite parent) {
mSnapshotHeapButton = new Button(parent, SWT.BORDER | SWT.PUSH);
mSnapshotHeapButton.setText(SNAPSHOT_HEAP_BUTTON_TEXT);
mSnapshotHeapButton.setLayoutData(new GridData());
@@ -371,6 +409,95 @@ public class NativeHeapPanel extends BaseHeapPanel {
c.requestNativeHeapInformation();
}
+ private void createLoadHeapDataButton(Composite parent) {
+ mLoadHeapDataButton = new Button(parent, SWT.BORDER | SWT.PUSH);
+ mLoadHeapDataButton.setText(LOAD_HEAP_DATA_BUTTON_TEXT);
+ mLoadHeapDataButton.setLayoutData(new GridData());
+
+ // disable by default, enabled only when a client is selected
+ mLoadHeapDataButton.setEnabled(false);
+
+ mLoadHeapDataButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent evt) {
+ loadHeapDataFromFile();
+ }
+ });
+ }
+
+ private void loadHeapDataFromFile() {
+ // pop up a file dialog and get the file to load
+ final String path = getHeapDumpToImport();
+ if (path == null) {
+ return;
+ }
+
+ Reader reader = null;
+ try {
+ reader = new FileReader(path);
+ } catch (FileNotFoundException e) {
+ // cannot occur since user input was via a FileDialog
+ }
+
+ Shell shell = Display.getDefault().getActiveShell();
+ ProgressMonitorDialog d = new ProgressMonitorDialog(shell);
+
+ NativeHeapDataImporter importer = new NativeHeapDataImporter(reader);
+ try {
+ d.run(true, true, importer);
+ } catch (InvocationTargetException e) {
+ // exception while parsing, display error to user and then return
+ MessageDialog.openError(shell,
+ "Error Importing Heap Data",
+ e.getCause().getMessage());
+ return;
+ } catch (InterruptedException e) {
+ // operation cancelled by user, simply return
+ return;
+ }
+
+ NativeHeapSnapshot snapshot = importer.getImportedSnapshot();
+
+ addToImportedSnapshots(snapshot); // save imported snapshot for future use
+ mNativeHeapSnapshots.add(snapshot); // add to currently displayed snapshots as well
+ mDiffSnapshots.add(null);
+
+ updateDisplay();
+ }
+
+ private void addToImportedSnapshots(NativeHeapSnapshot snapshot) {
+ Client c = getCurrentClient();
+
+ if (c == null) {
+ return;
+ }
+
+ Integer pid = c.getClientData().getPid();
+ List<NativeHeapSnapshot> importedSnapshots = mImportedSnapshotsPerPid.get(pid);
+ if (importedSnapshots == null) {
+ importedSnapshots = new ArrayList<NativeHeapSnapshot>();
+ }
+
+ importedSnapshots.add(snapshot);
+ mImportedSnapshotsPerPid.put(pid, importedSnapshots);
+ }
+
+ private String getHeapDumpToImport() {
+ FileDialog fileDialog = new FileDialog(Display.getDefault().getActiveShell(),
+ SWT.OPEN);
+
+ fileDialog.setText("Import Heap Dump");
+ fileDialog.setFilterExtensions(new String[] {"*.txt"});
+ fileDialog.setFilterPath(mPrefStore.getString(PREFS_LAST_IMPORTED_HEAPPATH));
+
+ String selectedFile = fileDialog.open();
+ if (selectedFile != null) {
+ // save the path to restore in future dialog open
+ mPrefStore.setValue(PREFS_LAST_IMPORTED_HEAPPATH, new File(selectedFile).getParent());
+ }
+ return selectedFile;
+ }
+
private void createDisplaySection(Composite parent) {
Composite c = new Composite(parent, SWT.NONE);
c.setLayout(new GridLayout(2, false));
diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/heap/NativeHeapDataImporterTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/heap/NativeHeapDataImporterTest.java
new file mode 100644
index 0000000..4487454
--- /dev/null
+++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/heap/NativeHeapDataImporterTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmuilib.heap;
+
+import com.android.ddmlib.NativeAllocationInfo;
+import com.android.ddmlib.NativeStackCallInfo;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+public class NativeHeapDataImporterTest extends TestCase {
+ private static final String BASIC_TEXT =
+ "Allocations: 1\n" +
+ "Size: 524292\n" +
+ "TotalSize: 524292\n" +
+ "BeginStacktrace:\n" +
+ " 40170bd8 /libc_malloc_leak.so --- getbacktrace --- /b/malloc_leak.c:258\n" +
+ " 400910d6 /lib/libc.so --- ca110c --- /bionic/malloc_debug_common.c:227\n" +
+ " 5dd6abfe /lib/libcgdrv.so --- 5dd6abfe ---\n" +
+ " 5dd98a8e /lib/libcgdrv.so --- 5dd98a8e ---\n" +
+ "EndStacktrace\n";
+
+ private NativeHeapDataImporter mImporter;
+
+ public void testImportValidAllocation() {
+ mImporter = createImporter(BASIC_TEXT);
+ try {
+ mImporter.run(new NullProgressMonitor());
+ } catch (InvocationTargetException e) {
+ fail("Unexpected exception while parsing text: " + e.getTargetException().getMessage());
+ } catch (InterruptedException e) {
+ fail("Tests are not interrupted!");
+ }
+
+ NativeHeapSnapshot snapshot = mImporter.getImportedSnapshot();
+ assertNotNull(snapshot);
+
+ // check whether all details have been parsed correctly
+ assertEquals(1, snapshot.getAllocations().size());
+
+ NativeAllocationInfo info = snapshot.getAllocations().get(0);
+
+ assertEquals(1, info.getAllocationCount());
+ assertEquals(524292, info.getSize());
+ assertEquals(true, info.isStackCallResolved());
+
+ List<NativeStackCallInfo> stack = info.getResolvedStackCall();
+ assertEquals(4, stack.size());
+ }
+
+ private NativeHeapDataImporter createImporter(String contentsToParse) {
+ StringReader r = new StringReader(contentsToParse);
+ return new NativeHeapDataImporter(r);
+ }
+}