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
.Collections
;
19 import java
.util
.Iterator
;
20 import java
.util
.List
;
21 import java
.util
.NoSuchElementException
;
22 import java
.util
.Objects
;
23 import java
.util
.concurrent
.atomic
.AtomicInteger
;
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
.jdt
.annotation
.NonNull
;
30 import org
.eclipse
.jdt
.annotation
.Nullable
;
31 import org
.eclipse
.swt
.widgets
.Composite
;
32 import org
.eclipse
.swt
.widgets
.Display
;
33 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.IAnalysisProgressListener
;
34 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.ISegmentStoreProvider
;
35 import org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.SubSecondTimeWithUnitFormat
;
36 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.Activator
;
37 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.views
.segmentstore
.scatter
.Messages
;
38 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.views
.segmentstore
.scatter
.SegmentStoreScatterGraphTooltipProvider
;
39 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegment
;
40 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegmentStore
;
41 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentComparators
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.IAnalysisModule
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceClosedSignal
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
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 static final int UNKNOWN_SIZE
= -1;
74 private final class CompactingSegmentStoreQuery
extends Job
{
75 private static final long MAX_POINTS
= 1000;
76 private final long fStart
;
77 private final long fEnd
;
79 private CompactingSegmentStoreQuery(long start
, long end
) {
80 super(Messages
.SegmentStoreScatterGraphViewer_compactTitle
);
86 protected IStatus
run(@Nullable IProgressMonitor monitor
) {
87 final IProgressMonitor statusMonitor
= monitor
;
89 if (statusMonitor
== null) {
90 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "Monitor is null"); //$NON-NLS-1$
93 ISegmentStoreProvider segmentProvider
= getSegmentProvider();
94 final long startTime
= fStart
;
95 final long endTime
= fEnd
;
96 if (segmentProvider
== null) {
97 redraw(statusMonitor
, startTime
, startTime
, Collections
.emptyList());
98 return new Status(IStatus
.WARNING
, Activator
.PLUGIN_ID
, "segment provider not available"); //$NON-NLS-1$
101 final ISegmentStore
<ISegment
> segStore
= segmentProvider
.getSegmentStore();
102 if (segStore
== null) {
103 redraw(statusMonitor
, startTime
, startTime
, Collections
.emptyList());
104 return new Status(IStatus
.INFO
, Activator
.PLUGIN_ID
, "Segment provider does not have segments"); //$NON-NLS-1$
107 fPixelStart
= startTime
;
108 fPixelSize
= Math
.max(1, (endTime
- startTime
) / MAX_POINTS
);
109 final Iterable
<ISegment
> intersectingElements
= segStore
.getIntersectingElements(startTime
, endTime
, SegmentComparators
.INTERVAL_START_COMPARATOR
);
110 final Iterable
<ISegment
> displayData
= compactList(startTime
, intersectingElements
);
112 redraw(statusMonitor
, startTime
, endTime
, displayData
);
114 if (statusMonitor
.isCanceled()) {
115 return Status
.CANCEL_STATUS
;
117 return Status
.OK_STATUS
;
120 * fDirty should have been incremented before creating a job, so
121 * we decrement it once the job is done
123 fDirty
.decrementAndGet();
128 private void redraw(final IProgressMonitor statusMonitor
, final long startTime
, final long endTime
, final Iterable
<@NonNull ISegment
> displayData
) {
129 fDisplayData
= displayData
;
131 * Increment at every redraw, since the content of the view is not
134 fDirty
.incrementAndGet();
135 Display
.getDefault().asyncExec(new Runnable() {
140 updateData(startTime
, endTime
, UNKNOWN_SIZE
, statusMonitor
);
142 /* Decrement once the redraw is done */
143 fDirty
.decrementAndGet();
149 private Iterable
<ISegment
> compactList(final long startTime
, final Iterable
<@NonNull ISegment
> iterableToCompact
) {
151 return new Iterable
<@NonNull ISegment
>() {
154 public Iterator
<ISegment
> iterator() {
156 return new Iterator
<@NonNull ISegment
>() {
158 private @Nullable ISegment fLast
= null;
159 private @Nullable ISegment fNext
= null;
160 private Iterator
<@NonNull ISegment
> fIterator
= iterableToCompact
.iterator();
163 public @NonNull ISegment
next() {
164 /* hasNext implies next != null */
168 return Objects
.requireNonNull(fLast
);
170 throw new NoSuchElementException();
174 public boolean hasNext() {
176 // iteration hasn't started yet.
177 if (fIterator
.hasNext()) {
178 fLast
= fIterator
.next();
179 if (fLast
.getStart() >= startTime
) {
187 // clear warning in calling overlaps below.
188 ISegment prev
= fLast
;
189 while (fNext
== null && fIterator
.hasNext()) {
190 ISegment tmp
= fIterator
.next();
191 if (tmp
.getStart() >= startTime
&& !overlaps(prev
, tmp
)) {
195 return fNext
!= null;
202 private boolean overlaps(ISegment last
, ISegment next
) {
203 long timePerPix
= fPixelSize
;
204 final long start
= last
.getStart();
205 final long pixelStart
= fPixelStart
;
206 final long pixelDuration
= start
- pixelStart
;
207 long startPixBoundL
= pixelDuration
/ timePerPix
* timePerPix
+ pixelStart
;
208 long startPixBoundR
= startPixBoundL
+ timePerPix
;
209 final long currentStart
= next
.getStart();
210 if (currentStart
>= startPixBoundL
&& currentStart
<= startPixBoundR
) {
211 long length
= last
.getLength();
212 long lengthNext
= next
.getLength();
213 long lengthLow
= length
/ timePerPix
* timePerPix
;
214 long lengthHigh
= lengthLow
+ timePerPix
;
215 return (lengthNext
>= lengthLow
&& lengthNext
<= lengthHigh
);
221 // ------------------------------------------------------------------------
223 // ------------------------------------------------------------------------
226 * Listener to update the model with the segment store provider results once
227 * its segment store is fully completed
229 private final class SegmentStoreProviderProgressListener
implements IAnalysisProgressListener
{
232 public void onComplete(ISegmentStoreProvider segmentProvider
, ISegmentStore
<ISegment
> segmentStore
) {
233 // Only update the model if trace that was analyzed is active trace
234 if (segmentProvider
.equals(getSegmentProvider())) {
235 updateModel(segmentStore
);
240 private long fPixelSize
= -1;
242 private long fPixelStart
= 0;
246 private Iterable
<@NonNull ISegment
> fDisplayData
= Collections
.emptyList();
249 * Provider completion listener
251 private SegmentStoreProviderProgressListener fListener
;
254 * Current segment provider
256 private @Nullable ISegmentStoreProvider fSegmentProvider
;
258 private @Nullable Job fCompactingJob
;
260 // ------------------------------------------------------------------------
262 // ------------------------------------------------------------------------
276 public AbstractSegmentStoreScatterGraphViewer(Composite parent
, String title
, String xLabel
, String yLabel
) {
277 super(parent
, title
, xLabel
, yLabel
);
278 setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this));
279 fListener
= new SegmentStoreProviderProgressListener();
280 ITmfTrace trace
= TmfTraceManager
.getInstance().getActiveTrace();
281 initializeProvider(trace
);
282 getSwtChart().getLegend().setVisible(false);
283 getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT
);
286 private final void initializeProvider(@Nullable ITmfTrace trace
) {
288 final ISegmentStoreProvider segmentStoreProvider
= getSegmentStoreProvider(trace
);
289 if (segmentStoreProvider
!= null) {
290 segmentStoreProvider
.addListener(fListener
);
291 setData(segmentStoreProvider
);
296 // ------------------------------------------------------------------------
298 // ------------------------------------------------------------------------
301 * Update the data in the graph
306 public void updateModel(@Nullable ISegmentStore
<ISegment
> dataInput
) {
307 // Update new window range
308 TmfTimeRange currentRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
309 long currentStart
= currentRange
.getStartTime().toNanos();
310 long currentEnd
= currentRange
.getEndTime().toNanos();
311 if (dataInput
== null) {
312 if (!getDisplay().isDisposed()) {
313 Display
.getDefault().syncExec(new Runnable() {
320 fDisplayData
= Collections
.emptyList();
322 setWindowRange(currentStart
, currentEnd
);
327 protected void initializeDataSource() {
328 ITmfTrace trace
= getTrace();
329 initializeProvider(trace
);
331 setData(getSegmentStoreProvider(trace
));
336 protected void updateData(final long start
, final long end
, int nb
, @Nullable IProgressMonitor monitor
) {
340 // Determine data that needs to be visible
341 List
<Double
> xSeries
= nb
!= UNKNOWN_SIZE ?
new ArrayList
<>(nb
) : new ArrayList
<>();
342 List
<Double
> ySeries
= nb
!= UNKNOWN_SIZE ?
new ArrayList
<>(nb
) : new ArrayList
<>();
343 // For each visible segments, add start time to x value and duration
345 for (ISegment segment
: fDisplayData
) {
346 if (monitor
!= null && monitor
.isCanceled()) {
349 xSeries
.add((double) (segment
.getStart() - start
));
350 ySeries
.add((double) segment
.getLength());
352 setXAxis(Doubles
.toArray(xSeries
));
353 setSeries(Messages
.SegmentStoreScatterGraphViewer_legend
, Doubles
.toArray(ySeries
));
358 protected void setWindowRange(final long windowStartTime
, final long windowEndTime
) {
359 super.setWindowRange(windowStartTime
, windowEndTime
);
363 protected ILineSeries
addSeries(@Nullable String seriesName
) {
364 ISeriesSet seriesSet
= getSwtChart().getSeriesSet();
365 ILineSeries series
= (ILineSeries
) seriesSet
.createSeries(SeriesType
.LINE
, seriesName
);
366 series
.setVisible(true);
367 series
.enableArea(false);
368 series
.setLineStyle(LineStyle
.NONE
);
369 series
.setSymbolType(PlotSymbolType
.DIAMOND
);
374 * Set the data into the viewer. If the provider is an analysis, it will
375 * update the model if the analysis is completed or run the analysis if not
379 * Segment store provider
381 public void setData(@Nullable ISegmentStoreProvider provider
) {
382 if (provider
== null) {
386 ISegmentStore
<ISegment
> segStore
= provider
.getSegmentStore();
387 // If results are not null, then segment store is completed and model
389 if (segStore
!= null) {
390 updateModel(segStore
);
391 setSegmentProvider(provider
);
395 provider
.addListener(fListener
);
396 if (provider
instanceof IAnalysisModule
) {
397 ((IAnalysisModule
) provider
).schedule();
399 setSegmentProvider(provider
);
403 * Returns the segment store provider
406 * The trace to consider
407 * @return the segment store provider
409 protected @Nullable abstract ISegmentStoreProvider
getSegmentStoreProvider(ITmfTrace trace
);
411 // ------------------------------------------------------------------------
413 // ------------------------------------------------------------------------
417 * Signal received when a different trace is selected
421 public void traceSelected(@Nullable TmfTraceSelectedSignal signal
) {
422 super.traceSelected(signal
);
423 if (signal
== null) {
426 ITmfTrace trace
= signal
.getTrace();
429 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
431 timeRange
.getStartTime().toNanos(),
432 timeRange
.getEndTime().toNanos());
433 setData(getSegmentStoreProvider(trace
));
440 * Signal received when trace is opened
444 public void traceOpened(@Nullable TmfTraceOpenedSignal signal
) {
445 super.traceOpened(signal
);
446 if (signal
== null) {
449 ITmfTrace trace
= signal
.getTrace();
453 final ISegmentStoreProvider segmentStoreProvider
= getSegmentStoreProvider(trace
);
454 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
456 timeRange
.getStartTime().toNanos(),
457 timeRange
.getEndTime().toNanos());
458 setData(segmentStoreProvider
);
464 protected void updateContent() {
466 * Update is requested, content is not up to date, fDirty will be
467 * decremented in the compacting job
469 fDirty
.incrementAndGet();
470 Job compactingJob
= fCompactingJob
;
471 if (compactingJob
!= null && compactingJob
.getState() == Job
.RUNNING
) {
472 compactingJob
.cancel();
474 compactingJob
= new CompactingSegmentStoreQuery(getWindowStartTime(), getWindowEndTime());
475 fCompactingJob
= compactingJob
;
476 compactingJob
.schedule();
481 * Signal received when last opened trace is closed
485 public void traceClosed(@Nullable TmfTraceClosedSignal signal
) {
486 super.traceClosed(signal
);
487 if (signal
!= null) {
488 // Check if there is no more opened trace
489 if (TmfTraceManager
.getInstance().getActiveTrace() == null) {
490 ISegmentStoreProvider provider
= getSegmentProvider();
491 if (provider
!= null) {
492 provider
.removeListener(fListener
);
500 private @Nullable ISegmentStoreProvider
getSegmentProvider() {
501 return fSegmentProvider
;
504 private void setSegmentProvider(ISegmentStoreProvider provider
) {
505 fSegmentProvider
= provider
;
509 public boolean isDirty() {
510 /* Check the parent's or this view's own dirtiness */
511 return super.isDirty() || (fDirty
.get() != 0);