FileDocCategorySizeDatePackage
NativeHeapPanel.javaAPI DocAndroid 1.5 API63094Wed May 06 22:41:08 BST 2009com.android.ddmuilib

NativeHeapPanel

public final class NativeHeapPanel extends BaseHeapPanel
Panel with native heap information.

Fields Summary
private static final int
NUM_PALETTE_ENTRIES
color palette and map legend. NATIVE is the last enum is a 0 based enum list, so we need Native+1 at least. We also need 2 more entries for free area and expansion area.
private static final String[]
mMapLegend
private static final org.eclipse.swt.graphics.PaletteData
mMapPalette
private static final int
ALLOC_DISPLAY_ALL
private static final int
ALLOC_DISPLAY_PRE_ZYGOTE
private static final int
ALLOC_DISPLAY_POST_ZYGOTE
private org.eclipse.swt.widgets.Display
mDisplay
private org.eclipse.swt.widgets.Composite
mBase
private org.eclipse.swt.widgets.Label
mUpdateStatus
private org.eclipse.swt.widgets.Combo
mAllocDisplayCombo
combo giving choice of what to display: all, pre-zygote, post-zygote
private org.eclipse.swt.widgets.Button
mFullUpdateButton
private org.eclipse.swt.widgets.Combo
mDisplayModeCombo
private org.eclipse.swt.widgets.Composite
mTopStackComposite
stack composite for mode (1-2) & 3
private org.eclipse.swt.custom.StackLayout
mTopStackLayout
private org.eclipse.swt.widgets.Composite
mAllocationStackComposite
stack composite for mode 1 & 2
private org.eclipse.swt.custom.StackLayout
mAllocationStackLayout
private org.eclipse.swt.widgets.Composite
mTableModeControl
top level container for mode 1 & 2
private org.eclipse.swt.widgets.Control
mAllocationModeTop
top level object for the allocation mode
private org.eclipse.swt.widgets.Control
mLibraryModeTopControl
top level for the library mode
private org.eclipse.swt.widgets.Composite
mPageUIComposite
composite for page UI and total memory display
private org.eclipse.swt.widgets.Label
mTotalMemoryLabel
private org.eclipse.swt.widgets.Label
mPageLabel
private org.eclipse.swt.widgets.Button
mPageNextButton
private org.eclipse.swt.widgets.Button
mPagePreviousButton
private org.eclipse.swt.widgets.Table
mAllocationTable
private org.eclipse.swt.widgets.Table
mLibraryTable
private org.eclipse.swt.widgets.Table
mLibraryAllocationTable
private org.eclipse.swt.widgets.Table
mDetailTable
private org.eclipse.swt.widgets.Label
mImage
private int
mAllocDisplayMode
private StackCallThread
mStackCallThread
pointer to current stackcall thread computation in order to quit it if required (new update requested)
private FillTableThread
mFillTableThread
Current Library Allocation table fill thread. killed if selection changes
private com.android.ddmlib.ClientData
mClientData
current client data. Used to access the malloc info when switching pages or selecting allocation to show stack call
private com.android.ddmlib.ClientData
mBackUpClientData
client data from a previous display. used when asking for an "update & diff"
private final ArrayList
mAllocations
list of NativeAllocationInfo objects filled with the list from ClientData
private final ArrayList
mDisplayedAllocations
list of the {@link NativeAllocationInfo} being displayed based on the selection of {@link #mAllocDisplayCombo}.
private final ArrayList
mBackUpAllocations
list of NativeAllocationInfo object kept as backup when doing an "update & diff"
private int
mBackUpTotalMemory
back up of the total memory, used when doing an "update & diff"
private int
mCurrentPage
private int
mPageCount
private final ArrayList
mLibraryAllocations
list of allocation per Library. This is created from the list of NativeAllocationInfo objects that is stored in the ClientData object. Since we don't keep this list around, it is recomputed everytime the client changes.
private static final int
NOT_SELECTED
private static final int
NOT_ENABLED
private static final int
ENABLED
private static final int
DISPLAY_PER_PAGE
private static final String
PREFS_ALLOCATION_SASH
private static final String
PREFS_LIBRARY_SASH
private static final String
PREFS_DETAIL_ADDRESS
private static final String
PREFS_DETAIL_LIBRARY
private static final String
PREFS_DETAIL_METHOD
private static final String
PREFS_DETAIL_FILE
private static final String
PREFS_DETAIL_LINE
private static final String
PREFS_ALLOC_TOTAL
private static final String
PREFS_ALLOC_COUNT
private static final String
PREFS_ALLOC_SIZE
private static final String
PREFS_ALLOC_LIBRARY
private static final String
PREFS_ALLOC_METHOD
private static final String
PREFS_ALLOC_FILE
private static final String
PREFS_LIB_LIBRARY
private static final String
PREFS_LIB_SIZE
private static final String
PREFS_LIB_COUNT
private static final String
PREFS_LIBALLOC_TOTAL
private static final String
PREFS_LIBALLOC_COUNT
private static final String
PREFS_LIBALLOC_SIZE
private static final String
PREFS_LIBALLOC_METHOD
private static DecimalFormat
sFormatter
static formatter object to format all numbers as #,###
private HashMap
mSourceCache
caching mechanism to avoid recomputing the backtrace for a particular address several times.
private long
mTotalSize
private org.eclipse.swt.widgets.Button
mSaveButton
private org.eclipse.swt.widgets.Button
mSymbolsButton
Constructors Summary
Methods Summary
public voidclientChanged(com.android.ddmlib.Client client, int changeMask)
Sent when an existing client information changed.

This is sent from a non UI thread.

param
client the updated client.
param
changeMask the bit mask describing the changed properties. It can contain any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
see
IClientChangeListener#clientChanged(Client, int)

        if (client == getCurrentClient()) {
            if ((changeMask & Client.CHANGE_NATIVE_HEAP_DATA) == Client.CHANGE_NATIVE_HEAP_DATA) {
                if (mBase.isDisposed())
                    return;

                mBase.getDisplay().asyncExec(new Runnable() {
                    public void run() {
                        clientSelected();
                    }
                });
            }
        }
    
public voidclientSelected()
Sent when a new client is selected. The new client can be accessed with {@link #getCurrentClient()}.

        if (mBase.isDisposed())
            return;

        Client client = getCurrentClient();

        mDisplayModeCombo.setEnabled(false);
        emptyTables();

        Log.d("ddms", "NativeHeapPanel: changed " + client);

        if (client != null) {
            ClientData cd = client.getClientData();
            mClientData = cd;

            // if (cd.getShowHeapUpdates())
            setUpdateStatus(ENABLED);
            // else
            // setUpdateStatus(NOT_ENABLED);

            initAllocationDisplay();

            //renderBitmap(cd);
        } else {
            mClientData = null;
            setUpdateStatus(NOT_SELECTED);
        }

        mBase.pack();
    
private voidcreateAllocationTopHalf(org.eclipse.swt.widgets.Composite b)

        final IPreferenceStore prefs = DdmUiPreferences.getStore();

        Composite base = new Composite(b, SWT.NONE);
        mAllocationModeTop = base;
        GridLayout gl = new GridLayout(1, false);
        gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
        gl.verticalSpacing = 0;
        base.setLayout(gl);
        base.setLayoutData(new GridData(GridData.FILL_BOTH));

        // horizontal layout for memory total and pages UI
        mPageUIComposite = new Composite(base, SWT.NONE);
        mPageUIComposite.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_BEGINNING));
        gl = new GridLayout(3, false);
        gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
        gl.horizontalSpacing = 0;
        mPageUIComposite.setLayout(gl);

        // Page UI
        mPagePreviousButton = new Button(mPageUIComposite, SWT.NONE);
        mPagePreviousButton.setText("<");
        mPagePreviousButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                mCurrentPage--;
                updatePageUI();
                emptyTables();
                fillAllocationTable();
            }
        });

        mPageNextButton = new Button(mPageUIComposite, SWT.NONE);
        mPageNextButton.setText(">");
        mPageNextButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                mCurrentPage++;
                updatePageUI();
                emptyTables();
                fillAllocationTable();
            }
        });

        mPageLabel = new Label(mPageUIComposite, SWT.NONE);
        mPageLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        updatePageUI();

        mAllocationTable = new Table(base, SWT.MULTI | SWT.FULL_SELECTION);
        mAllocationTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        mAllocationTable.setHeaderVisible(true);
        mAllocationTable.setLinesVisible(true);

        TableHelper.createTableColumn(mAllocationTable, "Total", SWT.RIGHT,
                "9,999,999", PREFS_ALLOC_TOTAL, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mAllocationTable, "Count", SWT.RIGHT,
                "9,999", PREFS_ALLOC_COUNT, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mAllocationTable, "Size", SWT.RIGHT,
                "999,999", PREFS_ALLOC_SIZE, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mAllocationTable, "Library", SWT.LEFT,
                "abcdefghijklmnopqrst", PREFS_ALLOC_LIBRARY, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mAllocationTable, "Method", SWT.LEFT,
                "abcdefghijklmnopqrst", PREFS_ALLOC_METHOD, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mAllocationTable, "File", SWT.LEFT,
                "abcdefghijklmnopqrstuvwxyz", PREFS_ALLOC_FILE, prefs); //$NON-NLS-1$

        mAllocationTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // get the selection index
                int index = mAllocationTable.getSelectionIndex();
                TableItem item = mAllocationTable.getItem(index);
                if (item != null && item.getData() instanceof NativeAllocationInfo) {
                    fillDetailTable((NativeAllocationInfo)item.getData());
                }
            }
        });
    
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)
Create our control(s).


        mDisplay = parent.getDisplay();

        mBase = new Composite(parent, SWT.NONE);
        GridLayout gl = new GridLayout(1, false);
        gl.horizontalSpacing = 0;
        gl.verticalSpacing = 0;
        mBase.setLayout(gl);
        mBase.setLayoutData(new GridData(GridData.FILL_BOTH));
        
        // composite for <update btn> <status>
        Composite tmp = new Composite(mBase, SWT.NONE);
        tmp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        tmp.setLayout(gl = new GridLayout(2, false));
        gl.marginWidth = gl.marginHeight = 0;

        mFullUpdateButton = new Button(tmp, SWT.NONE);
        mFullUpdateButton.setText("Full Update");
        mFullUpdateButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                mBackUpClientData = null;
                mDisplayModeCombo.setEnabled(false);
                mSaveButton.setEnabled(false);
                emptyTables();
                // if we already have a stack call computation for this
                // client
                // we stop it
                if (mStackCallThread != null &&
                        mStackCallThread.getClientData() == mClientData) {
                    mStackCallThread.quit();
                    mStackCallThread = null;
                }
                mLibraryAllocations.clear();
                Client client = getCurrentClient();
                if (client != null) {
                    client.requestNativeHeapInformation();
                }
            }
        });

        mUpdateStatus = new Label(tmp, SWT.NONE);
        mUpdateStatus.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // top layout for the combos and oter controls on the right.
        Composite top_layout = new Composite(mBase, SWT.NONE);
        top_layout.setLayout(gl = new GridLayout(4, false));
        gl.marginWidth = gl.marginHeight = 0;
        
        new Label(top_layout, SWT.NONE).setText("Show:");
        
        mAllocDisplayCombo = new Combo(top_layout, SWT.DROP_DOWN | SWT.READ_ONLY);
        mAllocDisplayCombo.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        mAllocDisplayCombo.add("All Allocations");
        mAllocDisplayCombo.add("Pre-Zygote Allocations");
        mAllocDisplayCombo.add("Zygote Child Allocations (Z)");
        mAllocDisplayCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onAllocDisplayChange();
            }
        });
        mAllocDisplayCombo.select(0);
        
        // separator
        Label separator = new Label(top_layout, SWT.SEPARATOR | SWT.VERTICAL);
        GridData gd;
        separator.setLayoutData(gd = new GridData(
                GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
        gd.heightHint = 0;
        gd.verticalSpan = 2;

        mSaveButton = new Button(top_layout, SWT.PUSH);
        mSaveButton.setText("Save...");
        mSaveButton.setEnabled(false);
        mSaveButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                FileDialog fileDialog = new FileDialog(mBase.getShell(), SWT.SAVE);

                fileDialog.setText("Save Allocations");
                fileDialog.setFileName("allocations.txt");

                String fileName = fileDialog.open();
                if (fileName != null) {
                    saveAllocations(fileName);
                }
            }
        });
        
        /*
         * TODO: either fix the diff mechanism or remove it altogether.
        mDiffUpdateButton = new Button(top_layout, SWT.NONE);
        mDiffUpdateButton.setText("Update && Diff");
        mDiffUpdateButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // since this is an update and diff, we need to store the
                // current list
                // of mallocs
                mBackUpAllocations.clear();
                mBackUpAllocations.addAll(mAllocations);
                mBackUpClientData = mClientData;
                mBackUpTotalMemory = mClientData.getTotalNativeMemory();

                mDisplayModeCombo.setEnabled(false);
                emptyTables();
                // if we already have a stack call computation for this
                // client
                // we stop it
                if (mStackCallThread != null &&
                        mStackCallThread.getClientData() == mClientData) {
                    mStackCallThread.quit();
                    mStackCallThread = null;
                }
                mLibraryAllocations.clear();
                Client client = getCurrentClient();
                if (client != null) {
                    client.requestNativeHeapInformation();
                }
            }
        });
        */

        Label l = new Label(top_layout, SWT.NONE);
        l.setText("Display:");

        mDisplayModeCombo = new Combo(top_layout, SWT.DROP_DOWN | SWT.READ_ONLY);
        mDisplayModeCombo.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        mDisplayModeCombo.setItems(new String[] { "Allocation List", "By Libraries" });
        mDisplayModeCombo.select(0);
        mDisplayModeCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                switchDisplayMode();
            }
        });
        mDisplayModeCombo.setEnabled(false);
        
        mSymbolsButton = new Button(top_layout, SWT.PUSH);
        mSymbolsButton.setText("Load Symbols");
        mSymbolsButton.setEnabled(false);


        // create a composite that will contains the actual content composites,
        // in stack mode layout.
        // This top level composite contains 2 other composites.
        // * one for both Allocations and Libraries mode
        // * one for flat mode (which is gone for now)

        mTopStackComposite = new Composite(mBase, SWT.NONE);
        mTopStackComposite.setLayout(mTopStackLayout = new StackLayout());
        mTopStackComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

        // create 1st and 2nd modes
        createTableDisplay(mTopStackComposite);

        mTopStackLayout.topControl = mTableModeControl;
        mTopStackComposite.layout();

        setUpdateStatus(NOT_SELECTED);

        // Work in progress
        // TODO add image display of native heap.
        //mImage = new Label(mBase, SWT.NONE);

        mBase.pack();

        return mBase;
    
