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

HeapPanel

public final class HeapPanel extends BaseHeapPanel
Base class for our information panels.

Fields Summary
private static final String
PREFS_STATS_COL_TYPE
private static final String
PREFS_STATS_COL_COUNT
private static final String
PREFS_STATS_COL_SIZE
private static final String
PREFS_STATS_COL_SMALLEST
private static final String
PREFS_STATS_COL_LARGEST
private static final String
PREFS_STATS_COL_MEDIAN
private static final String
PREFS_STATS_COL_AVERAGE
private static final int
NOT_SELECTED
private static final int
NOT_ENABLED
private static final int
ENABLED
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 boolean
DISPLAY_HEAP_BITMAP
private static final boolean
DISPLAY_HILBERT_BITMAP
private static final int
PLACEHOLDER_HILBERT_SIZE
private static final int
PLACEHOLDER_LINEAR_V_SIZE
private static final int
PLACEHOLDER_LINEAR_H_SIZE
private static final int[]
ZOOMS
private static final NumberFormat
sByteFormatter
private static final NumberFormat
sLargeByteFormatter
private static final NumberFormat
sCountFormatter
private org.eclipse.swt.widgets.Display
mDisplay
private org.eclipse.swt.widgets.Composite
mTop
private org.eclipse.swt.widgets.Label
mUpdateStatus
private org.eclipse.swt.widgets.Table
mHeapSummary
private org.eclipse.swt.widgets.Combo
mDisplayMode
private org.eclipse.swt.widgets.Composite
mDisplayBase
private org.eclipse.swt.custom.StackLayout
mDisplayStack
private org.eclipse.swt.widgets.Composite
mStatisticsBase
private org.eclipse.swt.widgets.Table
mStatisticsTable
private org.jfree.chart.JFreeChart
mChart
private org.jfree.experimental.chart.swt.ChartComposite
mChartComposite
private org.eclipse.swt.widgets.Button
mGcButton
private org.jfree.data.category.DefaultCategoryDataset
mAllocCountDataSet
private org.eclipse.swt.widgets.Composite
mLinearBase
private org.eclipse.swt.widgets.Label
mLinearHeapImage
private org.eclipse.swt.widgets.Composite
mHilbertBase
private org.eclipse.swt.widgets.Label
mHilbertHeapImage
private org.eclipse.swt.widgets.Group
mLegend
private org.eclipse.swt.widgets.Combo
mZoom
private org.eclipse.swt.graphics.Image
mHilbertImage
Image used for the hilbert display. Since we recreate a new image every time, we keep this one around to dispose it.
private org.eclipse.swt.graphics.Image
mLinearImage
private org.eclipse.swt.widgets.Composite[]
mLayout
private static final int
HILBERT_DIR_N
private static final int
HILBERT_DIR_S
private static final int
HILBERT_DIR_E
private static final int
HILBERT_DIR_W
Constructors Summary
Methods Summary
private static java.lang.StringaddCommasToNumber(long num)

        return sCountFormatter.format(num);
    
private static java.lang.StringapproximateByteCount(long bytes)

        double fracBytes = bytes;
        String units = "";
        if (fracBytes >= 1024) {
            fracBytes /= 1024;
            units = "K";
        }
        if (fracBytes >= 1024) {
            fracBytes /= 1024;
            units = "M";
        }
        if (fracBytes >= 1024) {
            fracBytes /= 1024;
            units = "G";
        }

        return sByteFormatter.format(fracBytes) + units;
    
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_HEAP_MODE) == Client.CHANGE_HEAP_MODE ||
                    (changeMask & Client.CHANGE_HEAP_DATA) == Client.CHANGE_HEAP_DATA) {
                try {
                    mTop.getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            clientSelected();
                        }
                    });
                } catch (SWTException e) {
                    // display is disposed (app is quitting most likely), we do nothing.
                }
            }
        }
    
