diff options
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.java | 1152 |
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; - } - } -} |