diff options
author | Siva Velusamy <vsiva@google.com> | 2011-10-06 09:35:33 -0700 |
---|---|---|
committer | Siva Velusamy <vsiva@google.com> | 2011-10-06 11:34:32 -0700 |
commit | ed552915f84f9f6acb5c34f3b9e413d0b1534af3 (patch) | |
tree | 630f60f046506aba733f1fa41d04421a0b07c829 /ddms | |
parent | 9a8de7350e1f8fb3a99c62c769e448d8e277f8fb (diff) | |
download | sdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.zip sdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.tar.gz sdk-ed552915f84f9f6acb5c34f3b9e413d0b1534af3.tar.bz2 |
Add support for importing saved heap data.
Change-Id: I8552af1754f5093dcdb156f7f81e3beef776835a
Diffstat (limited to 'ddms')
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); + } +} |