public voidclientSelected()
Sent when a new client is selected. The new client can be accessed with {@link #getCurrentClient()}.

        if (mTop.isDisposed())
            return;

        Client client = getCurrentClient();

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

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

            if (client.isHeapUpdateEnabled()) {
                mGcButton.setEnabled(true);
                mDisplayMode.setEnabled(true);
                setUpdateStatus(ENABLED);
            } else {
                setUpdateStatus(NOT_ENABLED);
                mGcButton.setEnabled(false);
                mDisplayMode.setEnabled(false);
            }

            fillSummaryTable(cd);
            
            int mode = mDisplayMode.getSelectionIndex();
            if (mode == 0) {
                fillDetailedTable(client, false /* forceRedraw */);
            } else {
                if (DISPLAY_HEAP_BITMAP) {
                    renderHeapData(cd, mode - 1, false /* forceRedraw */);
                }
            }
        } else {
            mGcButton.setEnabled(false);
            mDisplayMode.setEnabled(false);
            fillSummaryTable(null);
            fillDetailedTable(null, true);
            setUpdateStatus(NOT_SELECTED);
        }

        // sizes of things change frequently, so redo layout
        //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
        //        SWT.DEFAULT));
        mDisplayBase.layout();
        //mScrolledComposite.redraw();
    
private voidcreateChart()
Creates the chart below the statistics table

        mAllocCountDataSet = new DefaultCategoryDataset();
        mChart = ChartFactory.createBarChart(null, "Size", "Count", mAllocCountDataSet,
                PlotOrientation.VERTICAL, false, true, false);
        
        // get the font to make a proper title. We need to convert the swt font,
        // into an awt font.
        Font f = mStatisticsBase.getFont();
        FontData[] fData = f.getFontData();
        
        // event though on Mac OS there could be more than one fontData, we'll only use
        // the first one.
        FontData firstFontData = fData[0];
        
        java.awt.Font awtFont = SWTUtils.toAwtFont(mStatisticsBase.getDisplay(),
                firstFontData, true /* ensureSameSize */);

        mChart.setTitle(new TextTitle("Allocation count per size", awtFont));
        
        Plot plot = mChart.getPlot();
        if (plot instanceof CategoryPlot) {
            // get the plot
            CategoryPlot categoryPlot = (CategoryPlot)plot;
            
            // set the domain axis to draw labels that are displayed even with many values.
            CategoryAxis domainAxis = categoryPlot.getDomainAxis();
            domainAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
            
            CategoryItemRenderer renderer = categoryPlot.getRenderer();
            renderer.setBaseToolTipGenerator(new CategoryToolTipGenerator() {
                public String generateToolTip(CategoryDataset dataset, int row, int column) {
                    // get the key for the size of the allocation
                    ByteLong columnKey = (ByteLong)dataset.getColumnKey(column);
                    String rowKey = (String)dataset.getRowKey(row);
                    Number value = dataset.getValue(rowKey, columnKey);
                    
                    return String.format("%1$d %2$s of %3$d bytes", value.intValue(), rowKey, 
                            columnKey.getValue());
                }
            });
        }
        mChartComposite = new ChartComposite(mStatisticsBase, SWT.BORDER, mChart,
                ChartComposite.DEFAULT_WIDTH,
                ChartComposite.DEFAULT_HEIGHT,
                ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
                ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
                3000, // max draw width. We don't want it to zoom, so we put a big number
                3000, // max draw height. We don't want it to zoom, so we put a big number
                true,  // off-screen buffer
                true,  // properties
                true,  // save
                true,  // print
                false,  // zoom
                true);   // tooltips

        mChartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
    
