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