f737efe6f8d7daf3969dedbbaad670f0761ae53e
[deliverable/tracecompass.git] / tmf / org.lttng.scope.tmf2.views.core / src / org / lttng / scope / tmf2 / views / core / timegraph / model / provider / statesystem / StateSystemModelStateProvider.java
1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
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
10 package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
11
12 import java.util.Collections;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.concurrent.FutureTask;
17 import java.util.function.Function;
18
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
21 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
22 import org.lttng.scope.tmf2.views.core.MathUtils;
23 import org.lttng.scope.tmf2.views.core.TimeRange;
24 import org.lttng.scope.tmf2.views.core.config.ConfigOption;
25 import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.TimeGraphModelStateProvider;
26 import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
27 import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
28 import org.lttng.scope.tmf2.views.core.timegraph.model.render.StateDefinition;
29 import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.BasicTimeGraphStateInterval;
30 import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.MultiStateInterval;
31 import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateInterval;
32 import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
33 import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
34
35 import com.google.common.collect.Iterables;
36
37 import ca.polymtl.dorsal.libdelorean.ITmfStateSystem;
38 import ca.polymtl.dorsal.libdelorean.exceptions.AttributeNotFoundException;
39 import ca.polymtl.dorsal.libdelorean.exceptions.StateSystemDisposedException;
40 import ca.polymtl.dorsal.libdelorean.interval.ITmfStateInterval;
41
42 /**
43 * Basic implementation of a {@link TimeGraphModelStateProvider} backed by a state
44 * system.
45 *
46 * @author Alexandre Montplaisir
47 */
48 public class StateSystemModelStateProvider extends TimeGraphModelStateProvider {
49
50 /**
51 * The context of a single state interval. Should contain all the
52 * information required to generate the state interval in the render (name,
53 * color, etc.)
54 */
55 protected static final class StateIntervalContext {
56
57 /** State system */
58 public final ITmfStateSystem ss;
59 /** Base tree element */
60 public final StateSystemTimeGraphTreeElement baseTreeElement;
61 /** Source interval */
62 public final ITmfStateInterval sourceInterval;
63
64 /**
65 * Full state system query at the start of the interval
66 *
67 * FIXME Remove this!
68 */
69 public final List<ITmfStateInterval> fullQueryAtIntervalStart;
70
71 /**
72 * Constructor
73 *
74 * @param ss
75 * State system
76 * @param baseTreeElement
77 * Tree element for which the data should be fetched. It may
78 * not correspond directly to the state's tree element, a
79 * relative path may be used, for example for additional data
80 * stored in a separate attribute.
81 * @param sourceInterval
82 * The state system interval which will be represented by the
83 * model state interval
84 * @param fullQueryAtIntervalStart
85 * Full query at the start of the interval. FIXME Remove
86 * this! This should only be queried on-demand, not for every
87 * single interval
88 */
89 public StateIntervalContext(ITmfStateSystem ss,
90 StateSystemTimeGraphTreeElement baseTreeElement,
91 ITmfStateInterval sourceInterval,
92 List<ITmfStateInterval> fullQueryAtIntervalStart) {
93 this.ss = ss;
94 this.baseTreeElement = baseTreeElement;
95 this.sourceInterval = sourceInterval;
96 this.fullQueryAtIntervalStart = fullQueryAtIntervalStart;
97 }
98 }
99
100 private final String fStateSystemModuleId;
101 private final Function<StateIntervalContext, TimeGraphStateInterval> fIntervalMappingFunction;
102
103 /**
104 * This state system here is not necessarily the same as the one in the
105 * {@link StateSystemModelProvider}!
106 */
107 private transient @Nullable ITmfStateSystem fStateSystem = null;
108
109 /**
110 * Constructor
111 *
112 * TODO Maybe merge the various Functions into a single class?
113 *
114 * @param stateDefinitions
115 * The state definitions used in this provider
116 * @param stateSystemModuleId
117 * The ID of the state system from which to fetch the information
118 * @param stateNameMappingFunction
119 * Mapping function from state interval context to state name
120 * @param labelMappingFunction
121 * Mapping function from state interval context to state label
122 * @param colorMappingFunction
123 * Mapping function from state interval context to state color
124 * @param lineThicknessMappingFunction
125 * Mapping function from state interval context to line thickness
126 * @param propertiesMappingFunction
127 * Mapping function from state interval context to properties
128 */
129 public StateSystemModelStateProvider(
130 List<StateDefinition> stateDefinitions,
131 String stateSystemModuleId,
132 Function<StateIntervalContext, String> stateNameMappingFunction,
133 Function<StateIntervalContext, @Nullable String> labelMappingFunction,
134 Function<StateIntervalContext, ConfigOption<ColorDefinition>> colorMappingFunction,
135 Function<StateIntervalContext, ConfigOption<LineThickness>> lineThicknessMappingFunction,
136 Function<StateIntervalContext, Map<String, String>> propertiesMappingFunction) {
137
138 super(stateDefinitions);
139
140 fStateSystemModuleId = stateSystemModuleId;
141
142 fIntervalMappingFunction = ssCtx -> {
143 return new BasicTimeGraphStateInterval(
144 ssCtx.sourceInterval.getStartTime(),
145 ssCtx.sourceInterval.getEndTime(),
146 ssCtx.baseTreeElement,
147 stateNameMappingFunction.apply(ssCtx),
148 labelMappingFunction.apply(ssCtx),
149 colorMappingFunction.apply(ssCtx),
150 lineThicknessMappingFunction.apply(ssCtx),
151 propertiesMappingFunction.apply(ssCtx));
152 };
153
154 /*
155 * Change listener which will take care of keeping the target state
156 * system up to date.
157 */
158 traceProperty().addListener((obs, oldValue, newValue) -> {
159 ITmfTrace trace = newValue;
160 if (trace == null) {
161 fStateSystem = null;
162 return;
163 }
164
165 // FIXME Remove the extra thread once we move to Jabberwocky
166 Thread thread = new Thread(() -> {
167 fStateSystem = TmfStateSystemAnalysisModule.getStateSystem(trace, fStateSystemModuleId);
168 });
169 thread.start();
170 });
171
172 }
173
174 // ------------------------------------------------------------------------
175 // Render generation methods
176 // ------------------------------------------------------------------------
177
178 @Override
179 public TimeGraphStateRender getStateRender(TimeGraphTreeElement treeElement,
180 TimeRange timeRange, long resolution, @Nullable FutureTask<?> task) {
181
182 ITmfStateSystem ss = fStateSystem;
183 if (ss == null) {
184 /* Has been called with an invalid trace/treeElement */
185 throw new IllegalArgumentException();
186 }
187
188 if (task != null && task.isCancelled()) {
189 return TimeGraphStateRender.EMPTY_RENDER;
190 }
191
192 // FIXME Add generic type?
193 StateSystemTimeGraphTreeElement treeElem = (StateSystemTimeGraphTreeElement) treeElement;
194
195 /* Prepare the state intervals */
196 List<TimeGraphStateInterval> intervals;
197 try {
198 intervals = queryHistoryRange(ss, treeElem,
199 timeRange.getStart(), timeRange.getEnd(), resolution, task);
200 } catch (AttributeNotFoundException | StateSystemDisposedException e) {
201 intervals = Collections.emptyList();
202 }
203
204 return new TimeGraphStateRender(timeRange, treeElement, intervals);
205 }
206
207 private List<TimeGraphStateInterval> queryHistoryRange(ITmfStateSystem ss,
208 StateSystemTimeGraphTreeElement treeElem,
209 final long t1, final long t2, final long resolution,
210 @Nullable FutureTask<?> task)
211 throws AttributeNotFoundException, StateSystemDisposedException {
212
213 /* Validate the parameters. */
214 if (t2 < t1 || resolution <= 0) {
215 throw new IllegalArgumentException(ss.getSSID() + " Start:" + t1 + ", End:" + t2 + ", Resolution:" + resolution); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216 }
217
218 final List<TimeGraphStateInterval> modelIntervals = new LinkedList<>();
219 final int attributeQuark = treeElem.getSourceQuark();
220 ITmfStateInterval lastAddedInterval = null;
221
222 /* Actual valid start/end time of the range query. */
223 long tStart = Math.max(t1, ss.getStartTime());
224 long tEnd = Math.min(t2, ss.getCurrentEndTime());
225
226 /*
227 * First, iterate over the "resolution points" and keep all matching
228 * state intervals.
229 */
230 for (long ts = tStart; ts <= tEnd - resolution; ts += resolution) {
231 /*
232 * Skip queries if the corresponding interval was already included
233 */
234 if (lastAddedInterval != null && lastAddedInterval.getEndTime() >= ts) {
235 long nextTOffset = MathUtils.roundToClosestHigherMultiple(lastAddedInterval.getEndTime() - tStart, resolution);
236 long nextTs = tStart + nextTOffset;
237 if (nextTs == ts) {
238 /*
239 * The end time of the last interval happened to be exactly
240 * equal to the next resolution point. We will go to the
241 * resolution point after that then.
242 */
243 ts = nextTs;
244 } else {
245 /* 'ts' will get incremented at next loop */
246 ts = nextTs - resolution;
247 }
248 continue;
249 }
250
251 ITmfStateInterval stateSystemInterval = ss.querySingleState(ts, attributeQuark);
252
253 /*
254 * Only pick the interval if it fills the current resolution range,
255 * from 'ts' to 'ts + resolution' (or 'ts2').
256 */
257 long ts2 = ts + resolution;
258 if (stateSystemInterval.getStartTime() <= ts && stateSystemInterval.getEndTime() >= ts2) {
259 TimeGraphStateInterval interval = ssIntervalToModelInterval(ss, treeElem, stateSystemInterval);
260 modelIntervals.add(interval);
261 lastAddedInterval = stateSystemInterval;
262 }
263 }
264
265 /*
266 * For the very last interval, we'll use ['tEnd - resolution', 'tEnd']
267 * as a range condition instead.
268 */
269 long ts = Math.max(tStart, tEnd - resolution);
270 long ts2 = tEnd;
271 if (lastAddedInterval != null && lastAddedInterval.getEndTime() >= ts) {
272 /* Interval already included */
273 } else {
274 ITmfStateInterval stateSystemInterval = ss.querySingleState(ts, attributeQuark);
275 if (stateSystemInterval.getStartTime() <= ts && stateSystemInterval.getEndTime() >= ts2) {
276 TimeGraphStateInterval interval = ssIntervalToModelInterval(ss, treeElem, stateSystemInterval);
277 modelIntervals.add(interval);
278 }
279 }
280
281 /*
282 * 'modelIntervals' now contains all the "real" intervals we will want
283 * to display in the view. Poly-filla the holes in between each using
284 * multi-state intervals.
285 */
286 if (modelIntervals.size() < 2) {
287 return modelIntervals;
288 }
289
290 List<TimeGraphStateInterval> filledIntervals = new LinkedList<>();
291 /*
292 * Add the first real interval. There might be a multi-state at the
293 * beginning.
294 */
295 long firstRealIntervalStartTime = modelIntervals.get(0).getStartTime();
296 if (firstRealIntervalStartTime > tStart) {
297 filledIntervals.add(new MultiStateInterval(tStart, firstRealIntervalStartTime - 1, treeElem));
298 }
299 filledIntervals.add(modelIntervals.get(0));
300
301 for (int i = 1; i < modelIntervals.size(); i++) {
302 TimeGraphStateInterval interval1 = modelIntervals.get(i - 1);
303 TimeGraphStateInterval interval2 = modelIntervals.get(i);
304 long bound1 = interval1.getEndTime();
305 long bound2 = interval2.getStartTime();
306
307 /* (we've already inserted 'interval1' on the previous loop.) */
308 if (bound1 + 1 != bound2) {
309 TimeGraphStateInterval multiStateInterval = new MultiStateInterval(bound1 + 1, bound2 - 1, treeElem);
310 filledIntervals.add(multiStateInterval);
311 }
312 filledIntervals.add(interval2);
313 }
314
315 /* Add a multi-state at the end too, if needed */
316 long lastRealIntervalEndTime = Iterables.getLast(modelIntervals).getEndTime();
317 if (lastRealIntervalEndTime < t2) {
318 filledIntervals.add(new MultiStateInterval(lastRealIntervalEndTime + 1, t2, treeElem));
319 }
320
321 return filledIntervals;
322 }
323
324 private TimeGraphStateInterval ssIntervalToModelInterval(ITmfStateSystem ss,
325 StateSystemTimeGraphTreeElement treeElem, ITmfStateInterval interval) {
326 List<ITmfStateInterval> fullState;
327 try {
328 // TODO Big performance improvement low-hanging fruit here
329 fullState = ss.queryFullState(interval.getStartTime());
330 } catch (StateSystemDisposedException e) {
331 fullState = Collections.emptyList();
332 e.printStackTrace();
333 }
334 StateIntervalContext siCtx = new StateIntervalContext(ss, treeElem, interval, fullState);
335 return fIntervalMappingFunction.apply(siCtx);
336 }
337
338 }
This page took 0.037622 seconds and 4 git commands to generate.