aboutsummaryrefslogtreecommitdiffstats
path: root/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
diff options
context:
space:
mode:
Diffstat (limited to 'ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java')
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java1152
1 files changed, 0 insertions, 1152 deletions
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
deleted file mode 100644
index 5f7abe2..0000000
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/heap/NativeHeapPanel.java
+++ /dev/null
@@ -1,1152 +0,0 @@
-/*
- * 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.Client;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.NativeAllocationInfo;
-import com.android.ddmlib.NativeLibraryMapInfo;
-import com.android.ddmlib.NativeStackCallInfo;
-import com.android.ddmuilib.Addr2Line;
-import com.android.ddmuilib.BaseHeapPanel;
-import com.android.ddmuilib.ITableFocusListener;
-import com.android.ddmuilib.ITableFocusListener.IFocusedTableActivator;
-import com.android.ddmuilib.ImageLoader;
-import com.android.ddmuilib.TableHelper;
-
-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;
-import org.eclipse.swt.dnd.Clipboard;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FormAttachment;
-import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-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;
-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.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Panel to display native heap information. */
-public class NativeHeapPanel extends BaseHeapPanel {
- private static final boolean USE_OLD_RESOLVER;
- static {
- String useOldResolver = System.getenv("ANDROID_DDMS_OLD_SYMRESOLVER");
- if (useOldResolver != null && useOldResolver.equalsIgnoreCase("true")) {
- USE_OLD_RESOLVER = true;
- } else {
- USE_OLD_RESOLVER = false;
- }
- }
- private final int MAX_DISPLAYED_ERROR_ITEMS = 5;
-
- private static final String TOOLTIP_EXPORT_DATA = "Export Heap Data";
- private static final String TOOLTIP_ZYGOTE_ALLOCATIONS = "Show Zygote Allocations";
- private static final String TOOLTIP_DIFFS_ONLY = "Only show new allocations not present in previous snapshot";
- private static final String TOOLTIP_GROUPBY = "Group allocations by library.";
-
- private static final String EXPORT_DATA_IMAGE = "save.png";
- private static final String ZYGOTE_IMAGE = "zygote.png";
- private static final String DIFFS_ONLY_IMAGE = "diff.png";
- 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.";
- private static final String SYMBOL_SEARCH_PATH_TOOLTIP_TEXT =
- "Colon separated paths that contain unstripped libraries with debug symbols.\n"
- + "e.g.: <android-src>/out/target/product/generic/symbols/system/lib:/path/to/my/app/obj/local/armeabi";
-
- private static final String PREFS_SHOW_DIFFS_ONLY = "nativeheap.show.diffs.only";
- private static final String PREFS_SHOW_ZYGOTE_ALLOCATIONS = "nativeheap.show.zygote";
- 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;
-
- // Maintain the differences between a snapshot and its predecessor.
- // mDiffSnapshots[i] = mNativeHeapSnapshots[i] - mNativeHeapSnapshots[i-1]
- // The zeroth entry is null since there is no predecessor.
- // 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;
-
- private TreeViewer mDetailsTreeViewer;
- private TreeViewer mStackTraceTreeViewer;
- private NativeHeapProviderByAllocations mContentProviderByAllocations;
- private NativeHeapProviderByLibrary mContentProviderByLibrary;
- private NativeHeapLabelProvider mDetailsTreeLabelProvider;
-
- private ToolBar mDetailsToolBar;
- private ToolItem mGroupByButton;
- private ToolItem mDiffsOnlyButton;
- private ToolItem mShowZygoteAllocationsButton;
- private ToolItem mExportHeapDataButton;
-
- public NativeHeapPanel(IPreferenceStore prefStore) {
- mPrefStore = prefStore;
- mPrefStore.setDefault(PREFS_SASH_HEIGHT_PERCENT, 75);
- mPrefStore.setDefault(PREFS_SYMBOL_SEARCH_PATH, "");
- mPrefStore.setDefault(PREFS_GROUP_BY_LIBRARY, false);
- mPrefStore.setDefault(PREFS_SHOW_ZYGOTE_ALLOCATIONS, true);
- mPrefStore.setDefault(PREFS_SHOW_DIFFS_ONLY, false);
-
- mNativeHeapSnapshots = new ArrayList<NativeHeapSnapshot>();
- mDiffSnapshots = new ArrayList<NativeHeapSnapshot>();
- mImportedSnapshotsPerPid = new HashMap<Integer, List<NativeHeapSnapshot>>();
- }
-
- /** {@inheritDoc} */
- @Override
- public void clientChanged(final Client client, int changeMask) {
- if (client != getCurrentClient()) {
- return;
- }
-
- if ((changeMask & Client.CHANGE_NATIVE_HEAP_DATA) != Client.CHANGE_NATIVE_HEAP_DATA) {
- return;
- }
-
- List<NativeAllocationInfo> allocations = client.getClientData().getNativeAllocationList();
- if (allocations.size() == 0) {
- return;
- }
-
- // We need to clone this list since getClientData().getNativeAllocationList() clobbers
- // the list on future updates
- final List<NativeAllocationInfo> nativeAllocations = shallowCloneList(allocations);
-
- addNativeHeapSnapshot(new NativeHeapSnapshot(nativeAllocations));
- updateDisplay();
-
- // Attempt to resolve symbols in a separate thread.
- // The UI should be refreshed once the symbols have been resolved.
- if (USE_OLD_RESOLVER) {
- Thread t = new Thread(new SymbolResolverTask(nativeAllocations,
- client.getClientData().getMappedNativeLibraries()));
- t.setName("Address to Symbol Resolver");
- t.start();
- } else {
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- resolveSymbols();
- mDetailsTreeViewer.refresh();
- mStackTraceTreeViewer.refresh();
- }
-
- public void resolveSymbols() {
- Shell shell = Display.getDefault().getActiveShell();
- ProgressMonitorDialog d = new ProgressMonitorDialog(shell);
-
- NativeSymbolResolverTask resolver = new NativeSymbolResolverTask(
- nativeAllocations,
- client.getClientData().getMappedNativeLibraries(),
- mSymbolSearchPathText.getText());
-
- try {
- d.run(true, true, resolver);
- } catch (InvocationTargetException e) {
- MessageDialog.openError(shell,
- "Error Resolving Symbols",
- e.getCause().getMessage());
- return;
- } catch (InterruptedException e) {
- return;
- }
-
- MessageDialog.openInformation(shell, "Symbol Resolution Status",
- getResolutionStatusMessage(resolver));
- }
- });
- }
- }
-
- private String getResolutionStatusMessage(NativeSymbolResolverTask resolver) {
- StringBuilder sb = new StringBuilder();
- sb.append("Symbol Resolution Complete.\n\n");
-
- // show addresses that were not mapped
- Set<Long> unmappedAddresses = resolver.getUnmappedAddresses();
- if (unmappedAddresses.size() > 0) {
- sb.append(String.format("Unmapped addresses (%d): ",
- unmappedAddresses.size()));
- sb.append(getSampleForDisplay(unmappedAddresses));
- sb.append('\n');
- }
-
- // show libraries that were not present on disk
- Set<String> notFoundLibraries = resolver.getNotFoundLibraries();
- if (notFoundLibraries.size() > 0) {
- sb.append(String.format("Libraries not found on disk (%d): ",
- notFoundLibraries.size()));
- sb.append(getSampleForDisplay(notFoundLibraries));
- sb.append('\n');
- }
-
- // show addresses that were mapped but not resolved
- Set<Long> unresolvableAddresses = resolver.getUnresolvableAddresses();
- if (unresolvableAddresses.size() > 0) {
- sb.append(String.format("Unresolved addresses (%d): ",
- unresolvableAddresses.size()));
- sb.append(getSampleForDisplay(unresolvableAddresses));
- sb.append('\n');
- }
-
- if (resolver.getAddr2LineErrorMessage() != null) {
- sb.append("Error launching addr2line: ");
- sb.append(resolver.getAddr2LineErrorMessage());
- }
-
- return sb.toString();
- }
-
- /**
- * Get the string representation for a collection of items.
- * If there are more items than {@link #MAX_DISPLAYED_ERROR_ITEMS}, then only the first
- * {@link #MAX_DISPLAYED_ERROR_ITEMS} items are taken into account,
- * and an ellipsis is added at the end.
- */
- private String getSampleForDisplay(Collection<?> items) {
- StringBuilder sb = new StringBuilder();
-
- int c = 1;
- Iterator<?> it = items.iterator();
- while (it.hasNext()) {
- Object item = it.next();
- if (item instanceof Long) {
- sb.append(String.format("0x%x", item));
- } else {
- sb.append(item);
- }
-
- if (c == MAX_DISPLAYED_ERROR_ITEMS && it.hasNext()) {
- sb.append(", ...");
- break;
- } else if (it.hasNext()) {
- sb.append(", ");
- }
-
- c++;
- }
- return sb.toString();
- }
-
- private void addNativeHeapSnapshot(NativeHeapSnapshot snapshot) {
- mNativeHeapSnapshots.add(snapshot);
-
- // The diff snapshots are filled in lazily on demand.
- // But the list needs to be the same size as mNativeHeapSnapshots, so we add a null.
- mDiffSnapshots.add(null);
- }
-
- private List<NativeAllocationInfo> shallowCloneList(List<NativeAllocationInfo> allocations) {
- List<NativeAllocationInfo> clonedList =
- new ArrayList<NativeAllocationInfo>(allocations.size());
-
- for (NativeAllocationInfo i : allocations) {
- clonedList.add(i);
- }
-
- return clonedList;
- }
-
- @Override
- public void deviceSelected() {
- // pass
- }
-
- @Override
- public void clientSelected() {
- Client c = getCurrentClient();
-
- if (c == null) {
- // if there is no client selected, then we disable the buttons but leave the
- // display as is so that whatever snapshots are displayed continue to stay
- // visible to the user.
- mSnapshotHeapButton.setEnabled(false);
- mLoadHeapDataButton.setEnabled(false);
- return;
- }
-
- mNativeHeapSnapshots = new ArrayList<NativeHeapSnapshot>();
- mDiffSnapshots = new ArrayList<NativeHeapSnapshot>();
-
- mSnapshotHeapButton.setEnabled(true);
- mLoadHeapDataButton.setEnabled(true);
-
- List<NativeHeapSnapshot> importedSnapshots = mImportedSnapshotsPerPid.get(
- c.getClientData().getPid());
- if (importedSnapshots != null) {
- for (NativeHeapSnapshot n : importedSnapshots) {
- addNativeHeapSnapshot(n);
- }
- }
-
- List<NativeAllocationInfo> allocations = c.getClientData().getNativeAllocationList();
- allocations = shallowCloneList(allocations);
-
- if (allocations.size() > 0) {
- addNativeHeapSnapshot(new NativeHeapSnapshot(allocations));
- }
-
- updateDisplay();
- }
-
- private void updateDisplay() {
- Display.getDefault().syncExec(new Runnable() {
- @Override
- public void run() {
- updateSnapshotIndexCombo();
- updateToolbars();
-
- int lastSnapshotIndex = mNativeHeapSnapshots.size() - 1;
- displaySnapshot(lastSnapshotIndex);
- displayStackTraceForSelection();
- }
- });
- }
-
- private void displaySelectedSnapshot() {
- Display.getDefault().syncExec(new Runnable() {
- @Override
- public void run() {
- int idx = mSnapshotIndexCombo.getSelectionIndex();
- displaySnapshot(idx);
- }
- });
- }
-
- private void displaySnapshot(int index) {
- if (index < 0 || mNativeHeapSnapshots.size() == 0) {
- mDetailsTreeViewer.setInput(null);
- mMemoryAllocatedText.setText("");
- return;
- }
-
- assert index < mNativeHeapSnapshots.size() : "Invalid snapshot index";
-
- NativeHeapSnapshot snapshot = mNativeHeapSnapshots.get(index);
- if (mDiffsOnlyButton.getSelection() && index > 0) {
- snapshot = getDiffSnapshot(index);
- }
-
- mMemoryAllocatedText.setText(snapshot.getFormattedMemorySize());
- mMemoryAllocatedText.pack();
-
- mDetailsTreeLabelProvider.setTotalSize(snapshot.getTotalSize());
- mDetailsTreeViewer.setInput(snapshot);
- mDetailsTreeViewer.refresh();
- }
-
- /** Obtain the diff of snapshot[index] & snapshot[index-1] */
- private NativeHeapSnapshot getDiffSnapshot(int index) {
- // if it was already computed, simply return that
- NativeHeapSnapshot diffSnapshot = mDiffSnapshots.get(index);
- if (diffSnapshot != null) {
- return diffSnapshot;
- }
-
- // compute the diff
- NativeHeapSnapshot cur = mNativeHeapSnapshots.get(index);
- NativeHeapSnapshot prev = mNativeHeapSnapshots.get(index - 1);
- diffSnapshot = new NativeHeapDiffSnapshot(cur, prev);
-
- // cache for future use
- mDiffSnapshots.set(index, diffSnapshot);
-
- return diffSnapshot;
- }
-
- private void updateDisplayGrouping() {
- boolean groupByLibrary = mGroupByButton.getSelection();
- mPrefStore.setValue(PREFS_GROUP_BY_LIBRARY, groupByLibrary);
-
- if (groupByLibrary) {
- mDetailsTreeViewer.setContentProvider(mContentProviderByLibrary);
- } else {
- mDetailsTreeViewer.setContentProvider(mContentProviderByAllocations);
- }
- }
-
- private void updateDisplayForZygotes() {
- boolean displayZygoteMemory = mShowZygoteAllocationsButton.getSelection();
- mPrefStore.setValue(PREFS_SHOW_ZYGOTE_ALLOCATIONS, displayZygoteMemory);
-
- // inform the content providers of the zygote display setting
- mContentProviderByLibrary.displayZygoteMemory(displayZygoteMemory);
- mContentProviderByAllocations.displayZygoteMemory(displayZygoteMemory);
-
- // refresh the UI
- mDetailsTreeViewer.refresh();
- }
-
- private void updateSnapshotIndexCombo() {
- List<String> items = new ArrayList<String>();
-
- int numSnapshots = mNativeHeapSnapshots.size();
- for (int i = 0; i < numSnapshots; i++) {
- // offset indices by 1 so that users see index starting at 1 rather than 0
- items.add("Snapshot " + (i + 1));
- }
-
- mSnapshotIndexCombo.setItems(items.toArray(new String[0]));
-
- if (numSnapshots > 0) {
- mSnapshotIndexCombo.setEnabled(true);
- mSnapshotIndexCombo.select(numSnapshots - 1);
- } else {
- mSnapshotIndexCombo.setEnabled(false);
- }
- }
-
- private void updateToolbars() {
- int numSnapshots = mNativeHeapSnapshots.size();
- mExportHeapDataButton.setEnabled(numSnapshots > 0);
- }
-
- @Override
- protected Control createControl(Composite parent) {
- parent.setLayout(new GridLayout(1, false));
-
- Composite c = new Composite(parent, SWT.NONE);
- c.setLayout(new GridLayout(1, false));
- c.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- createControlsSection(c);
- createDetailsSection(c);
-
- // Initialize widget state based on whether a client
- // is selected or not.
- clientSelected();
-
- return c;
- }
-
- private void createControlsSection(Composite parent) {
- Composite c = new Composite(parent, SWT.NONE);
- c.setLayout(new GridLayout(3, false));
- c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- createGetHeapDataSection(c);
-
- Label l = new Label(c, SWT.SEPARATOR | SWT.VERTICAL);
- l.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-
- createDisplaySection(c);
- }
-
- 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());
-
- // disable by default, enabled only when a client is selected
- mSnapshotHeapButton.setEnabled(false);
-
- mSnapshotHeapButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent evt) {
- snapshotHeap();
- }
- });
- }
-
- private void snapshotHeap() {
- Client c = getCurrentClient();
- assert c != null : "Snapshot Heap could not have been enabled w/o a selected client.";
-
- // send an async request
- 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
- addNativeHeapSnapshot(snapshot); // add to currently displayed snapshots as well
-
- 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));
- c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- // Create: Display: __________________
- createLabel(c, "Display:");
- mSnapshotIndexCombo = new Combo(c, SWT.NONE | SWT.READ_ONLY);
- mSnapshotIndexCombo.setItems(new String[] {"No heap snapshots available."});
- mSnapshotIndexCombo.setEnabled(false);
- mSnapshotIndexCombo.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- displaySelectedSnapshot();
- }
- });
-
- // Create: Memory Allocated (bytes): _________________
- createLabel(c, "Memory Allocated:");
- mMemoryAllocatedText = new Label(c, SWT.NONE);
- GridData gd = new GridData();
- gd.widthHint = 100;
- mMemoryAllocatedText.setLayoutData(gd);
-
- // Create: Search Path: __________________
- createLabel(c, SYMBOL_SEARCH_PATH_LABEL_TEXT);
- mSymbolSearchPathText = new Text(c, SWT.BORDER);
- mSymbolSearchPathText.setMessage(SYMBOL_SEARCH_PATH_TEXT_MESSAGE);
- mSymbolSearchPathText.setToolTipText(SYMBOL_SEARCH_PATH_TOOLTIP_TEXT);
- mSymbolSearchPathText.addModifyListener(new ModifyListener() {
- @Override
- public void modifyText(ModifyEvent arg0) {
- String path = mSymbolSearchPathText.getText();
- updateSearchPath(path);
- mPrefStore.setValue(PREFS_SYMBOL_SEARCH_PATH, path);
- }
- });
- mSymbolSearchPathText.setText(mPrefStore.getString(PREFS_SYMBOL_SEARCH_PATH));
- mSymbolSearchPathText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- }
-
- private void updateSearchPath(String path) {
- Addr2Line.setSearchPath(path);
- }
-
- private void createLabel(Composite parent, String text) {
- Label l = new Label(parent, SWT.NONE);
- l.setText(text);
- GridData gd = new GridData();
- gd.horizontalAlignment = SWT.RIGHT;
- l.setLayoutData(gd);
- }
-
- /**
- * Create the details section displaying the details table and the stack trace
- * corresponding to the selection.
- *
- * The details is laid out like so:
- * Details Toolbar
- * Details Table
- * ------------sash---
- * Stack Trace Label
- * Stack Trace Text
- * There is a sash in between the two sections, and we need to save/restore the sash
- * preferences. Using FormLayout seems like the easiest solution here, but the layout
- * code looks ugly as a result.
- */
- private void createDetailsSection(Composite parent) {
- final Composite c = new Composite(parent, SWT.NONE);
- c.setLayout(new FormLayout());
- c.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- mDetailsToolBar = new ToolBar(c, SWT.FLAT | SWT.BORDER);
- initializeDetailsToolBar(mDetailsToolBar);
-
- Tree detailsTree = new Tree(c, SWT.VIRTUAL | SWT.BORDER | SWT.MULTI);
- initializeDetailsTree(detailsTree);
-
- final Sash sash = new Sash(c, SWT.HORIZONTAL | SWT.BORDER);
-
- Label stackTraceLabel = new Label(c, SWT.NONE);
- stackTraceLabel.setText("Stack Trace:");
-
- Tree stackTraceTree = new Tree(c, SWT.BORDER | SWT.MULTI);
- initializeStackTraceTree(stackTraceTree);
-
- // layout the widgets created above
- FormData data = new FormData();
- data.top = new FormAttachment(0, 0);
- data.left = new FormAttachment(0, 0);
- data.right = new FormAttachment(100, 0);
- mDetailsToolBar.setLayoutData(data);
-
- data = new FormData();
- data.top = new FormAttachment(mDetailsToolBar, 0);
- data.bottom = new FormAttachment(sash, 0);
- data.left = new FormAttachment(0, 0);
- data.right = new FormAttachment(100, 0);
- detailsTree.setLayoutData(data);
-
- final FormData sashData = new FormData();
- sashData.top = new FormAttachment(mPrefStore.getInt(PREFS_SASH_HEIGHT_PERCENT), 0);
- sashData.left = new FormAttachment(0, 0);
- sashData.right = new FormAttachment(100, 0);
- sash.setLayoutData(sashData);
-
- data = new FormData();
- data.top = new FormAttachment(sash, 0);
- data.left = new FormAttachment(0, 0);
- data.right = new FormAttachment(100, 0);
- stackTraceLabel.setLayoutData(data);
-
- data = new FormData();
- data.top = new FormAttachment(stackTraceLabel, 0);
- data.left = new FormAttachment(0, 0);
- data.bottom = new FormAttachment(100, 0);
- data.right = new FormAttachment(100, 0);
- stackTraceTree.setLayoutData(data);
-
- sash.addListener(SWT.Selection, new Listener() {
- @Override
- public void handleEvent(Event e) {
- Rectangle sashRect = sash.getBounds();
- Rectangle panelRect = c.getClientArea();
- int sashPercent = sashRect.y * 100 / panelRect.height;
- mPrefStore.setValue(PREFS_SASH_HEIGHT_PERCENT, sashPercent);
-
- sashData.top = new FormAttachment(0, e.y);
- c.layout();
- }
- });
- }
-
- private void initializeDetailsToolBar(ToolBar toolbar) {
- mGroupByButton = new ToolItem(toolbar, SWT.CHECK);
- mGroupByButton.setImage(ImageLoader.getDdmUiLibLoader().loadImage(GROUPBY_IMAGE,
- toolbar.getDisplay()));
- mGroupByButton.setToolTipText(TOOLTIP_GROUPBY);
- mGroupByButton.setSelection(mPrefStore.getBoolean(PREFS_GROUP_BY_LIBRARY));
- mGroupByButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- updateDisplayGrouping();
- }
- });
-
- mDiffsOnlyButton = new ToolItem(toolbar, SWT.CHECK);
- mDiffsOnlyButton.setImage(ImageLoader.getDdmUiLibLoader().loadImage(DIFFS_ONLY_IMAGE,
- toolbar.getDisplay()));
- mDiffsOnlyButton.setToolTipText(TOOLTIP_DIFFS_ONLY);
- mDiffsOnlyButton.setSelection(mPrefStore.getBoolean(PREFS_SHOW_DIFFS_ONLY));
- mDiffsOnlyButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- // simply refresh the display, as the display logic takes care of
- // the current state of the diffs only checkbox.
- int idx = mSnapshotIndexCombo.getSelectionIndex();
- displaySnapshot(idx);
- }
- });
-
- mShowZygoteAllocationsButton = new ToolItem(toolbar, SWT.CHECK);
- mShowZygoteAllocationsButton.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
- ZYGOTE_IMAGE, toolbar.getDisplay()));
- mShowZygoteAllocationsButton.setToolTipText(TOOLTIP_ZYGOTE_ALLOCATIONS);
- mShowZygoteAllocationsButton.setSelection(
- mPrefStore.getBoolean(PREFS_SHOW_ZYGOTE_ALLOCATIONS));
- mShowZygoteAllocationsButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- updateDisplayForZygotes();
- }
- });
-
- mExportHeapDataButton = new ToolItem(toolbar, SWT.PUSH);
- mExportHeapDataButton.setImage(ImageLoader.getDdmUiLibLoader().loadImage(
- EXPORT_DATA_IMAGE, toolbar.getDisplay()));
- mExportHeapDataButton.setToolTipText(TOOLTIP_EXPORT_DATA);
- mExportHeapDataButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- exportSnapshot();
- }
- });
- }
-
- /** Export currently displayed snapshot to a file */
- private void exportSnapshot() {
- int idx = mSnapshotIndexCombo.getSelectionIndex();
- String snapshotName = mSnapshotIndexCombo.getItem(idx);
-
- FileDialog fileDialog = new FileDialog(Display.getDefault().getActiveShell(),
- SWT.SAVE);
-
- fileDialog.setText("Save " + snapshotName);
- fileDialog.setFileName("allocations.txt");
-
- final String fileName = fileDialog.open();
- if (fileName == null) {
- return;
- }
-
- final NativeHeapSnapshot snapshot = mNativeHeapSnapshots.get(idx);
- Thread t = new Thread(new Runnable() {
- @Override
- public void run() {
- PrintWriter out;
- try {
- out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
- } catch (IOException e) {
- displayErrorMessage(e.getMessage());
- return;
- }
-
- for (NativeAllocationInfo alloc : snapshot.getAllocations()) {
- out.println(alloc.toString());
- }
- out.close();
- }
-
- private void displayErrorMessage(final String message) {
- Display.getDefault().syncExec(new Runnable() {
- @Override
- public void run() {
- MessageDialog.openError(Display.getDefault().getActiveShell(),
- "Failed to export heap data", message);
- }
- });
- }
- });
- t.setName("Saving Heap Data to File...");
- t.start();
- }
-
- private void initializeDetailsTree(Tree tree) {
- tree.setHeaderVisible(true);
- tree.setLinesVisible(true);
-
- List<String> properties = Arrays.asList(new String[] {
- "Library",
- "Total",
- "Percentage",
- "Count",
- "Size",
- "Method",
- });
-
- List<String> sampleValues = Arrays.asList(new String[] {
- "/path/in/device/to/system/library.so",
- "123456789",
- " 100%",
- "123456789",
- "123456789",
- "PossiblyLongDemangledMethodName",
- });
-
- // right align numeric values
- List<Integer> swtFlags = Arrays.asList(new Integer[] {
- SWT.LEFT,
- SWT.RIGHT,
- SWT.RIGHT,
- SWT.RIGHT,
- SWT.RIGHT,
- SWT.LEFT,
- });
-
- for (int i = 0; i < properties.size(); i++) {
- String p = properties.get(i);
- String v = sampleValues.get(i);
- int flags = swtFlags.get(i);
- TableHelper.createTreeColumn(tree, p, flags, v, getPref("details", p), mPrefStore);
- }
-
- mDetailsTreeViewer = new TreeViewer(tree);
-
- mDetailsTreeViewer.setUseHashlookup(true);
-
- boolean displayZygotes = mPrefStore.getBoolean(PREFS_SHOW_ZYGOTE_ALLOCATIONS);
- mContentProviderByAllocations = new NativeHeapProviderByAllocations(mDetailsTreeViewer,
- displayZygotes);
- mContentProviderByLibrary = new NativeHeapProviderByLibrary(mDetailsTreeViewer,
- displayZygotes);
- if (mPrefStore.getBoolean(PREFS_GROUP_BY_LIBRARY)) {
- mDetailsTreeViewer.setContentProvider(mContentProviderByLibrary);
- } else {
- mDetailsTreeViewer.setContentProvider(mContentProviderByAllocations);
- }
-
- mDetailsTreeLabelProvider = new NativeHeapLabelProvider();
- mDetailsTreeViewer.setLabelProvider(mDetailsTreeLabelProvider);
-
- mDetailsTreeViewer.setInput(null);
-
- tree.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent event) {
- displayStackTraceForSelection();
- }
- });
- }
-
- private void initializeStackTraceTree(Tree tree) {
- tree.setHeaderVisible(true);
- tree.setLinesVisible(true);
-
- List<String> properties = Arrays.asList(new String[] {
- "Address",
- "Library",
- "Method",
- "File",
- "Line",
- });
-
- List<String> sampleValues = Arrays.asList(new String[] {
- "0x1234_5678",
- "/path/in/device/to/system/library.so",
- "PossiblyLongDemangledMethodName",
- "/android/out/prefix/in/home/directory/to/path/in/device/to/system/library.so",
- "2000",
- });
-
- for (int i = 0; i < properties.size(); i++) {
- String p = properties.get(i);
- String v = sampleValues.get(i);
- TableHelper.createTreeColumn(tree, p, SWT.LEFT, v, getPref("stack", p), mPrefStore);
- }
-
- mStackTraceTreeViewer = new TreeViewer(tree);
-
- mStackTraceTreeViewer.setContentProvider(new NativeStackContentProvider());
- mStackTraceTreeViewer.setLabelProvider(new NativeStackLabelProvider());
-
- mStackTraceTreeViewer.setInput(null);
- }
-
- private void displayStackTraceForSelection() {
- TreeItem []items = mDetailsTreeViewer.getTree().getSelection();
- if (items.length == 0) {
- mStackTraceTreeViewer.setInput(null);
- return;
- }
-
- Object data = items[0].getData();
- if (!(data instanceof NativeAllocationInfo)) {
- mStackTraceTreeViewer.setInput(null);
- return;
- }
-
- NativeAllocationInfo info = (NativeAllocationInfo) data;
- if (info.isStackCallResolved()) {
- mStackTraceTreeViewer.setInput(info.getResolvedStackCall());
- } else {
- mStackTraceTreeViewer.setInput(info.getStackCallAddresses());
- }
- }
-
- private String getPref(String prefix, String s) {
- return "nativeheap.tree." + prefix + "." + s;
- }
-
- @Override
- public void setFocus() {
- }
-
- private ITableFocusListener mTableFocusListener;
-
- @Override
- public void setTableFocusListener(ITableFocusListener listener) {
- mTableFocusListener = listener;
-
- final Tree heapSitesTree = mDetailsTreeViewer.getTree();
- final IFocusedTableActivator heapSitesActivator = new IFocusedTableActivator() {
- @Override
- public void copy(Clipboard clipboard) {
- TreeItem[] items = heapSitesTree.getSelection();
- copyToClipboard(items, clipboard);
- }
-
- @Override
- public void selectAll() {
- heapSitesTree.selectAll();
- }
- };
-
- heapSitesTree.addFocusListener(new FocusListener() {
- @Override
- public void focusLost(FocusEvent arg0) {
- mTableFocusListener.focusLost(heapSitesActivator);
- }
-
- @Override
- public void focusGained(FocusEvent arg0) {
- mTableFocusListener.focusGained(heapSitesActivator);
- }
- });
-
- final Tree stackTraceTree = mStackTraceTreeViewer.getTree();
- final IFocusedTableActivator stackTraceActivator = new IFocusedTableActivator() {
- @Override
- public void copy(Clipboard clipboard) {
- TreeItem[] items = stackTraceTree.getSelection();
- copyToClipboard(items, clipboard);
- }
-
- @Override
- public void selectAll() {
- stackTraceTree.selectAll();
- }
- };
-
- stackTraceTree.addFocusListener(new FocusListener() {
- @Override
- public void focusLost(FocusEvent arg0) {
- mTableFocusListener.focusLost(stackTraceActivator);
- }
-
- @Override
- public void focusGained(FocusEvent arg0) {
- mTableFocusListener.focusGained(stackTraceActivator);
- }
- });
- }
-
- private void copyToClipboard(TreeItem[] items, Clipboard clipboard) {
- StringBuilder sb = new StringBuilder();
-
- for (TreeItem item : items) {
- Object data = item.getData();
- if (data != null) {
- sb.append(data.toString());
- sb.append('\n');
- }
- }
-
- String content = sb.toString();
- if (content.length() > 0) {
- clipboard.setContents(
- new Object[] {sb.toString()},
- new Transfer[] {TextTransfer.getInstance()}
- );
- }
- }
-
- private class SymbolResolverTask implements Runnable {
- private List<NativeAllocationInfo> mCallSites;
- private List<NativeLibraryMapInfo> mMappedLibraries;
- private Map<Long, NativeStackCallInfo> mResolvedSymbolCache;
-
- public SymbolResolverTask(List<NativeAllocationInfo> callSites,
- List<NativeLibraryMapInfo> mappedLibraries) {
- mCallSites = callSites;
- mMappedLibraries = mappedLibraries;
-
- mResolvedSymbolCache = new HashMap<Long, NativeStackCallInfo>();
- }
-
- @Override
- public void run() {
- for (NativeAllocationInfo callSite : mCallSites) {
- if (callSite.isStackCallResolved()) {
- continue;
- }
-
- List<Long> addresses = callSite.getStackCallAddresses();
- List<NativeStackCallInfo> resolvedStackInfo =
- new ArrayList<NativeStackCallInfo>(addresses.size());
-
- for (Long address : addresses) {
- NativeStackCallInfo info = mResolvedSymbolCache.get(address);
-
- if (info != null) {
- resolvedStackInfo.add(info);
- } else {
- info = resolveAddress(address);
- resolvedStackInfo.add(info);
- mResolvedSymbolCache.put(address, info);
- }
- }
-
- callSite.setResolvedStackCall(resolvedStackInfo);
- }
-
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- mDetailsTreeViewer.refresh();
- mStackTraceTreeViewer.refresh();
- }
- });
- }
-
- private NativeStackCallInfo resolveAddress(long addr) {
- NativeLibraryMapInfo library = getLibraryFor(addr);
-
- if (library != null) {
- Addr2Line process = Addr2Line.getProcess(library);
- if (process != null) {
- NativeStackCallInfo info = process.getAddress(addr);
- if (info != null) {
- return info;
- }
- }
- }
-
- return new NativeStackCallInfo(addr,
- library != null ? library.getLibraryName() : null,
- Long.toHexString(addr),
- "");
- }
-
- private NativeLibraryMapInfo getLibraryFor(long addr) {
- for (NativeLibraryMapInfo info : mMappedLibraries) {
- if (info.isWithinLibrary(addr)) {
- return info;
- }
- }
-
- Log.d("ddm-nativeheap", "Failed finding Library for " + Long.toHexString(addr));
- return null;
- }
- }
-}