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
.Collections
;
19 import java
.util
.HashSet
;
20 import java
.util
.Iterator
;
21 import java
.util
.LinkedList
;
24 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
25 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
26 import org
.eclipse
.jface
.viewers
.TreeViewer
;
27 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
28 import org
.eclipse
.jface
.viewers
.Viewer
;
29 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
30 import org
.eclipse
.linuxtools
.lttng
.control
.LttngCoreProviderFactory
;
31 import org
.eclipse
.linuxtools
.lttng
.request
.ILttngSyntEventRequest
;
32 import org
.eclipse
.linuxtools
.lttng
.state
.evProcessor
.AbsEventToHandlerResolver
;
33 import org
.eclipse
.linuxtools
.lttng
.ui
.TraceDebug
;
34 import org
.eclipse
.linuxtools
.lttng
.ui
.model
.trange
.ItemContainer
;
35 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.common
.AbsTimeUpdateView
;
36 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.common
.ParamsUpdater
;
37 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.evProcessor
.StatsTimeCountHandlerFactory
;
38 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.model
.StatisticsTreeNode
;
39 import org
.eclipse
.linuxtools
.lttng
.ui
.views
.statistics
.model
.StatisticsTreeRootFactory
;
40 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
41 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
42 import org
.eclipse
.linuxtools
.tmf
.experiment
.TmfExperiment
;
43 import org
.eclipse
.linuxtools
.tmf
.request
.ITmfDataRequest
.ExecutionType
;
44 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfExperimentSelectedSignal
;
45 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalHandler
;
46 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfTrace
;
47 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.timeAnalysis
.model
.ITmfTimeAnalysisEntry
;
48 import org
.eclipse
.swt
.SWT
;
49 import org
.eclipse
.swt
.events
.SelectionAdapter
;
50 import org
.eclipse
.swt
.events
.SelectionEvent
;
51 import org
.eclipse
.swt
.graphics
.Color
;
52 import org
.eclipse
.swt
.graphics
.Cursor
;
53 import org
.eclipse
.swt
.graphics
.Image
;
54 import org
.eclipse
.swt
.layout
.FillLayout
;
55 import org
.eclipse
.swt
.widgets
.Composite
;
56 import org
.eclipse
.swt
.widgets
.Display
;
57 import org
.eclipse
.swt
.widgets
.Event
;
58 import org
.eclipse
.swt
.widgets
.Listener
;
59 import org
.eclipse
.ui
.ISharedImages
;
60 import org
.eclipse
.ui
.PlatformUI
;
63 * <b><u>StatisticsView</u></b>
65 * The Statistics View displays statistics for traces.
67 * It is implemented according to the MVC pattern. - The model is a
68 * StatisticsTreeNode built by the State Manager. - The view is built with a
69 * TreeViewer. - The controller that keeps model and view synchronised is an
70 * observer of the model.
72 public class StatisticsView
extends AbsTimeUpdateView
{
73 public static final String ID
= "org.eclipse.linuxtools.lttng.ui.views.statistics"; //$NON-NLS-1$
74 private TreeViewer treeViewer
;
77 private final String LEVEL_COLUMN
= Messages
.StatisticsView_LevelColumn
;
78 private final String EVENTS_COUNT_COLUMN
= Messages
.StatisticsView_NbEventsColumn
;
79 private final String CPU_TIME_COLUMN
= Messages
.StatisticsView_CPUTimeColumn
;
80 private final String CUMULATIVE_CPU_TIME_COLUMN
= Messages
.StatisticsView_CumCPUTimeColumn
;
81 private final String ELAPSED_TIME_COLUMN
= Messages
.StatisticsView_ElapsedTimeColumn
;
83 // Table column tooltips
84 private final String LEVEL_COLUMN_TIP
= Messages
.StatisticsView_LevelColumnTip
;
85 private final String EVENTS_COUNT_COLUMN_TIP
= Messages
.StatisticsView_NbEventsTip
;
86 private final String CPU_TIME_COLUMN_TIP
= Messages
.StatisticsView_CPUTimeTip
;
87 private final String CUMULATIVE_CPU_TIME_COLUMN_TIP
= Messages
.StatisticsView_CumCPUTimeTip
;
88 private final String ELAPSED_TIME_COLUMN_TIP
= Messages
.StatisticsView_ElapsedTimeTip
;
90 // Level for which statistics should not be displayed.
91 private Set
<String
> folderLevels
= new HashSet
<String
>(Arrays
.asList(
92 new String
[] { "Event Types", "Modes", "Submodes", "CPUs", "Processes", "Functions" })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
94 // Levels for which sub-levels should not contain time-related statistics.
95 private Set
<String
> levelsWithEmptyTime
= new HashSet
<String
>(Arrays
.asList(
96 new String
[] { "Event Types" })); //$NON-NLS-1$
98 private DecimalFormat decimalFormat
= new DecimalFormat("0.#########"); //$NON-NLS-1$
99 private Cursor fwaitCursor
= null;
101 private static final Long STATS_INPUT_CHANGED_REFRESH
= 5000L;
103 // Used to draw bar charts in columns.
104 private interface ColumnPercentageProvider
{
105 public double getPercentage(StatisticsTreeNode node
);
109 * Contains all the information necessary to build a column of the table.
111 private class ColumnData
{
112 // Name of the column.
113 public final String header
;
114 // Width of the column.
115 public final int width
;
116 // Alignment of the column.
117 public final int alignment
;
118 // Tooltip of the column.
119 public final String tooltip
;
120 // Adapts a StatisticsTreeNode into the content of it's corresponding
121 // cell for that column.
122 public final ColumnLabelProvider labelProvider
;
123 // Used to sort elements of this column. Can be null.
124 public final ViewerComparator comparator
;
125 // Used to draw bar charts in this column. Can be null.
126 public final ColumnPercentageProvider percentageProvider
;
128 public ColumnData(String h
, int w
, int a
, String t
,
129 ColumnLabelProvider l
, ViewerComparator c
,
130 ColumnPercentageProvider p
) {
137 percentageProvider
= p
;
141 // List that will be used to create the table.
142 private ColumnData
[] columnDataList
= new ColumnData
[] {
143 new ColumnData(LEVEL_COLUMN
, 200, SWT
.LEFT
, LEVEL_COLUMN_TIP
,
144 new ColumnLabelProvider() {
146 public String
getText(Object element
) {
147 return ((StatisticsTreeNode
) element
).getKey();
151 public Image
getImage(Object element
) {
152 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
153 if (folderLevels
.contains(node
.getKey())) {
154 return PlatformUI
.getWorkbench()
155 .getSharedImages().getImage(
156 ISharedImages
.IMG_OBJ_FOLDER
);
158 return PlatformUI
.getWorkbench()
159 .getSharedImages().getImage(
160 ISharedImages
.IMG_OBJ_ELEMENT
);
163 }, new ViewerComparator() {
165 public int compare(Viewer viewer
, Object e1
, Object e2
) {
166 StatisticsTreeNode n1
= (StatisticsTreeNode
) e1
;
167 StatisticsTreeNode n2
= (StatisticsTreeNode
) e2
;
169 return n1
.getKey().compareTo(n2
.getKey());
172 new ColumnData(EVENTS_COUNT_COLUMN
, 125, SWT
.LEFT
,
173 EVENTS_COUNT_COLUMN_TIP
, new ColumnLabelProvider() {
175 public String
getText(Object element
) {
176 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
177 if (!folderLevels
.contains(node
.getKey())) {
178 return Long
.toString(node
.getValue().nbEvents
);
180 return ""; //$NON-NLS-1$
183 }, new ViewerComparator() {
185 public int compare(Viewer viewer
, Object e1
, Object e2
) {
186 StatisticsTreeNode n1
= (StatisticsTreeNode
) e1
;
187 StatisticsTreeNode n2
= (StatisticsTreeNode
) e2
;
189 return (int) (n1
.getValue().nbEvents
- n2
190 .getValue().nbEvents
);
192 }, new ColumnPercentageProvider() {
194 public double getPercentage(StatisticsTreeNode node
) {
195 StatisticsTreeNode parent
= node
;
197 parent
= parent
.getParent();
198 } while (parent
!= null
199 && parent
.getValue().nbEvents
== 0);
201 if (parent
== null) {
204 return (double) node
.getValue().nbEvents
205 / parent
.getValue().nbEvents
;
209 new ColumnData(CPU_TIME_COLUMN
, 125, SWT
.LEFT
, CPU_TIME_COLUMN_TIP
,
210 new ColumnLabelProvider() {
212 public String
getText(Object element
) {
213 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
215 if (folderLevels
.contains(node
.getKey())) {
216 return ""; //$NON-NLS-1$
217 } else if (node
.getParent() != null
218 && levelsWithEmptyTime
.contains(node
219 .getParent().getKey())) {
220 return ""; //$NON-NLS-1$
223 .format(node
.getValue().cpuTime
228 new ColumnData(CUMULATIVE_CPU_TIME_COLUMN
, 155, SWT
.LEFT
,
229 CUMULATIVE_CPU_TIME_COLUMN_TIP
, new ColumnLabelProvider() {
231 public String
getText(Object element
) {
232 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
233 if (folderLevels
.contains(node
.getKey())) {
234 return ""; //$NON-NLS-1$
235 } else if (node
.getParent() != null
236 && levelsWithEmptyTime
.contains(node
237 .getParent().getKey())) {
238 return ""; //$NON-NLS-1$
241 .format(node
.getValue().cumulativeCpuTime
246 new ColumnData(ELAPSED_TIME_COLUMN
, 100, SWT
.LEFT
,
247 ELAPSED_TIME_COLUMN_TIP
, new ColumnLabelProvider() {
249 public String
getText(Object element
) {
250 StatisticsTreeNode node
= (StatisticsTreeNode
) element
;
251 if (folderLevels
.contains(node
.getKey())) {
252 return ""; //$NON-NLS-1$
253 } else if (node
.getParent() != null
254 && levelsWithEmptyTime
.contains(node
255 .getParent().getKey())) {
256 return ""; //$NON-NLS-1$
259 .format(node
.getValue().elapsedTime
266 * Adapter TreeViewers can use to interact with StatisticsTreeNode objects.
268 * @see org.eclipse.jface.viewers.ITreeContentProvider
270 class TreeContentProvider
implements ITreeContentProvider
{
275 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang
279 public Object
[] getChildren(Object parentElement
) {
280 return ((StatisticsTreeNode
) parentElement
).getChildren().toArray();
287 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang
291 public Object
getParent(Object element
) {
292 return ((StatisticsTreeNode
) element
).getParent();
299 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang
303 public boolean hasChildren(Object element
) {
304 return ((StatisticsTreeNode
) element
).hasChildren();
311 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(
315 public Object
[] getElements(Object inputElement
) {
316 return getChildren(inputElement
);
322 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
325 public void dispose() {
332 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
333 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
337 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
341 public StatisticsView(String viewName
) {
345 private static final String STATISTICS_VIEW
= "StatisticsView"; //$NON-NLS-1$
346 public StatisticsView() {
347 this(STATISTICS_VIEW
);
354 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
358 public void createPartControl(Composite parent
) {
359 parent
.setLayout(new FillLayout());
361 treeViewer
= new TreeViewer(parent
, SWT
.BORDER
| SWT
.H_SCROLL
363 treeViewer
.setContentProvider(new TreeContentProvider());
364 treeViewer
.getTree().setHeaderVisible(true);
365 treeViewer
.setUseHashlookup(true);
367 for (final ColumnData columnData
: columnDataList
) {
368 final TreeViewerColumn treeColumn
= new TreeViewerColumn(
369 treeViewer
, columnData
.alignment
);
370 treeColumn
.getColumn().setText(columnData
.header
);
371 treeColumn
.getColumn().setWidth(columnData
.width
);
372 treeColumn
.getColumn().setToolTipText(columnData
.tooltip
);
373 if (columnData
.comparator
!= null) {
374 treeColumn
.getColumn().addSelectionListener(
375 new SelectionAdapter() {
377 public void widgetSelected(SelectionEvent e
) {
378 if (treeViewer
.getTree().getSortDirection() == SWT
.UP
379 || treeViewer
.getTree().getSortColumn() != treeColumn
382 .setComparator(columnData
.comparator
);
383 treeViewer
.getTree().setSortDirection(
387 .setComparator(new ViewerComparator() {
391 Object e1
, Object e2
) {
393 * columnData
.comparator
400 treeViewer
.getTree().setSortDirection(
403 treeViewer
.getTree().setSortColumn(
404 treeColumn
.getColumn());
408 treeColumn
.setLabelProvider(columnData
.labelProvider
);
411 // Handler that will draw the bar charts.
412 treeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
415 public void handleEvent(Event event
) {
416 if (columnDataList
[event
.index
].percentageProvider
!= null) {
417 StatisticsTreeNode node
= (StatisticsTreeNode
) event
.item
420 double percentage
= columnDataList
[event
.index
].percentageProvider
421 .getPercentage(node
);
422 if (percentage
== 0) {
426 if ((event
.detail
& SWT
.SELECTED
) > 0) {
427 Color oldForeground
= event
.gc
.getForeground();
428 event
.gc
.setForeground(event
.item
.getDisplay()
429 .getSystemColor(SWT
.COLOR_LIST_SELECTION
));
430 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
,
432 event
.gc
.setForeground(oldForeground
);
433 event
.detail
&= ~SWT
.SELECTED
;
436 int barWidth
= (int) ((treeViewer
.getTree().getColumn(1)
437 .getWidth() - 8) * percentage
);
438 int oldAlpha
= event
.gc
.getAlpha();
439 Color oldForeground
= event
.gc
.getForeground();
440 Color oldBackground
= event
.gc
.getBackground();
441 event
.gc
.setAlpha(64);
442 event
.gc
.setForeground(event
.item
.getDisplay()
443 .getSystemColor(SWT
.COLOR_BLUE
));
444 event
.gc
.setBackground(event
.item
.getDisplay()
445 .getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
446 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
,
448 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
,
450 event
.gc
.setForeground(oldForeground
);
451 event
.gc
.setBackground(oldBackground
);
452 event
.gc
.setAlpha(oldAlpha
);
453 event
.detail
&= ~SWT
.BACKGROUND
;
458 treeViewer
.setComparator(columnDataList
[0].comparator
);
459 treeViewer
.getTree().setSortColumn(treeViewer
.getTree().getColumn(0));
460 treeViewer
.getTree().setSortDirection(SWT
.DOWN
);
462 // Read current data if any available
463 TmfExperiment
<?
> experiment
= TmfExperiment
.getCurrentExperiment();
464 if (experiment
!= null) {
465 requestData(experiment
);
467 TraceDebug
.debug("No selected experiment information available"); //$NON-NLS-1$
472 public void dispose() {
474 if (fwaitCursor
!= null) {
475 fwaitCursor
.dispose();
479 StatisticsTreeRootFactory
.removeAll();
485 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
488 public void setFocus() {
489 treeViewer
.getTree().setFocus();
495 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#getInputChangedRefresh()
498 protected Long
getInputChangedRefresh() {
499 return STATS_INPUT_CHANGED_REFRESH
;
506 public AbsEventToHandlerResolver
getEventProcessor() {
507 return StatsTimeCountHandlerFactory
.getInstance();
514 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#waitCursor
518 protected void waitCursor(final boolean waitInd
) {
519 if ((treeViewer
== null) || (treeViewer
.getTree().isDisposed())) {
523 Display display
= treeViewer
.getControl().getDisplay();
524 if (fwaitCursor
== null) {
525 fwaitCursor
= new Cursor(display
, SWT
.CURSOR_WAIT
);
528 // Perform the updates on the UI thread
529 display
.asyncExec(new Runnable() {
532 if ((treeViewer
!= null) && (!treeViewer
.getTree().isDisposed())) {
533 Cursor cursor
= null; /* indicates default */
535 cursor
= fwaitCursor
;
537 treeViewer
.getControl().setCursor(cursor
);
544 public void ModelUpdatePrep(TmfTimeRange timeRange
, boolean clearAllData
) {
545 Object input
= treeViewer
.getInput();
546 if ((input
!= null) && (input
instanceof StatisticsTreeNode
) && (!treeViewer
.getTree().isDisposed())) {
547 ((StatisticsTreeNode
) input
).reset();
548 treeViewer
.getTree().getDisplay().asyncExec(new Runnable() {
552 if (!treeViewer
.getTree().isDisposed())
553 treeViewer
.refresh();
560 public void modelInputChanged(ILttngSyntEventRequest request
, boolean complete
) {
561 // Ignore update if disposed
562 if (treeViewer
.getTree().isDisposed()) return;
564 if(TraceDebug
.isSV() && complete
) {
567 TmfExperiment
<?
> experiment
= TmfExperiment
.getCurrentExperiment();
568 if(experiment
!= null) {
569 StatisticsTreeNode node
= StatisticsTreeRootFactory
.getStatTreeRoot(experiment
.getName());
570 printRecursively(node
);
575 treeViewer
.getTree().getDisplay().asyncExec(new Runnable() {
579 if (!treeViewer
.getTree().isDisposed())
580 treeViewer
.refresh();
584 private static int level
= 0;
585 private void printRecursively(StatisticsTreeNode node
) {
586 String tab
= ""; //$NON-NLS-1$
587 for (int i
= 0; i
< level
; i
++) {
588 tab
+= "\t"; //$NON-NLS-1$
591 TraceDebug
.traceSV(tab
+ node
.getContent());
592 if (node
.hasChildren()) {
593 LinkedList
<StatisticsTreeNode
> childreen
= (LinkedList
<StatisticsTreeNode
>)node
.getChildren();
594 Collections
.sort(childreen
);
595 for (Iterator
<StatisticsTreeNode
> iterator
= childreen
.iterator(); iterator
.hasNext();) {
596 StatisticsTreeNode statisticsTreeNode
= (StatisticsTreeNode
) iterator
.next();
597 printRecursively(statisticsTreeNode
);
606 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
608 * (org.eclipse.linuxtools.lttng.request.ILttngSyntEventRequest)
611 public void modelIncomplete(ILttngSyntEventRequest request
) {
612 Object input
= treeViewer
.getInput();
613 if (input
!= null && input
instanceof StatisticsTreeNode
) {
614 // The data from this experiment is invalid and shall be removed to
615 // refresh upon next selection
616 String name
= request
.getExperimentName();
617 StatisticsTreeRootFactory
.removeStatTreeRoot(name
);
625 public void experimentSelected(TmfExperimentSelectedSignal
<?
extends TmfEvent
> signal
) {
626 if (signal
!= null) {
627 TmfExperiment
<?
> experiment
= signal
.getExperiment();
628 String experimentName
= experiment
.getName();
630 if (StatisticsTreeRootFactory
.containsTreeRoot(experimentName
)) {
631 // The experiment root is already present
632 StatisticsTreeNode experimentTreeNode
= StatisticsTreeRootFactory
.getStatTreeRoot(experimentName
);
634 ITmfTrace
[] traces
= experiment
.getTraces();
636 // check if there is partial data loaded in the experiment
637 int numTraces
= experiment
.getTraces().length
;
638 int numNodeTraces
= experimentTreeNode
.getNbChildren();
640 if (numTraces
== numNodeTraces
) {
642 // Detect if the experiment contains the same traces as when
643 // previously selected
644 for (int i
= 0; i
< numTraces
; i
++) {
645 String traceName
= traces
[i
].getName();
646 if (!experimentTreeNode
.containsChild(traceName
)) {
653 // no need to reload data, all traces are already loaded
654 treeViewer
.setInput(experimentTreeNode
);
660 // if the data is not available or has changed, reload it
661 requestData(experiment
);
668 private void requestData(TmfExperiment
<?
> experiment
) {
669 if (experiment
!= null) {
670 StatisticsTreeNode treeModelRoot
= StatisticsTreeRootFactory
.getStatTreeRoot(experiment
.getName());
672 // if the model has contents, clear to start over
673 if (treeModelRoot
.hasChildren()) {
674 treeModelRoot
.reset();
677 // set input to a clean data model
678 treeViewer
.setInput(treeModelRoot
);
679 TmfTimeRange experimentTRange
= experiment
.getTimeRange();
681 // send the initial request, to start filling up model
682 dataRequest(experimentTRange
, experimentTRange
, true, ExecutionType
.BACKGROUND
);
684 TraceDebug
.debug("No selected experiment information available"); //$NON-NLS-1$
692 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#displayModel
693 * (org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.model.
694 * ITmfTimeAnalysisEntry[], long, long, boolean, long, long,
698 protected void displayModel(ITmfTimeAnalysisEntry
[] items
, long startBoundTime
, long endBoundTime
,
699 boolean updateTimeBounds
, long startVisibleWindow
, long endVisibleWindow
, Object source
) {
700 // No applicable to statistics view
706 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
710 protected ParamsUpdater
getParamsUpdater() {
711 // Not applicable to statistics view
716 protected ItemContainer
<?
> getItemContainer() {
717 // Not applicable to statistics view
723 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#getProviderId()
726 protected int getProviderId() {
727 return LttngCoreProviderFactory
.STATISTICS_LTTNG_SYTH_EVENT_PROVIDER
;