1 /*******************************************************************************
2 * Copyright (c) 2009 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Yann N. Dauphin (dhaemon@gmail.com) - Implementation
11 * Francois Chouinard (fchouinard@gmail.com) - Initial API
12 *******************************************************************************/
14 package org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
;
16 import java
.text
.DecimalFormat
;
17 import java
.util
.Arrays
;
18 import java
.util
.HashSet
;
21 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
22 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
23 import org
.eclipse
.jface
.viewers
.TreeViewer
;
24 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
25 import org
.eclipse
.jface
.viewers
.Viewer
;
26 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
27 import org
.eclipse
.linuxtools
.lttng
.request
.ILttngSyntEventRequest
;
28 import org
.eclipse
.linuxtools
.lttng
.state
.evProcessor
.AbsEventToHandlerResolver
;
29 import org
.eclipse
.linuxtools
.lttng
.ui
.TraceDebug
;
30 import org
.eclipse
.linuxtools
.lttng
.ui
.model
.trange
.ItemContainer
;
31 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.common
.AbsTimeUpdateView
;
32 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.common
.ParamsUpdater
;
33 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.evProcessor
.StatsTimeCountHandlerFactory
;
34 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.model
.StatisticsTreeNode
;
35 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.model
.StatisticsTreeRootFactory
;
36 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
37 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
38 import org
.eclipse
.linuxtools
.tmf
.experiment
.TmfExperiment
;
39 import org
.eclipse
.linuxtools
.tmf
.request
.ITmfDataRequest
.ExecutionType
;
40 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfExperimentSelectedSignal
;
41 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalHandler
;
42 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfTrace
;
43 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.timeAnalysis
.model
.ITmfTimeAnalysisEntry
;
44 import org
.eclipse
.swt
.SWT
;
45 import org
.eclipse
.swt
.events
.SelectionAdapter
;
46 import org
.eclipse
.swt
.events
.SelectionEvent
;
47 import org
.eclipse
.swt
.graphics
.Color
;
48 import org
.eclipse
.swt
.graphics
.Cursor
;
49 import org
.eclipse
.swt
.graphics
.Image
;
50 import org
.eclipse
.swt
.layout
.FillLayout
;
51 import org
.eclipse
.swt
.widgets
.Composite
;
52 import org
.eclipse
.swt
.widgets
.Display
;
53 import org
.eclipse
.swt
.widgets
.Event
;
54 import org
.eclipse
.swt
.widgets
.Listener
;
55 import org
.eclipse
.ui
.ISharedImages
;
56 import org
.eclipse
.ui
.PlatformUI
;
59 * <b><u>StatisticsView</u></b>
61 * The Statistics View displays statistics for traces.
63 * It is implemented according to the MVC pattern. - The model is a
64 * StatisticsTreeNode built by the State Manager. - The view is built with a
65 * TreeViewer. - The controller that keeps model and view synchronised is an
66 * observer of the model.
68 public class StatisticsView
extends AbsTimeUpdateView
{
69 public static final String ID
= "org.eclipse.linuxtools.lttng.ui.views.statistics";
70 private TreeViewer treeViewer
;
73 private final String LEVEL_COLUMN
= "Level";
74 private final String EVENTS_COUNT_COLUMN
= "Number of Events";
75 private final String CPU_TIME_COLUMN
= "CPU Time";
76 private final String CUMULATIVE_CPU_TIME_COLUMN
= "Cumulative CPU Time";
77 private final String ELAPSED_TIME_COLUMN
= "Elapsed Time";
79 // Table column tooltips
80 private final String LEVEL_COLUMN_TIP
= "Level at which statistics apply.";
81 private final String EVENTS_COUNT_COLUMN_TIP
= "Total amount of events that are tied to given resource.";
82 private final String CPU_TIME_COLUMN_TIP
= "Total amount of time the CPU was used excluding wait times(I/O, etc.) at that level.";
83 private final String CUMULATIVE_CPU_TIME_COLUMN_TIP
= "Total amount of time between the first and last event excluding wait times in a level.";
84 private final String ELAPSED_TIME_COLUMN_TIP
= "Total amount of time the CPU was used including wait times(I/O, etc.) at that level.";
86 // Level for which statistics should not be displayed.
87 private Set
<String
> folderLevels
= new HashSet
<String
>(Arrays
88 .asList(new String
[] { "Event Types", "Modes", "Submodes", "CPUs",
89 "Processes", "Functions" }));
91 // Levels for which sub-levels should not contain time-related statistics.
92 private Set
<String
> levelsWithEmptyTime
= new HashSet
<String
>(Arrays
93 .asList(new String
[] { "Event Types" }));
95 private DecimalFormat decimalFormat
= new DecimalFormat("0.#########");
96 private Cursor fwaitCursor
= null;
98 // Used to draw bar charts in columns.
99 private interface ColumnPercentageProvider
{
100 public double getPercentage(StatisticsTreeNode node
);
104 * Contains all the information necessary to build a column of the table.
106 private class ColumnData
{
107 // Name of the column.
108 public final String header
;
109 // Width of the column.
110 public final int width
;
111 // Alignment of the column.
112 public final int alignment
;
113 // Tooltip of the column.
114 public final String tooltip
;
115 // Adapts a StatisticsTreeNode into the content of it's corresponding
116 // cell for that column.
117 public final ColumnLabelProvider labelProvider
;
118 // Used to sort elements of this column. Can be null.
119 public final ViewerComparator comparator
;
120 // Used to draw bar charts in this column. Can be null.
121 public final ColumnPercentageProvider percentageProvider
;
123 public ColumnData(String h
, int w
, int a
, String t
,
124 ColumnLabelProvider l
, ViewerComparator c
,
125 ColumnPercentageProvider p
) {
132 percentageProvider
= p
;
136 // List that will be used to create the table.
137 private ColumnData
[] columnDataList
= new ColumnData
[] {
138 new ColumnData(LEVEL_COLUMN
, 200, SWT
.LEFT
, LEVEL_COLUMN_TIP
,
139 new ColumnLabelProvider() {
141 public String
getText(Object element
) {
142 return ((StatisticsTreeNode
) element
).getKey();
146 public Image
getImage(Object element
) {
147 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
148 if (folderLevels
.contains(node
.getKey())) {
149 return PlatformUI
.getWorkbench()
150 .getSharedImages().getImage(
151 ISharedImages
.IMG_OBJ_FOLDER
);
153 return PlatformUI
.getWorkbench()
154 .getSharedImages().getImage(
155 ISharedImages
.IMG_OBJ_ELEMENT
);
158 }, new ViewerComparator() {
160 public int compare(Viewer viewer
, Object e1
, Object e2
) {
161 StatisticsTreeNode n1
= (StatisticsTreeNode
) e1
;
162 StatisticsTreeNode n2
= (StatisticsTreeNode
) e2
;
164 return n1
.getKey().compareTo(n2
.getKey());
167 new ColumnData(EVENTS_COUNT_COLUMN
, 125, SWT
.LEFT
,
168 EVENTS_COUNT_COLUMN_TIP
, new ColumnLabelProvider() {
170 public String
getText(Object element
) {
171 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
172 if (!folderLevels
.contains(node
.getKey())) {
173 return Long
.toString(node
.getValue().nbEvents
);
178 }, new ViewerComparator() {
180 public int compare(Viewer viewer
, Object e1
, Object e2
) {
181 StatisticsTreeNode n1
= (StatisticsTreeNode
) e1
;
182 StatisticsTreeNode n2
= (StatisticsTreeNode
) e2
;
184 return (int) (n1
.getValue().nbEvents
- n2
185 .getValue().nbEvents
);
187 }, new ColumnPercentageProvider() {
188 public double getPercentage(StatisticsTreeNode node
) {
189 StatisticsTreeNode parent
= node
;
191 parent
= parent
.getParent();
192 } while (parent
!= null
193 && parent
.getValue().nbEvents
== 0);
195 if (parent
== null) {
198 return (double) node
.getValue().nbEvents
199 / parent
.getValue().nbEvents
;
203 new ColumnData(CPU_TIME_COLUMN
, 125, SWT
.LEFT
, CPU_TIME_COLUMN_TIP
,
204 new ColumnLabelProvider() {
206 public String
getText(Object element
) {
207 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
209 if (folderLevels
.contains(node
.getKey())) {
211 } else if (node
.getParent() != null
212 && levelsWithEmptyTime
.contains(node
213 .getParent().getKey())) {
217 .format(node
.getValue().cpuTime
222 new ColumnData(CUMULATIVE_CPU_TIME_COLUMN
, 155, SWT
.LEFT
,
223 CUMULATIVE_CPU_TIME_COLUMN_TIP
, new ColumnLabelProvider() {
225 public String
getText(Object element
) {
226 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
227 if (folderLevels
.contains(node
.getKey())) {
229 } else if (node
.getParent() != null
230 && levelsWithEmptyTime
.contains(node
231 .getParent().getKey())) {
235 .format(node
.getValue().cumulativeCpuTime
240 new ColumnData(ELAPSED_TIME_COLUMN
, 100, SWT
.LEFT
,
241 ELAPSED_TIME_COLUMN_TIP
, new ColumnLabelProvider() {
243 public String
getText(Object element
) {
244 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
245 if (folderLevels
.contains(node
.getKey())) {
247 } else if (node
.getParent() != null
248 && levelsWithEmptyTime
.contains(node
249 .getParent().getKey())) {
253 .format(node
.getValue().elapsedTime
260 * Adapter TreeViewers can use to interact with StatisticsTreeNode objects.
262 * @see org.eclipse.jface.viewers.ITreeContentProvider
264 class TreeContentProvider
implements ITreeContentProvider
{
269 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang
272 public Object
[] getChildren(Object parentElement
) {
273 return ((StatisticsTreeNode
) parentElement
).getChildren().toArray();
280 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang
283 public Object
getParent(Object element
) {
284 return ((StatisticsTreeNode
) element
).getParent();
291 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang
294 public boolean hasChildren(Object element
) {
295 return ((StatisticsTreeNode
) element
).hasChildren();
302 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(
305 public Object
[] getElements(Object inputElement
) {
306 return getChildren(inputElement
);
312 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
314 public void dispose() {
321 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
322 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
325 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
329 public StatisticsView(String viewName
) {
333 public StatisticsView() {
334 this("StatisticsView");
341 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
345 public void createPartControl(Composite parent
) {
346 parent
.setLayout(new FillLayout());
348 treeViewer
= new TreeViewer(parent
, SWT
.BORDER
| SWT
.H_SCROLL
350 treeViewer
.setContentProvider(new TreeContentProvider());
351 treeViewer
.getTree().setHeaderVisible(true);
352 treeViewer
.setUseHashlookup(true);
354 for (final ColumnData columnData
: columnDataList
) {
355 final TreeViewerColumn treeColumn
= new TreeViewerColumn(
356 treeViewer
, columnData
.alignment
);
357 treeColumn
.getColumn().setText(columnData
.header
);
358 treeColumn
.getColumn().setWidth(columnData
.width
);
359 treeColumn
.getColumn().setToolTipText(columnData
.tooltip
);
360 if (columnData
.comparator
!= null) {
361 treeColumn
.getColumn().addSelectionListener(
362 new SelectionAdapter() {
364 public void widgetSelected(SelectionEvent e
) {
365 if (treeViewer
.getTree().getSortDirection() == SWT
.UP
366 || treeViewer
.getTree().getSortColumn() != treeColumn
369 .setComparator(columnData
.comparator
);
370 treeViewer
.getTree().setSortDirection(
374 .setComparator(new ViewerComparator() {
378 Object e1
, Object e2
) {
380 * columnData
.comparator
387 treeViewer
.getTree().setSortDirection(
390 treeViewer
.getTree().setSortColumn(
391 treeColumn
.getColumn());
395 treeColumn
.setLabelProvider(columnData
.labelProvider
);
398 // Handler that will draw the bar charts.
399 treeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
401 public void handleEvent(Event event
) {
402 if (columnDataList
[event
.index
].percentageProvider
!= null) {
403 StatisticsTreeNode node
= (StatisticsTreeNode
) event
.item
406 double percentage
= columnDataList
[event
.index
].percentageProvider
407 .getPercentage(node
);
408 if (percentage
== 0) {
412 if ((event
.detail
& SWT
.SELECTED
) > 0) {
413 Color oldForeground
= event
.gc
.getForeground();
414 event
.gc
.setForeground(event
.item
.getDisplay()
415 .getSystemColor(SWT
.COLOR_LIST_SELECTION
));
416 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
,
418 event
.gc
.setForeground(oldForeground
);
419 event
.detail
&= ~SWT
.SELECTED
;
422 int barWidth
= (int) ((treeViewer
.getTree().getColumn(1)
423 .getWidth() - 8) * percentage
);
424 int oldAlpha
= event
.gc
.getAlpha();
425 Color oldForeground
= event
.gc
.getForeground();
426 Color oldBackground
= event
.gc
.getBackground();
427 event
.gc
.setAlpha(64);
428 event
.gc
.setForeground(event
.item
.getDisplay()
429 .getSystemColor(SWT
.COLOR_BLUE
));
430 event
.gc
.setBackground(event
.item
.getDisplay()
431 .getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
432 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
,
434 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
,
436 event
.gc
.setForeground(oldForeground
);
437 event
.gc
.setBackground(oldBackground
);
438 event
.gc
.setAlpha(oldAlpha
);
439 event
.detail
&= ~SWT
.BACKGROUND
;
444 treeViewer
.setComparator(columnDataList
[0].comparator
);
445 treeViewer
.getTree().setSortColumn(treeViewer
.getTree().getColumn(0));
446 treeViewer
.getTree().setSortDirection(SWT
.DOWN
);
448 // Read current data if any available
449 TmfExperiment
<?
> experiment
= TmfExperiment
.getCurrentExperiment();
450 if (experiment
!= null) {
451 requestData(experiment
);
453 TraceDebug
.debug("No selected experiment information available");
458 public void dispose() {
460 if (fwaitCursor
!= null) {
461 fwaitCursor
.dispose();
465 StatisticsTreeRootFactory
.removeAll();
471 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
474 public void setFocus() {
475 treeViewer
.getTree().setFocus();
483 public AbsEventToHandlerResolver
getEventProcessor() {
484 return StatsTimeCountHandlerFactory
.getInstance();
491 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#waitCursor
495 protected void waitCursor(final boolean waitInd
) {
496 if (treeViewer
== null) {
500 Display display
= treeViewer
.getControl().getDisplay();
501 if (fwaitCursor
== null) {
502 fwaitCursor
= new Cursor(display
, SWT
.CURSOR_WAIT
);
505 // Perform the updates on the UI thread
506 display
.asyncExec(new Runnable() {
508 Cursor cursor
= null; /* indicates default */
510 cursor
= fwaitCursor
;
512 treeViewer
.getControl().setCursor(cursor
);
518 public void ModelUpdatePrep(TmfTimeRange timeRange
, boolean clearAllData
) {
519 Object input
= treeViewer
.getInput();
520 if (input
!= null && input
instanceof StatisticsTreeNode
) {
521 ((StatisticsTreeNode
) input
).reset();
522 treeViewer
.getTree().getDisplay().asyncExec(new Runnable() {
525 treeViewer
.refresh();
532 public void modelInputChanged(ILttngSyntEventRequest request
, boolean complete
) {
533 treeViewer
.getTree().getDisplay().asyncExec(new Runnable() {
536 treeViewer
.refresh();
544 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
546 * (org.eclipse.linuxtools.lttng.request.ILttngSyntEventRequest)
549 public void modelIncomplete(ILttngSyntEventRequest request
) {
550 Object input
= treeViewer
.getInput();
551 if (input
!= null && input
instanceof StatisticsTreeNode
) {
552 // The data from this experiment is invalid and shall be removed to
553 // refresh upon next selection
554 String name
= ((StatisticsTreeNode
) input
).getKey();
555 StatisticsTreeRootFactory
.removeStatTreeRoot(name
);
563 public void experimentSelected(TmfExperimentSelectedSignal
<?
extends TmfEvent
> signal
) {
564 if (signal
!= null) {
565 TmfExperiment
<?
> experiment
= signal
.getExperiment();
566 String experimentName
= experiment
.getName();
568 if (StatisticsTreeRootFactory
.containsTreeRoot(experimentName
)) {
569 // The experiment root is already present
570 StatisticsTreeNode experimentTreeNode
= StatisticsTreeRootFactory
.getStatTreeRoot(experimentName
);
572 ITmfTrace
[] traces
= experiment
.getTraces();
574 // check if there is partial data loaded in the experiment
575 int numTraces
= experiment
.getTraces().length
;
576 int numNodeTraces
= experimentTreeNode
.getNbChildren();
578 if (numTraces
== numNodeTraces
) {
580 // Detect if the experiment contains the same traces as when
581 // previously selected
582 for (int i
= 0; i
< numTraces
; i
++) {
583 String traceName
= traces
[i
].getName();
584 if (!experimentTreeNode
.containsChild(traceName
)) {
591 // no need to reload data, all traces are already loaded
592 treeViewer
.setInput(experimentTreeNode
);
598 // if the data is not available or has changed, reload it
599 requestData(experiment
);
606 private void requestData(TmfExperiment
<?
> experiment
) {
607 if (experiment
!= null) {
608 StatisticsTreeNode treeModelRoot
= StatisticsTreeRootFactory
.getStatTreeRoot(experiment
.getName());
610 // if the model has contents, clear to start over
611 if (treeModelRoot
.hasChildren()) {
612 treeModelRoot
.reset();
615 // set input to a clean data model
616 treeViewer
.setInput(treeModelRoot
);
617 TmfTimeRange experimentTRange
= experiment
.getTimeRange();
619 // send the initial request, to start filling up model
620 dataRequest(experimentTRange
, experimentTRange
, true, ExecutionType
.BACKGROUND
);
622 TraceDebug
.debug("No selected experiment information available");
630 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#displayModel
631 * (org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.model.
632 * ITmfTimeAnalysisEntry[], long, long, boolean, long, long,
636 protected void displayModel(ITmfTimeAnalysisEntry
[] items
, long startBoundTime
, long endBoundTime
,
637 boolean updateTimeBounds
, long startVisibleWindow
, long endVisibleWindow
, Object source
) {
638 // No applicable to statistics view
644 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
648 protected ParamsUpdater
getParamsUpdater() {
649 // Not applicable to statistics view
654 protected ItemContainer
<?
> getItemContainer() {
655 // Not applicable to statistics view