private org.eclipse.swt.graphics.ImagecreateColorRect(org.eclipse.swt.widgets.Display display, org.eclipse.swt.graphics.RGB color)

        int width = 32;
        int height = 16;

        Image img = new Image(display, width, height);
        GC gc = new GC(img);
        gc.setBackground(new Color(display, color));
        gc.fillRectangle(0, 0, width, height);
        gc.dispose();
        return img;
    
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)
Create our control(s).

        mDisplay = parent.getDisplay();

        GridLayout gl;

        mTop = new Composite(parent, SWT.NONE);
        mTop.setLayout(new GridLayout(1, false));
        mTop.setLayoutData(new GridData(GridData.FILL_BOTH));

        mUpdateStatus = new Label(mTop, SWT.NONE);
        setUpdateStatus(NOT_SELECTED);

        Composite summarySection = new Composite(mTop, SWT.NONE);
        summarySection.setLayout(gl = new GridLayout(2, false));
        gl.marginHeight = gl.marginWidth = 0;

        mHeapSummary = createSummaryTable(summarySection);
        mGcButton = new Button(summarySection, SWT.PUSH);
        mGcButton.setText("Cause GC");
        mGcButton.setEnabled(false);
        mGcButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Client client = getCurrentClient();
                if (client != null) {
                    client.executeGarbageCollector();
                }
            }
        });

        Composite comboSection = new Composite(mTop, SWT.NONE);
        gl = new GridLayout(2, false);
        gl.marginHeight = gl.marginWidth = 0;
        comboSection.setLayout(gl);

        Label displayLabel = new Label(comboSection, SWT.NONE);
        displayLabel.setText("Display: ");

        mDisplayMode = new Combo(comboSection, SWT.READ_ONLY);
        mDisplayMode.setEnabled(false);
        mDisplayMode.add("Stats");
        if (DISPLAY_HEAP_BITMAP) {
            mDisplayMode.add("Linear");
            if (DISPLAY_HILBERT_BITMAP) {
                mDisplayMode.add("Hilbert");
            }
        }

        // the base of the displays.
        mDisplayBase = new Composite(mTop, SWT.NONE);
        mDisplayBase.setLayoutData(new GridData(GridData.FILL_BOTH));
        mDisplayStack = new StackLayout();
        mDisplayBase.setLayout(mDisplayStack);

        // create the statistics display
        mStatisticsBase = new Composite(mDisplayBase, SWT.NONE);
        //mStatisticsBase.setLayoutData(new GridData(GridData.FILL_BOTH));
        mStatisticsBase.setLayout(gl = new GridLayout(1, false));
        gl.marginHeight = gl.marginWidth = 0;
        mDisplayStack.topControl = mStatisticsBase;
        
        mStatisticsTable = createDetailedTable(mStatisticsBase);
        mStatisticsTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        
        createChart();

        //create the linear composite
        mLinearBase = new Composite(mDisplayBase, SWT.NONE);
        //mLinearBase.setLayoutData(new GridData());
        gl = new GridLayout(1, false);
        gl.marginHeight = gl.marginWidth = 0;
        mLinearBase.setLayout(gl);

        {
            mLinearHeapImage = new Label(mLinearBase, SWT.NONE);
            mLinearHeapImage.setLayoutData(new GridData());
            mLinearHeapImage.setImage(ImageHelper.createPlaceHolderArt(mDisplay,
                    PLACEHOLDER_LINEAR_H_SIZE, PLACEHOLDER_LINEAR_V_SIZE,
                    mDisplay.getSystemColor(SWT.COLOR_BLUE)));

            // create a composite to contain the bottom part (legend)
            Composite bottomSection = new Composite(mLinearBase, SWT.NONE);
            gl = new GridLayout(1, false);
            gl.marginHeight = gl.marginWidth = 0;
            bottomSection.setLayout(gl);

            createLegend(bottomSection);
        }

/*        
        mScrolledComposite = new ScrolledComposite(mTop, SWT.H_SCROLL | SWT.V_SCROLL);
        mScrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
        mScrolledComposite.setExpandHorizontal(true);
        mScrolledComposite.setExpandVertical(true);
        mScrolledComposite.setContent(mDisplayBase);
*/


        // create the hilbert display.
        mHilbertBase = new Composite(mDisplayBase, SWT.NONE);
        //mHilbertBase.setLayoutData(new GridData());
        gl = new GridLayout(2, false);
        gl.marginHeight = gl.marginWidth = 0;
        mHilbertBase.setLayout(gl);

        if (DISPLAY_HILBERT_BITMAP) {
            mHilbertHeapImage = new Label(mHilbertBase, SWT.NONE);
            mHilbertHeapImage.setLayoutData(new GridData());
            mHilbertHeapImage.setImage(ImageHelper.createPlaceHolderArt(mDisplay,
                    PLACEHOLDER_HILBERT_SIZE, PLACEHOLDER_HILBERT_SIZE,
                    mDisplay.getSystemColor(SWT.COLOR_BLUE)));

            // create a composite to contain the right part (legend + zoom)
            Composite rightSection = new Composite(mHilbertBase, SWT.NONE);
            gl = new GridLayout(1, false);
            gl.marginHeight = gl.marginWidth = 0;
            rightSection.setLayout(gl);

            Composite zoomComposite = new Composite(rightSection, SWT.NONE);
            gl = new GridLayout(2, false);
            zoomComposite.setLayout(gl);

            Label l = new Label(zoomComposite, SWT.NONE);
            l.setText("Zoom:");
            mZoom = new Combo(zoomComposite, SWT.READ_ONLY);
            for (int z : ZOOMS) {
                mZoom.add(String.format("%1$d%%", z)); // $NON-NLS-1$
            }

            mZoom.select(0);
            mZoom.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    setLegendText(mZoom.getSelectionIndex());
                    Client client = getCurrentClient();
                    if (client != null) {
                        renderHeapData(client.getClientData(), 1, true);
                        mTop.pack();
                    }
                }
            });

            createLegend(rightSection);
        }
        mHilbertBase.pack();

        mLayout = new Composite[] { mStatisticsBase, mLinearBase, mHilbertBase };
        mDisplayMode.select(0);
        mDisplayMode.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                int index = mDisplayMode.getSelectionIndex();
                Client client = getCurrentClient();

                if (client != null) {
                    if (index == 0) {
                        fillDetailedTable(client, true /* forceRedraw */);
                    } else {
                        renderHeapData(client.getClientData(), index-1, true /* forceRedraw */);
                    }
                }

                mDisplayStack.topControl = mLayout[index];
                //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
                //        SWT.DEFAULT));
                mDisplayBase.layout();
                //mScrolledComposite.redraw();
            }
        });

        //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
        //        SWT.DEFAULT));
        mDisplayBase.layout();
        //mScrolledComposite.redraw();

        return mTop;
    