private voidcreateDetailTable(org.eclipse.swt.widgets.Composite base)


        final IPreferenceStore prefs = DdmUiPreferences.getStore();

        mDetailTable = new Table(base, SWT.MULTI | SWT.FULL_SELECTION);
        mDetailTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        mDetailTable.setHeaderVisible(true);
        mDetailTable.setLinesVisible(true);

        TableHelper.createTableColumn(mDetailTable, "Address", SWT.RIGHT,
                "00000000", PREFS_DETAIL_ADDRESS, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mDetailTable, "Library", SWT.LEFT,
                "abcdefghijklmnopqrst", PREFS_DETAIL_LIBRARY, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mDetailTable, "Method", SWT.LEFT,
                "abcdefghijklmnopqrst", PREFS_DETAIL_METHOD, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mDetailTable, "File", SWT.LEFT,
                "abcdefghijklmnopqrstuvwxyz", PREFS_DETAIL_FILE, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mDetailTable, "Line", SWT.RIGHT,
                "9,999", PREFS_DETAIL_LINE, prefs); //$NON-NLS-1$
    
private voidcreateLibraryTopHalf(org.eclipse.swt.widgets.Composite base)

        final int minPanelWidth = 60;

        final IPreferenceStore prefs = DdmUiPreferences.getStore();

        // create a composite that'll contain 2 tables horizontally
        final Composite top = new Composite(base, SWT.NONE);
        mLibraryModeTopControl = top;
        top.setLayout(new FormLayout());
        top.setLayoutData(new GridData(GridData.FILL_BOTH));

        // first table: library
        mLibraryTable = new Table(top, SWT.MULTI | SWT.FULL_SELECTION);
        mLibraryTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        mLibraryTable.setHeaderVisible(true);
        mLibraryTable.setLinesVisible(true);

        TableHelper.createTableColumn(mLibraryTable, "Library", SWT.LEFT,
                "abcdefghijklmnopqrstuvwxyz", PREFS_LIB_LIBRARY, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mLibraryTable, "Size", SWT.RIGHT,
                "9,999,999", PREFS_LIB_SIZE, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mLibraryTable, "Count", SWT.RIGHT,
                "9,999", PREFS_LIB_COUNT, prefs); //$NON-NLS-1$

        mLibraryTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fillLibraryAllocationTable();
            }
        });

        final Sash sash = new Sash(top, SWT.VERTICAL);

        // 2nd table: allocation per library
        mLibraryAllocationTable = new Table(top, SWT.MULTI | SWT.FULL_SELECTION);
        mLibraryAllocationTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        mLibraryAllocationTable.setHeaderVisible(true);
        mLibraryAllocationTable.setLinesVisible(true);

        TableHelper.createTableColumn(mLibraryAllocationTable, "Total",
                SWT.RIGHT, "9,999,999", PREFS_LIBALLOC_TOTAL, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mLibraryAllocationTable, "Count",
                SWT.RIGHT, "9,999", PREFS_LIBALLOC_COUNT, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mLibraryAllocationTable, "Size",
                SWT.RIGHT, "999,999", PREFS_LIBALLOC_SIZE, prefs); //$NON-NLS-1$
        TableHelper.createTableColumn(mLibraryAllocationTable, "Method",
                SWT.LEFT, "abcdefghijklmnopqrst", PREFS_LIBALLOC_METHOD, prefs); //$NON-NLS-1$

        mLibraryAllocationTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // get the index of the selection in the library table
                int index1 = mLibraryTable.getSelectionIndex();
                // get the index in the library allocation table
                int index2 = mLibraryAllocationTable.getSelectionIndex();
                // get the MallocInfo object
                LibraryAllocations liballoc = mLibraryAllocations.get(index1);
                NativeAllocationInfo info = liballoc.getAllocation(index2);
                fillDetailTable(info);
            }
        });

        // form layout data
        FormData data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.bottom = new FormAttachment(100, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(sash, 0);
        mLibraryTable.setLayoutData(data);

        final FormData sashData = new FormData();
        if (prefs != null && prefs.contains(PREFS_LIBRARY_SASH)) {
            sashData.left = new FormAttachment(0,
                    prefs.getInt(PREFS_LIBRARY_SASH));
        } else {
            sashData.left = new FormAttachment(50, 0);
        }
        sashData.bottom = new FormAttachment(100, 0);
        sashData.top = new FormAttachment(0, 0); // 50% across
        sash.setLayoutData(sashData);

        data = new FormData();
        data.top = new FormAttachment(0, 0);
        data.bottom = new FormAttachment(100, 0);
        data.left = new FormAttachment(sash, 0);
        data.right = new FormAttachment(100, 0);
        mLibraryAllocationTable.setLayoutData(data);

        // allow resizes, but cap at minPanelWidth
        sash.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                Rectangle sashRect = sash.getBounds();
                Rectangle panelRect = top.getClientArea();
                int right = panelRect.width - sashRect.width - minPanelWidth;
                e.x = Math.max(Math.min(e.x, right), minPanelWidth);
                if (e.x != sashRect.x) {
                    sashData.left = new FormAttachment(0, e.x);
                    prefs.setValue(PREFS_LIBRARY_SASH, e.y);
                    top.layout();
                }
            }
        });
    
