analysis: Add abstract latency scatter chart view and use it in LTTng
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / AbstractSegmentStoreScatterGraphViewer.java
CommitLineData
03c96217
BH
1/******************************************************************************
2 * Copyright (c) 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 * France Lapointe Nguyen - Initial API and implementation
11 * Bernd Hufmann - Extracted abstract class from LatencyScatterGraphViewer
12 *******************************************************************************/
13
14package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore;
15
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Iterator;
20import java.util.List;
21
22import org.eclipse.core.runtime.IProgressMonitor;
23import org.eclipse.core.runtime.IStatus;
24import org.eclipse.core.runtime.Status;
25import org.eclipse.core.runtime.jobs.Job;
26import org.eclipse.jdt.annotation.Nullable;
27import org.eclipse.swt.graphics.Point;
28import org.eclipse.swt.widgets.Composite;
29import org.eclipse.swt.widgets.Display;
30import org.eclipse.tracecompass.analysis.timing.core.segmentstore.AbstractSegmentStoreAnalysisModule;
31import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
32import org.eclipse.tracecompass.common.core.NonNullUtils;
33import org.eclipse.tracecompass.internal.analysis.timing.ui.Activator;
34import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.Messages;
35import org.eclipse.tracecompass.segmentstore.core.ISegment;
36import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
37import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
38import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
39import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
40import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
41import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
42import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
43import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
44import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
45import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
46import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
47import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
48import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
49import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
50import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfChartTimeStampFormat;
51import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer;
52import org.swtchart.Chart;
53import org.swtchart.IAxis;
54import org.swtchart.IAxisTick;
55import org.swtchart.ILineSeries;
56import org.swtchart.ILineSeries.PlotSymbolType;
57import org.swtchart.ISeries.SeriesType;
58import org.swtchart.ISeriesSet;
59import org.swtchart.LineStyle;
60import org.swtchart.Range;
61
62/**
63 * Displays the segment store analysis data in a scatter graph
64 *
65 * @author France Lapointe Nguyen
66 * @author Matthew Khouzam - reduced memory usage
67 * @since 2.0
68 */
69public abstract class AbstractSegmentStoreScatterGraphViewer extends TmfCommonXLineChartViewer {
70 private static final List<ISegment> EMPTY_LIST = NonNullUtils.checkNotNull(Collections.<ISegment> emptyList());
71
72 private final class CompactingSegmentStoreQuery extends Job {
73 private static final long MAX_POINTS = 1000;
74 private final TmfTimeRange fCurrentRange;
75
76 private CompactingSegmentStoreQuery(TmfTimeRange currentRange) {
77 super(Messages.SegmentStoreScatterGraphViewer_compactTitle);
78 fCurrentRange = currentRange;
79 }
80
81 @Override
82 protected IStatus run(@Nullable IProgressMonitor monitor) {
83 final IProgressMonitor statusMonitor = monitor;
84 if (statusMonitor == null) {
85 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Monitor is null"); //$NON-NLS-1$
86 }
87
88 AbstractSegmentStoreAnalysisModule module = getAnalysisModule();
89 final long startTimeInNanos = getTimeInNanos(fCurrentRange.getStartTime());
90 final long endTimeInNanos = getTimeInNanos(fCurrentRange.getEndTime());
91 if (module == null) {
92 setWindowRange(startTimeInNanos, endTimeInNanos);
93 redraw(statusMonitor, startTimeInNanos, startTimeInNanos, EMPTY_LIST);
94 return new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Analysis module not available"); //$NON-NLS-1$
95 }
96
97 final ISegmentStore<ISegment> results = module.getResults();
98 if (results == null) {
99 setWindowRange(startTimeInNanos, endTimeInNanos);
100 redraw(statusMonitor, startTimeInNanos, startTimeInNanos, EMPTY_LIST);
101 return new Status(IStatus.INFO, Activator.PLUGIN_ID, "Analysis module does not have results"); //$NON-NLS-1$
102 }
103
104 final long startTime = fCurrentRange.getStartTime().getValue();
105 final long endTime = fCurrentRange.getEndTime().getValue();
106 fPixelStart = startTime;
107 fPixelSize = (endTime - startTime) / MAX_POINTS;
108 final Iterable<ISegment> intersectingElements = results.getIntersectingElements(startTime, endTime);
109
110 final List<ISegment> list = convertIterableToList(intersectingElements, statusMonitor);
111 final List<ISegment> displayData = (!list.isEmpty()) ? compactList(startTime, list, statusMonitor) : list;
112
113 setWindowRange(startTimeInNanos, endTimeInNanos);
114 redraw(statusMonitor, startTime, endTime, displayData);
115
116 if (statusMonitor.isCanceled()) {
117 return NonNullUtils.checkNotNull(Status.CANCEL_STATUS);
118 }
119 return NonNullUtils.checkNotNull(Status.OK_STATUS);
120
121 }
122
123 private void redraw(final IProgressMonitor statusMonitor, final long startTime, final long endTime, final List<ISegment> displayData) {
124 fDisplayData = displayData;
125 Display.getDefault().asyncExec(new Runnable() {
126
127 @Override
128 public void run() {
129 updateData(startTime, endTime, displayData.size(), statusMonitor);
130 }
131 });
132 }
133
134 private List<ISegment> compactList(final long startTime, final List<ISegment> listToCompact, final IProgressMonitor statusMonitor) {
135 List<ISegment> displayData = new ArrayList<>();
136 ISegment last = listToCompact.get(0);
137 if (last.getStart() >= startTime) {
138 displayData.add(last);
139 }
140 for (ISegment next : listToCompact) {
141 if (next.getStart() < startTime) {
142 continue;
143 }
144 if (statusMonitor.isCanceled()) {
145 return NonNullUtils.checkNotNull(Collections.<ISegment> emptyList());
146 }
147 if (!overlaps(last, next)) {
148 displayData.add(next);
149 last = next;
150 }
151 }
152 return displayData;
153 }
154
155 private List<ISegment> convertIterableToList(final Iterable<ISegment> iterable, final IProgressMonitor statusMonitor) {
156 final List<ISegment> list = new ArrayList<>();
157 for (ISegment seg : iterable) {
158 if (statusMonitor.isCanceled()) {
159 return NonNullUtils.checkNotNull(Collections.<ISegment> emptyList());
160 }
161 list.add(seg);
162 }
163 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
164 return list;
165 }
166
167 private boolean overlaps(ISegment last, ISegment next) {
168 long timePerPix = fPixelSize;
169 final long start = last.getStart();
170 final long pixelStart = fPixelStart;
171 final long pixelDuration = start - pixelStart;
172 long startPixBoundL = pixelDuration / timePerPix * timePerPix + pixelStart;
173 long startPixBoundR = startPixBoundL + timePerPix;
174 final long currentStart = next.getStart();
175 if (currentStart >= startPixBoundL && currentStart <= startPixBoundR) {
176 long length = last.getLength();
177 long lengthNext = next.getLength();
178 long lengthLow = length / timePerPix * timePerPix;
179 long lengthHigh = lengthLow + timePerPix;
180 return (lengthNext >= lengthLow && lengthNext <= lengthHigh);
181 }
182 return false;
183 }
184 }
185
186 // ------------------------------------------------------------------------
187 // Attributes
188 // ------------------------------------------------------------------------
189
190 /**
191 * Listener to update the model with the semgent store analysis results
192 * once the analysis is fully completed
193 */
194 private final class AnalysisProgressListener implements IAnalysisProgressListener {
195
196 @Override
197 public void onComplete(AbstractSegmentStoreAnalysisModule activeAnalysis, ISegmentStore<ISegment> results) {
198 // Only update the model if trace that was analyzed is active trace
199 if (activeAnalysis.equals(getAnalysisModule())) {
200 updateModel(results);
201 updateRange(TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange());
202 }
203 }
204 }
205
206 private long fPixelSize = -1;
207
208 private long fPixelStart = 0;
209 /**
210 * Data to display
211 */
212 private Collection<ISegment> fDisplayData = NonNullUtils.checkNotNull(Collections.<ISegment> emptyList());
213
214 /**
215 * Analysis completion listener
216 */
217 private AnalysisProgressListener fListener;
218
219 /**
220 * Current analysis module
221 */
222 private @Nullable AbstractSegmentStoreAnalysisModule fAnalysisModule;
223
224 private @Nullable Job fCompactingJob;
225
226 // ------------------------------------------------------------------------
227 // Constructor
228 // ------------------------------------------------------------------------
229
230 /**
231 * Constructor
232 *
233 * @param parent
234 * parent composite
235 * @param title
236 * name of the graph
237 * @param xLabel
238 * name of the x axis
239 * @param yLabel
240 * name of the y axis
241 */
242 public AbstractSegmentStoreScatterGraphViewer(Composite parent, String title, String xLabel, String yLabel) {
243 super(parent, title, xLabel, yLabel);
244 fListener = new AnalysisProgressListener();
245 ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
246 initializeModule(trace);
247 getSwtChart().getLegend().setVisible(false);
248 }
249
250 private final void initializeModule(@Nullable ITmfTrace trace) {
251 if (trace != null) {
252 final AbstractSegmentStoreAnalysisModule analysisModuleOfClass = getSegmentStoreAnalysisModule(trace);
253 if (analysisModuleOfClass != null) {
254 analysisModuleOfClass.addListener(fListener);
255 setData(analysisModuleOfClass);
256 updateRange(TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange());
257 }
258 }
259 }
260
261 // ------------------------------------------------------------------------
262 // Operations
263 // ------------------------------------------------------------------------
264
265 /**
266 * Update the data in the graph
267 *
268 * @param dataInput
269 * new model
270 */
271 public void updateModel(@Nullable ISegmentStore<ISegment> dataInput) {
272 // Update new window range
273 TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
274 long currentStart = getTimeInNanos(currentRange.getStartTime());
275 long currentEnd = getTimeInNanos(currentRange.getEndTime());
276 if (dataInput == null) {
277 if (!getDisplay().isDisposed()) {
278 Display.getDefault().syncExec(new Runnable() {
279 @Override
280 public void run() {
281 clearContent();
282 }
283 });
284 }
285 fDisplayData = NonNullUtils.checkNotNull(Collections.EMPTY_LIST);
286 } else {
287 Collection<ISegment> elements = (Collection<ISegment>) dataInput.getIntersectingElements(currentStart, currentEnd);
288 // getIntersectingElements can return an unsorted iterable, make
289 // sure our collection is sorted
290 ArrayList<ISegment> list = new ArrayList<>(elements);
291 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
292 fDisplayData = list;
293 }
294 setWindowRange(currentStart, currentEnd);
295 updateRange(currentRange);
296 }
297
298 @Override
299 protected void initializeDataSource() {
300 ITmfTrace trace = getTrace();
301 initializeModule(trace);
302 if (trace != null) {
303 setData(getSegmentStoreAnalysisModule(trace));
304 }
305 }
306
307 @Override
308 protected void updateData(final long start, final long end, int nb, @Nullable IProgressMonitor monitor) {
309 // Third parameter is not used by implementation
310 // Determine data that needs to be visible
311 Collection<ISegment> data = fDisplayData;
312
313 final int dataSize = (nb == 0) ? data.size() : nb;
314 if (dataSize == 0 || end == start) {
315 return;
316 }
317
318 final double[] xSeries = new double[dataSize];
319 final double[] ySeries = new double[dataSize];
320 // For each visible segments, add start time to x value and duration
321 // for y value
322 Iterator<ISegment> modelIter = data.iterator();
323 long maxTempY = 1;
324 for (int i = 0; i < dataSize; i++) {
325 if (modelIter.hasNext()) {
326 ISegment segment = modelIter.next();
327 xSeries[i] = segment.getStart() - start;
328 ySeries[i] = segment.getLength();
329 maxTempY = Math.max(maxTempY, segment.getLength());
330 }
331 }
332 final long maxY = maxTempY;
333 setXAxis(xSeries);
334 final Chart swtChart = getSwtChart();
335 if (swtChart.isDisposed() || xSeries.length < 1) {
336 return;
337 }
338 swtChart.updateLayout();
339 setSeries(Messages.SegmentStoreScatterGraphViewer_legend, ySeries); // $NON-NLS-1$
340 final TmfChartTimeStampFormat tmfChartTimeStampFormat = new TmfChartTimeStampFormat(getTimeOffset());
341 ILineSeries series = (ILineSeries) swtChart.getSeriesSet().getSeries(Messages.SegmentStoreScatterGraphViewer_legend);
342 if (series == null) {
343 series = addSeries(Messages.SegmentStoreScatterGraphViewer_legend);
344 }
345 series.setXSeries(xSeries);
346 /* Find the minimal and maximum values in this series */
347 series.setYSeries(ySeries);
348
349 final IAxis xAxis = swtChart.getAxisSet().getXAxis(0);
350 IAxisTick xTick = xAxis.getTick();
351 xTick.setFormat(tmfChartTimeStampFormat);
352 xAxis.setRange(new Range(0.0, end - start));
353 if (maxY > 0.0) {
354 swtChart.getAxisSet().getYAxis(0).setRange(new Range(0.0, maxY));
355 }
356 swtChart.redraw();
357
358 if (isSendTimeAlignSignals()) {
359 // The width of the chart might have changed and its
360 // time axis might be misaligned with the other views
361 Point viewPos = AbstractSegmentStoreScatterGraphViewer.this.getParent().getParent().toDisplay(0, 0);
362 int axisPos = swtChart.toDisplay(0, 0).x + getPointAreaOffset();
363 int timeAxisOffset = axisPos - viewPos.x;
364 TmfTimeViewAlignmentInfo timeAlignmentInfo = new TmfTimeViewAlignmentInfo(getControl().getShell(), viewPos, timeAxisOffset);
365 TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(AbstractSegmentStoreScatterGraphViewer.this, timeAlignmentInfo, true));
366 }
367 }
368
369 @Override
370 protected void setWindowRange(final long windowStartTime, final long windowEndTime) {
371 super.setWindowRange(windowStartTime, windowEndTime);
372 }
373
374 @Override
375 protected ILineSeries addSeries(@Nullable String seriesName) {
376 ISeriesSet seriesSet = getSwtChart().getSeriesSet();
377 ILineSeries series = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
378 series.setVisible(true);
379 series.enableArea(false);
380 series.setLineStyle(LineStyle.NONE);
381 series.setSymbolType(PlotSymbolType.DIAMOND);
382 return series;
383 }
384
385 /**
386 * Set the data into the viewer. Will update model is analysis is completed
387 * or run analysis if not completed
388 *
389 * @param analysis
390 * Segment store analysis module
391 */
392 public void setData(@Nullable AbstractSegmentStoreAnalysisModule analysis) {
393 if (analysis == null) {
394 updateModel(null);
395 return;
396 }
397 ISegmentStore<ISegment> results = analysis.getResults();
398 // If results are not null, then analysis is completed and model can be
399 // updated
400 if (results != null) {
401 updateModel(results);
402 setAnalysisModule(analysis);
403 return;
404 }
405 updateModel(null);
406 analysis.addListener(fListener);
407 analysis.schedule();
408 setAnalysisModule(analysis);
409 }
410
411 /**
412 * Returns the segment store analysis module
413 * @param trace
414 * The trace to consider
415 * @return the analysis module
416 */
417 protected @Nullable abstract AbstractSegmentStoreAnalysisModule getSegmentStoreAnalysisModule(ITmfTrace trace);
418
419
420 // ------------------------------------------------------------------------
421 // Signal handlers
422 // ------------------------------------------------------------------------
423
424 /**
425 * @param signal
426 * Signal received when a different trace is selected
427 */
428 @Override
429 @TmfSignalHandler
430 public void traceSelected(@Nullable TmfTraceSelectedSignal signal) {
431 super.traceSelected(signal);
432 if (signal == null) {
433 return;
434 }
435 ITmfTrace trace = signal.getTrace();
436 setTrace(trace);
437 if (trace != null) {
438 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
439 setWindowRange(
440 timeRange.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue(),
441 timeRange.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue());
442 setData(getSegmentStoreAnalysisModule(trace));
443 updateRange(timeRange);
444 }
445 }
446
447 /**
448 * @param signal
449 * Signal received when trace is opened
450 */
451 @Override
452 @TmfSignalHandler
453 public void traceOpened(@Nullable TmfTraceOpenedSignal signal) {
454 super.traceOpened(signal);
455 if (signal == null) {
456 return;
457 }
458 ITmfTrace trace = signal.getTrace();
459 setTrace(trace);
460 if (trace != null) {
461
462 final AbstractSegmentStoreAnalysisModule analysisModuleOfClass = getSegmentStoreAnalysisModule(trace);
463 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
464 setWindowRange(
465 getTimeInNanos(timeRange.getStartTime()),
466 getTimeInNanos(timeRange.getEndTime()));
467 setData(analysisModuleOfClass);
468 }
469
470 }
471
472 private void updateRange(final @Nullable TmfTimeRange timeRange) {
473 Job compactingJob = fCompactingJob;
474 if (compactingJob != null && compactingJob.getState() == Job.RUNNING) {
475 compactingJob.cancel();
476 }
477 compactingJob = new CompactingSegmentStoreQuery(NonNullUtils.checkNotNull(timeRange));
478 fCompactingJob = compactingJob;
479 compactingJob.schedule();
480 }
481
482 /**
483 * @param signal
484 * Signal received when last opened trace is closed
485 */
486 @Override
487 @TmfSignalHandler
488 public void traceClosed(@Nullable TmfTraceClosedSignal signal) {
489 super.traceClosed(signal);
490 if (signal != null) {
491 // Check if there is no more opened trace
492 if (TmfTraceManager.getInstance().getActiveTrace() == null) {
493 AbstractSegmentStoreAnalysisModule analysis = getAnalysisModule();
494 if (analysis != null) {
495 // TODO remove listener analysis.
496 }
497 clearContent();
498 }
499 }
500 refresh();
501 }
502
503 /**
504 * @param signal
505 * Signal received when window range is updated
506 */
507 @Override
508 @TmfSignalHandler
509 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal) {
510 super.windowRangeUpdated(signal);
511 if (signal == null) {
512 return;
513 }
514 if (getTrace() != null) {
515 final TmfTimeRange currentRange = signal.getCurrentRange();
516 updateRange(currentRange);
517 } else {
518 Activator.getDefault().logInfo("No Trace to update"); //$NON-NLS-1$
519 }
520 }
521
522 private @Nullable AbstractSegmentStoreAnalysisModule getAnalysisModule() {
523 return fAnalysisModule;
524 }
525
526 private void setAnalysisModule(AbstractSegmentStoreAnalysisModule analysisModule) {
527 fAnalysisModule = analysisModule;
528 }
529
530 private static long getTimeInNanos(final ITmfTimestamp currentTime) {
531 return currentTime.normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
532 }
533}
This page took 0.046514 seconds and 5 git commands to generate.