private org.eclipse.swt.widgets.TablecreateDetailedTable(org.eclipse.swt.widgets.Composite base)

        IPreferenceStore store = DdmUiPreferences.getStore();
        
        Table tab = new Table(base, SWT.SINGLE | SWT.FULL_SELECTION);
        tab.setHeaderVisible(true);
        tab.setLinesVisible(true);

        TableHelper.createTableColumn(tab, "Type", SWT.LEFT,
                "4-byte array (object[], int[], float[])", //$NON-NLS-1$
                PREFS_STATS_COL_TYPE, store);

        TableHelper.createTableColumn(tab, "Count", SWT.RIGHT,
                "00,000", //$NON-NLS-1$
                PREFS_STATS_COL_COUNT, store);

        TableHelper.createTableColumn(tab, "Total Size", SWT.RIGHT,
                "000.000 WW", //$NON-NLS-1$
                PREFS_STATS_COL_SIZE, store);

        TableHelper.createTableColumn(tab, "Smallest", SWT.RIGHT,
                "000.000 WW", //$NON-NLS-1$
                PREFS_STATS_COL_SMALLEST, store);

        TableHelper.createTableColumn(tab, "Largest", SWT.RIGHT,
                "000.000 WW", //$NON-NLS-1$
                PREFS_STATS_COL_LARGEST, store);

        TableHelper.createTableColumn(tab, "Median", SWT.RIGHT,
                "000.000 WW", //$NON-NLS-1$
                PREFS_STATS_COL_MEDIAN, store);

        TableHelper.createTableColumn(tab, "Average", SWT.RIGHT,
                "000.000 WW", //$NON-NLS-1$
                PREFS_STATS_COL_AVERAGE, store);

        tab.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                
                Client client = getCurrentClient();
                if (client != null) {
                    int index = mStatisticsTable.getSelectionIndex();
                    TableItem item = mStatisticsTable.getItem(index);
                    
                    if (item != null) {
                        Map<Integer, ArrayList<HeapSegmentElement>> heapMap =
                            client.getClientData().getVmHeapData().getProcessedHeapMap();
                        
                        ArrayList<HeapSegmentElement> list = heapMap.get(item.getData());
                        if (list != null) {
                            showChart(list);
                        }
                    }
                }

            }
        });
        
        return tab;
    
