ccce8ea741deecede8d68f8a7f924333ea626c2e
[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.signal.TmfExperimentSelectedSignal;
40 import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler;
41 import org.eclipse.linuxtools.tmf.trace.ITmfTrace;
42 import org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.model.ITmfTimeAnalysisEntry;
43 import org.eclipse.swt.SWT;
44 import org.eclipse.swt.events.SelectionAdapter;
45 import org.eclipse.swt.events.SelectionEvent;
46 import org.eclipse.swt.graphics.Color;
47 import org.eclipse.swt.graphics.Cursor;
48 import org.eclipse.swt.graphics.Image;
49 import org.eclipse.swt.layout.FillLayout;
50 import org.eclipse.swt.widgets.Composite;
51 import org.eclipse.swt.widgets.Display;
52 import org.eclipse.swt.widgets.Event;
53 import org.eclipse.swt.widgets.Listener;
54 import org.eclipse.ui.ISharedImages;
55 import org.eclipse.ui.PlatformUI;
56
57 /**
58 * <b><u>StatisticsView</u></b>
59 * <p>
60 * The Statistics View displays statistics for traces.
61 *
62 * It is implemented according to the MVC pattern. - The model is a
63 * StatisticsTreeNode built by the State Manager. - The view is built with a
64 * TreeViewer. - The controller that keeps model and view synchronised is an
65 * observer of the model.
66 */
67 public class StatisticsView extends AbsTimeUpdateView {
68 public static final String ID = "org.eclipse.linuxtools.lttng.ui.views.statistics";
69 private TreeViewer treeViewer;
70
71 // Table column names
72 private final String LEVEL_COLUMN = "Level";
73 private final String EVENTS_COUNT_COLUMN = "Number of Events";
74 private final String CPU_TIME_COLUMN = "CPU Time";
75 private final String CUMULATIVE_CPU_TIME_COLUMN = "Cumulative CPU Time";
76 private final String ELAPSED_TIME_COLUMN = "Elapsed Time";
77
78 // Table column tooltips
79 private final String LEVEL_COLUMN_TIP = "Level at which statistics apply.";
80 private final String EVENTS_COUNT_COLUMN_TIP = "Total amount of events that are tied to given resource.";
81 private final String CPU_TIME_COLUMN_TIP = "Total amount of time the CPU was used excluding wait times(I/O, etc.) at that level.";
82 private final String CUMULATIVE_CPU_TIME_COLUMN_TIP = "Total amount of time between the first and last event excluding wait times in a level.";
83 private final String ELAPSED_TIME_COLUMN_TIP = "Total amount of time the CPU was used including wait times(I/O, etc.) at that level.";
84
85 // Level for which statistics should not be displayed.
86 private Set<String> folderLevels = new HashSet<String>(Arrays
87 .asList(new String[] { "Event Types", "Modes", "Submodes", "CPUs",
88 "Processes", "Functions" }));
89
90 // Levels for which sub-levels should not contain time-related statistics.
91 private Set<String> levelsWithEmptyTime = new HashSet<String>(Arrays
92 .asList(new String[] { "Event Types" }));
93
94 private DecimalFormat decimalFormat = new DecimalFormat("0.#########");
95 private Cursor fwaitCursor = null;
96
97 // Used to draw bar charts in columns.
98 private interface ColumnPercentageProvider {
99 public double getPercentage(StatisticsTreeNode node);
100 }
101
102 /**
103 * Contains all the information necessary to build a column of the table.
104 */
105 private class ColumnData {
106 // Name of the column.
107 public final String header;
108 // Width of the column.
109 public final int width;
110 // Alignment of the column.
111 public final int alignment;
112 // Tooltip of the column.
113 public final String tooltip;
114 // Adapts a StatisticsTreeNode into the content of it's corresponding
115 // cell for that column.
116 public final ColumnLabelProvider labelProvider;
117 // Used to sort elements of this column. Can be null.
118 public final ViewerComparator comparator;
119 // Used to draw bar charts in this column. Can be null.
120 public final ColumnPercentageProvider percentageProvider;
121
122 public ColumnData(String h, int w, int a, String t,
123 ColumnLabelProvider l, ViewerComparator c,
124 ColumnPercentageProvider p) {
125 header = h;
126 width = w;
127 alignment = a;
128 tooltip = t;
129 labelProvider = l;
130 comparator = c;
131 percentageProvider = p;
132 }
133 };
134
135 // List that will be used to create the table.
136 private ColumnData[] columnDataList = new ColumnData[] {
137 new ColumnData(LEVEL_COLUMN, 200, SWT.LEFT, LEVEL_COLUMN_TIP,
138 new ColumnLabelProvider() {
139 @Override
140 public String getText(Object element) {
141 return ((StatisticsTreeNode) element).getKey();
142 }
143
144 @Override
145 public Image getImage(Object element) {
146 StatisticsTreeNode node = (StatisticsTreeNode) element;
147 if (folderLevels.contains(node.getKey())) {
148 return PlatformUI.getWorkbench()
149 .getSharedImages().getImage(
150 ISharedImages.IMG_OBJ_FOLDER);
151 } else {
152 return PlatformUI.getWorkbench()
153 .getSharedImages().getImage(
154 ISharedImages.IMG_OBJ_ELEMENT);
155 }
156 }
157 }, new ViewerComparator() {
158 @Override
159 public int compare(Viewer viewer, Object e1, Object e2) {
160 StatisticsTreeNode n1 = (StatisticsTreeNode) e1;
161 StatisticsTreeNode n2 = (StatisticsTreeNode) e2;
162
163 return n1.getKey().compareTo(n2.getKey());
164 }
165 }, null),
166 new ColumnData(EVENTS_COUNT_COLUMN, 125, SWT.LEFT,
167 EVENTS_COUNT_COLUMN_TIP, new ColumnLabelProvider() {
168 @Override
169 public String getText(Object element) {
170 StatisticsTreeNode node = (StatisticsTreeNode) element;
171 if (!folderLevels.contains(node.getKey())) {
172 return Long.toString(node.getValue().nbEvents);
173 } else {
174 return "";
175 }
176 }
177 }, new ViewerComparator() {
178 @Override
179 public int compare(Viewer viewer, Object e1, Object e2) {
180 StatisticsTreeNode n1 = (StatisticsTreeNode) e1;
181 StatisticsTreeNode n2 = (StatisticsTreeNode) e2;
182
183 return (int) (n1.getValue().nbEvents - n2
184 .getValue().nbEvents);
185 }
186 }, new ColumnPercentageProvider() {
187 public double getPercentage(StatisticsTreeNode node) {
188 StatisticsTreeNode parent = node;
189 do {
190 parent = parent.getParent();
191 } while (parent != null
192 && parent.getValue().nbEvents == 0);
193
194 if (parent == null) {
195 return 0;
196 } else {
197 return (double) node.getValue().nbEvents
198 / parent.getValue().nbEvents;
199 }
200 }
201 }),
202 new ColumnData(CPU_TIME_COLUMN, 125, SWT.LEFT, CPU_TIME_COLUMN_TIP,
203 new ColumnLabelProvider() {
204 @Override
205 public String getText(Object element) {
206 StatisticsTreeNode node = (StatisticsTreeNode) element;
207
208 if (folderLevels.contains(node.getKey())) {
209 return "";
210 } else if (node.getParent() != null
211 && levelsWithEmptyTime.contains(node
212 .getParent().getKey())) {
213 return "";
214 } else {
215 return decimalFormat
216 .format(node.getValue().cpuTime
217 / Math.pow(10, 9));
218 }
219 }
220 }, null, null),
221 new ColumnData(CUMULATIVE_CPU_TIME_COLUMN, 155, SWT.LEFT,
222 CUMULATIVE_CPU_TIME_COLUMN_TIP, new ColumnLabelProvider() {
223 @Override
224 public String getText(Object element) {
225 StatisticsTreeNode node = (StatisticsTreeNode) element;
226 if (folderLevels.contains(node.getKey())) {
227 return "";
228 } else if (node.getParent() != null
229 && levelsWithEmptyTime.contains(node
230 .getParent().getKey())) {
231 return "";
232 } else {
233 return decimalFormat
234 .format(node.getValue().cumulativeCpuTime
235 / Math.pow(10, 9));
236 }
237 }
238 }, null, null),
239 new ColumnData(ELAPSED_TIME_COLUMN, 100, SWT.LEFT,
240 ELAPSED_TIME_COLUMN_TIP, new ColumnLabelProvider() {
241 @Override
242 public String getText(Object element) {
243 StatisticsTreeNode node = (StatisticsTreeNode) element;
244 if (folderLevels.contains(node.getKey())) {
245 return "";
246 } else if (node.getParent() != null
247 && levelsWithEmptyTime.contains(node
248 .getParent().getKey())) {
249 return "";
250 } else {
251 return decimalFormat
252 .format(node.getValue().elapsedTime
253 / Math.pow(10, 9));
254 }
255 }
256 }, null, null) };
257
258 /**
259 * Adapter TreeViewers can use to interact with StatisticsTreeNode objects.
260 *
261 * @see org.eclipse.jface.viewers.ITreeContentProvider
262 */
263 class TreeContentProvider implements ITreeContentProvider {
264 /*
265 * (non-Javadoc)
266 *
267 * @see
268 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang
269 * .Object)
270 */
271 public Object[] getChildren(Object parentElement) {
272 return ((StatisticsTreeNode) parentElement).getChildren().toArray();
273 }
274
275 /*
276 * (non-Javadoc)
277 *
278 * @see
279 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang
280 * .Object)
281 */
282 public Object getParent(Object element) {
283 return ((StatisticsTreeNode) element).getParent();
284 }
285
286 /*
287 * (non-Javadoc)
288 *
289 * @see
290 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang
291 * .Object)
292 */
293 public boolean hasChildren(Object element) {
294 return ((StatisticsTreeNode) element).hasChildren();
295 }
296
297 /*
298 * (non-Javadoc)
299 *
300 * @see
301 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(
302 * java.lang.Object)
303 */
304 public Object[] getElements(Object inputElement) {
305 return getChildren(inputElement);
306 }
307
308 /*
309 * (non-Javadoc)
310 *
311 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
312 */
313 public void dispose() {
314 }
315
316 /*
317 * (non-Javadoc)
318 *
319 * @see
320 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse
321 * .jface.viewers.Viewer, java.lang.Object, java.lang.Object)
322 */
323 // @Override
324 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
325 }
326 }
327
328 public StatisticsView(String viewName) {
329 super(viewName);
330 }
331
332 public StatisticsView() {
333 this("StatisticsView");
334 }
335
336 /*
337 * (non-Javadoc)
338 *
339 * @see
340 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
341 * .Composite)
342 */
343 @Override
344 public void createPartControl(Composite parent) {
345 parent.setLayout(new FillLayout());
346
347 treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.H_SCROLL
348 | SWT.V_SCROLL);
349 treeViewer.setContentProvider(new TreeContentProvider());
350 treeViewer.getTree().setHeaderVisible(true);
351 treeViewer.setUseHashlookup(true);
352
353 for (final ColumnData columnData : columnDataList) {
354 final TreeViewerColumn treeColumn = new TreeViewerColumn(
355 treeViewer, columnData.alignment);
356 treeColumn.getColumn().setText(columnData.header);
357 treeColumn.getColumn().setWidth(columnData.width);
358 treeColumn.getColumn().setToolTipText(columnData.tooltip);
359 if (columnData.comparator != null) {
360 treeColumn.getColumn().addSelectionListener(
361 new SelectionAdapter() {
362 @Override
363 public void widgetSelected(SelectionEvent e) {
364 if (treeViewer.getTree().getSortDirection() == SWT.UP
365 || treeViewer.getTree().getSortColumn() != treeColumn
366 .getColumn()) {
367 treeViewer
368 .setComparator(columnData.comparator);
369 treeViewer.getTree().setSortDirection(
370 SWT.DOWN);
371 } else {
372 treeViewer
373 .setComparator(new ViewerComparator() {
374 @Override
375 public int compare(
376 Viewer viewer,
377 Object e1, Object e2) {
378 return -1
379 * columnData.comparator
380 .compare(
381 viewer,
382 e1,
383 e2);
384 }
385 });
386 treeViewer.getTree().setSortDirection(
387 SWT.UP);
388 }
389 treeViewer.getTree().setSortColumn(
390 treeColumn.getColumn());
391 }
392 });
393 }
394 treeColumn.setLabelProvider(columnData.labelProvider);
395 }
396
397 // Handler that will draw the bar charts.
398 treeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
399 // @Override
400 public void handleEvent(Event event) {
401 if (columnDataList[event.index].percentageProvider != null) {
402 StatisticsTreeNode node = (StatisticsTreeNode) event.item
403 .getData();
404
405 double percentage = columnDataList[event.index].percentageProvider
406 .getPercentage(node);
407 if (percentage == 0) {
408 return;
409 }
410
411 if ((event.detail & SWT.SELECTED) > 0) {
412 Color oldForeground = event.gc.getForeground();
413 event.gc.setForeground(event.item.getDisplay()
414 .getSystemColor(SWT.COLOR_LIST_SELECTION));
415 event.gc.fillRectangle(event.x, event.y, event.width,
416 event.height);
417 event.gc.setForeground(oldForeground);
418 event.detail &= ~SWT.SELECTED;
419 }
420
421 int barWidth = (int) ((treeViewer.getTree().getColumn(1)
422 .getWidth() - 8) * percentage);
423 int oldAlpha = event.gc.getAlpha();
424 Color oldForeground = event.gc.getForeground();
425 Color oldBackground = event.gc.getBackground();
426 event.gc.setAlpha(64);
427 event.gc.setForeground(event.item.getDisplay()
428 .getSystemColor(SWT.COLOR_BLUE));
429 event.gc.setBackground(event.item.getDisplay()
430 .getSystemColor(SWT.COLOR_LIST_BACKGROUND));
431 event.gc.fillGradientRectangle(event.x, event.y, barWidth,
432 event.height, true);
433 event.gc.drawRectangle(event.x, event.y, barWidth,
434 event.height);
435 event.gc.setForeground(oldForeground);
436 event.gc.setBackground(oldBackground);
437 event.gc.setAlpha(oldAlpha);
438 event.detail &= ~SWT.BACKGROUND;
439 }
440 }
441 });
442
443 treeViewer.setComparator(columnDataList[0].comparator);
444 treeViewer.getTree().setSortColumn(treeViewer.getTree().getColumn(0));
445 treeViewer.getTree().setSortDirection(SWT.DOWN);
446
447 // Read current data if any available
448 TmfExperiment<?> experiment = TmfExperiment.getCurrentExperiment();
449 if (experiment != null) {
450 requestData(experiment);
451 } else {
452 TraceDebug.debug("No selected experiment information available");
453 }
454 }
455
456 @Override
457 public void dispose() {
458 super.dispose();
459 if (fwaitCursor != null) {
460 fwaitCursor.dispose();
461 }
462
463 // clean the model
464 StatisticsTreeRootFactory.removeAll();
465 }
466
467 /*
468 * (non-Javadoc)
469 *
470 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
471 */
472 @Override
473 public void setFocus() {
474 treeViewer.getTree().setFocus();
475 }
476
477
478 /**
479 * @return
480 */
481 public AbsEventToHandlerResolver getEventProcessor() {
482 return StatsTimeCountHandlerFactory.getInstance();
483 }
484
485 /*
486 * (non-Javadoc)
487 *
488 * @see
489 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#waitCursor
490 * (boolean)
491 */
492 protected void waitCursor(final boolean waitInd) {
493 if (treeViewer == null) {
494 return;
495 }
496
497 Display display = treeViewer.getControl().getDisplay();
498 if (fwaitCursor == null) {
499 fwaitCursor = new Cursor(display, SWT.CURSOR_WAIT);
500 }
501
502 // Perform the updates on the UI thread
503 display.asyncExec(new Runnable() {
504 public void run() {
505 Cursor cursor = null; /* indicates default */
506 if (waitInd) {
507 cursor = fwaitCursor;
508 }
509 treeViewer.getControl().setCursor(cursor);
510 }
511 });
512 }
513
514 @Override
515 public void ModelUpdatePrep(TmfTimeRange timeRange, boolean clearAllData) {
516 Object input = treeViewer.getInput();
517 if (input != null && input instanceof StatisticsTreeNode) {
518 ((StatisticsTreeNode) input).reset();
519 treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
520 // @Override
521 public void run() {
522 treeViewer.refresh();
523 }
524 });
525 }
526 }
527
528 @Override
529 public void modelInputChanged(ILttngSyntEventRequest request, boolean complete) {
530 treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
531 // @Override
532 public void run() {
533 treeViewer.refresh();
534 }
535 });
536 }
537
538 /*
539 * (non-Javadoc)
540 *
541 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
542 * modelIncomplete
543 * (org.eclipse.linuxtools.lttng.request.ILttngSyntEventRequest)
544 */
545 @Override
546 public void modelIncomplete(ILttngSyntEventRequest request) {
547 Object input = treeViewer.getInput();
548 if (input != null && input instanceof StatisticsTreeNode) {
549 // The data from this experiment is invalid and shall be removed to
550 // refresh upon next selection
551 String name = ((StatisticsTreeNode) input).getKey();
552 StatisticsTreeRootFactory.removeStatTreeRoot(name);
553 }
554 }
555
556 /**
557 * @param signal
558 */
559 @TmfSignalHandler
560 public void experimentSelected(TmfExperimentSelectedSignal<? extends TmfEvent> signal) {
561 if (signal != null) {
562 TmfExperiment<?> experiment = signal.getExperiment();
563 String experimentName = experiment.getName();
564
565 if (StatisticsTreeRootFactory.containsTreeRoot(experimentName)) {
566 // The experiment root is already present
567 StatisticsTreeNode experimentTreeNode = StatisticsTreeRootFactory.getStatTreeRoot(experimentName);
568
569 ITmfTrace[] traces = experiment.getTraces();
570
571 // check if there is partial data loaded in the experiment
572 int numTraces = experiment.getTraces().length;
573 int numNodeTraces = experimentTreeNode.getNbChildren();
574
575 if (numTraces == numNodeTraces) {
576 boolean same = true;
577 // Detect if the experiment contains the same traces as when
578 // previously selected
579 for (int i = 0; i < numTraces; i++) {
580 String traceName = traces[i].getName();
581 if (!experimentTreeNode.containsChild(traceName)) {
582 same = false;
583 break;
584 }
585 }
586
587 if (same) {
588 // no need to reload data, all traces are already loaded
589 treeViewer.setInput(experimentTreeNode);
590 return;
591 }
592 }
593 }
594
595 // if the data is not available or has changed, reload it
596 requestData(experiment);
597 }
598 }
599
600 /**
601 * @param experiment
602 */
603 private void requestData(TmfExperiment<?> experiment) {
604 if (experiment != null) {
605 StatisticsTreeNode treeModelRoot = StatisticsTreeRootFactory.getStatTreeRoot(experiment.getName());
606
607 // if the model has contents, clear to start over
608 if (treeModelRoot.hasChildren()) {
609 treeModelRoot.reset();
610 }
611
612 // set input to a clean data model
613 treeViewer.setInput(treeModelRoot);
614 TmfTimeRange experimentTRange = experiment.getTimeRange();
615
616 // send the initial request, to start filling up model
617 dataRequest(experimentTRange, experimentTRange, true);
618 } else {
619 TraceDebug.debug("No selected experiment information available");
620 }
621 }
622
623 /*
624 * (non-Javadoc)
625 *
626 * @see
627 * org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#displayModel
628 * (org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.model.
629 * ITmfTimeAnalysisEntry[], long, long, boolean, long, long,
630 * java.lang.Object)
631 */
632 @Override
633 protected void displayModel(ITmfTimeAnalysisEntry[] items, long startBoundTime, long endBoundTime,
634 boolean updateTimeBounds, long startVisibleWindow, long endVisibleWindow, Object source) {
635 // No applicable to statistics view
636 }
637
638 /*
639 * (non-Javadoc)
640 *
641 * @see org.eclipse.linuxtools.lttng.ui.views.common.AbsTimeUpdateView#
642 * getParamsUpdater()
643 */
644 @Override
645 protected ParamsUpdater getParamsUpdater() {
646 // Not applicable to statistics view
647 return null;
648 }
649
650 @Override
651 protected ItemContainer<?> getItemContainer() {
652 // Not applicable to statistics view
653 return null;
654 }
655 }
This page took 0.043778 seconds and 4 git commands to generate.