1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 Ericsson
3 * All rights reserved. This program and the accompanying materials are
4 * made available under the terms of the Eclipse Public License v1.0 which
5 * accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Alexandre Montplaisir - Initial API and implementation
10 *******************************************************************************/
12 package org
.eclipse
.tracecompass
.internal
.tmf
.core
.statesystem
.backends
.partial
;
15 import java
.io
.FileInputStream
;
16 import java
.io
.PrintWriter
;
17 import java
.util
.List
;
19 import java
.util
.TreeMap
;
20 import java
.util
.concurrent
.CountDownLatch
;
22 import org
.eclipse
.jdt
.annotation
.NonNull
;
23 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
24 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
25 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.TmfStateInterval
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.request
.ITmfEventRequest
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.AbstractTmfStateProvider
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.ITmfStateProvider
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.ITmfTimestamp
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
42 * Partial state history back-end.
44 * This is a shim inserted between the real state system and a "real" history
45 * back-end. It will keep checkpoints, every n trace events (where n is called
46 * the granularity) and will only forward to the real state history the state
47 * intervals that crosses at least one checkpoint. Every other interval will
50 * This would mean that it can only answer queries exactly at the checkpoints.
51 * For any other timestamps (ie, most of the time), it will load the closest
52 * earlier checkpoint, and will re-feed the state-change-input with events from
53 * the trace, to restore the real state at the time that was requested.
55 * @author Alexandre Montplaisir
57 public class PartialHistoryBackend
implements IStateHistoryBackend
{
60 * A partial history needs the state input plugin to re-generate state
61 * between checkpoints.
63 private final @NonNull ITmfStateProvider fPartialInput
;
66 * Fake state system that is used for partially rebuilding the states (when
67 * going from a checkpoint to a target query timestamp).
69 private final @NonNull PartialStateSystem fPartialSS
;
71 /** Reference to the "real" state history that is used for storage */
72 private final @NonNull IStateHistoryBackend fInnerHistory
;
74 /** Checkpoints map, <Timestamp, Rank in the trace> */
75 private final @NonNull TreeMap
<Long
, Long
> fCheckpoints
= new TreeMap
<>();
77 /** Latch tracking if the initial checkpoint registration is done */
78 private final @NonNull CountDownLatch fCheckpointsReady
= new CountDownLatch(1);
80 private final long fGranularity
;
82 private long fLatestTime
;
88 * The state change input object that was used to build the
89 * upstream state system. This partial history will make its own
90 * copy (since they have different targets).
92 * The partial history's inner state system. It should already be
93 * assigned to partialInput.
95 * The real state history back-end to use. It's supposed to be
96 * modular, so it should be able to be of any type.
98 * Configuration parameter indicating how many trace events there
99 * should be between each checkpoint
101 public PartialHistoryBackend(ITmfStateProvider partialInput
, PartialStateSystem pss
,
102 IStateHistoryBackend realBackend
, long granularity
) {
103 if (granularity
<= 0 || partialInput
== null || pss
== null ||
104 partialInput
.getAssignedStateSystem() != pss
) {
105 throw new IllegalArgumentException();
108 final long startTime
= realBackend
.getStartTime();
110 fPartialInput
= partialInput
;
113 fInnerHistory
= realBackend
;
114 fGranularity
= granularity
;
116 fLatestTime
= startTime
;
118 registerCheckpoints();
121 private void registerCheckpoints() {
122 ITmfEventRequest request
= new CheckpointsRequest(fPartialInput
, fCheckpoints
);
123 fPartialInput
.getTrace().sendRequest(request
);
124 /* The request will countDown the checkpoints latch once it's finished */
128 public long getStartTime() {
129 return fInnerHistory
.getStartTime();
133 public long getEndTime() {
138 public void insertPastState(long stateStartTime
, long stateEndTime
,
139 int quark
, ITmfStateValue value
) throws TimeRangeException
{
140 waitForCheckpoints();
142 /* Update the latest time */
143 if (stateEndTime
> fLatestTime
) {
144 fLatestTime
= stateEndTime
;
148 * Check if the interval intersects the previous checkpoint. If so,
149 * insert it in the real history back-end.
151 * FIXME since intervals are inserted in order of rank, we could avoid
152 * doing a map lookup every time here (just compare with the known
155 if (stateStartTime
<= fCheckpoints
.floorKey(stateEndTime
)) {
156 fInnerHistory
.insertPastState(stateStartTime
, stateEndTime
, quark
, value
);
161 public void finishedBuilding(long endTime
) throws TimeRangeException
{
162 fInnerHistory
.finishedBuilding(endTime
);
166 public FileInputStream
supplyAttributeTreeReader() {
167 return fInnerHistory
.supplyAttributeTreeReader();
171 public File
supplyAttributeTreeWriterFile() {
172 return fInnerHistory
.supplyAttributeTreeWriterFile();
176 public long supplyAttributeTreeWriterFilePosition() {
177 return fInnerHistory
.supplyAttributeTreeWriterFilePosition();
181 public void removeFiles() {
182 fInnerHistory
.removeFiles();
186 public void dispose() {
187 fPartialInput
.dispose();
188 fPartialSS
.dispose();
189 fInnerHistory
.dispose();
193 public void doQuery(List
<ITmfStateInterval
> currentStateInfo
, long t
)
194 throws TimeRangeException
, StateSystemDisposedException
{
195 /* Wait for required steps to be done */
196 waitForCheckpoints();
197 fPartialSS
.getUpstreamSS().waitUntilBuilt();
199 if (!checkValidTime(t
)) {
200 throw new TimeRangeException();
203 /* Reload the previous checkpoint */
204 long checkpointTime
= fCheckpoints
.floorKey(t
);
205 fInnerHistory
.doQuery(currentStateInfo
, checkpointTime
);
208 * Set the initial contents of the partial state system (which is the
209 * contents of the query at the checkpoint).
211 fPartialSS
.takeQueryLock();
212 fPartialSS
.replaceOngoingState(currentStateInfo
);
214 /* Send an event request to update the state system to the target time. */
215 TmfTimeRange range
= new TmfTimeRange(
217 * The state at the checkpoint already includes any state change
218 * caused by the event(s) happening exactly at 'checkpointTime',
219 * if any. We must not include those events in the query.
221 new TmfTimestamp(checkpointTime
+ 1, ITmfTimestamp
.NANOSECOND_SCALE
),
222 new TmfTimestamp(t
, ITmfTimestamp
.NANOSECOND_SCALE
));
223 ITmfEventRequest request
= new PartialStateSystemRequest(fPartialInput
, range
);
224 fPartialInput
.getTrace().sendRequest(request
);
227 request
.waitForCompletion();
228 } catch (InterruptedException e
) {
233 * Now the partial state system should have the ongoing time we are
234 * looking for. However, the method expects a List of *state intervals*,
235 * not state values, so we'll create intervals with a dummy end time.
238 for (int i
= 0; i
< currentStateInfo
.size(); i
++) {
240 ITmfStateValue val
= null;
241 start
= ((ITmfStateSystem
) fPartialSS
).getOngoingStartTime(i
);
242 val
= ((ITmfStateSystem
) fPartialSS
).queryOngoingState(i
);
244 ITmfStateInterval interval
= new TmfStateInterval(start
, t
, i
, val
);
245 currentStateInfo
.set(i
, interval
);
247 } catch (AttributeNotFoundException e
) {
248 /* Should not happen, we iterate over existing values. */
252 fPartialSS
.releaseQueryLock();
256 * Single queries are not supported in partial histories. To get the same
257 * result you can do a full query, then call fullState.get(attribute).
260 public ITmfStateInterval
doSingularQuery(long t
, int attributeQuark
) {
261 throw new UnsupportedOperationException();
264 private boolean checkValidTime(long t
) {
265 return (t
>= getStartTime() && t
<= getEndTime());
269 public void debugPrint(PrintWriter writer
) {
270 // TODO Auto-generated method stub
273 private void waitForCheckpoints() {
275 fCheckpointsReady
.await();
276 } catch (InterruptedException e
) {
281 // ------------------------------------------------------------------------
282 // Event requests types
283 // ------------------------------------------------------------------------
285 private class CheckpointsRequest
extends TmfEventRequest
{
286 private final ITmfTrace trace
;
287 private final Map
<Long
, Long
> checkpts
;
288 private long eventCount
;
289 private long lastCheckpointAt
;
291 public CheckpointsRequest(ITmfStateProvider input
, Map
<Long
, Long
> checkpoints
) {
292 super(ITmfEvent
.class,
293 TmfTimeRange
.ETERNITY
,
295 ITmfEventRequest
.ALL_DATA
,
296 ITmfEventRequest
.ExecutionType
.FOREGROUND
);
298 this.trace
= input
.getTrace();
299 this.checkpts
= checkpoints
;
301 lastCheckpointAt
= 0;
303 /* Insert a checkpoint at the start of the trace */
304 checkpoints
.put(input
.getStartTime(), 0L);
308 public void handleData(final ITmfEvent event
) {
309 super.handleData(event
);
310 if (event
.getTrace() == trace
) {
313 /* Check if we need to register a new checkpoint */
314 if (eventCount
>= lastCheckpointAt
+ fGranularity
) {
315 checkpts
.put(event
.getTimestamp().getValue(), eventCount
);
316 lastCheckpointAt
= eventCount
;
322 public void handleCompleted() {
323 super.handleCompleted();
324 fCheckpointsReady
.countDown();
328 private class PartialStateSystemRequest
extends TmfEventRequest
{
329 private final ITmfStateProvider sci
;
330 private final ITmfTrace trace
;
332 PartialStateSystemRequest(ITmfStateProvider sci
, TmfTimeRange range
) {
333 super(ITmfEvent
.class,
336 ITmfEventRequest
.ALL_DATA
,
337 ITmfEventRequest
.ExecutionType
.BACKGROUND
);
339 this.trace
= sci
.getTrace();
343 public void handleData(final ITmfEvent event
) {
344 super.handleData(event
);
345 if (event
.getTrace() == trace
) {
346 sci
.processEvent(event
);
351 public void handleCompleted() {
353 * If we're using a threaded state provider, we need to make sure
354 * all events have been handled by the state system before doing
357 if (fPartialInput
instanceof AbstractTmfStateProvider
) {
358 ((AbstractTmfStateProvider
) fPartialInput
).waitForEmptyQueue();
360 super.handleCompleted();