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
;
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
.jdt
.annotation
.Nullable
;
28 import org
.eclipse
.swt
.graphics
.Point
;
29 import org
.eclipse
.swt
.widgets
.Composite
;
30 import org
.eclipse
.swt
.widgets
.Display
;
31 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.AbstractSegmentStoreAnalysisModule
;
32 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.IAnalysisProgressListener
;
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
.signal
.TmfSignalHandler
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
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
.ITmfTimestamp
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
50 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
51 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentInfo
;
52 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentSignal
;
53 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.TmfChartTimeStampFormat
;
54 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.linecharts
.TmfCommonXLineChartViewer
;
55 import org
.swtchart
.Chart
;
56 import org
.swtchart
.IAxis
;
57 import org
.swtchart
.IAxisTick
;
58 import org
.swtchart
.ILineSeries
;
59 import org
.swtchart
.ILineSeries
.PlotSymbolType
;
60 import org
.swtchart
.ISeries
.SeriesType
;
61 import org
.swtchart
.ISeriesSet
;
62 import org
.swtchart
.LineStyle
;
63 import org
.swtchart
.Range
;
66 * Displays the segment store analysis data in a scatter graph
68 * @author France Lapointe Nguyen
69 * @author Matthew Khouzam - reduced memory usage
72 public abstract class AbstractSegmentStoreScatterGraphViewer
extends TmfCommonXLineChartViewer
{
74 private static final Format FORMAT
= new SubSecondTimeWithUnitFormat();
76 private final class CompactingSegmentStoreQuery
extends Job
{
77 private static final long MAX_POINTS
= 1000;
78 private final TmfTimeRange fCurrentRange
;
80 private CompactingSegmentStoreQuery(TmfTimeRange currentRange
) {
81 super(Messages
.SegmentStoreScatterGraphViewer_compactTitle
);
82 fCurrentRange
= currentRange
;
86 protected IStatus
run(@Nullable IProgressMonitor monitor
) {
87 final IProgressMonitor statusMonitor
= monitor
;
88 if (statusMonitor
== null) {
89 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "Monitor is null"); //$NON-NLS-1$
92 AbstractSegmentStoreAnalysisModule module
= getAnalysisModule();
93 final long startTimeInNanos
= getTimeInNanos(fCurrentRange
.getStartTime());
94 final long endTimeInNanos
= getTimeInNanos(fCurrentRange
.getEndTime());
96 setWindowRange(startTimeInNanos
, endTimeInNanos
);
97 redraw(statusMonitor
, startTimeInNanos
, startTimeInNanos
, Collections
.EMPTY_LIST
);
98 return new Status(IStatus
.WARNING
, Activator
.PLUGIN_ID
, "Analysis module not available"); //$NON-NLS-1$
101 final ISegmentStore
<ISegment
> results
= module
.getResults();
102 if (results
== null) {
103 setWindowRange(startTimeInNanos
, endTimeInNanos
);
104 redraw(statusMonitor
, startTimeInNanos
, startTimeInNanos
, Collections
.EMPTY_LIST
);
105 return new Status(IStatus
.INFO
, Activator
.PLUGIN_ID
, "Analysis module does not have results"); //$NON-NLS-1$
108 final long startTime
= fCurrentRange
.getStartTime().getValue();
109 final long endTime
= fCurrentRange
.getEndTime().getValue();
110 fPixelStart
= startTime
;
111 fPixelSize
= (endTime
- startTime
) / MAX_POINTS
;
112 final Iterable
<ISegment
> intersectingElements
= results
.getIntersectingElements(startTime
, endTime
);
114 final List
<ISegment
> list
= convertIterableToList(intersectingElements
, statusMonitor
);
115 final List
<ISegment
> displayData
= (!list
.isEmpty()) ?
compactList(startTime
, list
, statusMonitor
) : list
;
117 setWindowRange(startTimeInNanos
, endTimeInNanos
);
118 redraw(statusMonitor
, startTime
, endTime
, displayData
);
120 if (statusMonitor
.isCanceled()) {
121 return NonNullUtils
.checkNotNull(Status
.CANCEL_STATUS
);
123 return NonNullUtils
.checkNotNull(Status
.OK_STATUS
);
127 private void redraw(final IProgressMonitor statusMonitor
, final long startTime
, final long endTime
, final List
<ISegment
> displayData
) {
128 fDisplayData
= displayData
;
129 Display
.getDefault().asyncExec(new Runnable() {
133 updateData(startTime
, endTime
, displayData
.size(), statusMonitor
);
138 private List
<ISegment
> compactList(final long startTime
, final List
<ISegment
> listToCompact
, final IProgressMonitor statusMonitor
) {
139 List
<ISegment
> displayData
= new ArrayList
<>();
140 ISegment last
= listToCompact
.get(0);
141 if (last
.getStart() >= startTime
) {
142 displayData
.add(last
);
144 for (ISegment next
: listToCompact
) {
145 if (next
.getStart() < startTime
) {
148 if (statusMonitor
.isCanceled()) {
149 return Collections
.EMPTY_LIST
;
151 if (!overlaps(last
, next
)) {
152 displayData
.add(next
);
159 private List
<ISegment
> convertIterableToList(final Iterable
<ISegment
> iterable
, final IProgressMonitor statusMonitor
) {
160 final List
<ISegment
> list
= new ArrayList
<>();
161 for (ISegment seg
: iterable
) {
162 if (statusMonitor
.isCanceled()) {
163 return Collections
.EMPTY_LIST
;
167 Collections
.sort(list
, SegmentComparators
.INTERVAL_START_COMPARATOR
);
171 private boolean overlaps(ISegment last
, ISegment next
) {
172 long timePerPix
= fPixelSize
;
173 final long start
= last
.getStart();
174 final long pixelStart
= fPixelStart
;
175 final long pixelDuration
= start
- pixelStart
;
176 long startPixBoundL
= pixelDuration
/ timePerPix
* timePerPix
+ pixelStart
;
177 long startPixBoundR
= startPixBoundL
+ timePerPix
;
178 final long currentStart
= next
.getStart();
179 if (currentStart
>= startPixBoundL
&& currentStart
<= startPixBoundR
) {
180 long length
= last
.getLength();
181 long lengthNext
= next
.getLength();
182 long lengthLow
= length
/ timePerPix
* timePerPix
;
183 long lengthHigh
= lengthLow
+ timePerPix
;
184 return (lengthNext
>= lengthLow
&& lengthNext
<= lengthHigh
);
190 // ------------------------------------------------------------------------
192 // ------------------------------------------------------------------------
195 * Listener to update the model with the semgent store analysis results
196 * once the analysis is fully completed
198 private final class AnalysisProgressListener
implements IAnalysisProgressListener
{
201 public void onComplete(AbstractSegmentStoreAnalysisModule activeAnalysis
, ISegmentStore
<ISegment
> results
) {
202 // Only update the model if trace that was analyzed is active trace
203 if (activeAnalysis
.equals(getAnalysisModule())) {
204 updateModel(results
);
205 updateRange(TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange());
210 private long fPixelSize
= -1;
212 private long fPixelStart
= 0;
216 private Collection
<ISegment
> fDisplayData
= Collections
.EMPTY_LIST
;
219 * Analysis completion listener
221 private AnalysisProgressListener fListener
;
224 * Current analysis module
226 private @Nullable AbstractSegmentStoreAnalysisModule fAnalysisModule
;
228 private @Nullable Job fCompactingJob
;
230 // ------------------------------------------------------------------------
232 // ------------------------------------------------------------------------
246 public AbstractSegmentStoreScatterGraphViewer(Composite parent
, String title
, String xLabel
, String yLabel
) {
247 super(parent
, title
, xLabel
, yLabel
);
248 setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this));
249 fListener
= new AnalysisProgressListener();
250 ITmfTrace trace
= TmfTraceManager
.getInstance().getActiveTrace();
251 initializeModule(trace
);
252 getSwtChart().getLegend().setVisible(false);
253 getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT
);
256 private final void initializeModule(@Nullable ITmfTrace trace
) {
258 final AbstractSegmentStoreAnalysisModule analysisModuleOfClass
= getSegmentStoreAnalysisModule(trace
);
259 if (analysisModuleOfClass
!= null) {
260 analysisModuleOfClass
.addListener(fListener
);
261 setData(analysisModuleOfClass
);
262 updateRange(TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange());
267 // ------------------------------------------------------------------------
269 // ------------------------------------------------------------------------
272 * Update the data in the graph
277 public void updateModel(@Nullable ISegmentStore
<ISegment
> dataInput
) {
278 // Update new window range
279 TmfTimeRange currentRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
280 long currentStart
= getTimeInNanos(currentRange
.getStartTime());
281 long currentEnd
= getTimeInNanos(currentRange
.getEndTime());
282 if (dataInput
== null) {
283 if (!getDisplay().isDisposed()) {
284 Display
.getDefault().syncExec(new Runnable() {
291 fDisplayData
= Collections
.EMPTY_LIST
;
293 Collection
<ISegment
> elements
= (Collection
<ISegment
>) dataInput
.getIntersectingElements(currentStart
, currentEnd
);
294 // getIntersectingElements can return an unsorted iterable, make
295 // sure our collection is sorted
296 ArrayList
<ISegment
> list
= new ArrayList
<>(elements
);
297 Collections
.sort(list
, SegmentComparators
.INTERVAL_START_COMPARATOR
);
300 setWindowRange(currentStart
, currentEnd
);
301 updateRange(currentRange
);
305 protected void initializeDataSource() {
306 ITmfTrace trace
= getTrace();
307 initializeModule(trace
);
309 setData(getSegmentStoreAnalysisModule(trace
));
314 protected void updateData(final long start
, final long end
, int nb
, @Nullable IProgressMonitor monitor
) {
315 // Third parameter is not used by implementation
316 // Determine data that needs to be visible
317 Collection
<ISegment
> data
= fDisplayData
;
319 final int dataSize
= (nb
== 0) ? data
.size() : nb
;
320 if (dataSize
== 0 || end
== start
) {
324 final double[] xSeries
= new double[dataSize
];
325 final double[] ySeries
= new double[dataSize
];
326 // For each visible segments, add start time to x value and duration
328 Iterator
<ISegment
> modelIter
= data
.iterator();
330 for (int i
= 0; i
< dataSize
; i
++) {
331 if (modelIter
.hasNext()) {
332 ISegment segment
= modelIter
.next();
333 xSeries
[i
] = segment
.getStart() - start
;
334 ySeries
[i
] = segment
.getLength();
335 maxTempY
= Math
.max(maxTempY
, segment
.getLength());
338 final long maxY
= maxTempY
;
340 final Chart swtChart
= getSwtChart();
341 if (swtChart
.isDisposed() || xSeries
.length
< 1) {
344 swtChart
.updateLayout();
345 setSeries(Messages
.SegmentStoreScatterGraphViewer_legend
, ySeries
); // $NON-NLS-1$
346 final TmfChartTimeStampFormat tmfChartTimeStampFormat
= new TmfChartTimeStampFormat(getTimeOffset());
347 ILineSeries series
= (ILineSeries
) swtChart
.getSeriesSet().getSeries(Messages
.SegmentStoreScatterGraphViewer_legend
);
348 if (series
== null) {
349 series
= addSeries(Messages
.SegmentStoreScatterGraphViewer_legend
);
351 series
.setXSeries(xSeries
);
352 /* Find the minimal and maximum values in this series */
353 series
.setYSeries(ySeries
);
355 final IAxis xAxis
= swtChart
.getAxisSet().getXAxis(0);
356 IAxisTick xTick
= xAxis
.getTick();
357 xTick
.setFormat(tmfChartTimeStampFormat
);
358 xAxis
.setRange(new Range(0.0, end
- start
));
360 swtChart
.getAxisSet().getYAxis(0).setRange(new Range(0.0, maxY
));
364 if (isSendTimeAlignSignals()) {
365 // The width of the chart might have changed and its
366 // time axis might be misaligned with the other views
367 Point viewPos
= AbstractSegmentStoreScatterGraphViewer
.this.getParent().getParent().toDisplay(0, 0);
368 int axisPos
= swtChart
.toDisplay(0, 0).x
+ getPointAreaOffset();
369 int timeAxisOffset
= axisPos
- viewPos
.x
;
370 TmfTimeViewAlignmentInfo timeAlignmentInfo
= new TmfTimeViewAlignmentInfo(getControl().getShell(), viewPos
, timeAxisOffset
);
371 TmfSignalManager
.dispatchSignal(new TmfTimeViewAlignmentSignal(AbstractSegmentStoreScatterGraphViewer
.this, timeAlignmentInfo
, true));
376 protected void setWindowRange(final long windowStartTime
, final long windowEndTime
) {
377 super.setWindowRange(windowStartTime
, windowEndTime
);
381 protected ILineSeries
addSeries(@Nullable String seriesName
) {
382 ISeriesSet seriesSet
= getSwtChart().getSeriesSet();
383 ILineSeries series
= (ILineSeries
) seriesSet
.createSeries(SeriesType
.LINE
, seriesName
);
384 series
.setVisible(true);
385 series
.enableArea(false);
386 series
.setLineStyle(LineStyle
.NONE
);
387 series
.setSymbolType(PlotSymbolType
.DIAMOND
);
392 * Set the data into the viewer. Will update model is analysis is completed
393 * or run analysis if not completed
396 * Segment store analysis module
398 public void setData(@Nullable AbstractSegmentStoreAnalysisModule analysis
) {
399 if (analysis
== null) {
403 ISegmentStore
<ISegment
> results
= analysis
.getResults();
404 // If results are not null, then analysis is completed and model can be
406 if (results
!= null) {
407 updateModel(results
);
408 setAnalysisModule(analysis
);
412 analysis
.addListener(fListener
);
414 setAnalysisModule(analysis
);
418 * Returns the segment store analysis module
421 * The trace to consider
422 * @return the analysis module
424 protected @Nullable abstract AbstractSegmentStoreAnalysisModule
getSegmentStoreAnalysisModule(ITmfTrace trace
);
426 // ------------------------------------------------------------------------
428 // ------------------------------------------------------------------------
432 * Signal received when a different trace is selected
436 public void traceSelected(@Nullable TmfTraceSelectedSignal signal
) {
437 super.traceSelected(signal
);
438 if (signal
== null) {
441 ITmfTrace trace
= signal
.getTrace();
444 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
446 timeRange
.getStartTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue(),
447 timeRange
.getEndTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue());
448 setData(getSegmentStoreAnalysisModule(trace
));
449 updateRange(timeRange
);
455 * Signal received when trace is opened
459 public void traceOpened(@Nullable TmfTraceOpenedSignal signal
) {
460 super.traceOpened(signal
);
461 if (signal
== null) {
464 ITmfTrace trace
= signal
.getTrace();
468 final AbstractSegmentStoreAnalysisModule analysisModuleOfClass
= getSegmentStoreAnalysisModule(trace
);
469 final TmfTimeRange timeRange
= TmfTraceManager
.getInstance().getCurrentTraceContext().getWindowRange();
471 getTimeInNanos(timeRange
.getStartTime()),
472 getTimeInNanos(timeRange
.getEndTime()));
473 setData(analysisModuleOfClass
);
478 private void updateRange(final @Nullable TmfTimeRange timeRange
) {
479 Job compactingJob
= fCompactingJob
;
480 if (compactingJob
!= null && compactingJob
.getState() == Job
.RUNNING
) {
481 compactingJob
.cancel();
483 compactingJob
= new CompactingSegmentStoreQuery(NonNullUtils
.checkNotNull(timeRange
));
484 fCompactingJob
= compactingJob
;
485 compactingJob
.schedule();
490 * Signal received when last opened trace is closed
494 public void traceClosed(@Nullable TmfTraceClosedSignal signal
) {
495 super.traceClosed(signal
);
496 if (signal
!= null) {
497 // Check if there is no more opened trace
498 if (TmfTraceManager
.getInstance().getActiveTrace() == null) {
499 AbstractSegmentStoreAnalysisModule analysis
= getAnalysisModule();
500 if (analysis
!= null) {
501 analysis
.removeListener(fListener
);
511 * Signal received when window range is updated
515 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal
) {
516 super.windowRangeUpdated(signal
);
517 if (signal
== null) {
520 if (getTrace() != null) {
521 final TmfTimeRange currentRange
= signal
.getCurrentRange();
522 updateRange(currentRange
);
524 Activator
.getDefault().logInfo("No Trace to update"); //$NON-NLS-1$
528 private @Nullable AbstractSegmentStoreAnalysisModule
getAnalysisModule() {
529 return fAnalysisModule
;
532 private void setAnalysisModule(AbstractSegmentStoreAnalysisModule analysisModule
) {
533 fAnalysisModule
= analysisModule
;
536 private static long getTimeInNanos(final ITmfTimestamp currentTime
) {
537 return currentTime
.normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();