private static org.eclipse.swt.graphics.PaletteDatacreatePalette()

        RGB colors[] = new RGB[NUM_PALETTE_ENTRIES];
        colors[0]
                = new RGB(192, 192, 192); // non-heap pixels are gray
        mMapLegend[0]
                = "(heap expansion area)";

        colors[1]
                = new RGB(0, 0, 0);       // free chunks are black
        mMapLegend[1]
                = "free";

        colors[HeapSegmentElement.KIND_OBJECT + 2]
                = new RGB(0, 0, 255);     // objects are blue
        mMapLegend[HeapSegmentElement.KIND_OBJECT + 2]
                = "data object";

        colors[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
                = new RGB(0, 255, 0);     // class objects are green
        mMapLegend[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
                = "class object";

        colors[HeapSegmentElement.KIND_ARRAY_1 + 2]
                = new RGB(255, 0, 0);     // byte/bool arrays are red
        mMapLegend[HeapSegmentElement.KIND_ARRAY_1 + 2]
                = "1-byte array (byte[], boolean[])";

        colors[HeapSegmentElement.KIND_ARRAY_2 + 2]
                = new RGB(255, 128, 0);   // short/char arrays are orange
        mMapLegend[HeapSegmentElement.KIND_ARRAY_2 + 2]
                = "2-byte array (short[], char[])";

        colors[HeapSegmentElement.KIND_ARRAY_4 + 2]
                = new RGB(255, 255, 0);   // obj/int/float arrays are yellow
        mMapLegend[HeapSegmentElement.KIND_ARRAY_4 + 2]
                = "4-byte array (object[], int[], float[])";

        colors[HeapSegmentElement.KIND_ARRAY_8 + 2]
                = new RGB(255, 128, 128); // long/double arrays are pink
        mMapLegend[HeapSegmentElement.KIND_ARRAY_8 + 2]
                = "8-byte array (long[], double[])";

        colors[HeapSegmentElement.KIND_UNKNOWN + 2]
                = new RGB(255, 0, 255);   // unknown objects are cyan
        mMapLegend[HeapSegmentElement.KIND_UNKNOWN + 2]
                = "unknown object";

        colors[HeapSegmentElement.KIND_NATIVE + 2]
                = new RGB(64, 64, 64);    // native objects are dark gray
        mMapLegend[HeapSegmentElement.KIND_NATIVE + 2]
                = "non-Java object";

        return new PaletteData(colors);
    
private voidcreateTableDisplay(org.eclipse.swt.widgets.Composite base)
Create the Table display. This includes a "detail" Table in the bottom half and 2 modes in the top half: allocation Table and library+allocations Tables.

param
base the top parent to create the display into

        final int minPanelWidth = 60;

        final IPreferenceStore prefs = DdmUiPreferences.getStore();

        // top level composite for mode 1 & 2
        mTableModeControl = new Composite(base, SWT.NONE);
        GridLayout gl = new GridLayout(1, false);
        gl.marginLeft = gl.marginRight = gl.marginTop = gl.marginBottom = 0;
        mTableModeControl.setLayout(gl);
        mTableModeControl.setLayoutData(new GridData(GridData.FILL_BOTH));

        mTotalMemoryLabel = new Label(mTableModeControl, SWT.NONE);
        mTotalMemoryLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        mTotalMemoryLabel.setText("Total Memory: 0 Bytes");

        // the top half of these modes is dynamic

        final Composite sash_composite = new Composite(mTableModeControl,
                SWT.NONE);
        sash_composite.setLayout(new FormLayout());
        sash_composite.setLayoutData(new GridData(GridData.FILL_BOTH));

        // create the stacked composite
        mAllocationStackComposite = new Composite(sash_composite, SWT.NONE);
        mAllocationStackLayout = new StackLayout();
        mAllocationStackComposite.setLayout(mAllocationStackLayout);
        mAllocationStackComposite.setLayoutData(new GridData(
                GridData.FILL_BOTH));

        // create the top half for mode 1
        createAllocationTopHalf(mAllocationStackComposite);

        // create the top half for mode 2
        createLibraryTopHalf(mAllocationStackComposite);

        final Sash sash = new Sash(sash_composite, SWT.HORIZONTAL);

        // bottom half of these modes is the same: detail table
        createDetailTable(sash_composite);

        // init value for stack
        mAllocationStackLayout.topControl = mAllocationModeTop;

        // form layout data
        FormData data = new FormData();
        data.top = new FormAttachment(mTotalMemoryLabel, 0);
        data.bottom = new FormAttachment(sash, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        mAllocationStackComposite.setLayoutData(data);

        final FormData sashData = new FormData();
        if (prefs != null && prefs.contains(PREFS_ALLOCATION_SASH)) {
            sashData.top = new FormAttachment(0,
                    prefs.getInt(PREFS_ALLOCATION_SASH));
        } else {
            sashData.top = new FormAttachment(50, 0); // 50% across
        }
        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.bottom = new FormAttachment(100, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        mDetailTable.setLayoutData(data);

        // allow resizes, but cap at minPanelWidth
        sash.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                Rectangle sashRect = sash.getBounds();
                Rectangle panelRect = sash_composite.getClientArea();
                int bottom = panelRect.height - sashRect.height - minPanelWidth;
                e.y = Math.max(Math.min(e.y, bottom), minPanelWidth);
                if (e.y != sashRect.y) {
                    sashData.top = new FormAttachment(0, e.y);
                    prefs.setValue(PREFS_ALLOCATION_SASH, e.y);
                    sash_composite.layout();
                }
            }
        });
    
public voiddeviceSelected()
Sent when a new device is selected. The new device can be accessed with {@link #getCurrentDevice()}.

        // pass
    
private voidemptyTables()

        mAllocationTable.removeAll();
        mLibraryTable.removeAll();
        mLibraryAllocationTable.removeAll();
        mDetailTable.removeAll();
    
private voidfillAllocationTable()

        // get the count
        int count = mDisplayedAllocations.size();

        // get our starting index
        int start = mCurrentPage * DISPLAY_PER_PAGE;

        // loop for DISPLAY_PER_PAGE or till we reach count
        int end = start + DISPLAY_PER_PAGE;

        for (int i = start; i < end && i < count; i++) {
            NativeAllocationInfo info = mDisplayedAllocations.get(i);

            TableItem item = null;

            if (mAllocDisplayMode == ALLOC_DISPLAY_ALL)  {
                item = new TableItem(mAllocationTable, SWT.NONE);
                item.setText(0, (info.isZygoteChild() ? "Z " : "") +
                        sFormatter.format(info.getSize() * info.getAllocationCount()));
                item.setText(1, sFormatter.format(info.getAllocationCount()));
                item.setText(2, sFormatter.format(info.getSize()));
            } else if (mAllocDisplayMode == ALLOC_DISPLAY_PRE_ZYGOTE ^ info.isZygoteChild()) {
                item = new TableItem(mAllocationTable, SWT.NONE);
                item.setText(0, sFormatter.format(info.getSize() * info.getAllocationCount()));
                item.setText(1, sFormatter.format(info.getAllocationCount()));
                item.setText(2, sFormatter.format(info.getSize()));
            } else {
                // skip this item
                continue;
            }

            item.setData(info);

            NativeStackCallInfo bti = info.getRelevantStackCallInfo();
            if (bti != null) {
                String lib = bti.getLibraryName();
                String method = bti.getMethodName();
                String source = bti.getSourceFile();
                if (lib != null)
                    item.setText(3, lib);
                if (method != null)
                    item.setText(4, method);
                if (source != null)
                    item.setText(5, source);
            }
        }
    
private voidfillDetailTable(com.android.ddmlib.NativeAllocationInfo mi)

        mDetailTable.removeAll();
        mDetailTable.setRedraw(false);
        
        try {
            // populate the detail Table with the back trace
            Long[] addresses = mi.getStackCallAddresses();
            NativeStackCallInfo[] resolvedStackCall = mi.getResolvedStackCall();
            
            if (resolvedStackCall == null) {
                return;
            }

            for (int i = 0 ; i < resolvedStackCall.length ; i++) {
                if (addresses[i] == null || addresses[i].longValue() == 0) {
                    continue;
                }
                
                long addr = addresses[i].longValue();
                NativeStackCallInfo source = resolvedStackCall[i];
                
                TableItem item = new TableItem(mDetailTable, SWT.NONE);
                item.setText(0, String.format("%08x", addr)); //$NON-NLS-1$
    
                String libraryName = source.getLibraryName();
                String methodName = source.getMethodName();
                String sourceFile = source.getSourceFile();
                int lineNumber = source.getLineNumber();
                
                if (libraryName != null)
                    item.setText(1, libraryName);
                if (methodName != null)
                    item.setText(2, methodName);
                if (sourceFile != null)
                    item.setText(3, sourceFile);
                if (lineNumber != -1)
                    item.setText(4, Integer.toString(lineNumber));
            }
        } finally {
            mDetailTable.setRedraw(true);
        }
    
private voidfillLibraryAllocationTable()

        mLibraryAllocationTable.removeAll();
        mDetailTable.removeAll();
        int index = mLibraryTable.getSelectionIndex();
        if (index != -1) {
            LibraryAllocations liballoc = mLibraryAllocations.get(index);
            // start a thread that will fill table 10 at a time to keep the ui
            // responsive, but first we kill the previous one if there was one
            if (mFillTableThread != null) {
                mFillTableThread.quit();
            }
            mFillTableThread = new FillTableThread(liballoc,
                    liballoc.getAllocationSize());
            mFillTableThread.start();
        }
    
private voidfillLibraryTable()

        // fill the library table
        sortAllocationsPerLibrary();

        for (LibraryAllocations liballoc : mLibraryAllocations) {
            if (liballoc != null) {
                TableItem item = new TableItem(mLibraryTable, SWT.NONE);
                String lib = liballoc.getLibrary();
                item.setText(0, lib != null ? lib : "");
                item.setText(1, sFormatter.format(liballoc.getSize()));
                item.setText(2, sFormatter.format(liballoc.getCount()));
            }
        }
    
private voidinitAllocationDisplay()

        mAllocations.clear();
        mAllocations.addAll(mClientData.getNativeAllocationList());
        
        updateAllocDisplayList();

        // if we have a previous clientdata and it matches the current one. we
        // do a diff between the new list and the old one.
        if (mBackUpClientData != null && mBackUpClientData == mClientData) {

            ArrayList<NativeAllocationInfo> add = new ArrayList<NativeAllocationInfo>();

            // we go through the list of NativeAllocationInfo in the new list and check if
            // there's one with the same exact data (size, allocation, count and
            // stackcall addresses) in the old list.
            // if we don't find any, we add it to the "add" list
            for (NativeAllocationInfo mi : mAllocations) {
                boolean found = false;
                for (NativeAllocationInfo old_mi : mBackUpAllocations) {
                    if (mi.equals(old_mi)) {
                        found = true;
                        break;
                    }
                }
                if (found == false) {
                    add.add(mi);
                }
            }

            // put the result in mAllocations
            mAllocations.clear();
            mAllocations.addAll(add);

            // display the difference in memory usage. This is computed
            // calculating the memory usage of the objects in mAllocations.
            int count = 0;
            for (NativeAllocationInfo allocInfo : mAllocations) {
                count += allocInfo.getSize() * allocInfo.getAllocationCount();
            }

            mTotalMemoryLabel.setText(String.format("Memory Difference: %1$s Bytes",
                    sFormatter.format(count)));
        }
        else {
            // display the full memory usage
            updateTotalMemoryDisplay();
            //mDiffUpdateButton.setEnabled(mClientData.getTotalNativeMemory() > 0);
        }
        mTotalMemoryLabel.pack();

        // update the page ui
        mDisplayModeCombo.select(0);

        mLibraryAllocations.clear();

        // reset to first page
        mCurrentPage = 0;

        // update the label
        updatePageUI();

        // now fill the allocation Table with the current page
        switchDisplayMode();

        // start the thread to compute the stack calls
        if (mAllocations.size() > 0) {
            mStackCallThread = new StackCallThread(mClientData);
            mStackCallThread.start();
        }
    
protected voidonAllocDisplayChange()

        mAllocDisplayMode = mAllocDisplayCombo.getSelectionIndex();
        
        // create the new list
        updateAllocDisplayList();
        
        updateTotalMemoryDisplay();

        // reset the ui.
        mCurrentPage = 0;
        updatePageUI();
        switchDisplayMode();
    
private voidrenderBitmap(com.android.ddmlib.ClientData cd)

        byte[] pixData;

        // Atomically get and clear the heap data.
        synchronized (cd) {
            if (serializeHeapData(cd.getVmHeapData()) == false) {
                // no change, we return.
                return;
            }

            pixData = getSerializedData();

            ImageData id = createLinearHeapImage(pixData, 200, mMapPalette);
            Image image = new Image(mBase.getDisplay(), id);
            mImage.setImage(image);
            mImage.pack(true);
        }
    
private voidsaveAllocations(java.lang.String fileName)

        try {
            PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
    
            for (NativeAllocationInfo alloc : mAllocations) {
                out.println(alloc.toString());
            }
            out.close();
        } catch (IOException e) {
            Log.e("Native", e);
        }
    
public voidsetFocus()
Sets the focus to the proper control inside the panel.

        // TODO
    
protected voidsetTableFocusListener()

        addTableToFocusListener(mAllocationTable);
        addTableToFocusListener(mLibraryTable);
        addTableToFocusListener(mLibraryAllocationTable);
        addTableToFocusListener(mDetailTable);
    
private voidsetUpdateStatus(int status)

        switch (status) {
            case NOT_SELECTED:
                mUpdateStatus.setText("Select a client to see heap info");
                mAllocDisplayCombo.setEnabled(false);
                mFullUpdateButton.setEnabled(false);
                //mDiffUpdateButton.setEnabled(false);
                break;
            case NOT_ENABLED:
                mUpdateStatus.setText("Heap updates are " + "NOT ENABLED for this client");
                mAllocDisplayCombo.setEnabled(false);
                mFullUpdateButton.setEnabled(false);
                //mDiffUpdateButton.setEnabled(false);
                break;
            case ENABLED:
                mUpdateStatus.setText("Press 'Full Update' to retrieve " + "latest data");
                mAllocDisplayCombo.setEnabled(true);
                mFullUpdateButton.setEnabled(true);
                //mDiffUpdateButton.setEnabled(true);
                break;
            default:
                throw new RuntimeException();
        }

        mUpdateStatus.pack();
    
private voidsortAllocationsPerLibrary()

        if (mClientData != null) {
            mLibraryAllocations.clear();
            
            // create a hash map of LibraryAllocations to access aggregate
            // objects already created
            HashMap<String, LibraryAllocations> libcache =
                new HashMap<String, LibraryAllocations>();

            // get the allocation count
            int count = mDisplayedAllocations.size();
            for (int i = 0; i < count; i++) {
                NativeAllocationInfo allocInfo = mDisplayedAllocations.get(i);

                NativeStackCallInfo stackCallInfo = allocInfo.getRelevantStackCallInfo();
                if (stackCallInfo != null) {
                    String libraryName = stackCallInfo.getLibraryName();
                    LibraryAllocations liballoc = libcache.get(libraryName);
                    if (liballoc == null) {
                        // didn't find a library allocation object already
                        // created so we create one
                        liballoc = new LibraryAllocations(libraryName);
                        // add it to the cache
                        libcache.put(libraryName, liballoc);
                        // add it to the list
                        mLibraryAllocations.add(liballoc);
                    }
                    // add the MallocInfo object to it.
                    liballoc.addAllocation(allocInfo);
                }
            }
            // now that the list is created, we need to compute the size and
            // sort it by size. This will also sort the MallocInfo objects
            // inside each LibraryAllocation objects.
            for (LibraryAllocations liballoc : mLibraryAllocations) {
                liballoc.computeAllocationSizeAndCount();
            }

            // now we sort it
            Collections.sort(mLibraryAllocations,
                    new Comparator<LibraryAllocations>() {
                public int compare(LibraryAllocations o1,
                        LibraryAllocations o2) {
                    return o2.getSize() - o1.getSize();
                }
            });
        }
    
private voidswitchDisplayMode()

        switch (mDisplayModeCombo.getSelectionIndex()) {
            case 0: {// allocations
                mTopStackLayout.topControl = mTableModeControl;
                mAllocationStackLayout.topControl = mAllocationModeTop;
                mAllocationStackComposite.layout();
                mTopStackComposite.layout();
                emptyTables();
                fillAllocationTable();
            }
                break;
            case 1: {// libraries
                mTopStackLayout.topControl = mTableModeControl;
                mAllocationStackLayout.topControl = mLibraryModeTopControl;
                mAllocationStackComposite.layout();
                mTopStackComposite.layout();
                emptyTables();
                fillLibraryTable();
            }
                break;
        }
    
private voidupdateAllocDisplayList()

        mTotalSize = 0;
        mDisplayedAllocations.clear();
        for (NativeAllocationInfo info : mAllocations) {
            if (mAllocDisplayMode == ALLOC_DISPLAY_ALL ||
                    (mAllocDisplayMode == ALLOC_DISPLAY_PRE_ZYGOTE ^ info.isZygoteChild())) {
                mDisplayedAllocations.add(info);
                mTotalSize += info.getSize() * info.getAllocationCount();
            } else {
                // skip this item
                continue;
            }
        }
        
        int count = mDisplayedAllocations.size();
        
        mPageCount = count / DISPLAY_PER_PAGE;

        // need to add a page for the rest of the div
        if ((count % DISPLAY_PER_PAGE) > 0) {
            mPageCount++;
        }
    
public voidupdateAllocationStackCalls(com.android.ddmlib.ClientData cd, int count)
Update the UI with the newly compute stack calls, unless the UI switched to a different client.

param
cd the ClientData for which the stack call are being computed.
param
count the current count of allocations for which the stack calls have been computed.

        // we have to check that the panel still shows the same clientdata than
        // the thread is computing for.
        if (cd == mClientData) {

            int total = mAllocations.size();

            if (count == total) {
                // we're done: do something
                mDisplayModeCombo.setEnabled(true);
                mSaveButton.setEnabled(true);
                
                mStackCallThread = null;
            } else {
                // work in progress, update the progress bar.
//                mUiThread.setStatusLine("Computing stack call: " + count
//                        + "/" + total);
            }

            // FIXME: attempt to only update when needed.
            // Because the number of pages is not related to mAllocations.size() anymore
            // due to pre-zygote/post-zygote display, update all the time.
            // At some point we should remove the pages anyway, since it's getting computed
            // really fast now.
//            if ((mCurrentPage + 1) * DISPLAY_PER_PAGE == count
//                    || (count == total && mCurrentPage == mPageCount - 1)) {
            try {
                // get the current selection of the allocation
                int index = mAllocationTable.getSelectionIndex();
                NativeAllocationInfo info = null;
                
                if (index != -1) {
                    info = (NativeAllocationInfo)mAllocationTable.getItem(index).getData();
                }

                // empty the table
                emptyTables();

                // fill it again
                fillAllocationTable();

                // reselect
                mAllocationTable.setSelection(index);

                // display detail table if needed
                if (info != null) {
                    fillDetailTable(info);
                }
            } catch (SWTException e) {
                if (mAllocationTable.isDisposed()) {
                    // looks like the table is disposed. Let's ignore it.
                } else {
                    throw e;
                }
            }

        } else {
            // old client still running. doesn't really matter.
        }
    
public voidupdateLibraryAllocationTable(com.android.ddmuilib.NativeHeapPanel$LibraryAllocations liballoc, int start, int end)

        try {
            if (mLibraryTable.isDisposed() == false) {
                int index = mLibraryTable.getSelectionIndex();
                if (index != -1) {
                    LibraryAllocations newliballoc = mLibraryAllocations.get(
                            index);
                    if (newliballoc == liballoc) {
                        int count = liballoc.getAllocationSize();
                        for (int i = start; i < end && i < count; i++) {
                            NativeAllocationInfo info = liballoc.getAllocation(i);

                            TableItem item = new TableItem(
                                    mLibraryAllocationTable, SWT.NONE);
                            item.setText(0, sFormatter.format(
                                    info.getSize() * info.getAllocationCount()));
                            item.setText(1, sFormatter.format(info.getAllocationCount()));
                            item.setText(2, sFormatter.format(info.getSize()));

                            NativeStackCallInfo stackCallInfo = info.getRelevantStackCallInfo();
                            if (stackCallInfo != null) {
                                item.setText(3, stackCallInfo.getMethodName());
                            }
                        }
                    } else {
                        // we should quit the thread
                        if (mFillTableThread != null) {
                            mFillTableThread.quit();
                            mFillTableThread = null;
                        }
                    }
                }
            }
        } catch (SWTException e) {
            Log.e("ddms", "error when updating the library allocation table");
        }
    
private voidupdatePageUI()


        // set the label and pack to update the layout, otherwise
        // the label will be cut off if the new size is bigger
        if (mPageCount == 0) {
            mPageLabel.setText("0 of 0 allocations.");
        } else {
            StringBuffer buffer = new StringBuffer();
            // get our starting index
            int start = (mCurrentPage * DISPLAY_PER_PAGE) + 1;
            // end index, taking into account the last page can be half full
            int count = mDisplayedAllocations.size();
            int end = Math.min(start + DISPLAY_PER_PAGE - 1, count);
            buffer.append(sFormatter.format(start));
            buffer.append(" - ");
            buffer.append(sFormatter.format(end));
            buffer.append(" of ");
            buffer.append(sFormatter.format(count));
            buffer.append(" allocations.");
            mPageLabel.setText(buffer.toString());
        }

        // handle the button enabled state.
        mPagePreviousButton.setEnabled(mCurrentPage > 0);
        // reminder: mCurrentPage starts at 0.
        mPageNextButton.setEnabled(mCurrentPage < mPageCount - 1);

        mPageLabel.pack();
        mPageUIComposite.pack();

    
private voidupdateTotalMemoryDisplay()

        switch (mAllocDisplayMode) {
            case ALLOC_DISPLAY_ALL:
                mTotalMemoryLabel.setText(String.format("Total Memory: %1$s Bytes",
                        sFormatter.format(mTotalSize)));
                break;
            case ALLOC_DISPLAY_PRE_ZYGOTE:
                mTotalMemoryLabel.setText(String.format("Zygote Memory: %1$s Bytes",
                        sFormatter.format(mTotalSize)));
                break;
            case ALLOC_DISPLAY_POST_ZYGOTE:
                mTotalMemoryLabel.setText(String.format("Post-zygote Memory: %1$s Bytes",
                        sFormatter.format(mTotalSize)));
                break;
        }