1 /*******************************************************************************
2 * Copyright (c) 2011, 2012 Ericsson
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
10 * Mathieu Denis <mathieu.denis@polymtl.ca> - Generalized version based on LTTng
11 * Bernd Hufmann - Updated to use trace reference in TmfEvent and streaming
12 * Mathieu Denis - New request added to update the statistics from the selected time range
13 * Mathieu Denis - Generalization of the view to instantiate a viewer specific to a trace type
15 *******************************************************************************/
17 package org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
;
19 import java
.lang
.reflect
.Constructor
;
20 import java
.lang
.reflect
.InvocationTargetException
;
22 import org
.eclipse
.core
.resources
.IResource
;
23 import org
.eclipse
.core
.runtime
.CoreException
;
24 import org
.eclipse
.core
.runtime
.IConfigurationElement
;
25 import org
.eclipse
.core
.runtime
.Platform
;
26 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Activator
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.TmfCommonConstants
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfTimeRange
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfTimestamp
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfDataRequest
.ExecutionType
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfEventRequest
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentDisposedSignal
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentRangeUpdatedSignal
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentSelectedSignal
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentUpdatedSignal
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfRangeSynchSignal
;
37 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
38 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
39 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfExperiment
;
40 import org
.eclipse
.linuxtools
.tmf
.ui
.project
.model
.TmfTraceType
;
41 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.TmfStatisticsViewer
;
42 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfStatisticsTreeNode
;
43 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfStatisticsTreeRootFactory
;
44 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.TmfView
;
45 import org
.eclipse
.swt
.widgets
.Composite
;
46 import org
.eclipse
.swt
.widgets
.Control
;
47 import org
.osgi
.framework
.Bundle
;
50 * The generic Statistics View displays statistics for any kind of traces.
52 * It is implemented according to the MVC pattern. - The model is a
53 * TmfStatisticsTreeNode built by the State Manager. - The view is built with a
54 * TreeViewer. - The controller that keeps model and view synchronized is an
55 * observer of the model.
58 * @author Mathieu Denis
60 public class TmfStatisticsView
extends TmfView
{
63 * The ID correspond to the package in which this class is embedded
65 public static final String ID
= "org.eclipse.linuxtools.tmf.ui.views.statistics"; //$NON-NLS-1$
70 public static final String TMF_STATISTICS_VIEW
= "StatisticsView"; //$NON-NLS-1$
73 * Stores the request to the experiment
75 protected ITmfEventRequest fRequest
= null;
78 * The viewer that builds the columns to show the statistics
80 private TmfStatisticsViewer fStatsViewer
;
83 * The initial window span (in nanoseconds)
87 public static final long INITIAL_WINDOW_SPAN
= (1L * 100 * 1000 * 1000); // .1sec
90 * Timestamp scale (nanosecond)
94 public static final byte TIME_SCALE
= -9;
97 * Stores a reference to the parent composite of this view
99 private Composite fParent
;
102 * Stores a reference to the experiment
104 private TmfExperiment fExperiment
;
107 * Flag to force request the data from trace
109 protected boolean fRequestData
= false;
112 * Default PAGE_SIZE for background requests
114 protected static final int PAGE_SIZE
= 50000;
117 * Stores the ranged request to the experiment
120 protected ITmfEventRequest fRequestRange
= null;
123 * Update synchronization parameter (used for streaming): Update busy
126 protected boolean fStatisticsUpdateBusy
= false;
129 * Update synchronization parameter (used for streaming): Update pending
132 protected boolean fStatisticsUpdatePending
= false;
135 * Update synchronization parameter (used for streaming): Pending Update
138 protected TmfTimeRange fStatisticsUpdateRange
= null;
141 * Update synchronization object.
143 protected final Object fStatisticsUpdateSyncObj
= new Object();
146 * Constructor of a statistics view.
148 * @param viewName The name to give to the view.
150 public TmfStatisticsView(String viewName
) {
155 * Default constructor.
157 public TmfStatisticsView() {
158 this(TMF_STATISTICS_VIEW
);
165 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
168 public void createPartControl(Composite parent
) {
170 TmfExperiment currentExperiment
= TmfExperiment
.getCurrentExperiment();
171 // Read current data if any available
172 if (currentExperiment
!= null) {
174 // Insert the statistics data into the tree
175 TmfExperimentSelectedSignal signal
= new TmfExperimentSelectedSignal(this, currentExperiment
);
176 experimentSelected(signal
);
179 fStatsViewer
= createStatisticsViewer();
181 * Updates the experiment field only at the end because
182 * experimentSelected signal verifies the old selected experiment to
183 * avoid reloading the same trace.
185 fExperiment
= currentExperiment
;
189 * Handles the signal about disposal of the current experiment.
192 * The disposed signal
195 public void experimentDisposed(TmfExperimentDisposedSignal signal
) {
196 if (signal
.getExperiment() != TmfExperiment
.getCurrentExperiment()) {
201 * Make sure there is no request running before removing the statistics
204 cancelOngoingRequest(fRequestRange
);
205 cancelOngoingRequest(fRequest
);
209 * Handler called when an experiment is selected. Checks if the experiment
210 * has changed and requests the selected experiment if it has not yet been
214 * Contains the information about the selection.
217 public void experimentSelected(TmfExperimentSelectedSignal signal
) {
218 if (signal
!= null) {
219 // Does not reload the same trace if already opened
220 if (fExperiment
== null
221 || signal
.getExperiment().toString().compareTo(fExperiment
.toString()) != 0) {
223 * Dispose the current viewer and adapt the new one to the trace
224 * type of the experiment selected
226 if (fStatsViewer
!= null) {
227 fStatsViewer
.dispose();
229 // Update the current experiment
230 fExperiment
= signal
.getExperiment();
231 fStatsViewer
= createStatisticsViewer();
234 String experimentName
= fExperiment
.getName();
235 String treeID
= fStatsViewer
.getTreeID(experimentName
);
237 setInput(treeID
, fExperiment
.getTraces());
240 requestData(fExperiment
, fExperiment
.getTimeRange());
241 fRequestData
= false;
248 * Initialize the viewer with the information received.
251 * The unique ID of the tree that is returned by
252 * {@link TmfStatisticsViewer#getTreeID(String)}
254 * The list of the traces to add in the tree.
257 public void setInput(String treeID
, ITmfTrace
[] traces
) {
258 if (TmfStatisticsTreeRootFactory
.containsTreeRoot(treeID
)) {
259 // The experiment root is already present
260 TmfStatisticsTreeNode experimentTreeNode
= TmfStatisticsTreeRootFactory
.getStatTreeRoot(treeID
);
262 // check if there is partial data loaded in the experiment
263 int numTraces
= traces
.length
;
264 int numNodeTraces
= experimentTreeNode
.getNbChildren();
266 if (numTraces
== numNodeTraces
) {
269 * Detect if the experiment contains the same traces as when
270 * previously selected
272 for (int i
= 0; i
< numTraces
; i
++) {
273 String traceName
= traces
[i
].getName();
274 if (!experimentTreeNode
.containsChild(traceName
)) {
281 // no need to reload data, all traces are already loaded
282 fStatsViewer
.setInput(experimentTreeNode
);
284 resetUpdateSynchronization();
288 experimentTreeNode
.reset();
291 TmfStatisticsTreeRootFactory
.addStatsTreeRoot(treeID
, fStatsViewer
.getStatisticData());
294 resetUpdateSynchronization();
296 TmfStatisticsTreeNode treeModelRoot
= TmfStatisticsTreeRootFactory
.getStatTreeRoot(treeID
);
298 // if the model has contents, clear to start over
299 if (treeModelRoot
.hasChildren()) {
300 treeModelRoot
.reset();
303 // set input to a clean data model
304 fStatsViewer
.setInput(treeModelRoot
);
310 * @param complete Should a pending update be sent afterwards or not
312 public void modelInputChanged(boolean complete
) {
313 Control viewerControl
= fStatsViewer
.getControl();
314 // Ignore update if disposed
315 if (viewerControl
.isDisposed()) {
319 fStatsViewer
.getControl().getDisplay().asyncExec(new Runnable() {
322 if (!fStatsViewer
.getControl().isDisposed()) {
323 fStatsViewer
.refresh();
334 * Called when an experiment request has failed or has been cancelled.
335 * Remove the data retrieved from the experiment from the statistics tree.
338 * The experiment name
340 public void modelIncomplete(String name
) {
341 Object input
= fStatsViewer
.getInput();
342 if (input
!= null && input
instanceof TmfStatisticsTreeNode
) {
344 * The data from this experiment is invalid and shall be removed to
345 * refresh upon next selection
347 TmfStatisticsTreeRootFactory
.removeStatTreeRoot(fStatsViewer
.getTreeID(name
));
349 // Reset synchronization information
350 resetUpdateSynchronization();
351 modelInputChanged(false);
353 fStatsViewer
.waitCursor(false);
357 * Handles the signal about new experiment range.
360 * The experiment range updated signal
363 public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal
) {
364 TmfExperiment experiment
= signal
.getExperiment();
366 if (!experiment
.equals(TmfExperiment
.getCurrentExperiment())) {
370 // Calculate the selected timerange to request
371 long startTime
= signal
.getRange().getStartTime().normalize(0, TIME_SCALE
).getValue();
372 TmfTimestamp startTS
= new TmfTimestamp(startTime
, TIME_SCALE
);
373 TmfTimestamp endTS
= new TmfTimestamp(startTime
+ INITIAL_WINDOW_SPAN
, TIME_SCALE
);
374 TmfTimeRange timeRange
= new TmfTimeRange(startTS
, endTS
);
376 requestTimeRangeData(experiment
, timeRange
);
377 requestData(experiment
, signal
.getRange());
383 * @see org.eclipse.linuxtools.tmf.ui.views.TmfView#dispose()
386 public void dispose() {
388 fStatsViewer
.dispose();
391 * Make sure there is no request running before removing the statistics
394 cancelOngoingRequest(fRequestRange
);
395 cancelOngoingRequest(fRequest
);
397 TmfStatisticsTreeRootFactory
.removeAll();
403 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
406 public void setFocus() {
407 fStatsViewer
.setFocus();
411 * Handles the experiment updated signal. This will detect new events in
412 * case the indexing is not coalesced with a statistics request.
415 * The experiment updated signal
420 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
421 TmfExperiment experiment
= signal
.getExperiment();
422 if (!experiment
.equals(TmfExperiment
.getCurrentExperiment())) {
427 for (TmfStatisticsTreeNode node
: ((TmfStatisticsTreeNode
) fStatsViewer
.getInput()).getChildren()) {
428 nbEvents
+= (int) node
.getValue().getTotal();
432 * In the normal case, the statistics request is coalesced with indexing
433 * and the number of events are the same, there is nothing to do. But if
434 * it's not the case, trigger a new request to count the new events.
436 if (nbEvents
< experiment
.getNbEvents()) {
437 requestData(experiment
, experiment
.getTimeRange());
442 * * Handles the time range updated signal. It updates the time range
446 * Contains the information about the new selected time range.
450 public void timeRangeUpdated(TmfRangeSynchSignal signal
) {
452 * It is possible that the time range changes while a request is
455 cancelOngoingRequest(fRequestRange
);
457 requestTimeRangeData(TmfExperiment
.getCurrentExperiment(), signal
.getCurrentRange());
461 * Get the statistics viewer for an experiment. If all traces in the
462 * experiment are of the same type, use the extension point specified.
464 * @return a statistics viewer of the appropriate type
467 protected TmfStatisticsViewer
createStatisticsViewer() {
468 if (fExperiment
== null) {
469 return new TmfStatisticsViewer(fParent
);
471 String commonTraceType
= null;
474 * Determine if the traces of the experiment are of the same type.
475 * If not, it uses the most generic one.
477 for (ITmfTrace trace
: fExperiment
.getTraces()) {
478 IResource resource
= trace
.getResource();
479 if (resource
== null) {
480 return new TmfStatisticsViewer(fParent
);
482 String traceType
= resource
.getPersistentProperty(TmfCommonConstants
.TRACETYPE
);
483 if (commonTraceType
!= null && !commonTraceType
.equals(traceType
)) {
484 return new TmfStatisticsViewer(fParent
);
486 commonTraceType
= traceType
;
488 if (commonTraceType
== null) {
489 return new TmfStatisticsViewer(fParent
);
492 * Search in the configuration if there is any viewer specified for
493 * this kind of trace type.
495 for (IConfigurationElement ce
: TmfTraceType
.getTypeElements()) {
496 if (ce
.getAttribute(TmfTraceType
.ID_ATTR
).equals(commonTraceType
)) {
497 IConfigurationElement
[] statisticsViewerCE
= ce
.getChildren(TmfTraceType
.STATISTICS_VIEWER_ELEM
);
498 if (statisticsViewerCE
.length
!= 1) {
501 String statisticsViewer
= statisticsViewerCE
[0].getAttribute(TmfTraceType
.CLASS_ATTR
);
502 if (statisticsViewer
== null || statisticsViewer
.length() == 0) {
505 Bundle bundle
= Platform
.getBundle(ce
.getContributor().getName());
506 Class
<?
> c
= bundle
.loadClass(statisticsViewer
);
507 Class
<?
>[] constructorArgs
= new Class
[] { Composite
.class };
508 Constructor
<?
> constructor
= c
.getConstructor(constructorArgs
);
509 Object
[] args
= new Object
[] { fParent
};
510 return (TmfStatisticsViewer
) constructor
.newInstance(args
);
513 } catch (CoreException e
) {
514 Activator
.getDefault().logError("Error creating statistics viewer : cannot find the property TmfCommonConstants.TRACETYPE", e
); //$NON-NLS-1$
515 } catch (ClassNotFoundException e
) {
516 Activator
.getDefault().logError("Error creating statistics viewer : cannot load the statistics viewer class", e
); //$NON-NLS-1$
517 } catch (NoSuchMethodException e
) {
518 Activator
.getDefault().logError("Error creating statistics viewer : constructor of the viewer doesn't exist", e
); //$NON-NLS-1$
519 } catch (InstantiationException e
) {
520 Activator
.getDefault().logError("Error creating statistics viewer : cannot instantiate the statistics viewer", e
); //$NON-NLS-1$
521 } catch (IllegalAccessException e
) {
522 Activator
.getDefault().logError("Error creating statistics viewer : cannot access the constructor of the viewer", e
); //$NON-NLS-1$
523 } catch (IllegalArgumentException e
) {
524 Activator
.getDefault().logError("Error creating statistics viewer : argument(s) sent to the constructor are illegal", e
); //$NON-NLS-1$
525 } catch (InvocationTargetException e
) {
526 Activator
.getDefault().logError("Error creating statistics viewer : the constructor of the viewer sent an exception", e
); //$NON-NLS-1$
528 return new TmfStatisticsViewer(fParent
);
532 * Performs the request for an experiment and populates the statistics tree
536 * Experiment for which we need the statistics data.
540 protected void requestData(final TmfExperiment experiment
, TmfTimeRange timeRange
) {
541 if (experiment
!= null) {
543 // Check if an update is already ongoing
544 if (checkUpdateBusy(timeRange
)) {
549 for (TmfStatisticsTreeNode node
: ((TmfStatisticsTreeNode
) fStatsViewer
.getInput()).getChildren()) {
550 index
+= (int) node
.getValue().getTotal();
553 // Prepare the global event request
554 fRequest
= new TmfStatisticsRequest(this, fStatsViewer
, experiment
, timeRange
, index
, ExecutionType
.BACKGROUND
, true);
556 experiment
.sendRequest(fRequest
);
557 fStatsViewer
.waitCursor(true);
562 * Performs the time range request for an experiment and populates the
563 * statistics tree with events.
566 * Experiment for which we need the statistics data.
571 protected void requestTimeRangeData(final TmfExperiment experiment
, TmfTimeRange timeRange
) {
572 if (experiment
!= null) {
573 resetTimeRangeValue();
574 // Prepare the partial event request
575 fRequestRange
= new TmfStatisticsRequest(this, fStatsViewer
, experiment
, timeRange
, 0, ExecutionType
.FOREGROUND
, false);
576 experiment
.sendRequest(fRequestRange
);
581 * Reset the number of events within the time range
585 protected void resetTimeRangeValue() {
586 // Reset the number of events in the time range
587 String treeID
= fStatsViewer
.getTreeID(TmfExperiment
.getCurrentExperiment().getName());
588 TmfStatisticsTreeNode treeModelRoot
= TmfStatisticsTreeRootFactory
.getStatTreeRoot(treeID
);
589 if (treeModelRoot
.hasChildren()) {
590 treeModelRoot
.resetTimeRangeValue();
595 * Return the size of the request when performing background request.
597 * @return the block size for background request.
599 protected int getIndexPageSize() {
604 * Cancels the current ongoing request
607 * The request to be canceled
610 protected void cancelOngoingRequest(ITmfEventRequest request
) {
611 if (request
!= null && !request
.isCompleted()) {
617 * Reset update synchronization information
619 protected void resetUpdateSynchronization() {
620 synchronized (fStatisticsUpdateSyncObj
) {
621 fStatisticsUpdateBusy
= false;
622 fStatisticsUpdatePending
= false;
623 fStatisticsUpdateRange
= null;
628 * Checks if statistic update is ongoing. If it is ongoing the new time
629 * range is stored as pending
633 * @return true if statistic update is ongoing else false
635 protected boolean checkUpdateBusy(TmfTimeRange timeRange
) {
636 synchronized (fStatisticsUpdateSyncObj
) {
637 if (fStatisticsUpdateBusy
) {
638 fStatisticsUpdatePending
= true;
639 if (fStatisticsUpdateRange
== null
640 || timeRange
.getEndTime().compareTo(fStatisticsUpdateRange
.getEndTime()) > 0) {
641 fStatisticsUpdateRange
= timeRange
;
645 fStatisticsUpdateBusy
= true;
651 * Sends pending request (if any)
653 protected void sendPendingUpdate() {
654 synchronized (fStatisticsUpdateSyncObj
) {
655 fStatisticsUpdateBusy
= false;
656 if (fStatisticsUpdatePending
) {
657 fStatisticsUpdatePending
= false;
658 requestData(TmfExperiment
.getCurrentExperiment(), fStatisticsUpdateRange
);
659 fStatisticsUpdateRange
= null;