1
/*******************************************************************************
2
* Copyright (c) 2008 Red Hat, Inc.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Elliott Baron <ebaron@redhat.com> - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.linuxtools.internal.valgrind.massif;
13
import java.text.DecimalFormat;
14
import java.util.ArrayList;
15
import java.util.List;
17
import org.eclipse.core.runtime.Path;
18
import org.eclipse.jface.action.Action;
19
import org.eclipse.jface.action.IAction;
20
import org.eclipse.jface.resource.JFaceResources;
21
import org.eclipse.jface.viewers.AbstractTreeViewer;
22
import org.eclipse.jface.viewers.ArrayContentProvider;
23
import org.eclipse.jface.viewers.DoubleClickEvent;
24
import org.eclipse.jface.viewers.IDoubleClickListener;
25
import org.eclipse.jface.viewers.IFontProvider;
26
import org.eclipse.jface.viewers.IStructuredSelection;
27
import org.eclipse.jface.viewers.ITableLabelProvider;
28
import org.eclipse.jface.viewers.LabelProvider;
29
import org.eclipse.jface.viewers.StructuredSelection;
30
import org.eclipse.jface.viewers.TableViewer;
31
import org.eclipse.jface.viewers.TableViewerColumn;
32
import org.eclipse.jface.viewers.Viewer;
33
import org.eclipse.jface.viewers.ViewerComparator;
34
import org.eclipse.linuxtools.internal.valgrind.massif.MassifSnapshot.SnapshotType;
35
import org.eclipse.linuxtools.internal.valgrind.massif.charting.ChartEditorInput;
36
import org.eclipse.linuxtools.internal.valgrind.massif.charting.ChartPNG;
37
import org.eclipse.linuxtools.internal.valgrind.massif.charting.HeapChart;
38
import org.eclipse.linuxtools.valgrind.ui.IValgrindToolView;
39
import org.eclipse.swt.SWT;
40
import org.eclipse.swt.custom.StackLayout;
41
import org.eclipse.swt.events.SelectionAdapter;
42
import org.eclipse.swt.events.SelectionEvent;
43
import org.eclipse.swt.events.SelectionListener;
44
import org.eclipse.swt.graphics.Font;
45
import org.eclipse.swt.graphics.Image;
46
import org.eclipse.swt.layout.GridData;
47
import org.eclipse.swt.widgets.Composite;
48
import org.eclipse.swt.widgets.Control;
49
import org.eclipse.swt.widgets.Display;
50
import org.eclipse.swt.widgets.FileDialog;
51
import org.eclipse.swt.widgets.Shell;
52
import org.eclipse.swt.widgets.Table;
53
import org.eclipse.swt.widgets.TableColumn;
54
import org.eclipse.ui.IEditorInput;
55
import org.eclipse.ui.IEditorPart;
56
import org.eclipse.ui.IWorkbenchPage;
57
import org.eclipse.ui.PartInitException;
58
import org.eclipse.ui.PlatformUI;
59
import org.eclipse.ui.part.ViewPart;
60
import org.eclipse.ui.plugin.AbstractUIPlugin;
62
public class MassifViewPart extends ViewPart implements IValgrindToolView {
64
protected static final String TITLE_STACKS = Messages
65
.getString("MassifViewPart.Stacks"); //$NON-NLS-1$
66
protected static final String TITLE_EXTRA = Messages
67
.getString("MassifViewPart.Extra_Heap"); //$NON-NLS-1$
68
protected static final String TITLE_USEFUL = Messages
69
.getString("MassifViewPart.Useful_Heap"); //$NON-NLS-1$
70
protected static final String TITLE_TOTAL = Messages
71
.getString("MassifViewPart.Total"); //$NON-NLS-1$
72
protected static final String TITLE_TIME = Messages
73
.getString("MassifViewPart.Time"); //$NON-NLS-1$
74
protected static final String TITLE_NUMBER = Messages
75
.getString("MassifViewPart.Snapshot"); //$NON-NLS-1$
76
protected static final String TREE_ACTION = MassifPlugin.PLUGIN_ID
77
+ ".treeAction"; //$NON-NLS-1$
78
public static final String CHART_ACTION = MassifPlugin.PLUGIN_ID
79
+ ".chartAction"; //$NON-NLS-1$
80
public static final String PID_ACTION = MassifPlugin.PLUGIN_ID
81
+ ".pidAction"; //$NON-NLS-1$
82
public static final String SAVE_CHART_ACTION = MassifPlugin.PLUGIN_ID
83
+ ".saveChartAction"; //$NON-NLS-1$
85
protected MassifOutput output;
86
protected Integer pid;
88
protected Composite top;
89
protected StackLayout stackLayout;
90
protected TableViewer viewer;
91
protected MassifTreeViewer treeViewer;
92
protected MassifHeapTreeNode[] nodes;
93
protected String chartName;
95
protected static final int COLUMN_SIZE = 125;
97
protected Action treeAction;
98
protected Action chartAction;
99
protected MassifPidMenuAction pidAction;
100
protected Action saveChartAction;
102
protected List<ChartEditorInput> chartInputs;
105
public void createPartControl(Composite parent) {
106
chartInputs = new ArrayList<ChartEditorInput>();
108
top = new Composite(parent, SWT.NONE);
109
stackLayout = new StackLayout();
110
top.setLayout(stackLayout);
111
top.setLayoutData(new GridData(GridData.FILL_BOTH));
113
viewer = new TableViewer(top, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL
114
| SWT.FULL_SELECTION);
116
Table table = viewer.getTable();
117
table.setLayoutData(new GridData(GridData.FILL_BOTH));
120
String[] columnTitles = { TITLE_NUMBER, TITLE_TIME, TITLE_TOTAL,
121
TITLE_USEFUL, TITLE_EXTRA, TITLE_STACKS };
123
for (int i = 0; i < columnTitles.length; i++) {
124
TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
125
column.getColumn().setText(columnTitles[i]);
126
column.getColumn().setWidth(COLUMN_SIZE);
127
column.getColumn().setResizable(true);
128
column.getColumn().addSelectionListener(getHeaderListener());
130
table.setHeaderVisible(true);
131
table.setLinesVisible(true);
133
viewer.setContentProvider(new ArrayContentProvider());
134
viewer.setLabelProvider(new MassifLabelProvider());
136
treeViewer = new MassifTreeViewer(top);
137
treeViewer.getViewer().getControl().setLayoutData(new GridData(GridData.FILL_BOTH));
139
viewer.addDoubleClickListener(new IDoubleClickListener() {
140
public void doubleClick(DoubleClickEvent event) {
141
MassifSnapshot snapshot = (MassifSnapshot) ((IStructuredSelection) event
142
.getSelection()).getFirstElement();
143
if (snapshot.getType() != SnapshotType.EMPTY) {
144
treeAction.setChecked(true);
145
setTopControl(treeViewer.getViewer().getControl());
146
treeViewer.getViewer().setSelection(new StructuredSelection(snapshot
148
treeViewer.getViewer().expandToLevel(snapshot.getRoot(),
149
AbstractTreeViewer.ALL_LEVELS);
154
stackLayout.topControl = viewer.getControl();
158
private String getUnitString(MassifSnapshot[] snapshots) {
160
MassifSnapshot snapshot = snapshots[0];
161
switch (snapshot.getUnit()) {
163
result = "B"; //$NON-NLS-1$
166
result = "i"; //$NON-NLS-1$
169
result = "ms"; //$NON-NLS-1$
175
private SelectionListener getHeaderListener() {
176
return new SelectionAdapter() {
178
public void widgetSelected(SelectionEvent e) {
179
TableColumn column = (TableColumn) e.widget;
180
Table table = viewer.getTable();
181
if (column.equals(table.getSortColumn())) {
182
int direction = table.getSortDirection() == SWT.UP ? SWT.DOWN
184
table.setSortDirection(direction);
186
table.setSortDirection(SWT.UP);
188
table.setSortColumn(column);
189
viewer.setComparator(new ViewerComparator() {
191
public int compare(Viewer viewer, Object o1, Object o2) {
192
Table table = ((TableViewer) viewer).getTable();
193
int direction = table.getSortDirection();
194
MassifSnapshot s1 = (MassifSnapshot) o1;
195
MassifSnapshot s2 = (MassifSnapshot) o2;
197
TableColumn column = table.getSortColumn();
198
if (column.getText().equals(TITLE_NUMBER)) {
199
result = s1.getNumber() - s2.getNumber();
200
} else if (column.getText().startsWith(TITLE_TIME)) {
201
result = s1.getTime() - s2.getTime();
202
} else if (column.getText().equals(TITLE_TOTAL)) {
203
result = s1.getTotal() - s2.getTotal();
204
} else if (column.getText().equals(TITLE_USEFUL)) {
205
result = s1.getHeapBytes() - s2.getHeapBytes();
206
} else if (column.getText().equals(TITLE_EXTRA)) {
207
result = s1.getHeapExtra() - s2.getHeapExtra();
209
result = s1.getStacks() - s2.getStacks();
212
// ascending / descending
213
result = direction == SWT.UP ? result : -result;
216
if (result > Integer.MAX_VALUE) {
217
result = Integer.MAX_VALUE;
218
} else if (result < Integer.MIN_VALUE) {
219
result = Integer.MIN_VALUE;
228
public IAction[] getToolbarActions() {
229
pidAction = new MassifPidMenuAction(this);
230
pidAction.setId(PID_ACTION);
232
chartAction = new Action(
233
Messages.getString("MassifViewPart.Display_Heap_Allocation"), IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
236
ChartEditorInput input = getChartInput(pid);
242
chartAction.setId(CHART_ACTION);
243
chartAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
244
MassifPlugin.PLUGIN_ID, "icons/linecharticon.gif")); //$NON-NLS-1$
245
chartAction.setToolTipText(Messages
246
.getString("MassifViewPart.Display_Heap_Allocation")); //$NON-NLS-1$
248
saveChartAction = new Action(Messages.getString("MassifViewPart.Save_Chart"), IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
251
ChartEditorInput currentInput = getChartInput(pid);
252
String path = getChartSavePath(currentInput.getName() + ".png"); //$NON-NLS-1$
254
ChartPNG renderer = new ChartPNG(currentInput.getChart());
255
renderer.renderPNG(Path.fromOSString(path));
259
saveChartAction.setId(SAVE_CHART_ACTION);
260
saveChartAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
261
MassifPlugin.PLUGIN_ID, "icons/chart-save.png")); //$NON-NLS-1$
262
saveChartAction.setToolTipText(Messages.getString("MassifViewPart.Save_Chart")); //$NON-NLS-1$
264
treeAction = new Action(
265
Messages.getString("MassifViewPart.Show_Heap_Tree"), IAction.AS_CHECK_BOX) { //$NON-NLS-1$
269
stackLayout.topControl = treeViewer.getViewer().getControl();
272
stackLayout.topControl = viewer.getControl();
277
treeAction.setId(TREE_ACTION);
278
treeAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
279
MassifPlugin.PLUGIN_ID, "icons/call_hierarchy.gif")); //$NON-NLS-1$
280
treeAction.setToolTipText(Messages
281
.getString("MassifViewPart.Show_Heap_Tree")); //$NON-NLS-1$
283
return new IAction[] { pidAction, chartAction, saveChartAction, treeAction };
286
protected String getChartSavePath(String defaultName) {
287
Shell parent = new Shell(Display.getDefault());
288
FileDialog dialog = new FileDialog(parent, SWT.SAVE);
289
dialog.setText(Messages.getString("MassifViewPart.Save_chart_dialog_title")); //$NON-NLS-1$
290
dialog.setOverwrite(true);
291
dialog.setFilterExtensions(new String[] { ".png" }); //$NON-NLS-1$
292
dialog.setFileName(defaultName);
294
return dialog.open();
297
protected void createChart(MassifSnapshot[] snapshots) {
298
String title = chartName + " [PID: " + pid + "]"; //$NON-NLS-1$//$NON-NLS-2$
299
HeapChart chart = new HeapChart(snapshots, title);
301
String name = getInputName(title);
302
ChartEditorInput input = new ChartEditorInput(chart, this, name, pid);
303
chartInputs.add(input);
309
protected void displayChart(final ChartEditorInput chartInput) {
310
Display.getDefault().syncExec(new Runnable() {
313
IWorkbenchPage page = PlatformUI.getWorkbench()
314
.getActiveWorkbenchWindow().getActivePage();
315
page.openEditor(chartInput, MassifPlugin.EDITOR_ID);
316
} catch (PartInitException e) {
323
protected String getInputName(String description) {
324
String launchName = description.substring(0, description
325
.indexOf("[massif")); //$NON-NLS-1$
326
return launchName.trim();
330
public void setFocus() {
331
viewer.getTable().setFocus();
334
public void refreshView() {
335
if (output != null && pid != null) {
336
MassifSnapshot[] snapshots = output.getSnapshots(pid);
337
pidAction.setPids(output.getPids());
338
if (snapshots != null) {
339
viewer.setInput(snapshots);
341
String timeWithUnit = TITLE_TIME + " (" + getUnitString(snapshots) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
342
for (TableColumn column : viewer.getTable().getColumns()) {
343
if (column.getText().startsWith(TITLE_TIME)) {
344
column.setText(timeWithUnit);
346
viewer.getTable().layout(true);
348
MassifSnapshot[] detailed = getDetailed(snapshots);
349
nodes = new MassifHeapTreeNode[detailed.length];
350
for (int i = 0; i < detailed.length; i++) {
351
nodes[i] = detailed[i].getRoot();
353
treeViewer.getViewer().setInput(nodes);
355
// create and display chart
356
if (snapshots.length > 0) {
357
ChartEditorInput input = getChartInput(pid);
359
createChart(snapshots);
370
public void dispose() {
371
// Close all chart editors to keep Valgrind output consistent throughout workbench
372
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
374
for (IEditorInput input : chartInputs) {
375
IEditorPart part = page.findEditor(input);
377
page.closeEditor(part, false);
384
public void setTopControl(Control control) {
385
stackLayout.topControl = control;
389
public void setOutput(MassifOutput output) {
390
this.output = output;
393
public MassifOutput getOutput() {
397
public void setPid(Integer pid) {
401
public Integer getPid() {
405
public MassifSnapshot[] getSnapshots() {
406
return output != null && pid != null ? output.getSnapshots(pid) : null;
409
public TableViewer getTableViewer() {
413
public MassifTreeViewer getTreeViewer() {
417
protected class MassifLabelProvider extends LabelProvider implements
418
ITableLabelProvider, IFontProvider {
420
public Image getColumnImage(Object element, int columnIndex) {
422
if (columnIndex == 0) {
423
MassifSnapshot snapshot = (MassifSnapshot) element;
424
switch (snapshot.getType()) {
429
image = AbstractUIPlugin
430
.imageDescriptorFromPlugin(MassifPlugin.PLUGIN_ID,
431
"icons/call_hierarchy.gif").createImage(); //$NON-NLS-1$
437
public String getColumnText(Object element, int columnIndex) {
438
MassifSnapshot snapshot = (MassifSnapshot) element;
439
DecimalFormat df = new DecimalFormat("#,##0"); //$NON-NLS-1$
440
switch (columnIndex) {
442
return df.format(snapshot.getNumber());
444
return df.format(snapshot.getTime());
446
return df.format(snapshot.getTotal());
448
return df.format(snapshot.getHeapBytes());
450
return df.format(snapshot.getHeapExtra());
452
return df.format(snapshot.getStacks());
456
public Font getFont(Object element) {
458
MassifSnapshot snapshot = (MassifSnapshot) element;
459
switch (snapshot.getType()) {
464
font = JFaceResources.getFontRegistry().getBold(
465
JFaceResources.DIALOG_FONT);
471
public void setChartName(String chartName) {
472
this.chartName = chartName;
475
private MassifSnapshot[] getDetailed(MassifSnapshot[] snapshots) {
476
ArrayList<MassifSnapshot> list = new ArrayList<MassifSnapshot>();
477
for (MassifSnapshot snapshot : snapshots) {
478
if (snapshot.getType() != SnapshotType.EMPTY) {
482
return list.toArray(new MassifSnapshot[list.size()]);
485
private ChartEditorInput getChartInput(Integer pid) {
486
ChartEditorInput result = null;
487
for (int i = 0; i < chartInputs.size(); i++) {
488
ChartEditorInput input = chartInputs.get(i);
489
if (input.getPid().equals(pid)) {