78185496b9d7480a55ce1b23c8a48b8148b76f2b
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / viewers / statistics / TmfStatisticsViewer.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 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 * Mathieu Denis <mathieu.denis@polymtl.ca> - Initial API and implementation
11 * Alexandre Montplaisir - Port to ITmfStatistics provider
12 * Patrick Tasse - Support selection range
13 * Bernd Hufmann - Fix range selection updates
14 *******************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.ui.viewers.statistics;
17
18 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
19
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.jface.viewers.TreeViewer;
30 import org.eclipse.jface.viewers.TreeViewerColumn;
31 import org.eclipse.jface.viewers.Viewer;
32 import org.eclipse.jface.viewers.ViewerComparator;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.events.SelectionAdapter;
35 import org.eclipse.swt.events.SelectionEvent;
36 import org.eclipse.swt.graphics.Color;
37 import org.eclipse.swt.graphics.Cursor;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Control;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Listener;
43 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
44 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
45 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
47 import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal;
48 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
49 import org.eclipse.tracecompass.tmf.core.statistics.ITmfStatistics;
50 import org.eclipse.tracecompass.tmf.core.statistics.TmfStatisticsEventTypesModule;
51 import org.eclipse.tracecompass.tmf.core.statistics.TmfStatisticsModule;
52 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
53 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
54 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
55 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
56 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
57 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
58 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
59 import org.eclipse.tracecompass.tmf.ui.TmfUiRefreshHandler;
60 import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
61 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfBaseColumnData;
62 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfBaseColumnDataProvider;
63 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfStatisticsFormatter;
64 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfStatisticsTree;
65 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfStatisticsTreeManager;
66 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfStatisticsTreeNode;
67 import org.eclipse.tracecompass.tmf.ui.viewers.statistics.model.TmfTreeContentProvider;
68
69 /**
70 * A basic viewer to display statistics in the statistics view.
71 *
72 * It is linked to a single ITmfTrace until its disposal.
73 *
74 * @author Mathieu Denis
75 */
76 public class TmfStatisticsViewer extends TmfViewer {
77
78 /** Timestamp scale used for all statistics (nanosecond) */
79 private static final byte TIME_SCALE = ITmfTimestamp.NANOSECOND_SCALE;
80
81 /** The delay (in ms) between each update in live-reading mode */
82 private static final long LIVE_UPDATE_DELAY = 1000;
83
84 /** The actual tree viewer to display */
85 private TreeViewer fTreeViewer;
86
87 /** The statistics tree linked to this viewer */
88 private TmfStatisticsTree fStatisticsData;
89
90 /** Update range synchronization object */
91 private final Object fStatisticsRangeUpdateSyncObj = new Object();
92
93 /** The trace that is displayed by this viewer */
94 private ITmfTrace fTrace;
95
96 /** Indicates to process all events */
97 private boolean fProcessAll;
98
99 /** View instance counter (for multiple statistics views) */
100 private static int fCountInstance = 0;
101
102 /** Number of this instance. Used as an instance ID. */
103 private int fInstanceNb;
104
105 /** Object to store the cursor while waiting for the trace to load */
106 private Cursor fWaitCursor = null;
107
108 /**
109 * Counts the number of times waitCursor() has been called. It avoids
110 * removing the waiting cursor, since there may be multiple requests running
111 * at the same time.
112 */
113 private int fWaitCursorCount = 0;
114
115 /** Tells to send a time range request when the trace gets updated. */
116 private boolean fSendRangeRequest = true;
117
118 private final Map<ITmfTrace, Job> fUpdateJobsPartial = new HashMap<>();
119 private final Map<ITmfTrace, Job> fUpdateJobsGlobal = new HashMap<>();
120
121 private TmfTimeRange fTimeRange;
122
123 private TmfTimeRange fTimeRangePartial;
124
125 /**
126 * Create a basic statistics viewer. To be used in conjunction with
127 * {@link TmfStatisticsViewer#init(Composite, String, ITmfTrace)}
128 *
129 * @param parent
130 * The parent composite that will hold the viewer
131 * @param viewerName
132 * The name that will be assigned to this viewer
133 * @param trace
134 * The trace that is displayed by this viewer
135 * @see TmfComponent
136 */
137 public TmfStatisticsViewer(Composite parent, String viewerName, ITmfTrace trace) {
138 init(parent, viewerName, trace);
139 }
140
141 /**
142 * Initialize the statistics viewer.
143 *
144 * @param parent
145 * The parent component of the viewer.
146 * @param viewerName
147 * The name to give to the viewer.
148 * @param trace
149 * The trace that will be displayed by the viewer.
150 */
151 public void init(Composite parent, String viewerName, ITmfTrace trace) {
152 super.init(parent, viewerName);
153 // Increment a counter to make sure the tree ID is unique.
154 fCountInstance++;
155 fInstanceNb = fCountInstance;
156 fTrace = trace;
157
158 // The viewer will process all events if he is assigned to an experiment
159 fProcessAll = (trace instanceof TmfExperiment);
160
161 initContent(parent);
162 initInput();
163 }
164
165 @Override
166 public void dispose() {
167 super.dispose();
168 if (fWaitCursor != null) {
169 fWaitCursor.dispose();
170 }
171
172 for (Job j : fUpdateJobsGlobal.values()) {
173 j.cancel();
174 }
175
176 for (Job j : fUpdateJobsPartial.values()) {
177 j.cancel();
178 }
179
180 // Clean the model for this viewer
181 TmfStatisticsTreeManager.removeStatTreeRoot(getTreeID());
182 }
183
184 // ------------------------------------------------------------------------
185 // Signal handlers
186 // ------------------------------------------------------------------------
187
188 /**
189 * Handles the signal about new trace range.
190 *
191 * @param signal
192 * The trace range updated signal
193 */
194 @TmfSignalHandler
195 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
196 ITmfTrace trace = signal.getTrace();
197 // validate
198 if (!isListeningTo(trace)) {
199 return;
200 }
201
202 synchronized (fStatisticsRangeUpdateSyncObj) {
203 // Sends the time range request only once from this method.
204 if (fSendRangeRequest) {
205 fSendRangeRequest = false;
206
207 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
208 TmfTimeRange timeRange = ctx.getSelectionRange();
209 requestTimeRangeData(trace, timeRange);
210 }
211 }
212 requestData(trace, signal.getRange());
213 }
214
215 /**
216 * Handles the time synch updated signal. It updates the time range
217 * statistics.
218 *
219 * @param signal
220 * Contains the information about the new selected time range.
221 */
222 @TmfSignalHandler
223 public void timeSynchUpdated(TmfTimeSynchSignal signal) {
224 if (fTrace == null) {
225 return;
226 }
227 ITmfTimestamp begin = signal.getBeginTime();
228 ITmfTimestamp end = signal.getEndTime();
229 TmfTimeRange timeRange = new TmfTimeRange(begin, end);
230 requestTimeRangeData(fTrace, timeRange);
231 }
232
233 // ------------------------------------------------------------------------
234 // Class methods
235 // ------------------------------------------------------------------------
236
237 /*
238 * Returns the primary control associated with this viewer.
239 *
240 * @return the SWT control which displays this viewer's content
241 */
242 @Override
243 public Control getControl() {
244 return fTreeViewer.getControl();
245 }
246
247 /**
248 * Get the input of the viewer.
249 *
250 * @return an object representing the input of the statistics viewer.
251 */
252 public Object getInput() {
253 return fTreeViewer.getInput();
254 }
255
256 /**
257 * This method can be overridden to implement another way of representing
258 * the statistics data and to retrieve the information for display.
259 *
260 * @return a TmfStatisticsData object.
261 */
262 public TmfStatisticsTree getStatisticData() {
263 if (fStatisticsData == null) {
264 fStatisticsData = new TmfStatisticsTree();
265 }
266 return fStatisticsData;
267 }
268
269 /**
270 * Returns a unique ID based on name to be associated with the statistics
271 * tree for this viewer. For a same name, it will always return the same ID.
272 *
273 * @return a unique statistics tree ID.
274 */
275 public String getTreeID() {
276 return getName() + fInstanceNb;
277 }
278
279 @Override
280 public void refresh() {
281 final Control viewerControl = getControl();
282 // Ignore update if disposed
283 if (viewerControl.isDisposed()) {
284 return;
285 }
286
287 TmfUiRefreshHandler.getInstance().queueUpdate(this, new Runnable() {
288 @Override
289 public void run() {
290 if (!viewerControl.isDisposed()) {
291 fTreeViewer.refresh();
292 }
293 }
294 });
295 }
296
297 /**
298 * Will force a request on the partial event count if one is needed.
299 */
300 public void sendPartialRequestOnNextUpdate() {
301 synchronized (fStatisticsRangeUpdateSyncObj) {
302 fSendRangeRequest = true;
303 }
304 }
305
306 /**
307 * Focus on the statistics tree of the viewer
308 */
309 public void setFocus() {
310 fTreeViewer.getTree().setFocus();
311 }
312
313 /**
314 * Cancels the request if it is not already completed
315 *
316 * @param request
317 * The request to be canceled
318 */
319 protected void cancelOngoingRequest(ITmfEventRequest request) {
320 if (request != null && !request.isCompleted()) {
321 request.cancel();
322 }
323 }
324
325 /**
326 * This method can be overridden to change the representation of the data in
327 * the columns.
328 *
329 * @return An object of type {@link TmfBaseColumnDataProvider}.
330 */
331 protected TmfBaseColumnDataProvider getColumnDataProvider() {
332 return new TmfBaseColumnDataProvider();
333 }
334
335 /**
336 * Initialize the content that will be drawn in this viewer
337 *
338 * @param parent
339 * The parent of the control to create
340 */
341 protected void initContent(Composite parent) {
342 final List<TmfBaseColumnData> columnDataList = getColumnDataProvider().getColumnData();
343
344 fTreeViewer = new TreeViewer(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
345 fTreeViewer.setContentProvider(new TmfTreeContentProvider());
346 fTreeViewer.getTree().setHeaderVisible(true);
347 fTreeViewer.setUseHashlookup(true);
348
349 // Creates the columns defined by the column data provider
350 for (final TmfBaseColumnData columnData : columnDataList) {
351 final TreeViewerColumn treeColumn = new TreeViewerColumn(fTreeViewer, columnData.getAlignment());
352 treeColumn.getColumn().setText(columnData.getHeader());
353 treeColumn.getColumn().setWidth(columnData.getWidth());
354 treeColumn.getColumn().setToolTipText(columnData.getTooltip());
355
356 // If is dummy column
357 if (columnData == columnDataList.get(TmfBaseColumnDataProvider.StatsColumn.DUMMY.getIndex())) {
358 treeColumn.getColumn().setResizable(false);
359 }
360
361 // A comparator is defined.
362 if (columnData.getComparator() != null) {
363 // Adds a listener on the columns header for sorting purpose.
364 treeColumn.getColumn().addSelectionListener(new SelectionAdapter() {
365
366 private ViewerComparator reverseComparator;
367
368 @Override
369 public void widgetSelected(SelectionEvent e) {
370 // Initializes the reverse comparator once.
371 if (reverseComparator == null) {
372 reverseComparator = new ViewerComparator() {
373 @Override
374 public int compare(Viewer viewer, Object e1, Object e2) {
375 return -1 * columnData.getComparator().compare(viewer, e1, e2);
376 }
377 };
378 }
379
380 if (fTreeViewer.getTree().getSortDirection() == SWT.UP
381 || fTreeViewer.getTree().getSortColumn() != treeColumn.getColumn()) {
382 /*
383 * Puts the descendant order if the old order was up
384 * or if the selected column has changed.
385 */
386 fTreeViewer.setComparator(columnData.getComparator());
387 fTreeViewer.getTree().setSortDirection(SWT.DOWN);
388 } else {
389 /*
390 * Puts the ascendant ordering if the selected
391 * column hasn't changed.
392 */
393 fTreeViewer.setComparator(reverseComparator);
394 fTreeViewer.getTree().setSortDirection(SWT.UP);
395 }
396 fTreeViewer.getTree().setSortColumn(treeColumn.getColumn());
397 }
398 });
399 }
400 treeColumn.setLabelProvider(columnData.getLabelProvider());
401 }
402
403 // Handler that will draw the percentages and the bar charts.
404 fTreeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
405 @Override
406 public void handleEvent(Event event) {
407 if (columnDataList.get(event.index).getPercentageProvider() != null) {
408
409 TmfStatisticsTreeNode node = (TmfStatisticsTreeNode) event.item.getData();
410
411 // If node is hidden, exit immediately.
412 if (TmfBaseColumnDataProvider.HIDDEN_FOLDER_LEVELS.contains(node.getName())) {
413 return;
414 }
415
416 // Otherwise, get percentage and draw bar and text if applicable.
417 double percentage = columnDataList.get(event.index).getPercentageProvider().getPercentage(node);
418
419 // The item is selected.
420 if ((event.detail & SWT.SELECTED) > 0) {
421 // Draws our own background to avoid overwriting the bar.
422 event.gc.fillRectangle(event.x, event.y, event.width, event.height);
423 event.detail &= ~SWT.SELECTED;
424 }
425
426 // Drawing the percentage text
427 // if events are present in top node
428 // and the current node is not the top node
429 // and if is total or partial events column.
430 // If not, exit the method.
431 if (!((event.index == TmfBaseColumnDataProvider.StatsColumn.TOTAL.getIndex() || event.index == TmfBaseColumnDataProvider.StatsColumn.PARTIAL.getIndex())
432 && node != node.getTop())) {
433 return;
434 }
435
436 long eventValue = event.index == TmfBaseColumnDataProvider.StatsColumn.TOTAL.getIndex() ?
437 node.getTop().getValues().getTotal() : node.getTop().getValues().getPartial();
438
439 if (eventValue != 0) {
440
441 int oldAlpha = event.gc.getAlpha();
442 Color oldForeground = event.gc.getForeground();
443 Color oldBackground = event.gc.getBackground();
444
445 // Bar to draw
446 if (percentage != 0) {
447 /*
448 * Draws a transparent gradient rectangle from the
449 * color of foreground and background.
450 */
451 int barWidth = (int) ((fTreeViewer.getTree().getColumn(event.index).getWidth() - 8) * percentage);
452 event.gc.setAlpha(64);
453 event.gc.setForeground(event.item.getDisplay().getSystemColor(SWT.COLOR_BLUE));
454 event.gc.setBackground(event.item.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
455 event.gc.fillGradientRectangle(event.x, event.y, barWidth, event.height, true);
456 event.gc.drawRectangle(event.x, event.y, barWidth, event.height);
457
458 // Restore old values
459 event.gc.setBackground(oldBackground);
460 event.gc.setAlpha(oldAlpha);
461 event.detail &= ~SWT.BACKGROUND;
462
463 }
464
465 String percentageText = TmfStatisticsFormatter.toPercentageText(percentage);
466 String absoluteNumberText = TmfStatisticsFormatter.toColumnData(node, TmfBaseColumnDataProvider.StatsColumn.getColumn(event.index));
467
468 if (event.width > event.gc.stringExtent(percentageText).x + event.gc.stringExtent(absoluteNumberText).x) {
469 int textHeight = event.gc.stringExtent(percentageText).y;
470 event.gc.setForeground(event.item.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
471 event.gc.drawText(percentageText, event.x, event.y + (event.height - textHeight) / 2, true);
472 }
473
474 // Restores old values
475 event.gc.setForeground(oldForeground);
476
477 }
478 }
479 }
480
481 });
482
483 // Initializes the comparator parameters
484 fTreeViewer.setComparator(columnDataList.get(0).getComparator());
485 fTreeViewer.getTree().setSortColumn(fTreeViewer.getTree().getColumn(0));
486 fTreeViewer.getTree().setSortDirection(SWT.DOWN);
487 }
488
489 /**
490 * Initializes the input for the tree viewer.
491 */
492 protected void initInput() {
493 String treeID = getTreeID();
494 TmfStatisticsTreeNode statisticsTreeNode;
495 if (TmfStatisticsTreeManager.containsTreeRoot(treeID)) {
496 // The statistics root is already present
497 statisticsTreeNode = TmfStatisticsTreeManager.getStatTreeRoot(treeID);
498
499 // Checks if the trace is already in the statistics tree.
500 int numNodeTraces = statisticsTreeNode.getNbChildren();
501
502 Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace);
503 int numTraces = traces.size();
504
505 if (numTraces == numNodeTraces) {
506 boolean same = true;
507 /*
508 * Checks if the experiment contains the same traces as when
509 * previously selected.
510 */
511 for (ITmfTrace trace : traces) {
512 String traceName = trace.getName();
513 if (!statisticsTreeNode.containsChild(traceName)) {
514 same = false;
515 break;
516 }
517 }
518
519 if (same) {
520 // No need to reload data, all traces are already loaded
521 fTreeViewer.setInput(statisticsTreeNode);
522 return;
523 }
524 // Clears the old content to start over
525 statisticsTreeNode.reset();
526 }
527 } else {
528 // Creates a new tree
529 statisticsTreeNode = TmfStatisticsTreeManager.addStatsTreeRoot(treeID, getStatisticData());
530 }
531
532 // Sets the input to a clean data model
533 fTreeViewer.setInput(statisticsTreeNode);
534 }
535
536 /**
537 * Tells if the viewer is listening to a trace.
538 *
539 * @param trace
540 * The trace that the viewer may be listening
541 * @return true if the viewer is listening to the trace, false otherwise
542 */
543 protected boolean isListeningTo(ITmfTrace trace) {
544 if (fProcessAll || trace == fTrace) {
545 return true;
546 }
547 return false;
548 }
549
550 /**
551 * Called when an trace request has been completed successfully.
552 *
553 * @param global
554 * Tells if the request is a global or time range (partial)
555 * request.
556 */
557 protected void modelComplete(boolean global) {
558 refresh();
559 waitCursor(false);
560 }
561
562 /**
563 * Called when an trace request has failed or has been cancelled.
564 *
565 * @param isGlobalRequest
566 * Tells if the request is a global or time range (partial)
567 * request.
568 */
569 protected void modelIncomplete(boolean isGlobalRequest) {
570 if (isGlobalRequest) { // Clean the global statistics
571 /*
572 * No need to reset the global number of events, since the index of
573 * the last requested event is known.
574 */
575 } else { // Clean the partial statistics
576 resetTimeRangeValue();
577 }
578 refresh();
579 waitCursor(false);
580 }
581
582 /**
583 * Sends the request to the trace for the whole trace
584 *
585 * @param trace
586 * The trace used to send the request
587 * @param timeRange
588 * The range to request to the trace
589 */
590 protected void requestData(final ITmfTrace trace, final TmfTimeRange timeRange) {
591 buildStatisticsTree(trace, timeRange, true);
592 }
593
594 /**
595 * Sends the time range request from the trace
596 *
597 * @param trace
598 * The trace used to send the request
599 * @param timeRange
600 * The range to request to the trace
601 */
602 protected void requestTimeRangeData(final ITmfTrace trace, final TmfTimeRange timeRange) {
603 buildStatisticsTree(trace, timeRange, false);
604 }
605
606 /**
607 * Requests all the data of the trace to the state system which contains
608 * information about the statistics.
609 *
610 * Since the viewer may be listening to multiple traces, it may receive an
611 * experiment rather than a single trace. The filtering is done with the
612 * method {@link #isListeningTo(String trace)}.
613 *
614 * @param trace
615 * The trace for which a request must be done
616 * @param timeRange
617 * The time range that will be requested to the state system
618 * @param isGlobal
619 * Tells if the request is for the global event count or the
620 * partial one.
621 */
622 private void buildStatisticsTree(final ITmfTrace trace, final TmfTimeRange timeRange, final boolean isGlobal) {
623 final TmfStatisticsTree statsData = TmfStatisticsTreeManager.getStatTree(getTreeID());
624 if (statsData == null) {
625 return;
626 }
627
628 Map<ITmfTrace, Job> updateJobs;
629 if (isGlobal) {
630 updateJobs = fUpdateJobsGlobal;
631 fTimeRange = timeRange;
632 } else {
633 updateJobs = fUpdateJobsPartial;
634 fTimeRangePartial = timeRange;
635 }
636
637 for (ITmfTrace aTrace : TmfTraceManager.getTraceSet(trace)) {
638 aTrace = checkNotNull(aTrace);
639 if (!isListeningTo(aTrace)) {
640 continue;
641 }
642
643 /* Retrieve the statistics object */
644 final TmfStatisticsModule statsMod = TmfTraceUtils.getAnalysisModuleOfClass(aTrace, TmfStatisticsModule.class, TmfStatisticsModule.ID);
645 if (statsMod == null) {
646 /* No statistics module available for this trace */
647 continue;
648 }
649
650 Job job = updateJobs.get(aTrace);
651 if (job == null) {
652 job = new UpdateJob("Statistics update", aTrace, isGlobal, statsMod); //$NON-NLS-1$
653 updateJobs.put(aTrace, job);
654 job.setSystem(true);
655 job.schedule();
656 }
657 }
658 }
659
660 private class UpdateJob extends Job {
661
662 private final ITmfTrace fJobTrace;
663 private final boolean fIsGlobal;
664 private final TmfStatisticsModule fStatsMod;
665
666 private UpdateJob(String name, ITmfTrace trace, boolean isGlobal, TmfStatisticsModule statsMod) {
667 super(name);
668 fJobTrace = trace;
669 fIsGlobal = isGlobal;
670 fStatsMod = statsMod;
671 }
672
673 @Override
674 protected IStatus run(IProgressMonitor monitor) {
675
676 /* Wait until the analysis is ready to be queried */
677 fStatsMod.waitForInitialization();
678 ITmfStatistics stats = fStatsMod.getStatistics();
679 if (stats == null) {
680 /* It should have worked, but didn't */
681 throw new IllegalStateException();
682 }
683
684 /*
685 * TODO Eventually this could be exposed through the
686 * TmfStateSystemAnalysisModule directly.
687 */
688 ITmfStateSystem ss = fStatsMod.getStateSystem(TmfStatisticsEventTypesModule.ID);
689 if (ss == null) {
690 /* It should be instantiated after the
691 * statsMod.waitForInitialization() above. */
692 throw new IllegalStateException();
693 }
694
695
696 /*
697 * Periodically update the statistics while they are
698 * being built (or, if the back-end is already completely
699 * built, it will skip over the while() immediately.
700 */
701 long start = 0;
702 long end = 0;
703 boolean finished = false;
704 do {
705 if (monitor.isCanceled()) {
706 return Status.CANCEL_STATUS;
707 }
708 finished = ss.waitUntilBuilt(LIVE_UPDATE_DELAY);
709
710 TmfTimeRange localtimeRange = fIsGlobal ? fTimeRange : fTimeRangePartial;
711 /*
712 * The generic statistics are stored in nanoseconds, so
713 * we must make sure the time range is scaled correctly.
714 */
715 start = localtimeRange.getStartTime().normalize(0, TIME_SCALE).getValue();
716 end = localtimeRange.getEndTime().normalize(0, TIME_SCALE).getValue();
717
718 Map<String, Long> map = stats.getEventTypesInRange(start, end);
719 updateStats(map);
720 } while (!finished);
721
722 /* Query one last time for the final values */
723 Map<String, Long> map = stats.getEventTypesInRange(start, end);
724 updateStats(map);
725
726 /*
727 * Remove job from map so that new range selection updates can
728 * be processed.
729 */
730 Map<ITmfTrace, Job> updateJobs;
731 if (fIsGlobal) {
732 updateJobs = fUpdateJobsGlobal;
733 } else {
734 updateJobs = fUpdateJobsPartial;
735 }
736 updateJobs.remove(fJobTrace);
737 return Status.OK_STATUS;
738 }
739
740 /*
741 * Update statistics for a given trace
742 */
743 private void updateStats(Map<String, Long> eventsPerType) {
744
745 final TmfStatisticsTree statsData = TmfStatisticsTreeManager.getStatTree(getTreeID());
746 if (statsData == null) {
747 /* The stat tree has been disposed, abort mission. */
748 return;
749 }
750
751 Map<String, Long> map = eventsPerType;
752 String name = fJobTrace.getName();
753
754
755 /*
756 * "Global", "partial", "total", etc., it's all very confusing...
757 *
758 * The base view shows the total count for the trace and for
759 * each even types, organized in columns like this:
760 *
761 * | Global | Time range |
762 * trace name | A | B |
763 * Event Type | | |
764 * <event 1> | C | D |
765 * <event 2> | ... | ... |
766 * ... | | |
767 *
768 * Here, we called the cells like this:
769 * A : GlobalTotal
770 * B : TimeRangeTotal
771 * C : GlobalTypeCount(s)
772 * D : TimeRangeTypeCount(s)
773 */
774
775 /* Fill in an the event counts (either cells C or D) */
776 for (Map.Entry<String, Long> entry : map.entrySet()) {
777 statsData.setTypeCount(name, entry.getKey(), fIsGlobal, entry.getValue());
778 }
779
780 /*
781 * Calculate the totals (cell A or B, depending if isGlobal). We will
782 * use the results of the previous request instead of sending another
783 * one.
784 */
785 long globalTotal = 0;
786 for (long val : map.values()) {
787 globalTotal += val;
788 }
789 statsData.setTotal(name, fIsGlobal, globalTotal);
790
791 modelComplete(fIsGlobal);
792 }
793 }
794
795 /**
796 * Resets the number of events within the time range
797 */
798 protected void resetTimeRangeValue() {
799 TmfStatisticsTreeNode treeModelRoot = TmfStatisticsTreeManager.getStatTreeRoot(getTreeID());
800 if (treeModelRoot != null && treeModelRoot.hasChildren()) {
801 treeModelRoot.resetTimeRangeValue();
802 }
803 }
804
805 /**
806 * When the trace is loading the cursor will be different so the user knows
807 * that the processing is not finished yet.
808 *
809 * Calls to this method are stacked.
810 *
811 * @param waitRequested
812 * Indicates if we need to show the waiting cursor, or the
813 * default one.
814 */
815 protected void waitCursor(final boolean waitRequested) {
816 if ((fTreeViewer == null) || (fTreeViewer.getTree().isDisposed())) {
817 return;
818 }
819
820 boolean needsUpdate = false;
821 Display display = fTreeViewer.getControl().getDisplay();
822 if (waitRequested) {
823 fWaitCursorCount++;
824 if (fWaitCursor == null) { // The cursor hasn't been initialized yet
825 fWaitCursor = new Cursor(display, SWT.CURSOR_WAIT);
826 }
827 if (fWaitCursorCount == 1) { // The cursor is not in waiting mode
828 needsUpdate = true;
829 }
830 } else {
831 if (fWaitCursorCount > 0) { // The cursor is in waiting mode
832 fWaitCursorCount--;
833 if (fWaitCursorCount == 0) { // No more reason to wait
834 // Put back the default cursor
835 needsUpdate = true;
836 }
837 }
838 }
839
840 if (needsUpdate) {
841 // Performs the updates on the UI thread
842 display.asyncExec(new Runnable() {
843 @Override
844 public void run() {
845 if ((fTreeViewer != null)
846 && (!fTreeViewer.getTree().isDisposed())) {
847 Cursor cursor = null; // indicates default
848 if (waitRequested) {
849 cursor = fWaitCursor;
850 }
851 fTreeViewer.getControl().setCursor(cursor);
852 }
853 }
854 });
855 }
856 }
857 }
This page took 0.055549 seconds and 4 git commands to generate.