private org.eclipse.swt.graphics.ImageDatacreateHilbertHeapImage(byte[] pixData)

        int w, h;

        // Pick an image size that the largest of heaps will fit into.
        w = (int)Math.sqrt((double)((16 * 1024 * 1024)/8));

        // Space-filling curves require a power-of-2 width.
        w = nextPow2(w);
        h = w;

        // Create the heap image.
        ImageData id = new ImageData(w, h, 8, mMapPalette);

        // Copy the data into the image
        //int maxX = zOrderData(id, pixData);
        Point maxP = hilbertOrderData(id, pixData);

        // update the max size to make it a round number once the zoom is applied
        int factor = 100 / ZOOMS[mZoom.getSelectionIndex()];
        if (factor != 1) {
            int tmp = maxP.x % factor;
            if (tmp != 0) {
                maxP.x += factor - tmp;
            }

            tmp = maxP.y % factor;
            if (tmp != 0) {
                maxP.y += factor - tmp;
            }
        }

        if (maxP.y < id.height) {
            // Crop the image down to the interesting part.
            id = new ImageData(id.width, maxP.y, id.depth, id.palette,
                               id.scanlinePad, id.data);
        }

        if (maxP.x < id.width) {
            // crop the image again. A bit trickier this time.
           ImageData croppedId = new ImageData(maxP.x, id.height, id.depth, id.palette);

           int[] buffer = new int[maxP.x];
           for (int l = 0 ; l < id.height; l++) {
               id.getPixels(0, l, maxP.x, buffer, 0);
               croppedId.setPixels(0, l, maxP.x, buffer, 0);
           }

           id = croppedId;
        }

        // apply the zoom
        if (factor != 1) {
            id = id.scaledTo(id.width / factor, id.height / factor);
        }

        return id;
    
private voidcreateLegend(org.eclipse.swt.widgets.Composite parent)

        mLegend = new Group(parent, SWT.NONE);
        mLegend.setText(getLegendText(0));

        mLegend.setLayout(new GridLayout(2, false));

        RGB[] colors = mMapPalette.colors;

        for (int i = 0; i < NUM_PALETTE_ENTRIES; i++) {
            Image tmpImage = createColorRect(parent.getDisplay(), colors[i]);

            Label l = new Label(mLegend, SWT.NONE);
            l.setImage(tmpImage);

            l = new Label(mLegend, SWT.NONE);
            l.setText(mMapLegend[i]);
        }
    
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 org.eclipse.swt.widgets.TablecreateSummaryTable(org.eclipse.swt.widgets.Composite base)

        Table tab = new Table(base, SWT.SINGLE | SWT.FULL_SELECTION);
        tab.setHeaderVisible(true);
        tab.setLinesVisible(true);

        TableColumn col;

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("ID");
        col.pack();

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("000.000WW"); // $NON-NLS-1$
        col.pack();
        col.setText("Heap Size");

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("000.000WW"); // $NON-NLS-1$
        col.pack();
        col.setText("Allocated");

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("000.000WW"); // $NON-NLS-1$
        col.pack();
        col.setText("Free");

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("000.00%"); // $NON-NLS-1$
        col.pack();
        col.setText("% Used");

        col = new TableColumn(tab, SWT.RIGHT);
        col.setText("000,000,000"); // $NON-NLS-1$
        col.pack();
        col.setText("# Objects");

        return tab;
    
