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