2010-09-15 Francois Chouinard <fchouinard@gmail.com> Contribution for Bug287563
[deliverable/tracecompass.git] / org.eclipse.linuxtools.lttng.ui / src / org / eclipse / linuxtools / lttng / ui / views / statistics / StatisticsView.java
1 /*******************************************************************************
2 * Copyright (c) 2009 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Yann N. Dauphin (dhaemon@gmail.com) - Implementation
11 * Francois Chouinard (fchouinard@gmail.com) - Initial API
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.lttng.ui.views.statistics;
15
16 import java.text.DecimalFormat;
17 import java.util.Arrays;
18 import java.util.HashSet;
19 import java.util.Set;
20
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;
57
58 /**
59 * <b><u>StatisticsView</u></b>
60 * <p>
61 * The Statistics View displays statistics for traces.
62 *
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.
67 */
68 public class StatisticsView extends AbsTimeUpdateView {
69 public static final String ID = "org.eclipse.linuxtools.lttng.ui.views.statistics";
70 private TreeViewer treeViewer;
71
72 // Table column names
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";
78
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.";
85
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" }));
90
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" }));
94
95 private DecimalFormat decimalFormat = new DecimalFormat("0.#########");
96 private Cursor fwaitCursor = null;
97
98 // Used to draw bar charts in columns.
99 private interface ColumnPercentageProvider {
100 public double getPercentage(StatisticsTreeNode node);
101 }
102
103 /**
104 * Contains all the information necessary to build a column of the table.
105 */
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;
122
123 public ColumnData(String h, int w, int a, String t,
124 ColumnLabelProvider l, ViewerComparator c,
125 ColumnPercentageProvider p) {
126 header = h;
127 width = w;
128 alignment = a;
129 tooltip = t;
130 labelProvider = l;
131 comparator = c;
132 percentageProvider = p;
133 }
134 };
135
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() {
140 @Override
141 public String getText(Object element) {
142 return ((StatisticsTreeNode) element).getKey();
143 }
144
145 @Override
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);
152 } else {
153 return PlatformUI.getWorkbench()
154 .getSharedImages().getImage(
155 ISharedImages.IMG_OBJ_ELEMENT);
156 }
157 }
158 }, new ViewerComparator() {
159 @Override
160 public int compare(Viewer viewer, Object e1, Object e2) {
161 StatisticsTreeNode n1 = (StatisticsTreeNode) e1;
162 StatisticsTreeNode n2 = (StatisticsTreeNode) e2;
163
164 return n1.getKey().compareTo(n2.getKey());
165 }
166 }, null),
167 new ColumnData(EVENTS_COUNT_COLUMN, 125, SWT.LEFT,
168 EVENTS_COUNT_COLUMN_TIP, new ColumnLabelProvider() {
169 @Override
170 public String getText(Object element) {
171 StatisticsTreeNode node = (StatisticsTreeNode) element;
172 if (!folderLevels.contains(node.getKey())) {
173 return Long.toString(node.getValue().nbEvents);
174 } else {
175 return "";
176 }
177 }
178 }, new ViewerComparator() {
179 @Override
180 public int compare(Viewer viewer, Object e1, Object e2) {
181 StatisticsTreeNode n1 = (StatisticsTreeNode) e1;
182 StatisticsTreeNode n2 = (StatisticsTreeNode) e2;
183
184 return (int) (n1.getValue().nbEvents - n2
185 .getValue().nbEvents);
186 }
187 }, new ColumnPercentageProvider() {
188 public double getPercentage(StatisticsTreeNode node) {
189 StatisticsTreeNode parent = node;
190 do {
191 parent = parent.getParent();
192 } while (parent != null
193 && parent.getValue().nbEvents == 0);
194
195 if (parent == null) {
196 return 0;
197 } else {
198 return (double) node.getValue().nbEvents
199 / parent.getValue().nbEvents;
200 }
201 }
202 }),
203 new ColumnData(CPU_TIME_COLUMN, 125, SWT.LEFT, CPU_TIME_COLUMN_TIP,
204 new ColumnLabelProvider() {
205 @Override
206 public String getText(Object element) {
207 StatisticsTreeNode node = (StatisticsTreeNode) element;
208
209 if (folderLevels.contains(node.getKey())) {
210 return "";
211 } else if (node.getParent() != null
212 && levelsWithEmptyTime.contains(node
213 .getParent().getKey())) {
214 return "";
215 } else {
216 return decimalFormat
217 .format(node.getValue().cpuTime
218 / Math.pow(10, 9));
219 }
220 }
221 }, null, null),
222 new ColumnData(CUMULATIVE_CPU_TIME_COLUMN, 155, SWT.LEFT,
223 CUMULATIVE_CPU_TIME_COLUMN_TIP, new ColumnLabelProvider() {
224 @Override
225 public String getText(Object element) {
226 StatisticsTreeNode node = (StatisticsTreeNode) element;
227 if (folderLevels.contains(node.getKey())) {
228 return "";
229 } else if (node.getParent() != null
230 && levelsWithEmptyTime.contains(node
231 .getParent().getKey())) {
232 return "";
233 } else {
234 return decimalFormat
235 .format(node.getValue().cumulativeCpuTime
236 / Math.pow(10, 9));
237 }
238 }
239 }, null, null),
240 new ColumnData(ELAPSED_TIME_COLUMN, 100, SWT.LEFT,
241 ELAPSED_TIME_COLUMN_TIP, new ColumnLabelProvider() {
242 @Override
243 public String getText(Object element) {
244 StatisticsTreeNode node = (StatisticsTreeNode) element;
245 if (folderLevels.contains(node.getKey())) {
246 return "";
247 } else if (node.getParent() != null
248 && levelsWithEmptyTime.contains(node
249 .getParent().getKey())) {
250 return "";
251 } else {
252 return decimalFormat
253 .format(node.getValue().elapsedTime
254 / Math.pow(10, 9));
255 }
256 }
257 }, null, null) };
258
259 /**
260 * Adapter TreeViewers can use to interact with StatisticsTreeNode objects.
261 *
262 * @see org.eclipse.jface.viewers.ITreeContentProvider
263 */
264 class TreeContentProvider implements ITreeContentProvider {
265 /*
266 * (non-Javadoc)
267 *
268 * @see
269 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang
270 * .Object)
271 */
272 public Object[] getChildren(Object parentElement) {
273 return ((StatisticsTreeNode) parentElement).getChildren().toArray();
274 }
275
276 /*
277 * (non-Javadoc)
278 *
279 * @see
280 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang
281 * .Object)
282 */
283 public Object getParent(Object element) {
284 return ((StatisticsTreeNode) element).getParent();
285 }
286
287 /*
288 * (non-Javadoc)
289 *
290 * @see
291 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang
292 * .Object)
293 */
294 public boolean hasChildren(Object element) {
295 return ((StatisticsTreeNode) element).hasChildren();
296 }
297
298 /*
299 * (non-Javadoc)
300 *
301 * @see
302 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(
303 * java.lang.Object)
304 */
305 public Object[] getElements(Object inputElement) {
306 return getChildren(inputElement);
307 }
308
309 /*
310 * (non-Javadoc)
311 *
312 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
313 */
314 public void dispose() {
315 }
316
317 /*
318 * (non-Javadoc)
319 *
320 * @see
321 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
322 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
323 */
324 // @Override
325 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
326 }
327 }
328
329 public StatisticsView(String viewName) {
330 super(viewName);
331 }
332
333 public StatisticsView() {
334 this("StatisticsView");
335 }
336
337 /*
338 * (non-Javadoc)
339 *
340 * @see
341 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
342 * .Composite)
343 */
344 @Override
345 public void createPartControl(Composite parent) {
346 parent.setLayout(new FillLayout());
347
348 treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.H_SCROLL
349 | SWT.V_SCROLL);
350 treeViewer.setContentProvider(new TreeContentProvider());
351 treeViewer.getTree().setHeaderVisible(true);
352 treeViewer.setUseHashlookup(true);
353
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() {
363 @Override
364 public void widgetSelected(SelectionEvent e) {
365 if (treeViewer.getTree().getSortDirection() == SWT.UP
366 || treeViewer.getTree().getSortColumn() != treeColumn
367 .getColumn()) {
368 treeViewer
369 .setComparator(columnData.comparator);
370 treeViewer.getTree().setSortDirection(
371 SWT.DOWN);
372 } else {
373 treeViewer
374 .setComparator(new ViewerComparator() {
375 @Override
376 public int compare(
377 Viewer viewer,
378 Object e1, Object e2) {
379 return -1
380 * columnData.comparator
381 .compare(
382 viewer,
383 e1,
384 e2);
385 }
386 });
387 treeViewer.getTree().setSortDirection(
388 SWT.UP);
389 }
390 treeViewer.getTree().setSortColumn(
391 treeColumn.getColumn());
392 }
393 });
394 }
395 treeColumn.setLabelProvider(columnData.labelProvider);
396 }
397
398 // Handler that will draw the bar charts.
399 treeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
400 // @Override
401 public void handleEvent(Event event) {
402 if (columnDataList[event.index].percentageProvider != null) {
403 StatisticsTreeNode node = (StatisticsTreeNode) event.item
404 .getData();
405
406 double percentage = columnDataList[event.index].percentageProvider
407 .getPercentage(node);
408 if (percentage == 0) {
409 return;
410 }
411
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,
417 event.height);
418 event.gc.setForeground(oldForeground);
419 event.detail &= ~SWT.SELECTED;
420 }
421
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,
433 event.height, true);
434 event.gc.drawRectangle(event.x, event.y, barWidth,
435 event.height);
436 event.gc.setForeground(oldForeground);
437 event.gc.setBackground(oldBackground);
438 event.gc.setAlpha(oldAlpha);
439 event.detail &= ~SWT.BACKGROUND;
440 }
441 }
442 });
443
444 treeViewer.setComparator(columnDataList[0].comparator);
445 treeViewer.getTree().setSortColumn(treeViewer.getTree().getColumn(0));
446 treeViewer.getTree().setSortDirection(SWT.DOWN);
447
448 // Read current data if any available
449 TmfExperiment<?> experiment = TmfExperiment.getCurrentExperiment();
450 if (experiment != null) {
451 requestData(experiment);
452 } else {
453 TraceDebug.debug("No selected experiment information available");
454 }
455 }
456
457 @Override
458 public void dispose() {
459 super.dispose();
460 if (fwaitCursor != null) {
461 fwaitCursor.dispose();
462 }
463
464 // clean the model
465 StatisticsTreeRootFactory.removeAll();
466 }
467
468 /*
469 * (non-Javadoc)
470 *
471 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
472 */
473 @Override
474 public void setFocus() {
475 treeViewer.getTree().setFocus();
476 }
477
478
479 /**
480 * @return
481 */
482 @Override
483 public AbsEventToHandlerResolver getEventProcessor() {
484 return StatsTimeCountHandlerFactory.getInstance();
485 }
486
487 /*
488 * (non-Javadoc)
489 *
490 * @see
491 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#waitCursor
492 * (boolean)
493 */
494 @Override
495 protected void waitCursor(final boolean waitInd) {
496 if (treeViewer == null) {
497 return;
498 }
499
500 Display display = treeViewer.getControl().getDisplay();
501 if (fwaitCursor == null) {
502 fwaitCursor = new Cursor(display, SWT.CURSOR_WAIT);
503 }
504
505 // Perform the updates on the UI thread
506 display.asyncExec(new Runnable() {
507 public void run() {
508 Cursor cursor = null; /* indicates default */
509 if (waitInd) {
510 cursor = fwaitCursor;
511 }
512 treeViewer.getControl().setCursor(cursor);
513 }
514 });
515 }
516
517 @Override
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() {
523 // @Override
524 public void run() {
525 treeViewer.refresh();
526 }
527 });
528 }
529 }
530
531 @Override
532 public void modelInputChanged(ILttngSyntEventRequest request, boolean complete) {
533 treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
534 // @Override
535 public void run() {
536 treeViewer.refresh();
537 }
538 });
539 }
540
541 /*
542 * (non-Javadoc)
543 *
544 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
545 * modelIncomplete
546 * (org.eclipse.linuxtools.lttng.request.ILttngSyntEventRequest)
547 */
548 @Override
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);
556 }
557 }
558
559 /**
560 * @param signal
561 */
562 @TmfSignalHandler
563 public void experimentSelected(TmfExperimentSelectedSignal<? extends TmfEvent> signal) {
564 if (signal != null) {
565 TmfExperiment<?> experiment = signal.getExperiment();
566 String experimentName = experiment.getName();
567
568 if (StatisticsTreeRootFactory.containsTreeRoot(experimentName)) {
569 // The experiment root is already present
570 StatisticsTreeNode experimentTreeNode = StatisticsTreeRootFactory.getStatTreeRoot(experimentName);
571
572 ITmfTrace[] traces = experiment.getTraces();
573
574 // check if there is partial data loaded in the experiment
575 int numTraces = experiment.getTraces().length;
576 int numNodeTraces = experimentTreeNode.getNbChildren();
577
578 if (numTraces == numNodeTraces) {
579 boolean same = true;
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)) {
585 same = false;
586 break;
587 }
588 }
589
590 if (same) {
591 // no need to reload data, all traces are already loaded
592 treeViewer.setInput(experimentTreeNode);
593 return;
594 }
595 }
596 }
597
598 // if the data is not available or has changed, reload it
599 requestData(experiment);
600 }
601 }
602
603 /**
604 * @param experiment
605 */
606 private void requestData(TmfExperiment<?> experiment) {
607 if (experiment != null) {
608 StatisticsTreeNode treeModelRoot = StatisticsTreeRootFactory.getStatTreeRoot(experiment.getName());
609
610 // if the model has contents, clear to start over
611 if (treeModelRoot.hasChildren()) {
612 treeModelRoot.reset();
613 }
614
615 // set input to a clean data model
616 treeViewer.setInput(treeModelRoot);
617 TmfTimeRange experimentTRange = experiment.getTimeRange();
618
619 // send the initial request, to start filling up model
620 dataRequest(experimentTRange, experimentTRange, true, ExecutionType.BACKGROUND);
621 } else {
622 TraceDebug.debug("No selected experiment information available");
623 }
624 }
625
626 /*
627 * (non-Javadoc)
628 *
629 * @see
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,
633 * java.lang.Object)
634 */
635 @Override
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
639 }
640
641 /*
642 * (non-Javadoc)
643 *
644 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
645 * getParamsUpdater()
646 */
647 @Override
648 protected ParamsUpdater getParamsUpdater() {
649 // Not applicable to statistics view
650 return null;
651 }
652
653 @Override
654 protected ItemContainer<?> getItemContainer() {
655 // Not applicable to statistics view
656 return null;
657 }
658 }
This page took 0.045515 seconds and 5 git commands to generate.