public voiddeviceSelected()
Sent when a new device is selected. The new device can be accessed with {@link #getCurrentDevice()}

        // pass
    
private voidfillDetailedTable(com.android.ddmlib.Client client, boolean forceRedraw)

        // first check if the client is invalid or heap updates are not enabled.
        if (client == null || client.isHeapUpdateEnabled() == false) {
            mStatisticsTable.removeAll();
            showChart(null);
            return;
        }
        
        ClientData cd = client.getClientData();

        Map<Integer, ArrayList<HeapSegmentElement>> heapMap;

        // Atomically get and clear the heap data.
        synchronized (cd) {
            if (serializeHeapData(cd.getVmHeapData()) == false && forceRedraw == false) {
                // no change, we return.
                return;
            }
            
            heapMap = cd.getVmHeapData().getProcessedHeapMap();
        }
        
        // we have new data, lets display it.
        
        // First, get the current selection, and its key.
        int index = mStatisticsTable.getSelectionIndex();
        Integer selectedKey = null;
        if (index != -1) {
            selectedKey = (Integer)mStatisticsTable.getItem(index).getData();
        }

        // disable redraws and remove all from the table.
        mStatisticsTable.setRedraw(false);
        mStatisticsTable.removeAll();
        
        if (heapMap != null) {
            int selectedIndex = -1;
            ArrayList<HeapSegmentElement> selectedList = null;
            
            // get the keys
            Set<Integer> keys = heapMap.keySet();
            int iter = 0; // use a manual iter int because Set<?> doesn't have an index
            // based accessor.
            for (Integer key : keys) {
                ArrayList<HeapSegmentElement> list = heapMap.get(key);

                // check if this is the key that is supposed to be selected
                if (key.equals(selectedKey)) {
                    selectedIndex = iter;
                    selectedList = list;
                }
                iter++;

                TableItem item = new TableItem(mStatisticsTable, SWT.NONE);
                item.setData(key);

                // get the type
                item.setText(0, mMapLegend[key]);
                
                // set the count, smallest, largest
                int count = list.size();
                item.setText(1, addCommasToNumber(count));
                
                if (count > 0) {
                    item.setText(3, prettyByteCount(list.get(0).getLength()));
                    item.setText(4, prettyByteCount(list.get(count-1).getLength()));

                    int median = count / 2;
                    HeapSegmentElement element = list.get(median);
                    long size = element.getLength();
                    item.setText(5, prettyByteCount(size));

                    long totalSize = 0;
                    for (int i = 0 ; i < count; i++) {
                        element = list.get(i);
                        
                        size = element.getLength();
                        totalSize += size;
                    }
                    
                    // set the average and total
                    item.setText(2, prettyByteCount(totalSize));
                    item.setText(6, prettyByteCount(totalSize / count));
                }
            }

            mStatisticsTable.setRedraw(true);
            
            if (selectedIndex != -1) {
                mStatisticsTable.setSelection(selectedIndex);
                showChart(selectedList);
            } else {
                showChart(null);
            }
        } else {
            mStatisticsTable.setRedraw(true);
        }
    
private voidfillSummaryTable(com.android.ddmlib.ClientData cd)

        if (mHeapSummary.isDisposed()) {
            return;
        }

        mHeapSummary.setRedraw(false);
        mHeapSummary.removeAll();

        if (cd != null) {
            synchronized (cd) {
                Iterator<Integer> iter = cd.getVmHeapIds();
    
                while (iter.hasNext()) {
                    Integer id = iter.next();
                    Map<String, Long> heapInfo = cd.getVmHeapInfo(id);
                    if (heapInfo == null) {
                        continue;
                    }
                    long sizeInBytes = heapInfo.get(ClientData.HEAP_SIZE_BYTES);
                    long bytesAllocated = heapInfo.get(ClientData.HEAP_BYTES_ALLOCATED);
                    long objectsAllocated = heapInfo.get(ClientData.HEAP_OBJECTS_ALLOCATED);
    
                    TableItem item = new TableItem(mHeapSummary, SWT.NONE);
                    item.setText(0, id.toString());
    
                    item.setText(1, prettyByteCount(sizeInBytes));
                    item.setText(2, prettyByteCount(bytesAllocated));
                    item.setText(3, prettyByteCount(sizeInBytes - bytesAllocated));
                    item.setText(4, fractionalPercent(bytesAllocated, sizeInBytes));
                    item.setText(5, addCommasToNumber(objectsAllocated));
                }
            }
        }

        mHeapSummary.pack();
        mHeapSummary.setRedraw(true);
    
private static java.lang.StringfractionalPercent(long num, long denom)

        double val = (double)num / (double)denom;
        val *= 100;

        NumberFormat nf = NumberFormat.getInstance();
        nf.setMinimumFractionDigits(2);
        nf.setMaximumFractionDigits(2);
        return nf.format(val) + "%";
    
private java.lang.StringgetLegendText(int level)

        int bytes = 8 * (100 / ZOOMS[level]);

        return String.format("Key (1 pixel = %1$d bytes)", bytes);
    
private org.eclipse.swt.graphics.PointhilbertOrderData(org.eclipse.swt.graphics.ImageData id, byte[] pixData)


        int order = 0;
        for (int n = 1; n < id.width; n *= 2) {
            order++;
        }
        /* Skanky; use an otherwise-unused ImageData field
         * to keep track of maxX.
         */
        Point p = new Point(0,0);
        int oldIdX = id.x;
        int oldIdY = id.y;
        id.x = id.y = 0;
        try {
            hilbertWalk(id, new ByteArrayInputStream(pixData),
                        order, 0, 0, HILBERT_DIR_E);
            p.x = id.x;
            p.y = id.y;
        } catch (IOException ex) {
            System.err.println("Exception during hilbertWalk()");
            p.x = id.height;
            p.y = id.width;
        }
        id.x = oldIdX;
        id.y = oldIdY;
        return p;
    
private voidhilbertWalk(org.eclipse.swt.graphics.ImageData id, java.io.InputStream pixData, int order, int x, int y, int dir)


         
                                    
                               
        if (x >= id.width || y >= id.height) {
            return;
        } else if (order == 0) {
            try {
                int p = pixData.read();
                if (p >= 0) {
                    // flip along x=y axis;  assume width == height
                    id.setPixel(y, x, p);

                    /* Skanky; use an otherwise-unused ImageData field
                     * to keep track of the max x,y used. Note that x and y are inverted.
                     */
                    if (y > id.x) {
                        id.x = y;
                    }
                    if (x > id.y) {
                        id.y = x;
                    }
                }
//xxx just give up; don't bother walking the rest of the image
            } catch (IllegalArgumentException ex) {
                System.out.println("bad pixels: order " + order +
                        ", dir " + dir +
                        ", w " + id.width +
                        ", h " + id.height +
                        ", x " + x +
                        ", y " + y);
                throw ex;
            }
        } else {
            order--;
            int delta = 1 << order;
            int nextX = x + delta;
            int nextY = y + delta;

            switch (dir) {
            case HILBERT_DIR_E:
                hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_N);
                hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_E);
                hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_E);
                hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_S);
                break;
            case HILBERT_DIR_N:
                hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_E);
                hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_N);
                hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_N);
                hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_W);
                break;
            case HILBERT_DIR_S:
                hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_W);
                hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_S);
                hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_S);
                hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_E);
                break;
            case HILBERT_DIR_W:
                hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_S);
                hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_W);
                hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_W);
                hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_N);
                break;
            default:
                throw new RuntimeException("Unexpected Hilbert direction " +
                                           dir);
            }
        }
    
