2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.ddmuilib;
19 import com.android.ddmlib.Client;
20 import com.android.ddmlib.ClientData;
21 import com.android.ddmlib.Log;
22 import com.android.ddmlib.NativeAllocationInfo;
23 import com.android.ddmlib.NativeLibraryMapInfo;
24 import com.android.ddmlib.NativeStackCallInfo;
25 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
26 import com.android.ddmlib.HeapSegment.HeapSegmentElement;
27 import com.android.ddmuilib.annotation.WorkerThread;
29 import org.eclipse.jface.preference.IPreferenceStore;
30 import org.eclipse.swt.SWT;
31 import org.eclipse.swt.SWTException;
32 import org.eclipse.swt.custom.StackLayout;
33 import org.eclipse.swt.events.SelectionAdapter;
34 import org.eclipse.swt.events.SelectionEvent;
35 import org.eclipse.swt.graphics.Image;
36 import org.eclipse.swt.graphics.ImageData;
37 import org.eclipse.swt.graphics.PaletteData;
38 import org.eclipse.swt.graphics.RGB;
39 import org.eclipse.swt.graphics.Rectangle;
40 import org.eclipse.swt.layout.FormAttachment;
41 import org.eclipse.swt.layout.FormData;
42 import org.eclipse.swt.layout.FormLayout;
43 import org.eclipse.swt.layout.GridData;
44 import org.eclipse.swt.layout.GridLayout;
45 import org.eclipse.swt.widgets.Button;
46 import org.eclipse.swt.widgets.Combo;
47 import org.eclipse.swt.widgets.Composite;
48 import org.eclipse.swt.widgets.Control;
49 import org.eclipse.swt.widgets.Display;
50 import org.eclipse.swt.widgets.Event;
51 import org.eclipse.swt.widgets.FileDialog;
52 import org.eclipse.swt.widgets.Label;
53 import org.eclipse.swt.widgets.Listener;
54 import org.eclipse.swt.widgets.Sash;
55 import org.eclipse.swt.widgets.Table;
56 import org.eclipse.swt.widgets.TableItem;
58 import java.io.BufferedWriter;
59 import java.io.FileWriter;
60 import java.io.IOException;
61 import java.io.PrintWriter;
62 import java.text.DecimalFormat;
63 import java.text.NumberFormat;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.Comparator;
67 import java.util.HashMap;
68 import java.util.Iterator;
71 * Panel with native heap information.
73 public final class NativeHeapPanel extends BaseHeapPanel {
75 /** color palette and map legend. NATIVE is the last enum is a 0 based enum list, so we need
76 * Native+1 at least. We also need 2 more entries for free area and expansion area. */
77 private static final int NUM_PALETTE_ENTRIES = HeapSegmentElement.KIND_NATIVE+2 +1;
78 private static final String[] mMapLegend = new String[NUM_PALETTE_ENTRIES];
79 private static final PaletteData mMapPalette = createPalette();
81 private static final int ALLOC_DISPLAY_ALL = 0;
82 private static final int ALLOC_DISPLAY_PRE_ZYGOTE = 1;
83 private static final int ALLOC_DISPLAY_POST_ZYGOTE = 2;
85 private Display mDisplay;
87 private Composite mBase;
89 private Label mUpdateStatus;
91 /** combo giving choice of what to display: all, pre-zygote, post-zygote */
92 private Combo mAllocDisplayCombo;
94 private Button mFullUpdateButton;
96 // see CreateControl()
97 //private Button mDiffUpdateButton;
99 private Combo mDisplayModeCombo;
101 /** stack composite for mode (1-2) & 3 */
102 private Composite mTopStackComposite;
104 private StackLayout mTopStackLayout;
106 /** stack composite for mode 1 & 2 */
107 private Composite mAllocationStackComposite;
109 private StackLayout mAllocationStackLayout;
111 /** top level container for mode 1 & 2 */
112 private Composite mTableModeControl;
114 /** top level object for the allocation mode */
115 private Control mAllocationModeTop;
117 /** top level for the library mode */
118 private Control mLibraryModeTopControl;
120 /** composite for page UI and total memory display */
121 private Composite mPageUIComposite;
123 private Label mTotalMemoryLabel;
125 private Label mPageLabel;
127 private Button mPageNextButton;
129 private Button mPagePreviousButton;
131 private Table mAllocationTable;
133 private Table mLibraryTable;
135 private Table mLibraryAllocationTable;
137 private Table mDetailTable;
139 private Label mImage;
141 private int mAllocDisplayMode = ALLOC_DISPLAY_ALL;
144 * pointer to current stackcall thread computation in order to quit it if
145 * required (new update requested)
147 private StackCallThread mStackCallThread;
149 /** Current Library Allocation table fill thread. killed if selection changes */
150 private FillTableThread mFillTableThread;
153 * current client data. Used to access the malloc info when switching pages
154 * or selecting allocation to show stack call
156 private ClientData mClientData;
159 * client data from a previous display. used when asking for an "update & diff"
161 private ClientData mBackUpClientData;
163 /** list of NativeAllocationInfo objects filled with the list from ClientData */
164 private final ArrayList<NativeAllocationInfo> mAllocations =
165 new ArrayList<NativeAllocationInfo>();
167 /** list of the {@link NativeAllocationInfo} being displayed based on the selection
168 * of {@link #mAllocDisplayCombo}.
170 private final ArrayList<NativeAllocationInfo> mDisplayedAllocations =
171 new ArrayList<NativeAllocationInfo>();
173 /** list of NativeAllocationInfo object kept as backup when doing an "update & diff" */
174 private final ArrayList<NativeAllocationInfo> mBackUpAllocations =
175 new ArrayList<NativeAllocationInfo>();
177 /** back up of the total memory, used when doing an "update & diff" */
178 private int mBackUpTotalMemory;
180 private int mCurrentPage = 0;
182 private int mPageCount = 0;
185 * list of allocation per Library. This is created from the list of
186 * NativeAllocationInfo objects that is stored in the ClientData object. Since we
187 * don't keep this list around, it is recomputed everytime the client
190 private final ArrayList<LibraryAllocations> mLibraryAllocations =
191 new ArrayList<LibraryAllocations>();
193 /* args to setUpdateStatus() */
194 private static final int NOT_SELECTED = 0;
196 private static final int NOT_ENABLED = 1;
198 private static final int ENABLED = 2;
200 private static final int DISPLAY_PER_PAGE = 20;
202 private static final String PREFS_ALLOCATION_SASH = "NHallocSash"; //$NON-NLS-1$
203 private static final String PREFS_LIBRARY_SASH = "NHlibrarySash"; //$NON-NLS-1$
204 private static final String PREFS_DETAIL_ADDRESS = "NHdetailAddress"; //$NON-NLS-1$
205 private static final String PREFS_DETAIL_LIBRARY = "NHdetailLibrary"; //$NON-NLS-1$
206 private static final String PREFS_DETAIL_METHOD = "NHdetailMethod"; //$NON-NLS-1$
207 private static final String PREFS_DETAIL_FILE = "NHdetailFile"; //$NON-NLS-1$
208 private static final String PREFS_DETAIL_LINE = "NHdetailLine"; //$NON-NLS-1$
209 private static final String PREFS_ALLOC_TOTAL = "NHallocTotal"; //$NON-NLS-1$
210 private static final String PREFS_ALLOC_COUNT = "NHallocCount"; //$NON-NLS-1$
211 private static final String PREFS_ALLOC_SIZE = "NHallocSize"; //$NON-NLS-1$
212 private static final String PREFS_ALLOC_LIBRARY = "NHallocLib"; //$NON-NLS-1$
213 private static final String PREFS_ALLOC_METHOD = "NHallocMethod"; //$NON-NLS-1$
214 private static final String PREFS_ALLOC_FILE = "NHallocFile"; //$NON-NLS-1$
215 private static final String PREFS_LIB_LIBRARY = "NHlibLibrary"; //$NON-NLS-1$
216 private static final String PREFS_LIB_SIZE = "NHlibSize"; //$NON-NLS-1$
217 private static final String PREFS_LIB_COUNT = "NHlibCount"; //$NON-NLS-1$
218 private static final String PREFS_LIBALLOC_TOTAL = "NHlibAllocTotal"; //$NON-NLS-1$
219 private static final String PREFS_LIBALLOC_COUNT = "NHlibAllocCount"; //$NON-NLS-1$
220 private static final String PREFS_LIBALLOC_SIZE = "NHlibAllocSize"; //$NON-NLS-1$
221 private static final String PREFS_LIBALLOC_METHOD = "NHlibAllocMethod"; //$NON-NLS-1$
223 /** static formatter object to format all numbers as #,### */
224 private static DecimalFormat sFormatter;
226 sFormatter = (DecimalFormat)NumberFormat.getInstance();
227 if (sFormatter != null)
228 sFormatter = new DecimalFormat("#,###");
230 sFormatter.applyPattern("#,###");
235 * caching mechanism to avoid recomputing the backtrace for a particular
236 * address several times.
238 private HashMap<Long, NativeStackCallInfo> mSourceCache =
239 new HashMap<Long,NativeStackCallInfo>();
240 private long mTotalSize;
241 private Button mSaveButton;
242 private Button mSymbolsButton;
245 * thread class to convert the address call into method, file and line
246 * number in the background.
248 private class StackCallThread extends BackgroundThread {
249 private ClientData mClientData;
251 public StackCallThread(ClientData cd) {
255 public ClientData getClientData() {
261 // loop through all the NativeAllocationInfo and init them
262 Iterator<NativeAllocationInfo> iter = mAllocations.iterator();
263 int total = mAllocations.size();
265 while (iter.hasNext()) {
270 NativeAllocationInfo info = iter.next();
271 if (info.isStackCallResolved() == false) {
272 final Long[] list = info.getStackCallAddresses();
273 final int size = list.length;
275 ArrayList<NativeStackCallInfo> resolvedStackCall =
276 new ArrayList<NativeStackCallInfo>();
278 for (int i = 0; i < size; i++) {
281 // first check if the addr has already been converted.
282 NativeStackCallInfo source = mSourceCache.get(addr);
284 // if not we convert it
285 if (source == null) {
286 source = sourceForAddr(addr);
287 mSourceCache.put(addr, source);
290 resolvedStackCall.add(source);
293 info.setResolvedStackCall(resolvedStackCall);
295 // after every DISPLAY_PER_PAGE we ask for a ui refresh, unless
296 // we reach total, since we also do it after the loop
297 // (only an issue in case we have a perfect number of page)
299 if ((count % DISPLAY_PER_PAGE) == 0 && count != total) {
300 if (updateNHAllocationStackCalls(mClientData, count) == false) {
301 // looks like the app is quitting, so we just
302 // stopped the thread
308 updateNHAllocationStackCalls(mClientData, count);
311 private NativeStackCallInfo sourceForAddr(long addr) {
312 NativeLibraryMapInfo library = getLibraryFor(addr);
314 if (library != null) {
316 Addr2Line process = Addr2Line.getProcess(library.getLibraryName());
317 if (process != null) {
318 // remove the base of the library address
319 long value = addr - library.getStartAddress();
320 NativeStackCallInfo info = process.getAddress(value);
327 return new NativeStackCallInfo(library != null ? library.getLibraryName() : null,
328 Long.toHexString(addr), "");
331 private NativeLibraryMapInfo getLibraryFor(long addr) {
332 Iterator<NativeLibraryMapInfo> it = mClientData.getNativeLibraryMapInfo();
334 while (it.hasNext()) {
335 NativeLibraryMapInfo info = it.next();
337 if (info.isWithinLibrary(addr)) {
342 Log.d("ddm-nativeheap", "Failed finding Library for " + Long.toHexString(addr));
347 * update the Native Heap panel with the amount of allocation for which the
348 * stack call has been computed. This is called from a non UI thread, but
349 * will be executed in the UI thread.
351 * @param count the amount of allocation
352 * @return false if the display was disposed and the update couldn't happen
354 private boolean updateNHAllocationStackCalls(final ClientData clientData, final int count) {
355 if (mDisplay.isDisposed() == false) {
356 mDisplay.asyncExec(new Runnable() {
358 updateAllocationStackCalls(clientData, count);
367 private class FillTableThread extends BackgroundThread {
368 private LibraryAllocations mLibAlloc;
372 public FillTableThread(LibraryAllocations liballoc, int m) {
373 mLibAlloc = liballoc;
379 for (int i = mMax; i > 0 && isQuitting() == false; i -= 10) {
380 updateNHLibraryAllocationTable(mLibAlloc, mMax - i, mMax - i + 10);
385 * updates the library allocation table in the Native Heap panel. This is
386 * called from a non UI thread, but will be executed in the UI thread.
388 * @param liballoc the current library allocation object being displayed
389 * @param start start index of items that need to be displayed
390 * @param end end index of the items that need to be displayed
392 private void updateNHLibraryAllocationTable(final LibraryAllocations libAlloc,
393 final int start, final int end) {
394 if (mDisplay.isDisposed() == false) {
395 mDisplay.asyncExec(new Runnable() {
397 updateLibraryAllocationTable(libAlloc, start, end);
405 /** class to aggregate allocations per library */
406 public static class LibraryAllocations {
407 private String mLibrary;
409 private final ArrayList<NativeAllocationInfo> mLibAllocations =
410 new ArrayList<NativeAllocationInfo>();
416 /** construct the aggregate object for a library */
417 public LibraryAllocations(final String lib) {
421 /** get the library name */
422 public String getLibrary() {
426 /** add a NativeAllocationInfo object to this aggregate object */
427 public void addAllocation(NativeAllocationInfo info) {
428 mLibAllocations.add(info);
431 /** get an iterator on the NativeAllocationInfo objects */
432 public Iterator<NativeAllocationInfo> getAllocations() {
433 return mLibAllocations.iterator();
436 /** get a NativeAllocationInfo object by index */
437 public NativeAllocationInfo getAllocation(int index) {
438 return mLibAllocations.get(index);
441 /** returns the NativeAllocationInfo object count */
442 public int getAllocationSize() {
443 return mLibAllocations.size();
446 /** returns the total allocation size */
447 public int getSize() {
451 /** returns the number of allocations */
452 public int getCount() {
457 * compute the allocation count and size for allocation objects added
458 * through <code>addAllocation()</code>, and sort the objects by
459 * total allocation size.
461 public void computeAllocationSizeAndCount() {
464 for (NativeAllocationInfo info : mLibAllocations) {
465 mCount += info.getAllocationCount();
466 mSize += info.getAllocationCount() * info.getSize();
468 Collections.sort(mLibAllocations, new Comparator<NativeAllocationInfo>() {
469 public int compare(NativeAllocationInfo o1, NativeAllocationInfo o2) {
470 return o2.getAllocationCount() * o2.getSize() -
471 o1.getAllocationCount() * o1.getSize();
478 * Create our control(s).
481 protected Control createControl(Composite parent) {
483 mDisplay = parent.getDisplay();
485 mBase = new Composite(parent, SWT.NONE);
486 GridLayout gl = new GridLayout(1, false);
487 gl.horizontalSpacing = 0;
488 gl.verticalSpacing = 0;
490 mBase.setLayoutData(new GridData(GridData.FILL_BOTH));
492 // composite for <update btn> <status>
493 Composite tmp = new Composite(mBase, SWT.NONE);
494 tmp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
495 tmp.setLayout(gl = new GridLayout(2, false));
496 gl.marginWidth = gl.marginHeight = 0;
498 mFullUpdateButton = new Button(tmp, SWT.NONE);
499 mFullUpdateButton.setText("Full Update");
500 mFullUpdateButton.addSelectionListener(new SelectionAdapter() {
502 public void widgetSelected(SelectionEvent e) {
503 mBackUpClientData = null;
504 mDisplayModeCombo.setEnabled(false);
505 mSaveButton.setEnabled(false);
507 // if we already have a stack call computation for this
510 if (mStackCallThread != null &&
511 mStackCallThread.getClientData() == mClientData) {
512 mStackCallThread.quit();
513 mStackCallThread = null;
515 mLibraryAllocations.clear();
516 Client client = getCurrentClient();
517 if (client != null) {
518 client.requestNativeHeapInformation();
523 mUpdateStatus = new Label(tmp, SWT.NONE);
524 mUpdateStatus.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
526 // top layout for the combos and oter controls on the right.
527 Composite top_layout = new Composite(mBase, SWT.NONE);
528 top_layout.setLayout(gl = new GridLayout(4, false));
529 gl.marginWidth = gl.marginHeight = 0;
531 new Label(top_layout, SWT.NONE).setText("Show:");
533 mAllocDisplayCombo = new Combo(top_layout, SWT.DROP_DOWN | SWT.READ_ONLY);
534 mAllocDisplayCombo.setLayoutData(new GridData(
535 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
536 mAllocDisplayCombo.add("All Allocations");
537 mAllocDisplayCombo.add("Pre-Zygote Allocations");
538 mAllocDisplayCombo.add("Zygote Child Allocations (Z)");
539 mAllocDisplayCombo.addSelectionListener(new SelectionAdapter() {
541 public void widgetSelected(SelectionEvent e) {
542 onAllocDisplayChange();
545 mAllocDisplayCombo.select(0);
548 Label separator = new Label(top_layout, SWT.SEPARATOR | SWT.VERTICAL);
550 separator.setLayoutData(gd = new GridData(
551 GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
555 mSaveButton = new Button(top_layout, SWT.PUSH);
556 mSaveButton.setText("Save...");
557 mSaveButton.setEnabled(false);
558 mSaveButton.addSelectionListener(new SelectionAdapter() {
560 public void widgetSelected(SelectionEvent e) {
561 FileDialog fileDialog = new FileDialog(mBase.getShell(), SWT.SAVE);
563 fileDialog.setText("Save Allocations");
564 fileDialog.setFileName("allocations.txt");
566 String fileName = fileDialog.open();
567 if (fileName != null) {
568 saveAllocations(fileName);
574 * TODO: either fix the diff mechanism or remove it altogether.
575 mDiffUpdateButton = new Button(top_layout, SWT.NONE);
576 mDiffUpdateButton.setText("Update && Diff");
577 mDiffUpdateButton.addSelectionListener(new SelectionAdapter() {
579 public void widgetSelected(SelectionEvent e) {
580 // since this is an update and diff, we need to store the
583 mBackUpAllocations.clear();
584 mBackUpAllocations.addAll(mAllocations);
585 mBackUpClientData = mClientData;
586 mBackUpTotalMemory = mClientData.getTotalNativeMemory();
588 mDisplayModeCombo.setEnabled(false);
590 // if we already have a stack call computation for this
593 if (mStackCallThread != null &&
594 mStackCallThread.getClientData() == mClientData) {
595 mStackCallThread.quit();
596 mStackCallThread = null;
598 mLibraryAllocations.clear();
599 Client client = getCurrentClient();
600 if (client != null) {
601 client.requestNativeHeapInformation();
607 Label l = new Label(top_layout, SWT.NONE);
608 l.setText("Display:");
610 mDisplayModeCombo = new Combo(top_layout, SWT.DROP_DOWN | SWT.READ_ONLY);
611 mDisplayModeCombo.setLayoutData(new GridData(
612 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
613 mDisplayModeCombo.setItems(new String[] { "Allocation List", "By Libraries" });
614 mDisplayModeCombo.select(0);
615 mDisplayModeCombo.addSelectionListener(new SelectionAdapter() {
617 public void widgetSelected(SelectionEvent e) {
621 mDisplayModeCombo.setEnabled(false);
623 mSymbolsButton = new Button(top_layout, SWT.PUSH);
624 mSymbolsButton.setText("Load Symbols");
625 mSymbolsButton.setEnabled(false);
628 // create a composite that will contains the actual content composites,
629 // in stack mode layout.
630 // This top level composite contains 2 other composites.
631 // * one for both Allocations and Libraries mode
632 // * one for flat mode (which is gone for now)
634 mTopStackComposite = new Composite(mBase, SWT.NONE);
635 mTopStackComposite.setLayout(mTopStackLayout = new StackLayout());
636 mTopStackComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
638 // create 1st and 2nd modes
639 createTableDisplay(mTopStackComposite);
641 mTopStackLayout.topControl = mTableModeControl;
642 mTopStackComposite.layout();
644 setUpdateStatus(NOT_SELECTED);
647 // TODO add image display of native heap.
648 //mImage = new Label(mBase, SWT.NONE);
656 * Sets the focus to the proper control inside the panel.
659 public void setFocus() {
665 * Sent when an existing client information changed.
667 * This is sent from a non UI thread.
668 * @param client the updated client.
669 * @param changeMask the bit mask describing the changed properties. It can contain
670 * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
671 * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
672 * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
673 * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
675 * @see IClientChangeListener#clientChanged(Client, int)
677 public void clientChanged(final Client client, int changeMask) {
678 if (client == getCurrentClient()) {
679 if ((changeMask & Client.CHANGE_NATIVE_HEAP_DATA) == Client.CHANGE_NATIVE_HEAP_DATA) {
680 if (mBase.isDisposed())
683 mBase.getDisplay().asyncExec(new Runnable() {
693 * Sent when a new device is selected. The new device can be accessed
694 * with {@link #getCurrentDevice()}.
697 public void deviceSelected() {
702 * Sent when a new client is selected. The new client can be accessed
703 * with {@link #getCurrentClient()}.
706 public void clientSelected() {
707 if (mBase.isDisposed())
710 Client client = getCurrentClient();
712 mDisplayModeCombo.setEnabled(false);
715 Log.d("ddms", "NativeHeapPanel: changed " + client);
717 if (client != null) {
718 ClientData cd = client.getClientData();
721 // if (cd.getShowHeapUpdates())
722 setUpdateStatus(ENABLED);
724 // setUpdateStatus(NOT_ENABLED);
726 initAllocationDisplay();
731 setUpdateStatus(NOT_SELECTED);
738 * Update the UI with the newly compute stack calls, unless the UI switched
739 * to a different client.
741 * @param cd the ClientData for which the stack call are being computed.
742 * @param count the current count of allocations for which the stack calls
743 * have been computed.
746 public void updateAllocationStackCalls(ClientData cd, int count) {
747 // we have to check that the panel still shows the same clientdata than
748 // the thread is computing for.
749 if (cd == mClientData) {
751 int total = mAllocations.size();
753 if (count == total) {
754 // we're done: do something
755 mDisplayModeCombo.setEnabled(true);
756 mSaveButton.setEnabled(true);
758 mStackCallThread = null;
760 // work in progress, update the progress bar.
761 // mUiThread.setStatusLine("Computing stack call: " + count
765 // FIXME: attempt to only update when needed.
766 // Because the number of pages is not related to mAllocations.size() anymore
767 // due to pre-zygote/post-zygote display, update all the time.
768 // At some point we should remove the pages anyway, since it's getting computed
770 // if ((mCurrentPage + 1) * DISPLAY_PER_PAGE == count
771 // || (count == total && mCurrentPage == mPageCount - 1)) {
773 // get the current selection of the allocation
774 int index = mAllocationTable.getSelectionIndex();
775 NativeAllocationInfo info = null;
778 info = (NativeAllocationInfo)mAllocationTable.getItem(index).getData();
785 fillAllocationTable();
788 mAllocationTable.setSelection(index);
790 // display detail table if needed
792 fillDetailTable(info);
794 } catch (SWTException e) {
795 if (mAllocationTable.isDisposed()) {
796 // looks like the table is disposed. Let's ignore it.
803 // old client still running. doesn't really matter.
808 protected void setTableFocusListener() {
809 addTableToFocusListener(mAllocationTable);
810 addTableToFocusListener(mLibraryTable);
811 addTableToFocusListener(mLibraryAllocationTable);
812 addTableToFocusListener(mDetailTable);
815 protected void onAllocDisplayChange() {
816 mAllocDisplayMode = mAllocDisplayCombo.getSelectionIndex();
818 // create the new list
819 updateAllocDisplayList();
821 updateTotalMemoryDisplay();
829 private void updateAllocDisplayList() {
831 mDisplayedAllocations.clear();
832 for (NativeAllocationInfo info : mAllocations) {
833 if (mAllocDisplayMode == ALLOC_DISPLAY_ALL ||
834 (mAllocDisplayMode == ALLOC_DISPLAY_PRE_ZYGOTE ^ info.isZygoteChild())) {
835 mDisplayedAllocations.add(info);
836 mTotalSize += info.getSize() * info.getAllocationCount();
843 int count = mDisplayedAllocations.size();
845 mPageCount = count / DISPLAY_PER_PAGE;
847 // need to add a page for the rest of the div
848 if ((count % DISPLAY_PER_PAGE) > 0) {
853 private void updateTotalMemoryDisplay() {
854 switch (mAllocDisplayMode) {
855 case ALLOC_DISPLAY_ALL:
856 mTotalMemoryLabel.setText(String.format("Total Memory: %1$s Bytes",
857 sFormatter.format(mTotalSize)));
859 case ALLOC_DISPLAY_PRE_ZYGOTE:
860 mTotalMemoryLabel.setText(String.format("Zygote Memory: %1$s Bytes",
861 sFormatter.format(mTotalSize)));
863 case ALLOC_DISPLAY_POST_ZYGOTE:
864 mTotalMemoryLabel.setText(String.format("Post-zygote Memory: %1$s Bytes",
865 sFormatter.format(mTotalSize)));
871 private void switchDisplayMode() {
872 switch (mDisplayModeCombo.getSelectionIndex()) {
873 case 0: {// allocations
874 mTopStackLayout.topControl = mTableModeControl;
875 mAllocationStackLayout.topControl = mAllocationModeTop;
876 mAllocationStackComposite.layout();
877 mTopStackComposite.layout();
879 fillAllocationTable();
882 case 1: {// libraries
883 mTopStackLayout.topControl = mTableModeControl;
884 mAllocationStackLayout.topControl = mLibraryModeTopControl;
885 mAllocationStackComposite.layout();
886 mTopStackComposite.layout();
894 private void initAllocationDisplay() {
895 mAllocations.clear();
896 mAllocations.addAll(mClientData.getNativeAllocationList());
898 updateAllocDisplayList();
900 // if we have a previous clientdata and it matches the current one. we
901 // do a diff between the new list and the old one.
902 if (mBackUpClientData != null && mBackUpClientData == mClientData) {
904 ArrayList<NativeAllocationInfo> add = new ArrayList<NativeAllocationInfo>();
906 // we go through the list of NativeAllocationInfo in the new list and check if
907 // there's one with the same exact data (size, allocation, count and
908 // stackcall addresses) in the old list.
909 // if we don't find any, we add it to the "add" list
910 for (NativeAllocationInfo mi : mAllocations) {
911 boolean found = false;
912 for (NativeAllocationInfo old_mi : mBackUpAllocations) {
913 if (mi.equals(old_mi)) {
918 if (found == false) {
923 // put the result in mAllocations
924 mAllocations.clear();
925 mAllocations.addAll(add);
927 // display the difference in memory usage. This is computed
928 // calculating the memory usage of the objects in mAllocations.
930 for (NativeAllocationInfo allocInfo : mAllocations) {
931 count += allocInfo.getSize() * allocInfo.getAllocationCount();
934 mTotalMemoryLabel.setText(String.format("Memory Difference: %1$s Bytes",
935 sFormatter.format(count)));
938 // display the full memory usage
939 updateTotalMemoryDisplay();
940 //mDiffUpdateButton.setEnabled(mClientData.getTotalNativeMemory() > 0);
942 mTotalMemoryLabel.pack();
944 // update the page ui
945 mDisplayModeCombo.select(0);
947 mLibraryAllocations.clear();
949 // reset to first page
955 // now fill the allocation Table with the current page
958 // start the thread to compute the stack calls
959 if (mAllocations.size() > 0) {
960 mStackCallThread = new StackCallThread(mClientData);
961 mStackCallThread.start();
965 private void updatePageUI() {
967 // set the label and pack to update the layout, otherwise
968 // the label will be cut off if the new size is bigger
969 if (mPageCount == 0) {
970 mPageLabel.setText("0 of 0 allocations.");
972 StringBuffer buffer = new StringBuffer();
973 // get our starting index
974 int start = (mCurrentPage * DISPLAY_PER_PAGE) + 1;
975 // end index, taking into account the last page can be half full
976 int count = mDisplayedAllocations.size();
977 int end = Math.min(start + DISPLAY_PER_PAGE - 1, count);
978 buffer.append(sFormatter.format(start));
979 buffer.append(" - ");
980 buffer.append(sFormatter.format(end));
981 buffer.append(" of ");
982 buffer.append(sFormatter.format(count));
983 buffer.append(" allocations.");
984 mPageLabel.setText(buffer.toString());
987 // handle the button enabled state.
988 mPagePreviousButton.setEnabled(mCurrentPage > 0);
989 // reminder: mCurrentPage starts at 0.
990 mPageNextButton.setEnabled(mCurrentPage < mPageCount - 1);
993 mPageUIComposite.pack();
997 private void fillAllocationTable() {
999 int count = mDisplayedAllocations.size();
1001 // get our starting index
1002 int start = mCurrentPage * DISPLAY_PER_PAGE;
1004 // loop for DISPLAY_PER_PAGE or till we reach count
1005 int end = start + DISPLAY_PER_PAGE;
1007 for (int i = start; i < end && i < count; i++) {
1008 NativeAllocationInfo info = mDisplayedAllocations.get(i);
1010 TableItem item = null;
1012 if (mAllocDisplayMode == ALLOC_DISPLAY_ALL) {
1013 item = new TableItem(mAllocationTable, SWT.NONE);
1014 item.setText(0, (info.isZygoteChild() ? "Z " : "") +
1015 sFormatter.format(info.getSize() * info.getAllocationCount()));
1016 item.setText(1, sFormatter.format(info.getAllocationCount()));
1017 item.setText(2, sFormatter.format(info.getSize()));
1018 } else if (mAllocDisplayMode == ALLOC_DISPLAY_PRE_ZYGOTE ^ info.isZygoteChild()) {
1019 item = new TableItem(mAllocationTable, SWT.NONE);
1020 item.setText(0, sFormatter.format(info.getSize() * info.getAllocationCount()));
1021 item.setText(1, sFormatter.format(info.getAllocationCount()));
1022 item.setText(2, sFormatter.format(info.getSize()));
1030 NativeStackCallInfo bti = info.getRelevantStackCallInfo();
1032 String lib = bti.getLibraryName();
1033 String method = bti.getMethodName();
1034 String source = bti.getSourceFile();
1036 item.setText(3, lib);
1038 item.setText(4, method);
1040 item.setText(5, source);
1045 private void fillLibraryTable() {
1046 // fill the library table
1047 sortAllocationsPerLibrary();
1049 for (LibraryAllocations liballoc : mLibraryAllocations) {
1050 if (liballoc != null) {
1051 TableItem item = new TableItem(mLibraryTable, SWT.NONE);
1052 String lib = liballoc.getLibrary();
1053 item.setText(0, lib != null ? lib : "");
1054 item.setText(1, sFormatter.format(liballoc.getSize()));
1055 item.setText(2, sFormatter.format(liballoc.getCount()));
1060 private void fillLibraryAllocationTable() {
1061 mLibraryAllocationTable.removeAll();
1062 mDetailTable.removeAll();
1063 int index = mLibraryTable.getSelectionIndex();
1065 LibraryAllocations liballoc = mLibraryAllocations.get(index);
1066 // start a thread that will fill table 10 at a time to keep the ui
1067 // responsive, but first we kill the previous one if there was one
1068 if (mFillTableThread != null) {
1069 mFillTableThread.quit();
1071 mFillTableThread = new FillTableThread(liballoc,
1072 liballoc.getAllocationSize());
1073 mFillTableThread.start();
1077 public void updateLibraryAllocationTable(LibraryAllocations liballoc,
1078 int start, int end) {
1080 if (mLibraryTable.isDisposed() == false) {
1081 int index = mLibraryTable.getSelectionIndex();
1083 LibraryAllocations newliballoc = mLibraryAllocations.get(
1085 if (newliballoc == liballoc) {
1086 int count = liballoc.getAllocationSize();
1087 for (int i = start; i < end && i < count; i++) {
1088 NativeAllocationInfo info = liballoc.getAllocation(i);
1090 TableItem item = new TableItem(
1091 mLibraryAllocationTable, SWT.NONE);
1092 item.setText(0, sFormatter.format(
1093 info.getSize() * info.getAllocationCount()));
1094 item.setText(1, sFormatter.format(info.getAllocationCount()));
1095 item.setText(2, sFormatter.format(info.getSize()));
1097 NativeStackCallInfo stackCallInfo = info.getRelevantStackCallInfo();
1098 if (stackCallInfo != null) {
1099 item.setText(3, stackCallInfo.getMethodName());
1103 // we should quit the thread
1104 if (mFillTableThread != null) {
1105 mFillTableThread.quit();
1106 mFillTableThread = null;
1111 } catch (SWTException e) {
1112 Log.e("ddms", "error when updating the library allocation table");
1116 private void fillDetailTable(final NativeAllocationInfo mi) {
1117 mDetailTable.removeAll();
1118 mDetailTable.setRedraw(false);
1121 // populate the detail Table with the back trace
1122 Long[] addresses = mi.getStackCallAddresses();
1123 NativeStackCallInfo[] resolvedStackCall = mi.getResolvedStackCall();
1125 if (resolvedStackCall == null) {
1129 for (int i = 0 ; i < resolvedStackCall.length ; i++) {
1130 if (addresses[i] == null || addresses[i].longValue() == 0) {
1134 long addr = addresses[i].longValue();
1135 NativeStackCallInfo source = resolvedStackCall[i];
1137 TableItem item = new TableItem(mDetailTable, SWT.NONE);
1138 item.setText(0, String.format("%08x", addr)); //$NON-NLS-1$
1140 String libraryName = source.getLibraryName();
1141 String methodName = source.getMethodName();
1142 String sourceFile = source.getSourceFile();
1143 int lineNumber = source.getLineNumber();
1145 if (libraryName != null)
1146 item.setText(1, libraryName);
1147 if (methodName != null)
1148 item.setText(2, methodName);
1149 if (sourceFile != null)
1150 item.setText(3, sourceFile);
1151 if (lineNumber != -1)
1152 item.setText(4, Integer.toString(lineNumber));
1155 mDetailTable.setRedraw(true);
1160 * Are updates enabled?
1162 private void setUpdateStatus(int status) {
1165 mUpdateStatus.setText("Select a client to see heap info");
1166 mAllocDisplayCombo.setEnabled(false);
1167 mFullUpdateButton.setEnabled(false);
1168 //mDiffUpdateButton.setEnabled(false);
1171 mUpdateStatus.setText("Heap updates are " + "NOT ENABLED for this client");
1172 mAllocDisplayCombo.setEnabled(false);
1173 mFullUpdateButton.setEnabled(false);
1174 //mDiffUpdateButton.setEnabled(false);
1177 mUpdateStatus.setText("Press 'Full Update' to retrieve " + "latest data");
1178 mAllocDisplayCombo.setEnabled(true);
1179 mFullUpdateButton.setEnabled(true);
1180 //mDiffUpdateButton.setEnabled(true);
1183 throw new RuntimeException();
1186 mUpdateStatus.pack();
1190 * Create the Table display. This includes a "detail" Table in the bottom
1191 * half and 2 modes in the top half: allocation Table and
1192 * library+allocations Tables.
1194 * @param base the top parent to create the display into
1196 private void createTableDisplay(Composite base) {
1197 final int minPanelWidth = 60;
1199 final IPreferenceStore prefs = DdmUiPreferences.getStore();
1201 // top level composite for mode 1 & 2
1202 mTableModeControl = new Composite(base, SWT.NONE);
1203 GridLayout gl = new GridLayout(1, false);
1204 gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
1205 mTableModeControl.setLayout(gl);
1206 mTableModeControl.setLayoutData(new GridData(GridData.FILL_BOTH));
1208 mTotalMemoryLabel = new Label(mTableModeControl, SWT.NONE);
1209 mTotalMemoryLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1210 mTotalMemoryLabel.setText("Total Memory: 0 Bytes");
1212 // the top half of these modes is dynamic
1214 final Composite sash_composite = new Composite(mTableModeControl,
1216 sash_composite.setLayout(new FormLayout());
1217 sash_composite.setLayoutData(new GridData(GridData.FILL_BOTH));
1219 // create the stacked composite
1220 mAllocationStackComposite = new Composite(sash_composite, SWT.NONE);
1221 mAllocationStackLayout = new StackLayout();
1222 mAllocationStackComposite.setLayout(mAllocationStackLayout);
1223 mAllocationStackComposite.setLayoutData(new GridData(
1224 GridData.FILL_BOTH));
1226 // create the top half for mode 1
1227 createAllocationTopHalf(mAllocationStackComposite);
1229 // create the top half for mode 2
1230 createLibraryTopHalf(mAllocationStackComposite);
1232 final Sash sash = new Sash(sash_composite, SWT.HORIZONTAL);
1234 // bottom half of these modes is the same: detail table
1235 createDetailTable(sash_composite);
1237 // init value for stack
1238 mAllocationStackLayout.topControl = mAllocationModeTop;
1241 FormData data = new FormData();
1242 data.top = new FormAttachment(mTotalMemoryLabel, 0);
1243 data.bottom = new FormAttachment(sash, 0);
1244 data.left = new FormAttachment(0, 0);
1245 data.right = new FormAttachment(100, 0);
1246 mAllocationStackComposite.setLayoutData(data);
1248 final FormData sashData = new FormData();
1249 if (prefs != null && prefs.contains(PREFS_ALLOCATION_SASH)) {
1250 sashData.top = new FormAttachment(0,
1251 prefs.getInt(PREFS_ALLOCATION_SASH));
1253 sashData.top = new FormAttachment(50, 0); // 50% across
1255 sashData.left = new FormAttachment(0, 0);
1256 sashData.right = new FormAttachment(100, 0);
1257 sash.setLayoutData(sashData);
1259 data = new FormData();
1260 data.top = new FormAttachment(sash, 0);
1261 data.bottom = new FormAttachment(100, 0);
1262 data.left = new FormAttachment(0, 0);
1263 data.right = new FormAttachment(100, 0);
1264 mDetailTable.setLayoutData(data);
1266 // allow resizes, but cap at minPanelWidth
1267 sash.addListener(SWT.Selection, new Listener() {
1268 public void handleEvent(Event e) {
1269 Rectangle sashRect = sash.getBounds();
1270 Rectangle panelRect = sash_composite.getClientArea();
1271 int bottom = panelRect.height - sashRect.height - minPanelWidth;
1272 e.y = Math.max(Math.min(e.y, bottom), minPanelWidth);
1273 if (e.y != sashRect.y) {
1274 sashData.top = new FormAttachment(0, e.y);
1275 prefs.setValue(PREFS_ALLOCATION_SASH, e.y);
1276 sash_composite.layout();
1282 private void createDetailTable(Composite base) {
1284 final IPreferenceStore prefs = DdmUiPreferences.getStore();
1286 mDetailTable = new Table(base, SWT.MULTI | SWT.FULL_SELECTION);
1287 mDetailTable.setLayoutData(new GridData(GridData.FILL_BOTH));
1288 mDetailTable.setHeaderVisible(true);
1289 mDetailTable.setLinesVisible(true);
1291 TableHelper.createTableColumn(mDetailTable, "Address", SWT.RIGHT,
1292 "00000000", PREFS_DETAIL_ADDRESS, prefs); //$NON-NLS-1$
1293 TableHelper.createTableColumn(mDetailTable, "Library", SWT.LEFT,
1294 "abcdefghijklmnopqrst", PREFS_DETAIL_LIBRARY, prefs); //$NON-NLS-1$
1295 TableHelper.createTableColumn(mDetailTable, "Method", SWT.LEFT,
1296 "abcdefghijklmnopqrst", PREFS_DETAIL_METHOD, prefs); //$NON-NLS-1$
1297 TableHelper.createTableColumn(mDetailTable, "File", SWT.LEFT,
1298 "abcdefghijklmnopqrstuvwxyz", PREFS_DETAIL_FILE, prefs); //$NON-NLS-1$
1299 TableHelper.createTableColumn(mDetailTable, "Line", SWT.RIGHT,
1300 "9,999", PREFS_DETAIL_LINE, prefs); //$NON-NLS-1$
1303 private void createAllocationTopHalf(Composite b) {
1304 final IPreferenceStore prefs = DdmUiPreferences.getStore();
1306 Composite base = new Composite(b, SWT.NONE);
1307 mAllocationModeTop = base;
1308 GridLayout gl = new GridLayout(1, false);
1309 gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
1310 gl.verticalSpacing = 0;
1312 base.setLayoutData(new GridData(GridData.FILL_BOTH));
1314 // horizontal layout for memory total and pages UI
1315 mPageUIComposite = new Composite(base, SWT.NONE);
1316 mPageUIComposite.setLayoutData(new GridData(
1317 GridData.HORIZONTAL_ALIGN_BEGINNING));
1318 gl = new GridLayout(3, false);
1319 gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
1320 gl.horizontalSpacing = 0;
1321 mPageUIComposite.setLayout(gl);
1324 mPagePreviousButton = new Button(mPageUIComposite, SWT.NONE);
1325 mPagePreviousButton.setText("<");
1326 mPagePreviousButton.addSelectionListener(new SelectionAdapter() {
1328 public void widgetSelected(SelectionEvent e) {
1332 fillAllocationTable();
1336 mPageNextButton = new Button(mPageUIComposite, SWT.NONE);
1337 mPageNextButton.setText(">");
1338 mPageNextButton.addSelectionListener(new SelectionAdapter() {
1340 public void widgetSelected(SelectionEvent e) {
1344 fillAllocationTable();
1348 mPageLabel = new Label(mPageUIComposite, SWT.NONE);
1349 mPageLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1353 mAllocationTable = new Table(base, SWT.MULTI | SWT.FULL_SELECTION);
1354 mAllocationTable.setLayoutData(new GridData(GridData.FILL_BOTH));
1355 mAllocationTable.setHeaderVisible(true);
1356 mAllocationTable.setLinesVisible(true);
1358 TableHelper.createTableColumn(mAllocationTable, "Total", SWT.RIGHT,
1359 "9,999,999", PREFS_ALLOC_TOTAL, prefs); //$NON-NLS-1$
1360 TableHelper.createTableColumn(mAllocationTable, "Count", SWT.RIGHT,
1361 "9,999", PREFS_ALLOC_COUNT, prefs); //$NON-NLS-1$
1362 TableHelper.createTableColumn(mAllocationTable, "Size", SWT.RIGHT,
1363 "999,999", PREFS_ALLOC_SIZE, prefs); //$NON-NLS-1$
1364 TableHelper.createTableColumn(mAllocationTable, "Library", SWT.LEFT,
1365 "abcdefghijklmnopqrst", PREFS_ALLOC_LIBRARY, prefs); //$NON-NLS-1$
1366 TableHelper.createTableColumn(mAllocationTable, "Method", SWT.LEFT,
1367 "abcdefghijklmnopqrst", PREFS_ALLOC_METHOD, prefs); //$NON-NLS-1$
1368 TableHelper.createTableColumn(mAllocationTable, "File", SWT.LEFT,
1369 "abcdefghijklmnopqrstuvwxyz", PREFS_ALLOC_FILE, prefs); //$NON-NLS-1$
1371 mAllocationTable.addSelectionListener(new SelectionAdapter() {
1373 public void widgetSelected(SelectionEvent e) {
1374 // get the selection index
1375 int index = mAllocationTable.getSelectionIndex();
1376 TableItem item = mAllocationTable.getItem(index);
1377 if (item != null && item.getData() instanceof NativeAllocationInfo) {
1378 fillDetailTable((NativeAllocationInfo)item.getData());
1384 private void createLibraryTopHalf(Composite base) {
1385 final int minPanelWidth = 60;
1387 final IPreferenceStore prefs = DdmUiPreferences.getStore();
1389 // create a composite that'll contain 2 tables horizontally
1390 final Composite top = new Composite(base, SWT.NONE);
1391 mLibraryModeTopControl = top;
1392 top.setLayout(new FormLayout());
1393 top.setLayoutData(new GridData(GridData.FILL_BOTH));
1395 // first table: library
1396 mLibraryTable = new Table(top, SWT.MULTI | SWT.FULL_SELECTION);
1397 mLibraryTable.setLayoutData(new GridData(GridData.FILL_BOTH));
1398 mLibraryTable.setHeaderVisible(true);
1399 mLibraryTable.setLinesVisible(true);
1401 TableHelper.createTableColumn(mLibraryTable, "Library", SWT.LEFT,
1402 "abcdefghijklmnopqrstuvwxyz", PREFS_LIB_LIBRARY, prefs); //$NON-NLS-1$
1403 TableHelper.createTableColumn(mLibraryTable, "Size", SWT.RIGHT,
1404 "9,999,999", PREFS_LIB_SIZE, prefs); //$NON-NLS-1$
1405 TableHelper.createTableColumn(mLibraryTable, "Count", SWT.RIGHT,
1406 "9,999", PREFS_LIB_COUNT, prefs); //$NON-NLS-1$
1408 mLibraryTable.addSelectionListener(new SelectionAdapter() {
1410 public void widgetSelected(SelectionEvent e) {
1411 fillLibraryAllocationTable();
1415 final Sash sash = new Sash(top, SWT.VERTICAL);
1417 // 2nd table: allocation per library
1418 mLibraryAllocationTable = new Table(top, SWT.MULTI | SWT.FULL_SELECTION);
1419 mLibraryAllocationTable.setLayoutData(new GridData(GridData.FILL_BOTH));
1420 mLibraryAllocationTable.setHeaderVisible(true);
1421 mLibraryAllocationTable.setLinesVisible(true);
1423 TableHelper.createTableColumn(mLibraryAllocationTable, "Total",
1424 SWT.RIGHT, "9,999,999", PREFS_LIBALLOC_TOTAL, prefs); //$NON-NLS-1$
1425 TableHelper.createTableColumn(mLibraryAllocationTable, "Count",
1426 SWT.RIGHT, "9,999", PREFS_LIBALLOC_COUNT, prefs); //$NON-NLS-1$
1427 TableHelper.createTableColumn(mLibraryAllocationTable, "Size",
1428 SWT.RIGHT, "999,999", PREFS_LIBALLOC_SIZE, prefs); //$NON-NLS-1$
1429 TableHelper.createTableColumn(mLibraryAllocationTable, "Method",
1430 SWT.LEFT, "abcdefghijklmnopqrst", PREFS_LIBALLOC_METHOD, prefs); //$NON-NLS-1$
1432 mLibraryAllocationTable.addSelectionListener(new SelectionAdapter() {
1434 public void widgetSelected(SelectionEvent e) {
1435 // get the index of the selection in the library table
1436 int index1 = mLibraryTable.getSelectionIndex();
1437 // get the index in the library allocation table
1438 int index2 = mLibraryAllocationTable.getSelectionIndex();
1439 // get the MallocInfo object
1440 LibraryAllocations liballoc = mLibraryAllocations.get(index1);
1441 NativeAllocationInfo info = liballoc.getAllocation(index2);
1442 fillDetailTable(info);
1447 FormData data = new FormData();
1448 data.top = new FormAttachment(0, 0);
1449 data.bottom = new FormAttachment(100, 0);
1450 data.left = new FormAttachment(0, 0);
1451 data.right = new FormAttachment(sash, 0);
1452 mLibraryTable.setLayoutData(data);
1454 final FormData sashData = new FormData();
1455 if (prefs != null && prefs.contains(PREFS_LIBRARY_SASH)) {
1456 sashData.left = new FormAttachment(0,
1457 prefs.getInt(PREFS_LIBRARY_SASH));
1459 sashData.left = new FormAttachment(50, 0);
1461 sashData.bottom = new FormAttachment(100, 0);
1462 sashData.top = new FormAttachment(0, 0); // 50% across
1463 sash.setLayoutData(sashData);
1465 data = new FormData();
1466 data.top = new FormAttachment(0, 0);
1467 data.bottom = new FormAttachment(100, 0);
1468 data.left = new FormAttachment(sash, 0);
1469 data.right = new FormAttachment(100, 0);
1470 mLibraryAllocationTable.setLayoutData(data);
1472 // allow resizes, but cap at minPanelWidth
1473 sash.addListener(SWT.Selection, new Listener() {
1474 public void handleEvent(Event e) {
1475 Rectangle sashRect = sash.getBounds();
1476 Rectangle panelRect = top.getClientArea();
1477 int right = panelRect.width - sashRect.width - minPanelWidth;
1478 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1479 if (e.x != sashRect.x) {
1480 sashData.left = new FormAttachment(0, e.x);
1481 prefs.setValue(PREFS_LIBRARY_SASH, e.y);
1488 private void emptyTables() {
1489 mAllocationTable.removeAll();
1490 mLibraryTable.removeAll();
1491 mLibraryAllocationTable.removeAll();
1492 mDetailTable.removeAll();
1495 private void sortAllocationsPerLibrary() {
1496 if (mClientData != null) {
1497 mLibraryAllocations.clear();
1499 // create a hash map of LibraryAllocations to access aggregate
1500 // objects already created
1501 HashMap<String, LibraryAllocations> libcache =
1502 new HashMap<String, LibraryAllocations>();
1504 // get the allocation count
1505 int count = mDisplayedAllocations.size();
1506 for (int i = 0; i < count; i++) {
1507 NativeAllocationInfo allocInfo = mDisplayedAllocations.get(i);
1509 NativeStackCallInfo stackCallInfo = allocInfo.getRelevantStackCallInfo();
1510 if (stackCallInfo != null) {
1511 String libraryName = stackCallInfo.getLibraryName();
1512 LibraryAllocations liballoc = libcache.get(libraryName);
1513 if (liballoc == null) {
1514 // didn't find a library allocation object already
1515 // created so we create one
1516 liballoc = new LibraryAllocations(libraryName);
1517 // add it to the cache
1518 libcache.put(libraryName, liballoc);
1519 // add it to the list
1520 mLibraryAllocations.add(liballoc);
1522 // add the MallocInfo object to it.
1523 liballoc.addAllocation(allocInfo);
1526 // now that the list is created, we need to compute the size and
1527 // sort it by size. This will also sort the MallocInfo objects
1528 // inside each LibraryAllocation objects.
1529 for (LibraryAllocations liballoc : mLibraryAllocations) {
1530 liballoc.computeAllocationSizeAndCount();
1534 Collections.sort(mLibraryAllocations,
1535 new Comparator<LibraryAllocations>() {
1536 public int compare(LibraryAllocations o1,
1537 LibraryAllocations o2) {
1538 return o2.getSize() - o1.getSize();
1544 private void renderBitmap(ClientData cd) {
1547 // Atomically get and clear the heap data.
1549 if (serializeHeapData(cd.getVmHeapData()) == false) {
1550 // no change, we return.
1554 pixData = getSerializedData();
1556 ImageData id = createLinearHeapImage(pixData, 200, mMapPalette);
1557 Image image = new Image(mBase.getDisplay(), id);
1558 mImage.setImage(image);
1564 * Create color palette for map. Set up titles for legend.
1566 private static PaletteData createPalette() {
1567 RGB colors[] = new RGB[NUM_PALETTE_ENTRIES];
1569 = new RGB(192, 192, 192); // non-heap pixels are gray
1571 = "(heap expansion area)";
1574 = new RGB(0, 0, 0); // free chunks are black
1578 colors[HeapSegmentElement.KIND_OBJECT + 2]
1579 = new RGB(0, 0, 255); // objects are blue
1580 mMapLegend[HeapSegmentElement.KIND_OBJECT + 2]
1583 colors[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
1584 = new RGB(0, 255, 0); // class objects are green
1585 mMapLegend[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
1588 colors[HeapSegmentElement.KIND_ARRAY_1 + 2]
1589 = new RGB(255, 0, 0); // byte/bool arrays are red
1590 mMapLegend[HeapSegmentElement.KIND_ARRAY_1 + 2]
1591 = "1-byte array (byte[], boolean[])";
1593 colors[HeapSegmentElement.KIND_ARRAY_2 + 2]
1594 = new RGB(255, 128, 0); // short/char arrays are orange
1595 mMapLegend[HeapSegmentElement.KIND_ARRAY_2 + 2]
1596 = "2-byte array (short[], char[])";
1598 colors[HeapSegmentElement.KIND_ARRAY_4 + 2]
1599 = new RGB(255, 255, 0); // obj/int/float arrays are yellow
1600 mMapLegend[HeapSegmentElement.KIND_ARRAY_4 + 2]
1601 = "4-byte array (object[], int[], float[])";
1603 colors[HeapSegmentElement.KIND_ARRAY_8 + 2]
1604 = new RGB(255, 128, 128); // long/double arrays are pink
1605 mMapLegend[HeapSegmentElement.KIND_ARRAY_8 + 2]
1606 = "8-byte array (long[], double[])";
1608 colors[HeapSegmentElement.KIND_UNKNOWN + 2]
1609 = new RGB(255, 0, 255); // unknown objects are cyan
1610 mMapLegend[HeapSegmentElement.KIND_UNKNOWN + 2]
1613 colors[HeapSegmentElement.KIND_NATIVE + 2]
1614 = new RGB(64, 64, 64); // native objects are dark gray
1615 mMapLegend[HeapSegmentElement.KIND_NATIVE + 2]
1616 = "non-Java object";
1618 return new PaletteData(colors);
1621 private void saveAllocations(String fileName) {
1623 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
1625 for (NativeAllocationInfo alloc : mAllocations) {
1626 out.println(alloc.toString());
1629 } catch (IOException e) {