1 /******************************************************************************
2 * Copyright (c) 2015, 2016 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 * France Lapointe Nguyen - Initial API and implementation
11 * Bernd Hufmann - Extracted abstract class from LatencyScatterGraphViewer
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.scatter
;
16 import java
.text
.Format
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Collection
;
19 import java
.util
.Collections
;
20 import java
.util
.Iterator
;
21 import java
.util
.List
;
22 import java
.util
.concurrent
.atomic
.AtomicInteger
;
24 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
25 import org
.eclipse
.core
.runtime
.IStatus
;
26 import org
.eclipse
.core
.runtime
.Status
;
27 import org
.eclipse
.core
.runtime
.jobs
.Job
;
28 import org
.eclipse
.jdt
.annotation
.Nullable
;
29 import org
.eclipse
.swt
.widgets
.Composite
;
30 import org
.eclipse
.swt
.widgets
.Display
;
31 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.IAnalysisProgressListener
;
32 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.ISegmentStoreProvider
;
33 import org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.SubSecondTimeWithUnitFormat
;
34 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
35 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.Activator
;
36 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.views
.segmentstore
.scatter
.Messages
;
37 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.views
.segmentstore
.scatter
.SegmentStoreScatterGraphTooltipProvider
;
38 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegment
;
39 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegmentStore
;
40 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentComparators
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.IAnalysisModule
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceClosedSignal
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfWindowRangeUpdatedSignal
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
50 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.linecharts
.TmfCommonXLineChartViewer
;
51 import org
.swtchart
.ILineSeries
;
52 import org
.swtchart
.ILineSeries
.PlotSymbolType
;
53 import org
.swtchart
.ISeries
.SeriesType
;
54 import org
.swtchart
.ISeriesSet
;
55 import org
.swtchart
.LineStyle
;
57 import com
.google
.common
.primitives
.Doubles
;
60 * Displays the segment store provider data in a scatter graph
62 * @author France Lapointe Nguyen
63 * @author Matthew Khouzam - reduced memory usage
66 public abstract class AbstractSegmentStoreScatterGraphViewer
extends TmfCommonXLineChartViewer
{
68 private static final Format FORMAT
= new SubSecondTimeWithUnitFormat();
70 private final AtomicInteger fDirty
= new AtomicInteger();
72 private final class CompactingSegmentStoreQuery
extends Job
{
73 private static final long MAX_POINTS
= 1000;
74 private final TmfTimeRange fCurrentRange
;
76 private CompactingSegmentStoreQuery(TmfTimeRange currentRange
) {
77 super(Messages
.SegmentStoreScatterGraphViewer_compactTitle
);
78 fCurrentRange
= currentRange
;
82 protected IStatus
run(@Nullable IProgressMonitor monitor
) {
83 final IProgressMonitor statusMonitor
= monitor
;
85 if (statusMonitor
== null) {
86 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "Monitor is null"); //$NON-NLS-1$
89 ISegmentStoreProvider segmentProvider
= getSegmentProvider();
90 final long startTimeInNanos
= fCurrentRange
.getStartTime().toNanos();
91 final long endTimeInNanos
= fCurrentRange
.getEndTime().toNanos();
92 if (segmentProvider
== null) {
93 setWindowRange(startTimeInNanos
, endTimeInNanos
);
94 redraw(statusMonitor
, startTimeInNanos
, startTimeInNanos
, Collections
.EMPTY_LIST
);
95 return new Status(IStatus
.WARNING
, Activator
.PLUGIN_ID
, "segment provider not available"); //$NON-NLS-1$
98 final ISegmentStore
<ISegment
> segStore
= segmentProvider
.getSegmentStore();
99 if (segStore
== null) {
100 setWindowRange(startTimeInNanos
, endTimeInNanos
);
101 redraw(statusMonitor
, startTimeInNanos
, startTimeInNanos
, Collections
.EMPTY_LIST
);
102 return new Status(IStatus
.INFO
, Activator
.PLUGIN_ID
, "Segment provider does not have segments"); //$NON-NLS-1$
105 final long startTime
= fCurrentRange
.getStartTime().getValue();
106 final long endTime
= fCurrentRange
.getEndTime().getValue();
107 fPixelStart
= startTime
;
108 fPixelSize
= Math
.max(1, (endTime
- startTime
) / MAX_POINTS
);
109 final Iterable
<ISegment
> intersectingElements
= segStore
.getIntersectingElements(startTime
, endTime
);
111 final List
<ISegment
> list
= convertIterableToList(intersectingElements
, statusMonitor
);
112 final List
<ISegment
> displayData
= (!list
.isEmpty()) ?
compactList(startTime
, list
, statusMonitor
) : list
;
114 setWindowRange(startTimeInNanos
, endTimeInNanos
);
115 redraw(statusMonitor
, startTime
, endTime
, displayData
);
117 if (statusMonitor
.isCanceled()) {
118 return Status
.CANCEL_STATUS
;
120 return Status
.OK_STATUS
;
123 * fDirty should have been incremented before creating a job, so
124 * we decrement it once the job is done
126 fDirty
.decrementAndGet();
131 private void redraw(final IProgressMonitor statusMonitor
, final long startTime
, final long endTime
, final List
<ISegment
> displayData
) {
132 fDisplayData
= displayData
;
134 * Increment at every redraw, since the content of the view is not
137 fDirty
.incrementAndGet();
138 Display
.getDefault().asyncExec(new Runnable() {
143 updateData(startTime
, endTime
, displayData
.size(), statusMonitor
);
145 /* Decrement once the redraw is done */
146 fDirty
.decrementAndGet();
152 private List
<ISegment
> compactList(final long startTime
, final List
<ISegment
> listToCompact
, final IProgressMonitor statusMonitor
) {
153 List
<ISegment
> displayData
= new ArrayList
<>();
154 ISegment last
= listToCompact
.get(0);
155 if (last
.getStart() >= startTime
) {
156 displayData
.add(last
);
158 for (ISegment next
: listToCompact
) {
159 if (next
.getStart() < startTime
) {
162 if (statusMonitor
.isCanceled()) {
163 return Collections
.EMPTY_LIST
;
165 if (!overlaps(last
, next
)) {
166 displayData
.add(next
);
173 private List
<ISegment
> convertIterableToList(final Iterable
<ISegment
> iterable
, final IProgressMonitor statusMonitor
) {
174 final List
<ISegment
> list
= new ArrayList
<>();
175 for (ISegment seg
: iterable
) {
176 if (statusMonitor
.isCanceled()) {
177 return Collections
.EMPTY_LIST
;
181 Collections
.sort(list
, SegmentComparators
.INTERVAL_START_COMPARATOR
);
185 private boolean overlaps(ISegment last
, ISegment next
) {
186 long timePerPix
= fPixelSize
;
187 final long start
= last
.getStart();
188 final long pixelStart
= fPixelStart
;
189 final long pixelDuration
= start
- pixelStart
;
190 long startPixBoundL
= pixelDuration
/ timePerPix
* timePerPix
+ pixelStart
;
191 long startPixBoundR
= startPixBoundL
+ timePerPix
;
192 final long currentStart
= next
.getStart();
193 if (currentStart
>= startPixBoundL
&& currentStart
<= startPixBoundR
) {
194 long length
= last
.getLength();
195 long lengthNext
= next
.getLength();
196 long lengthLow
= length
/ timePerPix
* timePerPix
;
197 long lengthHigh
= lengthLow
+ timePerPix
;
198 return (lengthNext
>= lengthLow
&& lengthNext
<= lengthHigh
);
204 // ------------------------------------------------------------------------
206 // ------------------------------------------------------------------------
209 * Listener to update the model with the segment store provider results once
210 * its segment store is fully completed
212 private final class SegmentStoreProviderProgressListener
implements IAnalysisProgressListener
{
215 public void onComplete(ISegmentStoreProvider segmentProvider
, ISegmentStore
<ISegment
> segmentStore
) {
216 // Only update the model if trace that was analyzed is active trace
217 if (segmentProvider
.equals(getSegmentProvider())) {
218 updateModel(segmentStore
);
223 private long fPixelSize
= -1;
225 private long fPixelStart
= 0;
229 private Collection
<ISegment
> fDisplayData
= Collections
.EMPTY_LIST
;
232 * Provider completion listener
234 private SegmentStoreProviderProgressListener fListener
;
237 * Current segment provider
239 private @Nullable ISegmentStoreProvider fSegmentProvider
;
241 private @Nullable Job fCompactingJob
;
243 // ------------------------------------------------------------------------
245 // ------------------------------------------------------------------------
259 public AbstractSegmentStoreScatterGraphViewer(Composite parent
, String title
, String xLabel
, String yLabel
) {
260 super(parent
, title
, xLabel
, yLabel
);
261 setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this));
262 fListener
= new SegmentStoreProviderProgressListener();
263 ITmfTrace trace
= TmfTraceManager
.getInstance().getActiveTrace();
264 initializeProvider(trace
);
265 getSwtChart().getLegend().setVisible(false);
266 getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT
);
269 private final void initializeProvider(@Nullable ITmfTrace trace
) {
271 final ISegmentStoreProvider segmentStoreProvider
= getSegmentStoreProvider(trace
);
272 if (segmentStoreProvider
!= null) {
273 segmentStoreProvider
.addListener(fListener
);
274 setData(segmentStoreProvider
);
279 // ------------------------------------------------------------------------
281 // ------------------------------------------------------------------------
284 * Update the data in the graph
289 public void updateModel(@Nullable ISegmentStore
<ISegment
> dataInput
) {
290 // Update new window range
291 TmfTimeRange currentRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
292 long currentStart
= currentRange
.getStartTime().toNanos();
293 long currentEnd
= currentRange
.getEndTime().toNanos();
294 if (dataInput
== null) {
295 if (!getDisplay().isDisposed()) {
296 Display
.getDefault().syncExec(new Runnable() {
303 fDisplayData
= Collections
.EMPTY_LIST
;
305 Collection
<ISegment
> elements
= (Collection
<ISegment
>) dataInput
.getIntersectingElements(currentStart
, currentEnd
);
306 // getIntersectingElements can return an unsorted iterable, make
307 // sure our collection is sorted
308 ArrayList
<ISegment
> list
= new ArrayList
<>(elements
);
309 Collections
.sort(list
, SegmentComparators
.INTERVAL_START_COMPARATOR
);
312 setWindowRange(currentStart
, currentEnd
);
313 updateRange(currentRange
);
317 protected void initializeDataSource() {
318 ITmfTrace trace
= getTrace();
319 initializeProvider(trace
);
321 setData(getSegmentStoreProvider(trace
));
326 protected void updateData(final long start
, final long end
, int nb
, @Nullable IProgressMonitor monitor
) {
327 // Third parameter is not used by implementation
328 // Determine data that needs to be visible
329 Collection
<ISegment
> data
= fDisplayData
;
331 final int dataSize
= (nb
== 0) ? data
.size() : nb
;
336 List
<Double
> xSeries
= new ArrayList
<>(dataSize
);
337 List
<Double
> ySeries
= new ArrayList
<>(dataSize
);
338 // For each visible segments, add start time to x value and duration
340 Iterator
<ISegment
> modelIter
= data
.iterator();
341 while (modelIter
.hasNext()) {
342 ISegment segment
= modelIter
.next();
343 xSeries
.add((double) (segment
.getStart() - start
));
344 ySeries
.add((double) segment
.getLength());
346 setXAxis(Doubles
.toArray(xSeries
));
347 setSeries(Messages
.SegmentStoreScatterGraphViewer_legend
, Doubles
.toArray(ySeries
));
352 protected void setWindowRange(final long windowStartTime
, final long windowEndTime
) {
353 super.setWindowRange(windowStartTime
, windowEndTime
);
357 protected ILineSeries
addSeries(@Nullable String seriesName
) {
358 ISeriesSet seriesSet
= getSwtChart().getSeriesSet();
359 ILineSeries series
= (ILineSeries
) seriesSet
.createSeries(SeriesType
.LINE
, seriesName
);
360 series
.setVisible(true);
361 series
.enableArea(false);
362 series
.setLineStyle(LineStyle
.NONE
);
363 series
.setSymbolType(PlotSymbolType
.DIAMOND
);
368 * Set the data into the viewer. If the provider is an analysis, it will
369 * update the model if the analysis is completed or run the analysis if not
373 * Segment store provider
375 public void setData(@Nullable ISegmentStoreProvider provider
) {
376 if (provider
== null) {
380 ISegmentStore
<ISegment
> segStore
= provider
.getSegmentStore();
381 // If results are not null, then segment store is completed and model
383 if (segStore
!= null) {
384 updateModel(segStore
);
385 setSegmentProvider(provider
);
389 provider
.addListener(fListener
);
390 if (provider
instanceof IAnalysisModule
) {
391 ((IAnalysisModule
) provider
).schedule();
393 setSegmentProvider(provider
);
397 * Returns the segment store provider
400 * The trace to consider
401 * @return the segment store provider
403 protected @Nullable abstract ISegmentStoreProvider
getSegmentStoreProvider(ITmfTrace trace
);
405 // ------------------------------------------------------------------------
407 // ------------------------------------------------------------------------
411 * Signal received when a different trace is selected
415 public void traceSelected(@Nullable TmfTraceSelectedSignal signal
) {
416 super.traceSelected(signal
);
417 if (signal
== null) {
420 ITmfTrace trace
= signal
.getTrace();
423 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
425 timeRange
.getStartTime().toNanos(),
426 timeRange
.getEndTime().toNanos());
427 setData(getSegmentStoreProvider(trace
));
428 updateRange(timeRange
);
434 * Signal received when trace is opened
438 public void traceOpened(@Nullable TmfTraceOpenedSignal signal
) {
439 super.traceOpened(signal
);
440 if (signal
== null) {
443 ITmfTrace trace
= signal
.getTrace();
447 final ISegmentStoreProvider segmentStoreProvider
= getSegmentStoreProvider(trace
);
448 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
450 timeRange
.getStartTime().toNanos(),
451 timeRange
.getEndTime().toNanos());
452 setData(segmentStoreProvider
);
457 private void updateRange(final @Nullable TmfTimeRange timeRange
) {
459 * Update is request, content is not up to date, fDirty will be
460 * decremented in the compacting job
462 fDirty
.incrementAndGet();
463 Job compactingJob
= fCompactingJob
;
464 if (compactingJob
!= null && compactingJob
.getState() == Job
.RUNNING
) {
465 compactingJob
.cancel();
467 compactingJob
= new CompactingSegmentStoreQuery(NonNullUtils
.checkNotNull(timeRange
));
468 fCompactingJob
= compactingJob
;
469 compactingJob
.schedule();
474 * Signal received when last opened trace is closed
478 public void traceClosed(@Nullable TmfTraceClosedSignal signal
) {
479 super.traceClosed(signal
);
480 if (signal
!= null) {
481 // Check if there is no more opened trace
482 if (TmfTraceManager
.getInstance().getActiveTrace() == null) {
483 ISegmentStoreProvider provider
= getSegmentProvider();
484 if (provider
!= null) {
485 provider
.removeListener(fListener
);
495 * Signal received when window range is updated
499 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal
) {
500 super.windowRangeUpdated(signal
);
501 if (signal
== null) {
504 if (getTrace() != null) {
505 final TmfTimeRange currentRange
= signal
.getCurrentRange();
506 updateRange(currentRange
);
508 Activator
.getDefault().logInfo("No Trace to update"); //$NON-NLS-1$
512 private @Nullable ISegmentStoreProvider
getSegmentProvider() {
513 return fSegmentProvider
;
516 private void setSegmentProvider(ISegmentStoreProvider provider
) {
517 fSegmentProvider
= provider
;
521 public boolean isDirty() {
522 /* Check the parent's or this view's own dirtiness */
523 return super.isDirty() || (fDirty
.get() != 0);