private intnextPow2(int value)
Return the closest power of two greater than or equal to value.

param
value the return value will be >= value
return
a power of two >= value. If value > 2^31, 2^31 is returned.

        for (int i = 31; i >= 0; --i) {
            if ((value & (1<<i)) != 0) {
                if (i < 31) {
                    return 1<<(i + 1);
                } else {
                    return 1<<31;
                }
            }
        }
        return 0;
    
private static java.lang.StringprettyByteCount(long bytes)

        double fracBytes = bytes;
        String units = " B";
        if (fracBytes < 1024) {
            return sByteFormatter.format(fracBytes) + units;
        } else {
            fracBytes /= 1024;
            units = " KB";
        }
        if (fracBytes >= 1024) {
            fracBytes /= 1024;
            units = " MB";
        }
        if (fracBytes >= 1024) {
            fracBytes /= 1024;
            units = " GB";
        }

        return sLargeByteFormatter.format(fracBytes) + units;
    
private voidrenderHeapData(com.android.ddmlib.ClientData cd, int mode, boolean forceRedraw)
Convert the raw heap data to an image. We know we're running in the UI thread, so we can issue graphics commands directly. http://help.eclipse.org/help31/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html

param
cd The client data
param
mode The display mode. 0 = linear, 1 = hilbert.
param
forceRedraw

        Image image;

        byte[] pixData;

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

            pixData = getSerializedData();
        }

        if (pixData != null) {
            ImageData id;
            if (mode == 1) {
                id = createHilbertHeapImage(pixData);
            } else {
                id = createLinearHeapImage(pixData, 200, mMapPalette);
            }

            image = new Image(mDisplay, id);
        } else {
            // Render a placeholder image.
            int width, height;
            if (mode == 1) {
                width = height = PLACEHOLDER_HILBERT_SIZE;
            } else {
                width = PLACEHOLDER_LINEAR_H_SIZE;
                height = PLACEHOLDER_LINEAR_V_SIZE;
            }
            image = new Image(mDisplay, width, height);
            GC gc = new GC(image);
            gc.setForeground(mDisplay.getSystemColor(SWT.COLOR_RED));
            gc.drawLine(0, 0, width-1, height-1);
            gc.dispose();
            gc = null;
        }

        // set the new image

        if (mode == 1) {
            if (mHilbertImage != null) {
                mHilbertImage.dispose();
            }

            mHilbertImage = image;
            mHilbertHeapImage.setImage(mHilbertImage);
            mHilbertHeapImage.pack(true);
            mHilbertBase.layout();
            mHilbertBase.pack(true);
        } else {
            if (mLinearImage != null) {
                mLinearImage.dispose();
            }

            mLinearImage = image;
            mLinearHeapImage.setImage(mLinearImage);
            mLinearHeapImage.pack(true);
            mLinearBase.layout();
            mLinearBase.pack(true);
        }
    
