• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ddmuilib;
18 
19 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
20 import com.android.ddmlib.Client;
21 import com.android.ddmlib.ClientData;
22 import com.android.ddmlib.HeapSegment.HeapSegmentElement;
23 import com.android.ddmlib.Log;
24 
25 import org.eclipse.jface.preference.IPreferenceStore;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.SWTException;
28 import org.eclipse.swt.custom.StackLayout;
29 import org.eclipse.swt.events.SelectionAdapter;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.graphics.Color;
32 import org.eclipse.swt.graphics.Font;
33 import org.eclipse.swt.graphics.FontData;
34 import org.eclipse.swt.graphics.GC;
35 import org.eclipse.swt.graphics.Image;
36 import org.eclipse.swt.graphics.ImageData;
37 import org.eclipse.swt.graphics.PaletteData;
38 import org.eclipse.swt.graphics.Point;
39 import org.eclipse.swt.graphics.RGB;
40 import org.eclipse.swt.layout.GridData;
41 import org.eclipse.swt.layout.GridLayout;
42 import org.eclipse.swt.widgets.Button;
43 import org.eclipse.swt.widgets.Combo;
44 import org.eclipse.swt.widgets.Composite;
45 import org.eclipse.swt.widgets.Control;
46 import org.eclipse.swt.widgets.Display;
47 import org.eclipse.swt.widgets.Group;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.swt.widgets.Table;
50 import org.eclipse.swt.widgets.TableColumn;
51 import org.eclipse.swt.widgets.TableItem;
52 import org.jfree.chart.ChartFactory;
53 import org.jfree.chart.JFreeChart;
54 import org.jfree.chart.axis.CategoryAxis;
55 import org.jfree.chart.axis.CategoryLabelPositions;
56 import org.jfree.chart.labels.CategoryToolTipGenerator;
57 import org.jfree.chart.plot.CategoryPlot;
58 import org.jfree.chart.plot.Plot;
59 import org.jfree.chart.plot.PlotOrientation;
60 import org.jfree.chart.renderer.category.CategoryItemRenderer;
61 import org.jfree.chart.title.TextTitle;
62 import org.jfree.data.category.CategoryDataset;
63 import org.jfree.data.category.DefaultCategoryDataset;
64 import org.jfree.experimental.chart.swt.ChartComposite;
65 import org.jfree.experimental.swt.SWTUtils;
66 
67 import java.io.ByteArrayInputStream;
68 import java.io.IOException;
69 import java.io.InputStream;
70 import java.text.NumberFormat;
71 import java.util.ArrayList;
72 import java.util.Iterator;
73 import java.util.Map;
74 import java.util.Set;
75 
76 
77 /**
78  * Base class for our information panels.
79  */
80 public final class HeapPanel extends BaseHeapPanel {
81     private static final String PREFS_STATS_COL_TYPE = "heapPanel.col0"; //$NON-NLS-1$
82     private static final String PREFS_STATS_COL_COUNT = "heapPanel.col1"; //$NON-NLS-1$
83     private static final String PREFS_STATS_COL_SIZE = "heapPanel.col2"; //$NON-NLS-1$
84     private static final String PREFS_STATS_COL_SMALLEST = "heapPanel.col3"; //$NON-NLS-1$
85     private static final String PREFS_STATS_COL_LARGEST = "heapPanel.col4"; //$NON-NLS-1$
86     private static final String PREFS_STATS_COL_MEDIAN = "heapPanel.col5"; //$NON-NLS-1$
87     private static final String PREFS_STATS_COL_AVERAGE = "heapPanel.col6"; //$NON-NLS-1$
88 
89     /* args to setUpdateStatus() */
90     private static final int NOT_SELECTED   = 0;
91     private static final int NOT_ENABLED    = 1;
92     private static final int ENABLED        = 2;
93 
94     /** color palette and map legend. NATIVE is the last enum is a 0 based enum list, so we need
95      * Native+1 at least. We also need 2 more entries for free area and expansion area.  */
96     private static final int NUM_PALETTE_ENTRIES = HeapSegmentElement.KIND_NATIVE+2 +1;
97     private static final String[] mMapLegend = new String[NUM_PALETTE_ENTRIES];
98     private static final PaletteData mMapPalette = createPalette();
99 
100     private static final boolean DISPLAY_HEAP_BITMAP = false;
101     private static final boolean DISPLAY_HILBERT_BITMAP = false;
102 
103     private static final int PLACEHOLDER_HILBERT_SIZE = 200;
104     private static final int PLACEHOLDER_LINEAR_V_SIZE = 100;
105     private static final int PLACEHOLDER_LINEAR_H_SIZE = 300;
106 
107     private static final int[] ZOOMS = {100, 50, 25};
108 
109     private static final NumberFormat sByteFormatter = NumberFormat.getInstance();
110     private static final NumberFormat sLargeByteFormatter = NumberFormat.getInstance();
111     private static final NumberFormat sCountFormatter = NumberFormat.getInstance();
112 
113     static {
114         sByteFormatter.setMinimumFractionDigits(0);
115         sByteFormatter.setMaximumFractionDigits(1);
116         sLargeByteFormatter.setMinimumFractionDigits(3);
117         sLargeByteFormatter.setMaximumFractionDigits(3);
118 
119         sCountFormatter.setGroupingUsed(true);
120     }
121 
122     private Display mDisplay;
123 
124     private Composite mTop; // real top
125     private Label mUpdateStatus;
126     private Table mHeapSummary;
127     private Combo mDisplayMode;
128 
129     //private ScrolledComposite mScrolledComposite;
130 
131     private Composite mDisplayBase; // base of the displays.
132     private StackLayout mDisplayStack;
133 
134     private Composite mStatisticsBase;
135     private Table mStatisticsTable;
136     private JFreeChart mChart;
137     private ChartComposite mChartComposite;
138     private Button mGcButton;
139     private DefaultCategoryDataset mAllocCountDataSet;
140 
141     private Composite mLinearBase;
142     private Label mLinearHeapImage;
143 
144     private Composite mHilbertBase;
145     private Label mHilbertHeapImage;
146     private Group mLegend;
147     private Combo mZoom;
148 
149     /** Image used for the hilbert display. Since we recreate a new image every time, we
150      * keep this one around to dispose it. */
151     private Image mHilbertImage;
152     private Image mLinearImage;
153     private Composite[] mLayout;
154 
155     /*
156      * Create color palette for map.  Set up titles for legend.
157      */
createPalette()158     private static PaletteData createPalette() {
159         RGB colors[] = new RGB[NUM_PALETTE_ENTRIES];
160         colors[0]
161                 = new RGB(192, 192, 192); // non-heap pixels are gray
162         mMapLegend[0]
163                 = "(heap expansion area)";
164 
165         colors[1]
166                 = new RGB(0, 0, 0);       // free chunks are black
167         mMapLegend[1]
168                 = "free";
169 
170         colors[HeapSegmentElement.KIND_OBJECT + 2]
171                 = new RGB(0, 0, 255);     // objects are blue
172         mMapLegend[HeapSegmentElement.KIND_OBJECT + 2]
173                 = "data object";
174 
175         colors[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
176                 = new RGB(0, 255, 0);     // class objects are green
177         mMapLegend[HeapSegmentElement.KIND_CLASS_OBJECT + 2]
178                 = "class object";
179 
180         colors[HeapSegmentElement.KIND_ARRAY_1 + 2]
181                 = new RGB(255, 0, 0);     // byte/bool arrays are red
182         mMapLegend[HeapSegmentElement.KIND_ARRAY_1 + 2]
183                 = "1-byte array (byte[], boolean[])";
184 
185         colors[HeapSegmentElement.KIND_ARRAY_2 + 2]
186                 = new RGB(255, 128, 0);   // short/char arrays are orange
187         mMapLegend[HeapSegmentElement.KIND_ARRAY_2 + 2]
188                 = "2-byte array (short[], char[])";
189 
190         colors[HeapSegmentElement.KIND_ARRAY_4 + 2]
191                 = new RGB(255, 255, 0);   // obj/int/float arrays are yellow
192         mMapLegend[HeapSegmentElement.KIND_ARRAY_4 + 2]
193                 = "4-byte array (object[], int[], float[])";
194 
195         colors[HeapSegmentElement.KIND_ARRAY_8 + 2]
196                 = new RGB(255, 128, 128); // long/double arrays are pink
197         mMapLegend[HeapSegmentElement.KIND_ARRAY_8 + 2]
198                 = "8-byte array (long[], double[])";
199 
200         colors[HeapSegmentElement.KIND_UNKNOWN + 2]
201                 = new RGB(255, 0, 255);   // unknown objects are cyan
202         mMapLegend[HeapSegmentElement.KIND_UNKNOWN + 2]
203                 = "unknown object";
204 
205         colors[HeapSegmentElement.KIND_NATIVE + 2]
206                 = new RGB(64, 64, 64);    // native objects are dark gray
207         mMapLegend[HeapSegmentElement.KIND_NATIVE + 2]
208                 = "non-Java object";
209 
210         return new PaletteData(colors);
211     }
212 
213     /**
214      * Sent when an existing client information changed.
215      * <p/>
216      * This is sent from a non UI thread.
217      * @param client the updated client.
218      * @param changeMask the bit mask describing the changed properties. It can contain
219      * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
220      * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
221      * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
222      * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
223      *
224      * @see IClientChangeListener#clientChanged(Client, int)
225      */
226     @Override
clientChanged(final Client client, int changeMask)227     public void clientChanged(final Client client, int changeMask) {
228         if (client == getCurrentClient()) {
229             if ((changeMask & Client.CHANGE_HEAP_MODE) == Client.CHANGE_HEAP_MODE ||
230                     (changeMask & Client.CHANGE_HEAP_DATA) == Client.CHANGE_HEAP_DATA) {
231                 try {
232                     mTop.getDisplay().asyncExec(new Runnable() {
233                         @Override
234                         public void run() {
235                             clientSelected();
236                         }
237                     });
238                 } catch (SWTException e) {
239                     // display is disposed (app is quitting most likely), we do nothing.
240                 }
241             }
242         }
243     }
244 
245     /**
246      * Sent when a new device is selected. The new device can be accessed
247      * with {@link #getCurrentDevice()}
248      */
249     @Override
deviceSelected()250     public void deviceSelected() {
251         // pass
252     }
253 
254     /**
255      * Sent when a new client is selected. The new client can be accessed
256      * with {@link #getCurrentClient()}.
257      */
258     @Override
clientSelected()259     public void clientSelected() {
260         if (mTop.isDisposed())
261             return;
262 
263         Client client = getCurrentClient();
264 
265         Log.d("ddms", "HeapPanel: changed " + client);
266 
267         if (client != null) {
268             ClientData cd = client.getClientData();
269 
270             if (client.isHeapUpdateEnabled()) {
271                 mGcButton.setEnabled(true);
272                 mDisplayMode.setEnabled(true);
273                 setUpdateStatus(ENABLED);
274             } else {
275                 setUpdateStatus(NOT_ENABLED);
276                 mGcButton.setEnabled(false);
277                 mDisplayMode.setEnabled(false);
278             }
279 
280             fillSummaryTable(cd);
281 
282             int mode = mDisplayMode.getSelectionIndex();
283             if (mode == 0) {
284                 fillDetailedTable(client, false /* forceRedraw */);
285             } else {
286                 if (DISPLAY_HEAP_BITMAP) {
287                     renderHeapData(cd, mode - 1, false /* forceRedraw */);
288                 }
289             }
290         } else {
291             mGcButton.setEnabled(false);
292             mDisplayMode.setEnabled(false);
293             fillSummaryTable(null);
294             fillDetailedTable(null, true);
295             setUpdateStatus(NOT_SELECTED);
296         }
297 
298         // sizes of things change frequently, so redo layout
299         //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
300         //        SWT.DEFAULT));
301         mDisplayBase.layout();
302         //mScrolledComposite.redraw();
303     }
304 
305     /**
306      * Create our control(s).
307      */
308     @Override
createControl(Composite parent)309     protected Control createControl(Composite parent) {
310         mDisplay = parent.getDisplay();
311 
312         GridLayout gl;
313 
314         mTop = new Composite(parent, SWT.NONE);
315         mTop.setLayout(new GridLayout(1, false));
316         mTop.setLayoutData(new GridData(GridData.FILL_BOTH));
317 
318         mUpdateStatus = new Label(mTop, SWT.NONE);
319         setUpdateStatus(NOT_SELECTED);
320 
321         Composite summarySection = new Composite(mTop, SWT.NONE);
322         summarySection.setLayout(gl = new GridLayout(2, false));
323         gl.marginHeight = gl.marginWidth = 0;
324 
325         mHeapSummary = createSummaryTable(summarySection);
326         mGcButton = new Button(summarySection, SWT.PUSH);
327         mGcButton.setText("Cause GC");
328         mGcButton.setEnabled(false);
329         mGcButton.addSelectionListener(new SelectionAdapter() {
330             @Override
331             public void widgetSelected(SelectionEvent e) {
332                 Client client = getCurrentClient();
333                 if (client != null) {
334                     client.executeGarbageCollector();
335                 }
336             }
337         });
338 
339         Composite comboSection = new Composite(mTop, SWT.NONE);
340         gl = new GridLayout(2, false);
341         gl.marginHeight = gl.marginWidth = 0;
342         comboSection.setLayout(gl);
343 
344         Label displayLabel = new Label(comboSection, SWT.NONE);
345         displayLabel.setText("Display: ");
346 
347         mDisplayMode = new Combo(comboSection, SWT.READ_ONLY);
348         mDisplayMode.setEnabled(false);
349         mDisplayMode.add("Stats");
350         if (DISPLAY_HEAP_BITMAP) {
351             mDisplayMode.add("Linear");
352             if (DISPLAY_HILBERT_BITMAP) {
353                 mDisplayMode.add("Hilbert");
354             }
355         }
356 
357         // the base of the displays.
358         mDisplayBase = new Composite(mTop, SWT.NONE);
359         mDisplayBase.setLayoutData(new GridData(GridData.FILL_BOTH));
360         mDisplayStack = new StackLayout();
361         mDisplayBase.setLayout(mDisplayStack);
362 
363         // create the statistics display
364         mStatisticsBase = new Composite(mDisplayBase, SWT.NONE);
365         //mStatisticsBase.setLayoutData(new GridData(GridData.FILL_BOTH));
366         mStatisticsBase.setLayout(gl = new GridLayout(1, false));
367         gl.marginHeight = gl.marginWidth = 0;
368         mDisplayStack.topControl = mStatisticsBase;
369 
370         mStatisticsTable = createDetailedTable(mStatisticsBase);
371         mStatisticsTable.setLayoutData(new GridData(GridData.FILL_BOTH));
372 
373         createChart();
374 
375         //create the linear composite
376         mLinearBase = new Composite(mDisplayBase, SWT.NONE);
377         //mLinearBase.setLayoutData(new GridData());
378         gl = new GridLayout(1, false);
379         gl.marginHeight = gl.marginWidth = 0;
380         mLinearBase.setLayout(gl);
381 
382         {
383             mLinearHeapImage = new Label(mLinearBase, SWT.NONE);
384             mLinearHeapImage.setLayoutData(new GridData());
385             mLinearHeapImage.setImage(ImageLoader.createPlaceHolderArt(mDisplay,
386                     PLACEHOLDER_LINEAR_H_SIZE, PLACEHOLDER_LINEAR_V_SIZE,
387                     mDisplay.getSystemColor(SWT.COLOR_BLUE)));
388 
389             // create a composite to contain the bottom part (legend)
390             Composite bottomSection = new Composite(mLinearBase, SWT.NONE);
391             gl = new GridLayout(1, false);
392             gl.marginHeight = gl.marginWidth = 0;
393             bottomSection.setLayout(gl);
394 
395             createLegend(bottomSection);
396         }
397 
398 /*
399         mScrolledComposite = new ScrolledComposite(mTop, SWT.H_SCROLL | SWT.V_SCROLL);
400         mScrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
401         mScrolledComposite.setExpandHorizontal(true);
402         mScrolledComposite.setExpandVertical(true);
403         mScrolledComposite.setContent(mDisplayBase);
404 */
405 
406 
407         // create the hilbert display.
408         mHilbertBase = new Composite(mDisplayBase, SWT.NONE);
409         //mHilbertBase.setLayoutData(new GridData());
410         gl = new GridLayout(2, false);
411         gl.marginHeight = gl.marginWidth = 0;
412         mHilbertBase.setLayout(gl);
413 
414         if (DISPLAY_HILBERT_BITMAP) {
415             mHilbertHeapImage = new Label(mHilbertBase, SWT.NONE);
416             mHilbertHeapImage.setLayoutData(new GridData());
417             mHilbertHeapImage.setImage(ImageLoader.createPlaceHolderArt(mDisplay,
418                     PLACEHOLDER_HILBERT_SIZE, PLACEHOLDER_HILBERT_SIZE,
419                     mDisplay.getSystemColor(SWT.COLOR_BLUE)));
420 
421             // create a composite to contain the right part (legend + zoom)
422             Composite rightSection = new Composite(mHilbertBase, SWT.NONE);
423             gl = new GridLayout(1, false);
424             gl.marginHeight = gl.marginWidth = 0;
425             rightSection.setLayout(gl);
426 
427             Composite zoomComposite = new Composite(rightSection, SWT.NONE);
428             gl = new GridLayout(2, false);
429             zoomComposite.setLayout(gl);
430 
431             Label l = new Label(zoomComposite, SWT.NONE);
432             l.setText("Zoom:");
433             mZoom = new Combo(zoomComposite, SWT.READ_ONLY);
434             for (int z : ZOOMS) {
435                 mZoom.add(String.format("%1$d%%", z)); //$NON-NLS-1$
436             }
437 
438             mZoom.select(0);
439             mZoom.addSelectionListener(new SelectionAdapter() {
440                 @Override
441                 public void widgetSelected(SelectionEvent e) {
442                     setLegendText(mZoom.getSelectionIndex());
443                     Client client = getCurrentClient();
444                     if (client != null) {
445                         renderHeapData(client.getClientData(), 1, true);
446                         mTop.pack();
447                     }
448                 }
449             });
450 
451             createLegend(rightSection);
452         }
453         mHilbertBase.pack();
454 
455         mLayout = new Composite[] { mStatisticsBase, mLinearBase, mHilbertBase };
456         mDisplayMode.select(0);
457         mDisplayMode.addSelectionListener(new SelectionAdapter() {
458             @Override
459             public void widgetSelected(SelectionEvent e) {
460                 int index = mDisplayMode.getSelectionIndex();
461                 Client client = getCurrentClient();
462 
463                 if (client != null) {
464                     if (index == 0) {
465                         fillDetailedTable(client, true /* forceRedraw */);
466                     } else {
467                         renderHeapData(client.getClientData(), index-1, true /* forceRedraw */);
468                     }
469                 }
470 
471                 mDisplayStack.topControl = mLayout[index];
472                 //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
473                 //        SWT.DEFAULT));
474                 mDisplayBase.layout();
475                 //mScrolledComposite.redraw();
476             }
477         });
478 
479         //mScrolledComposite.setMinSize(mDisplayStack.topControl.computeSize(SWT.DEFAULT,
480         //        SWT.DEFAULT));
481         mDisplayBase.layout();
482         //mScrolledComposite.redraw();
483 
484         return mTop;
485     }
486 
487     /**
488      * Sets the focus to the proper control inside the panel.
489      */
490     @Override
setFocus()491     public void setFocus() {
492         mHeapSummary.setFocus();
493     }
494 
495 
createSummaryTable(Composite base)496     private Table createSummaryTable(Composite base) {
497         Table tab = new Table(base, SWT.SINGLE | SWT.FULL_SELECTION);
498         tab.setHeaderVisible(true);
499         tab.setLinesVisible(true);
500 
501         TableColumn col;
502 
503         col = new TableColumn(tab, SWT.RIGHT);
504         col.setText("ID");
505         col.pack();
506 
507         col = new TableColumn(tab, SWT.RIGHT);
508         col.setText("000.000WW"); //$NON-NLS-1$
509         col.pack();
510         col.setText("Heap Size");
511 
512         col = new TableColumn(tab, SWT.RIGHT);
513         col.setText("000.000WW"); //$NON-NLS-1$
514         col.pack();
515         col.setText("Allocated");
516 
517         col = new TableColumn(tab, SWT.RIGHT);
518         col.setText("000.000WW"); //$NON-NLS-1$
519         col.pack();
520         col.setText("Free");
521 
522         col = new TableColumn(tab, SWT.RIGHT);
523         col.setText("000.00%"); //$NON-NLS-1$
524         col.pack();
525         col.setText("% Used");
526 
527         col = new TableColumn(tab, SWT.RIGHT);
528         col.setText("000,000,000"); //$NON-NLS-1$
529         col.pack();
530         col.setText("# Objects");
531 
532         // make sure there is always one empty item so that one table row is always displayed.
533         TableItem item = new TableItem(tab, SWT.NONE);
534         item.setText("");
535 
536         return tab;
537     }
538 
createDetailedTable(Composite base)539     private Table createDetailedTable(Composite base) {
540         IPreferenceStore store = DdmUiPreferences.getStore();
541 
542         Table tab = new Table(base, SWT.SINGLE | SWT.FULL_SELECTION);
543         tab.setHeaderVisible(true);
544         tab.setLinesVisible(true);
545 
546         TableHelper.createTableColumn(tab, "Type", SWT.LEFT,
547                 "4-byte array (object[], int[], float[])", //$NON-NLS-1$
548                 PREFS_STATS_COL_TYPE, store);
549 
550         TableHelper.createTableColumn(tab, "Count", SWT.RIGHT,
551                 "00,000", //$NON-NLS-1$
552                 PREFS_STATS_COL_COUNT, store);
553 
554         TableHelper.createTableColumn(tab, "Total Size", SWT.RIGHT,
555                 "000.000 WW", //$NON-NLS-1$
556                 PREFS_STATS_COL_SIZE, store);
557 
558         TableHelper.createTableColumn(tab, "Smallest", SWT.RIGHT,
559                 "000.000 WW", //$NON-NLS-1$
560                 PREFS_STATS_COL_SMALLEST, store);
561 
562         TableHelper.createTableColumn(tab, "Largest", SWT.RIGHT,
563                 "000.000 WW", //$NON-NLS-1$
564                 PREFS_STATS_COL_LARGEST, store);
565 
566         TableHelper.createTableColumn(tab, "Median", SWT.RIGHT,
567                 "000.000 WW", //$NON-NLS-1$
568                 PREFS_STATS_COL_MEDIAN, store);
569 
570         TableHelper.createTableColumn(tab, "Average", SWT.RIGHT,
571                 "000.000 WW", //$NON-NLS-1$
572                 PREFS_STATS_COL_AVERAGE, store);
573 
574         tab.addSelectionListener(new SelectionAdapter() {
575             @Override
576             public void widgetSelected(SelectionEvent e) {
577 
578                 Client client = getCurrentClient();
579                 if (client != null) {
580                     int index = mStatisticsTable.getSelectionIndex();
581                     TableItem item = mStatisticsTable.getItem(index);
582 
583                     if (item != null) {
584                         Map<Integer, ArrayList<HeapSegmentElement>> heapMap =
585                             client.getClientData().getVmHeapData().getProcessedHeapMap();
586 
587                         ArrayList<HeapSegmentElement> list = heapMap.get(item.getData());
588                         if (list != null) {
589                             showChart(list);
590                         }
591                     }
592                 }
593 
594             }
595         });
596 
597         return tab;
598     }
599 
600     /**
601      * Creates the chart below the statistics table
602      */
createChart()603     private void createChart() {
604         mAllocCountDataSet = new DefaultCategoryDataset();
605         mChart = ChartFactory.createBarChart(null, "Size", "Count", mAllocCountDataSet,
606                 PlotOrientation.VERTICAL, false, true, false);
607 
608         // get the font to make a proper title. We need to convert the swt font,
609         // into an awt font.
610         Font f = mStatisticsBase.getFont();
611         FontData[] fData = f.getFontData();
612 
613         // event though on Mac OS there could be more than one fontData, we'll only use
614         // the first one.
615         FontData firstFontData = fData[0];
616 
617         java.awt.Font awtFont = SWTUtils.toAwtFont(mStatisticsBase.getDisplay(),
618                 firstFontData, true /* ensureSameSize */);
619 
620         mChart.setTitle(new TextTitle("Allocation count per size", awtFont));
621 
622         Plot plot = mChart.getPlot();
623         if (plot instanceof CategoryPlot) {
624             // get the plot
625             CategoryPlot categoryPlot = (CategoryPlot)plot;
626 
627             // set the domain axis to draw labels that are displayed even with many values.
628             CategoryAxis domainAxis = categoryPlot.getDomainAxis();
629             domainAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
630 
631             CategoryItemRenderer renderer = categoryPlot.getRenderer();
632             renderer.setBaseToolTipGenerator(new CategoryToolTipGenerator() {
633                 @Override
634                 public String generateToolTip(CategoryDataset dataset, int row, int column) {
635                     // get the key for the size of the allocation
636                     ByteLong columnKey = (ByteLong)dataset.getColumnKey(column);
637                     String rowKey = (String)dataset.getRowKey(row);
638                     Number value = dataset.getValue(rowKey, columnKey);
639 
640                     return String.format("%1$d %2$s of %3$d bytes", value.intValue(), rowKey,
641                             columnKey.getValue());
642                 }
643             });
644         }
645         mChartComposite = new ChartComposite(mStatisticsBase, SWT.BORDER, mChart,
646                 ChartComposite.DEFAULT_WIDTH,
647                 ChartComposite.DEFAULT_HEIGHT,
648                 ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
649                 ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
650                 3000, // max draw width. We don't want it to zoom, so we put a big number
651                 3000, // max draw height. We don't want it to zoom, so we put a big number
652                 true,  // off-screen buffer
653                 true,  // properties
654                 true,  // save
655                 true,  // print
656                 false,  // zoom
657                 true);   // tooltips
658 
659         mChartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
660     }
661 
prettyByteCount(long bytes)662     private static String prettyByteCount(long bytes) {
663         double fracBytes = bytes;
664         String units = " B";
665         if (fracBytes < 1024) {
666             return sByteFormatter.format(fracBytes) + units;
667         } else {
668             fracBytes /= 1024;
669             units = " KB";
670         }
671         if (fracBytes >= 1024) {
672             fracBytes /= 1024;
673             units = " MB";
674         }
675         if (fracBytes >= 1024) {
676             fracBytes /= 1024;
677             units = " GB";
678         }
679 
680         return sLargeByteFormatter.format(fracBytes) + units;
681     }
682 
approximateByteCount(long bytes)683     private static String approximateByteCount(long bytes) {
684         double fracBytes = bytes;
685         String units = "";
686         if (fracBytes >= 1024) {
687             fracBytes /= 1024;
688             units = "K";
689         }
690         if (fracBytes >= 1024) {
691             fracBytes /= 1024;
692             units = "M";
693         }
694         if (fracBytes >= 1024) {
695             fracBytes /= 1024;
696             units = "G";
697         }
698 
699         return sByteFormatter.format(fracBytes) + units;
700     }
701 
addCommasToNumber(long num)702     private static String addCommasToNumber(long num) {
703         return sCountFormatter.format(num);
704     }
705 
fractionalPercent(long num, long denom)706     private static String fractionalPercent(long num, long denom) {
707         double val = (double)num / (double)denom;
708         val *= 100;
709 
710         NumberFormat nf = NumberFormat.getInstance();
711         nf.setMinimumFractionDigits(2);
712         nf.setMaximumFractionDigits(2);
713         return nf.format(val) + "%";
714     }
715 
fillSummaryTable(ClientData cd)716     private void fillSummaryTable(ClientData cd) {
717         if (mHeapSummary.isDisposed()) {
718             return;
719         }
720 
721         mHeapSummary.setRedraw(false);
722         mHeapSummary.removeAll();
723 
724         int numRows = 0;
725         if (cd != null) {
726             synchronized (cd) {
727                 Iterator<Integer> iter = cd.getVmHeapIds();
728 
729                 while (iter.hasNext()) {
730                     numRows++;
731                     Integer id = iter.next();
732                     Map<String, Long> heapInfo = cd.getVmHeapInfo(id);
733                     if (heapInfo == null) {
734                         continue;
735                     }
736                     long sizeInBytes = heapInfo.get(ClientData.HEAP_SIZE_BYTES);
737                     long bytesAllocated = heapInfo.get(ClientData.HEAP_BYTES_ALLOCATED);
738                     long objectsAllocated = heapInfo.get(ClientData.HEAP_OBJECTS_ALLOCATED);
739 
740                     TableItem item = new TableItem(mHeapSummary, SWT.NONE);
741                     item.setText(0, id.toString());
742 
743                     item.setText(1, prettyByteCount(sizeInBytes));
744                     item.setText(2, prettyByteCount(bytesAllocated));
745                     item.setText(3, prettyByteCount(sizeInBytes - bytesAllocated));
746                     item.setText(4, fractionalPercent(bytesAllocated, sizeInBytes));
747                     item.setText(5, addCommasToNumber(objectsAllocated));
748                 }
749             }
750         }
751 
752         if (numRows == 0) {
753             // make sure there is always one empty item so that one table row is always displayed.
754             TableItem item = new TableItem(mHeapSummary, SWT.NONE);
755             item.setText("");
756         }
757 
758         mHeapSummary.pack();
759         mHeapSummary.setRedraw(true);
760     }
761 
fillDetailedTable(Client client, boolean forceRedraw)762     private void fillDetailedTable(Client client, boolean forceRedraw) {
763         // first check if the client is invalid or heap updates are not enabled.
764         if (client == null || client.isHeapUpdateEnabled() == false) {
765             mStatisticsTable.removeAll();
766             showChart(null);
767             return;
768         }
769 
770         ClientData cd = client.getClientData();
771 
772         Map<Integer, ArrayList<HeapSegmentElement>> heapMap;
773 
774         // Atomically get and clear the heap data.
775         synchronized (cd) {
776             if (serializeHeapData(cd.getVmHeapData()) == false && forceRedraw == false) {
777                 // no change, we return.
778                 return;
779             }
780 
781             heapMap = cd.getVmHeapData().getProcessedHeapMap();
782         }
783 
784         // we have new data, lets display it.
785 
786         // First, get the current selection, and its key.
787         int index = mStatisticsTable.getSelectionIndex();
788         Integer selectedKey = null;
789         if (index != -1) {
790             selectedKey = (Integer)mStatisticsTable.getItem(index).getData();
791         }
792 
793         // disable redraws and remove all from the table.
794         mStatisticsTable.setRedraw(false);
795         mStatisticsTable.removeAll();
796 
797         if (heapMap != null) {
798             int selectedIndex = -1;
799             ArrayList<HeapSegmentElement> selectedList = null;
800 
801             // get the keys
802             Set<Integer> keys = heapMap.keySet();
803             int iter = 0; // use a manual iter int because Set<?> doesn't have an index
804             // based accessor.
805             for (Integer key : keys) {
806                 ArrayList<HeapSegmentElement> list = heapMap.get(key);
807 
808                 // check if this is the key that is supposed to be selected
809                 if (key.equals(selectedKey)) {
810                     selectedIndex = iter;
811                     selectedList = list;
812                 }
813                 iter++;
814 
815                 TableItem item = new TableItem(mStatisticsTable, SWT.NONE);
816                 item.setData(key);
817 
818                 // get the type
819                 item.setText(0, mMapLegend[key]);
820 
821                 // set the count, smallest, largest
822                 int count = list.size();
823                 item.setText(1, addCommasToNumber(count));
824 
825                 if (count > 0) {
826                     item.setText(3, prettyByteCount(list.get(0).getLength()));
827                     item.setText(4, prettyByteCount(list.get(count-1).getLength()));
828 
829                     int median = count / 2;
830                     HeapSegmentElement element = list.get(median);
831                     long size = element.getLength();
832                     item.setText(5, prettyByteCount(size));
833 
834                     long totalSize = 0;
835                     for (int i = 0 ; i < count; i++) {
836                         element = list.get(i);
837 
838                         size = element.getLength();
839                         totalSize += size;
840                     }
841 
842                     // set the average and total
843                     item.setText(2, prettyByteCount(totalSize));
844                     item.setText(6, prettyByteCount(totalSize / count));
845                 }
846             }
847 
848             mStatisticsTable.setRedraw(true);
849 
850             if (selectedIndex != -1) {
851                 mStatisticsTable.setSelection(selectedIndex);
852                 showChart(selectedList);
853             } else {
854                 showChart(null);
855             }
856         } else {
857             mStatisticsTable.setRedraw(true);
858         }
859     }
860 
861     private static class ByteLong implements Comparable<ByteLong> {
862         private long mValue;
863 
ByteLong(long value)864         private ByteLong(long value) {
865             mValue = value;
866         }
867 
getValue()868         public long getValue() {
869             return mValue;
870         }
871 
872         @Override
toString()873         public String toString() {
874             return approximateByteCount(mValue);
875         }
876 
877         @Override
compareTo(ByteLong other)878         public int compareTo(ByteLong other) {
879             if (mValue != other.mValue) {
880                 return mValue < other.mValue ? -1 : 1;
881             }
882             return 0;
883         }
884 
885     }
886 
887     /**
888      * Fills the chart with the content of the list of {@link HeapSegmentElement}.
889      */
showChart(ArrayList<HeapSegmentElement> list)890     private void showChart(ArrayList<HeapSegmentElement> list) {
891         mAllocCountDataSet.clear();
892 
893         if (list != null) {
894             String rowKey = "Alloc Count";
895 
896             long currentSize = -1;
897             int currentCount = 0;
898             for (HeapSegmentElement element : list) {
899                 if (element.getLength() != currentSize) {
900                     if (currentSize != -1) {
901                         ByteLong columnKey = new ByteLong(currentSize);
902                         mAllocCountDataSet.addValue(currentCount, rowKey, columnKey);
903                     }
904 
905                     currentSize = element.getLength();
906                     currentCount = 1;
907                 } else {
908                     currentCount++;
909                 }
910             }
911 
912             // add the last item
913             if (currentSize != -1) {
914                 ByteLong columnKey = new ByteLong(currentSize);
915                 mAllocCountDataSet.addValue(currentCount, rowKey, columnKey);
916             }
917         }
918     }
919 
920     /*
921      * Add a color legend to the specified table.
922      */
createLegend(Composite parent)923     private void createLegend(Composite parent) {
924         mLegend = new Group(parent, SWT.NONE);
925         mLegend.setText(getLegendText(0));
926 
927         mLegend.setLayout(new GridLayout(2, false));
928 
929         RGB[] colors = mMapPalette.colors;
930 
931         for (int i = 0; i < NUM_PALETTE_ENTRIES; i++) {
932             Image tmpImage = createColorRect(parent.getDisplay(), colors[i]);
933 
934             Label l = new Label(mLegend, SWT.NONE);
935             l.setImage(tmpImage);
936 
937             l = new Label(mLegend, SWT.NONE);
938             l.setText(mMapLegend[i]);
939         }
940     }
941 
getLegendText(int level)942     private String getLegendText(int level) {
943         int bytes = 8 * (100 / ZOOMS[level]);
944 
945         return String.format("Key (1 pixel = %1$d bytes)", bytes);
946     }
947 
setLegendText(int level)948     private void setLegendText(int level) {
949         mLegend.setText(getLegendText(level));
950 
951     }
952 
953     /*
954      * Create a nice rectangle in the specified color.
955      */
createColorRect(Display display, RGB color)956     private Image createColorRect(Display display, RGB color) {
957         int width = 32;
958         int height = 16;
959 
960         Image img = new Image(display, width, height);
961         GC gc = new GC(img);
962         gc.setBackground(new Color(display, color));
963         gc.fillRectangle(0, 0, width, height);
964         gc.dispose();
965         return img;
966     }
967 
968 
969     /*
970      * Are updates enabled?
971      */
setUpdateStatus(int status)972     private void setUpdateStatus(int status) {
973         switch (status) {
974             case NOT_SELECTED:
975                 mUpdateStatus.setText("Select a client to see heap updates");
976                 break;
977             case NOT_ENABLED:
978                 mUpdateStatus.setText("Heap updates are " +
979                                       "NOT ENABLED for this client");
980                 break;
981             case ENABLED:
982                 mUpdateStatus.setText("Heap updates will happen after " +
983                                       "every GC for this client");
984                 break;
985             default:
986                 throw new RuntimeException();
987         }
988 
989         mUpdateStatus.pack();
990     }
991 
992 
993     /**
994      * Return the closest power of two greater than or equal to value.
995      *
996      * @param value the return value will be >= value
997      * @return a power of two >= value.  If value > 2^31, 2^31 is returned.
998      */
999 //xxx use Integer.highestOneBit() or numberOfLeadingZeros().
nextPow2(int value)1000     private int nextPow2(int value) {
1001         for (int i = 31; i >= 0; --i) {
1002             if ((value & (1<<i)) != 0) {
1003                 if (i < 31) {
1004                     return 1<<(i + 1);
1005                 } else {
1006                     return 1<<31;
1007                 }
1008             }
1009         }
1010         return 0;
1011     }
1012 
zOrderData(ImageData id, byte pixData[])1013     private int zOrderData(ImageData id, byte pixData[]) {
1014         int maxX = 0;
1015         for (int i = 0; i < pixData.length; i++) {
1016             /* Tread the pixData index as a z-order curve index and
1017              * decompose into Cartesian coordinates.
1018              */
1019             int x = (i & 1) |
1020                     ((i >>> 2) & 1) << 1 |
1021                     ((i >>> 4) & 1) << 2 |
1022                     ((i >>> 6) & 1) << 3 |
1023                     ((i >>> 8) & 1) << 4 |
1024                     ((i >>> 10) & 1) << 5 |
1025                     ((i >>> 12) & 1) << 6 |
1026                     ((i >>> 14) & 1) << 7 |
1027                     ((i >>> 16) & 1) << 8 |
1028                     ((i >>> 18) & 1) << 9 |
1029                     ((i >>> 20) & 1) << 10 |
1030                     ((i >>> 22) & 1) << 11 |
1031                     ((i >>> 24) & 1) << 12 |
1032                     ((i >>> 26) & 1) << 13 |
1033                     ((i >>> 28) & 1) << 14 |
1034                     ((i >>> 30) & 1) << 15;
1035             int y = ((i >>> 1) & 1) << 0 |
1036                     ((i >>> 3) & 1) << 1 |
1037                     ((i >>> 5) & 1) << 2 |
1038                     ((i >>> 7) & 1) << 3 |
1039                     ((i >>> 9) & 1) << 4 |
1040                     ((i >>> 11) & 1) << 5 |
1041                     ((i >>> 13) & 1) << 6 |
1042                     ((i >>> 15) & 1) << 7 |
1043                     ((i >>> 17) & 1) << 8 |
1044                     ((i >>> 19) & 1) << 9 |
1045                     ((i >>> 21) & 1) << 10 |
1046                     ((i >>> 23) & 1) << 11 |
1047                     ((i >>> 25) & 1) << 12 |
1048                     ((i >>> 27) & 1) << 13 |
1049                     ((i >>> 29) & 1) << 14 |
1050                     ((i >>> 31) & 1) << 15;
1051             try {
1052                 id.setPixel(x, y, pixData[i]);
1053                 if (x > maxX) {
1054                     maxX = x;
1055                 }
1056             } catch (IllegalArgumentException ex) {
1057                 System.out.println("bad pixels: i " + i +
1058                         ", w " + id.width +
1059                         ", h " + id.height +
1060                         ", x " + x +
1061                         ", y " + y);
1062                 throw ex;
1063             }
1064         }
1065         return maxX;
1066     }
1067 
1068     private final static int HILBERT_DIR_N = 0;
1069     private final static int HILBERT_DIR_S = 1;
1070     private final static int HILBERT_DIR_E = 2;
1071     private final static int HILBERT_DIR_W = 3;
1072 
hilbertWalk(ImageData id, InputStream pixData, int order, int x, int y, int dir)1073     private void hilbertWalk(ImageData id, InputStream pixData,
1074                              int order, int x, int y, int dir)
1075                              throws IOException {
1076         if (x >= id.width || y >= id.height) {
1077             return;
1078         } else if (order == 0) {
1079             try {
1080                 int p = pixData.read();
1081                 if (p >= 0) {
1082                     // flip along x=y axis;  assume width == height
1083                     id.setPixel(y, x, p);
1084 
1085                     /* Skanky; use an otherwise-unused ImageData field
1086                      * to keep track of the max x,y used. Note that x and y are inverted.
1087                      */
1088                     if (y > id.x) {
1089                         id.x = y;
1090                     }
1091                     if (x > id.y) {
1092                         id.y = x;
1093                     }
1094                 }
1095 //xxx just give up; don't bother walking the rest of the image
1096             } catch (IllegalArgumentException ex) {
1097                 System.out.println("bad pixels: order " + order +
1098                         ", dir " + dir +
1099                         ", w " + id.width +
1100                         ", h " + id.height +
1101                         ", x " + x +
1102                         ", y " + y);
1103                 throw ex;
1104             }
1105         } else {
1106             order--;
1107             int delta = 1 << order;
1108             int nextX = x + delta;
1109             int nextY = y + delta;
1110 
1111             switch (dir) {
1112             case HILBERT_DIR_E:
1113                 hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_N);
1114                 hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_E);
1115                 hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_E);
1116                 hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_S);
1117                 break;
1118             case HILBERT_DIR_N:
1119                 hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_E);
1120                 hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_N);
1121                 hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_N);
1122                 hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_W);
1123                 break;
1124             case HILBERT_DIR_S:
1125                 hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_W);
1126                 hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_S);
1127                 hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_S);
1128                 hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_E);
1129                 break;
1130             case HILBERT_DIR_W:
1131                 hilbertWalk(id, pixData, order, nextX, nextY, HILBERT_DIR_S);
1132                 hilbertWalk(id, pixData, order, nextX,     y, HILBERT_DIR_W);
1133                 hilbertWalk(id, pixData, order,     x,     y, HILBERT_DIR_W);
1134                 hilbertWalk(id, pixData, order,     x, nextY, HILBERT_DIR_N);
1135                 break;
1136             default:
1137                 throw new RuntimeException("Unexpected Hilbert direction " +
1138                                            dir);
1139             }
1140         }
1141     }
1142 
hilbertOrderData(ImageData id, byte pixData[])1143     private Point hilbertOrderData(ImageData id, byte pixData[]) {
1144 
1145         int order = 0;
1146         for (int n = 1; n < id.width; n *= 2) {
1147             order++;
1148         }
1149         /* Skanky; use an otherwise-unused ImageData field
1150          * to keep track of maxX.
1151          */
1152         Point p = new Point(0,0);
1153         int oldIdX = id.x;
1154         int oldIdY = id.y;
1155         id.x = id.y = 0;
1156         try {
1157             hilbertWalk(id, new ByteArrayInputStream(pixData),
1158                         order, 0, 0, HILBERT_DIR_E);
1159             p.x = id.x;
1160             p.y = id.y;
1161         } catch (IOException ex) {
1162             System.err.println("Exception during hilbertWalk()");
1163             p.x = id.height;
1164             p.y = id.width;
1165         }
1166         id.x = oldIdX;
1167         id.y = oldIdY;
1168         return p;
1169     }
1170 
createHilbertHeapImage(byte pixData[])1171     private ImageData createHilbertHeapImage(byte pixData[]) {
1172         int w, h;
1173 
1174         // Pick an image size that the largest of heaps will fit into.
1175         w = (int)Math.sqrt(((16 * 1024 * 1024)/8));
1176 
1177         // Space-filling curves require a power-of-2 width.
1178         w = nextPow2(w);
1179         h = w;
1180 
1181         // Create the heap image.
1182         ImageData id = new ImageData(w, h, 8, mMapPalette);
1183 
1184         // Copy the data into the image
1185         //int maxX = zOrderData(id, pixData);
1186         Point maxP = hilbertOrderData(id, pixData);
1187 
1188         // update the max size to make it a round number once the zoom is applied
1189         int factor = 100 / ZOOMS[mZoom.getSelectionIndex()];
1190         if (factor != 1) {
1191             int tmp = maxP.x % factor;
1192             if (tmp != 0) {
1193                 maxP.x += factor - tmp;
1194             }
1195 
1196             tmp = maxP.y % factor;
1197             if (tmp != 0) {
1198                 maxP.y += factor - tmp;
1199             }
1200         }
1201 
1202         if (maxP.y < id.height) {
1203             // Crop the image down to the interesting part.
1204             id = new ImageData(id.width, maxP.y, id.depth, id.palette,
1205                                id.scanlinePad, id.data);
1206         }
1207 
1208         if (maxP.x < id.width) {
1209             // crop the image again. A bit trickier this time.
1210            ImageData croppedId = new ImageData(maxP.x, id.height, id.depth, id.palette);
1211 
1212            int[] buffer = new int[maxP.x];
1213            for (int l = 0 ; l < id.height; l++) {
1214                id.getPixels(0, l, maxP.x, buffer, 0);
1215                croppedId.setPixels(0, l, maxP.x, buffer, 0);
1216            }
1217 
1218            id = croppedId;
1219         }
1220 
1221         // apply the zoom
1222         if (factor != 1) {
1223             id = id.scaledTo(id.width / factor, id.height / factor);
1224         }
1225 
1226         return id;
1227     }
1228 
1229     /**
1230      * Convert the raw heap data to an image.  We know we're running in
1231      * the UI thread, so we can issue graphics commands directly.
1232      *
1233      * http://help.eclipse.org/help31/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html
1234      *
1235      * @param cd The client data
1236      * @param mode The display mode. 0 = linear, 1 = hilbert.
1237      * @param forceRedraw
1238      */
renderHeapData(ClientData cd, int mode, boolean forceRedraw)1239     private void renderHeapData(ClientData cd, int mode, boolean forceRedraw) {
1240         Image image;
1241 
1242         byte[] pixData;
1243 
1244         // Atomically get and clear the heap data.
1245         synchronized (cd) {
1246             if (serializeHeapData(cd.getVmHeapData()) == false && forceRedraw == false) {
1247                 // no change, we return.
1248                 return;
1249             }
1250 
1251             pixData = getSerializedData();
1252         }
1253 
1254         if (pixData != null) {
1255             ImageData id;
1256             if (mode == 1) {
1257                 id = createHilbertHeapImage(pixData);
1258             } else {
1259                 id = createLinearHeapImage(pixData, 200, mMapPalette);
1260             }
1261 
1262             image = new Image(mDisplay, id);
1263         } else {
1264             // Render a placeholder image.
1265             int width, height;
1266             if (mode == 1) {
1267                 width = height = PLACEHOLDER_HILBERT_SIZE;
1268             } else {
1269                 width = PLACEHOLDER_LINEAR_H_SIZE;
1270                 height = PLACEHOLDER_LINEAR_V_SIZE;
1271             }
1272             image = new Image(mDisplay, width, height);
1273             GC gc = new GC(image);
1274             gc.setForeground(mDisplay.getSystemColor(SWT.COLOR_RED));
1275             gc.drawLine(0, 0, width-1, height-1);
1276             gc.dispose();
1277             gc = null;
1278         }
1279 
1280         // set the new image
1281 
1282         if (mode == 1) {
1283             if (mHilbertImage != null) {
1284                 mHilbertImage.dispose();
1285             }
1286 
1287             mHilbertImage = image;
1288             mHilbertHeapImage.setImage(mHilbertImage);
1289             mHilbertHeapImage.pack(true);
1290             mHilbertBase.layout();
1291             mHilbertBase.pack(true);
1292         } else {
1293             if (mLinearImage != null) {
1294                 mLinearImage.dispose();
1295             }
1296 
1297             mLinearImage = image;
1298             mLinearHeapImage.setImage(mLinearImage);
1299             mLinearHeapImage.pack(true);
1300             mLinearBase.layout();
1301             mLinearBase.pack(true);
1302         }
1303     }
1304 
1305     @Override
setTableFocusListener()1306     protected void setTableFocusListener() {
1307         addTableToFocusListener(mHeapSummary);
1308     }
1309 }
1310 
1311