1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
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 *******************************************************************************/
10 package org
.lttng
.scope
.tmf2
.views
.core
.timegraph
.model
.provider
.statesystem
;
12 import java
.util
.Collections
;
13 import java
.util
.LinkedList
;
14 import java
.util
.List
;
16 import java
.util
.concurrent
.FutureTask
;
17 import java
.util
.function
.Function
;
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
;
35 import com
.google
.common
.collect
.Iterables
;
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
;
43 * Basic implementation of a {@link TimeGraphModelStateProvider} backed by a state
46 * @author Alexandre Montplaisir
48 public class StateSystemModelStateProvider
extends TimeGraphModelStateProvider
{
51 * The context of a single state interval. Should contain all the
52 * information required to generate the state interval in the render (name,
55 protected static final class StateIntervalContext
{
58 public final ITmfStateSystem ss
;
59 /** Base tree element */
60 public final StateSystemTimeGraphTreeElement baseTreeElement
;
61 /** Source interval */
62 public final ITmfStateInterval sourceInterval
;
69 * @param baseTreeElement
70 * Tree element for which the data should be fetched. It may
71 * not correspond directly to the state's tree element, a
72 * relative path may be used, for example for additional data
73 * stored in a separate attribute.
74 * @param sourceInterval
75 * The state system interval which will be represented by the
76 * model state interval
78 public StateIntervalContext(ITmfStateSystem ss
,
79 StateSystemTimeGraphTreeElement baseTreeElement
,
80 ITmfStateInterval sourceInterval
) {
82 this.baseTreeElement
= baseTreeElement
;
83 this.sourceInterval
= sourceInterval
;
87 private final String fStateSystemModuleId
;
88 private final Function
<StateIntervalContext
, TimeGraphStateInterval
> fIntervalMappingFunction
;
91 * This state system here is not necessarily the same as the one in the
92 * {@link StateSystemModelProvider}!
94 private transient @Nullable ITmfStateSystem fStateSystem
= null;
99 * TODO Maybe merge the various Functions into a single class?
101 * @param stateDefinitions
102 * The state definitions used in this provider
103 * @param stateSystemModuleId
104 * The ID of the state system from which to fetch the information
105 * @param stateNameMappingFunction
106 * Mapping function from state interval context to state name
107 * @param labelMappingFunction
108 * Mapping function from state interval context to state label
109 * @param colorMappingFunction
110 * Mapping function from state interval context to state color
111 * @param lineThicknessMappingFunction
112 * Mapping function from state interval context to line thickness
113 * @param propertiesMappingFunction
114 * Mapping function from state interval context to properties
116 public StateSystemModelStateProvider(
117 List
<StateDefinition
> stateDefinitions
,
118 String stateSystemModuleId
,
119 Function
<StateIntervalContext
, String
> stateNameMappingFunction
,
120 Function
<StateIntervalContext
, @Nullable String
> labelMappingFunction
,
121 Function
<StateIntervalContext
, ConfigOption
<ColorDefinition
>> colorMappingFunction
,
122 Function
<StateIntervalContext
, ConfigOption
<LineThickness
>> lineThicknessMappingFunction
,
123 Function
<StateIntervalContext
, Map
<String
, String
>> propertiesMappingFunction
) {
125 super(stateDefinitions
);
127 fStateSystemModuleId
= stateSystemModuleId
;
129 fIntervalMappingFunction
= ssCtx
-> {
130 return new BasicTimeGraphStateInterval(
131 ssCtx
.sourceInterval
.getStartTime(),
132 ssCtx
.sourceInterval
.getEndTime(),
133 ssCtx
.baseTreeElement
,
134 stateNameMappingFunction
.apply(ssCtx
),
135 labelMappingFunction
.apply(ssCtx
),
136 colorMappingFunction
.apply(ssCtx
),
137 lineThicknessMappingFunction
.apply(ssCtx
),
138 propertiesMappingFunction
.apply(ssCtx
));
142 * Change listener which will take care of keeping the target state
145 traceProperty().addListener((obs
, oldValue
, newValue
) -> {
146 ITmfTrace trace
= newValue
;
152 // FIXME Remove the extra thread once we move to Jabberwocky
153 Thread thread
= new Thread(() -> {
154 fStateSystem
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, fStateSystemModuleId
);
161 // ------------------------------------------------------------------------
162 // Render generation methods
163 // ------------------------------------------------------------------------
166 public TimeGraphStateRender
getStateRender(TimeGraphTreeElement treeElement
,
167 TimeRange timeRange
, long resolution
, @Nullable FutureTask
<?
> task
) {
169 ITmfStateSystem ss
= fStateSystem
;
171 * Sometimes ss is null with uninitialized or empty views, just keep the model
175 || (task
!= null && task
.isCancelled())
176 /* "Title" entries should be ignored */
177 || !(treeElement
instanceof StateSystemTimeGraphTreeElement
)) {
179 return TimeGraphStateRender
.EMPTY_RENDER
;
181 StateSystemTimeGraphTreeElement treeElem
= (StateSystemTimeGraphTreeElement
) treeElement
;
183 /* Prepare the state intervals */
184 List
<TimeGraphStateInterval
> intervals
;
186 intervals
= queryHistoryRange(ss
, treeElem
,
187 timeRange
.getStart(), timeRange
.getEnd(), resolution
, task
);
188 } catch (AttributeNotFoundException
| StateSystemDisposedException e
) {
189 intervals
= Collections
.emptyList();
192 return new TimeGraphStateRender(timeRange
, treeElement
, intervals
);
195 private List
<TimeGraphStateInterval
> queryHistoryRange(ITmfStateSystem ss
,
196 StateSystemTimeGraphTreeElement treeElem
,
197 final long t1
, final long t2
, final long resolution
,
198 @Nullable FutureTask
<?
> task
)
199 throws AttributeNotFoundException
, StateSystemDisposedException
{
201 /* Validate the parameters. */
202 if (t2
< t1
|| resolution
<= 0) {
203 throw new IllegalArgumentException(ss
.getSSID() + " Start:" + t1
+ ", End:" + t2
+ ", Resolution:" + resolution
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
206 final List
<TimeGraphStateInterval
> modelIntervals
= new LinkedList
<>();
207 final int attributeQuark
= treeElem
.getSourceQuark();
208 ITmfStateInterval lastAddedInterval
= null;
210 /* Actual valid start/end time of the range query. */
211 long tStart
= Math
.max(t1
, ss
.getStartTime());
212 long tEnd
= Math
.min(t2
, ss
.getCurrentEndTime());
215 * First, iterate over the "resolution points" and keep all matching
218 for (long ts
= tStart
; ts
<= tEnd
- resolution
; ts
+= resolution
) {
220 * Skip queries if the corresponding interval was already included
222 if (lastAddedInterval
!= null && lastAddedInterval
.getEndTime() >= ts
) {
223 long nextTOffset
= MathUtils
.roundToClosestHigherMultiple(lastAddedInterval
.getEndTime() - tStart
, resolution
);
224 long nextTs
= tStart
+ nextTOffset
;
227 * The end time of the last interval happened to be exactly
228 * equal to the next resolution point. We will go to the
229 * resolution point after that then.
233 /* 'ts' will get incremented at next loop */
234 ts
= nextTs
- resolution
;
239 ITmfStateInterval stateSystemInterval
= ss
.querySingleState(ts
, attributeQuark
);
242 * Only pick the interval if it fills the current resolution range,
243 * from 'ts' to 'ts + resolution' (or 'ts2').
245 long ts2
= ts
+ resolution
;
246 if (stateSystemInterval
.getStartTime() <= ts
&& stateSystemInterval
.getEndTime() >= ts2
) {
247 TimeGraphStateInterval interval
= ssIntervalToModelInterval(ss
, treeElem
, stateSystemInterval
);
248 modelIntervals
.add(interval
);
249 lastAddedInterval
= stateSystemInterval
;
254 * For the very last interval, we'll use ['tEnd - resolution', 'tEnd']
255 * as a range condition instead.
257 long ts
= Math
.max(tStart
, tEnd
- resolution
);
259 if (lastAddedInterval
!= null && lastAddedInterval
.getEndTime() >= ts
) {
260 /* Interval already included */
262 ITmfStateInterval stateSystemInterval
= ss
.querySingleState(ts
, attributeQuark
);
263 if (stateSystemInterval
.getStartTime() <= ts
&& stateSystemInterval
.getEndTime() >= ts2
) {
264 TimeGraphStateInterval interval
= ssIntervalToModelInterval(ss
, treeElem
, stateSystemInterval
);
265 modelIntervals
.add(interval
);
270 * 'modelIntervals' now contains all the "real" intervals we will want
271 * to display in the view. Poly-filla the holes in between each using
272 * multi-state intervals.
274 if (modelIntervals
.size() < 2) {
275 return modelIntervals
;
278 List
<TimeGraphStateInterval
> filledIntervals
= new LinkedList
<>();
280 * Add the first real interval. There might be a multi-state at the
283 long firstRealIntervalStartTime
= modelIntervals
.get(0).getStartTime();
284 if (firstRealIntervalStartTime
> tStart
) {
285 filledIntervals
.add(new MultiStateInterval(tStart
, firstRealIntervalStartTime
- 1, treeElem
));
287 filledIntervals
.add(modelIntervals
.get(0));
289 for (int i
= 1; i
< modelIntervals
.size(); i
++) {
290 TimeGraphStateInterval interval1
= modelIntervals
.get(i
- 1);
291 TimeGraphStateInterval interval2
= modelIntervals
.get(i
);
292 long bound1
= interval1
.getEndTime();
293 long bound2
= interval2
.getStartTime();
295 /* (we've already inserted 'interval1' on the previous loop.) */
296 if (bound1
+ 1 != bound2
) {
297 TimeGraphStateInterval multiStateInterval
= new MultiStateInterval(bound1
+ 1, bound2
- 1, treeElem
);
298 filledIntervals
.add(multiStateInterval
);
300 filledIntervals
.add(interval2
);
303 /* Add a multi-state at the end too, if needed */
304 long lastRealIntervalEndTime
= Iterables
.getLast(modelIntervals
).getEndTime();
305 if (lastRealIntervalEndTime
< t2
) {
306 filledIntervals
.add(new MultiStateInterval(lastRealIntervalEndTime
+ 1, t2
, treeElem
));
309 return filledIntervals
;
312 private TimeGraphStateInterval
ssIntervalToModelInterval(ITmfStateSystem ss
,
313 StateSystemTimeGraphTreeElement treeElem
, ITmfStateInterval interval
) {
314 StateIntervalContext siCtx
= new StateIntervalContext(ss
, treeElem
, interval
);
315 return fIntervalMappingFunction
.apply(siCtx
);