public voidsetFocus()
Sets the focus to the proper control inside the panel.

        mHeapSummary.setFocus();
    
private voidsetLegendText(int level)

        mLegend.setText(getLegendText(level));

    
protected voidsetTableFocusListener()

        addTableToFocusListener(mHeapSummary);
    
private voidsetUpdateStatus(int status)

        switch (status) {
            case NOT_SELECTED:
                mUpdateStatus.setText("Select a client to see heap updates");
                break;
            case NOT_ENABLED:
                mUpdateStatus.setText("Heap updates are " +
                                      "NOT ENABLED for this client");
                break;
            case ENABLED:
                mUpdateStatus.setText("Heap updates will happen after " +
                                      "every GC for this client");
                break;
            default:
                throw new RuntimeException();
        }

        mUpdateStatus.pack();
    
private voidshowChart(java.util.ArrayList list)
Fills the chart with the content of the list of {@link HeapSegmentElement}.

        mAllocCountDataSet.clear();

        if (list != null) {
            String rowKey = "Alloc Count";
    
            long currentSize = -1;
            int currentCount = 0;
            for (HeapSegmentElement element : list) {
                if (element.getLength() != currentSize) {
                    if (currentSize != -1) {
                        ByteLong columnKey = new ByteLong(currentSize);
                        mAllocCountDataSet.addValue(currentCount, rowKey, columnKey);
                    }
                    
                    currentSize = element.getLength();
                    currentCount = 1;
                } else {
                    currentCount++;
                }
            }
            
            // add the last item
            if (currentSize != -1) {
                ByteLong columnKey = new ByteLong(currentSize);
                mAllocCountDataSet.addValue(currentCount, rowKey, columnKey);
            }
        }
    
private intzOrderData(org.eclipse.swt.graphics.ImageData id, byte[] pixData)

        int maxX = 0;
        for (int i = 0; i < pixData.length; i++) {
            /* Tread the pixData index as a z-order curve index and
             * decompose into Cartesian coordinates.
             */
            int x = (i & 1) |
                    ((i >>> 2) & 1) << 1 |
                    ((i >>> 4) & 1) << 2 |
                    ((i >>> 6) & 1) << 3 |
                    ((i >>> 8) & 1) << 4 |
                    ((i >>> 10) & 1) << 5 |
                    ((i >>> 12) & 1) << 6 |
                    ((i >>> 14) & 1) << 7 |
                    ((i >>> 16) & 1) << 8 |
                    ((i >>> 18) & 1) << 9 |
                    ((i >>> 20) & 1) << 10 |
                    ((i >>> 22) & 1) << 11 |
                    ((i >>> 24) & 1) << 12 |
                    ((i >>> 26) & 1) << 13 |
                    ((i >>> 28) & 1) << 14 |
                    ((i >>> 30) & 1) << 15;
            int y = ((i >>> 1) & 1) << 0 |
                    ((i >>> 3) & 1) << 1 |
                    ((i >>> 5) & 1) << 2 |
                    ((i >>> 7) & 1) << 3 |
                    ((i >>> 9) & 1) << 4 |
                    ((i >>> 11) & 1) << 5 |
                    ((i >>> 13) & 1) << 6 |
                    ((i >>> 15) & 1) << 7 |
                    ((i >>> 17) & 1) << 8 |
                    ((i >>> 19) & 1) << 9 |
                    ((i >>> 21) & 1) << 10 |
                    ((i >>> 23) & 1) << 11 |
                    ((i >>> 25) & 1) << 12 |
                    ((i >>> 27) & 1) << 13 |
                    ((i >>> 29) & 1) << 14 |
                    ((i >>> 31) & 1) << 15;
            try {
                id.setPixel(x, y, pixData[i]);
                if (x > maxX) {
                    maxX = x;
                }
            } catch (IllegalArgumentException ex) {
                System.out.println("bad pixels: i " + i +
                        ", w " + id.width +
                        ", h " + id.height +
                        ", x " + x +
                        ", y " + y);
                throw ex;
            }
        }
        return maxX;