1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
6 * All rights reserved. This program and the accompanying materials are
7 * made available under the terms of the Eclipse Public License v1.0 which
8 * accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
12 * Alexandre Montplaisir - Initial API and implementation
13 * Patrick Tasse - Add message to exceptions
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
;
18 import java
.io
.PrintWriter
;
19 import java
.util
.ArrayList
;
20 import java
.util
.List
;
21 import java
.util
.concurrent
.locks
.ReentrantReadWriteLock
;
23 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
24 import org
.eclipse
.jdt
.annotation
.Nullable
;
25 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.TmfStateInterval
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
.Type
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
36 * The Transient State is used to build intervals from punctual state changes.
37 * It contains a "state info" vector similar to the "current state", except here
38 * we also record the start time of every state stored in it.
40 * We can then build {@link ITmfStateInterval}'s, to be inserted in a
41 * {@link IStateHistoryBackend} when we detect state changes : the "start time"
42 * of the interval will be the recorded time we have here, and the "end time"
43 * will be the timestamp of the new state-changing event we just read.
45 * @author Alexandre Montplaisir
48 public class TransientState
{
50 /* Indicates where to insert state changes that we generate */
51 private final IStateHistoryBackend fBackend
;
53 private final ReentrantReadWriteLock fRWLock
= new ReentrantReadWriteLock(false);
55 private volatile boolean fIsActive
;
56 private volatile long fLatestTime
;
58 /* A method accessing these arrays will have to go through the lock */
59 private List
<ITmfStateValue
> fOngoingStateInfo
;
60 private List
<Long
> fOngoingStateStartTimes
;
61 private List
<Type
> fStateValueTypes
;
67 * The back-end in which to insert the generated state intervals
69 public TransientState(IStateHistoryBackend backend
) {
72 fOngoingStateInfo
= new ArrayList
<>();
73 fOngoingStateStartTimes
= new ArrayList
<>();
74 fStateValueTypes
= new ArrayList
<>();
76 fLatestTime
= backend
.getStartTime();
80 * Get the latest time we have seen so far.
82 * @return The latest time seen in the transient state
84 public long getLatestTime() {
89 * Retrieve the ongoing state value for a given index (attribute quark).
92 * The quark of the attribute to look for
93 * @return The corresponding state value
94 * @throws AttributeNotFoundException
95 * If the quark is invalid
97 public ITmfStateValue
getOngoingStateValue(int quark
) throws AttributeNotFoundException
{
98 fRWLock
.readLock().lock();
100 checkValidAttribute(quark
);
101 ITmfStateValue ret
= fOngoingStateInfo
.get(quark
);
103 throw new IllegalStateException("Null interval stored in transient state"); //$NON-NLS-1$
107 fRWLock
.readLock().unlock();
112 * Retrieve the start time of the state in which the given attribute is in.
115 * The quark of the attribute to look for
116 * @return The start time of the current state for this attribute
117 * @throws AttributeNotFoundException
118 * If the quark is invalid
120 public long getOngoingStartTime(int quark
) throws AttributeNotFoundException
{
121 fRWLock
.readLock().lock();
123 checkValidAttribute(quark
);
124 return fOngoingStateStartTimes
.get(quark
);
126 fRWLock
.readLock().unlock();
131 * Modify the current state for a given attribute. This will not update the
132 * "ongoing state start time" in any way, so be careful when using this.
135 * The quark of the attribute to modify
137 * The state value the attribute should have
138 * @throws AttributeNotFoundException
139 * If the quark is invalid
141 public void changeOngoingStateValue(int quark
, ITmfStateValue newValue
)
142 throws AttributeNotFoundException
{
143 fRWLock
.writeLock().lock();
145 checkValidAttribute(quark
);
146 fOngoingStateInfo
.set(quark
, newValue
);
148 fRWLock
.writeLock().unlock();
153 * Convenience method to return the "ongoing" value for a given attribute as
154 * a dummy interval whose end time = the current latest time.
157 * The quark of the attribute
158 * @return An interval representing the current state (but whose end time is
159 * the current one, and probably not the "final" one)
160 * @throws AttributeNotFoundException
161 * If the quark is invalid
163 public ITmfStateInterval
getOngoingInterval(int quark
) throws AttributeNotFoundException
{
164 fRWLock
.readLock().lock();
166 checkValidAttribute(quark
);
167 return new TmfStateInterval(fOngoingStateStartTimes
.get(quark
), fLatestTime
,
168 quark
, fOngoingStateInfo
.get(quark
));
170 fRWLock
.readLock().unlock();
175 * Try to get the state interval valid for time/quark, if it is present in
176 * this transient state. If it is not (for example, a new value is active
177 * since after the specified timestamp) then null will be returned.
180 * The timestamp to look for
182 * The quark of the attribute to look for
183 * @return The corresponding TmfStateInterval object if we could find it in
184 * this transient state, or null if we couldn't.
186 public @Nullable ITmfStateInterval
getIntervalAt(long time
, int quark
) {
187 fRWLock
.readLock().lock();
189 checkValidAttribute(quark
);
190 if (!isActive() || time
< fOngoingStateStartTimes
.get(quark
)) {
193 return new TmfStateInterval(fOngoingStateStartTimes
.get(quark
),
194 fLatestTime
, quark
, fOngoingStateInfo
.get(quark
));
195 } catch (AttributeNotFoundException e
) {
198 fRWLock
.readLock().unlock();
202 private void checkValidAttribute(int quark
) throws AttributeNotFoundException
{
203 if (quark
> fOngoingStateInfo
.size() - 1 || quark
< 0) {
204 throw new AttributeNotFoundException(fBackend
.getSSID() + " Quark:" + quark
); //$NON-NLS-1$
209 * More advanced version of {@link #changeOngoingStateValue}. Replaces the
210 * complete ongoingStateInfo in one go, and updates the
211 * ongoingStateStartTimes and #stateValuesTypes accordingly. BE VERY CAREFUL
214 * @param newStateIntervals
215 * The List of intervals that will represent the new
216 * "ongoing state". Their end times don't matter, we will only
217 * check their value and start times.
219 public void replaceOngoingState(List
<ITmfStateInterval
> newStateIntervals
) {
220 final int size
= newStateIntervals
.size();
222 fRWLock
.writeLock().lock();
224 fOngoingStateInfo
= new ArrayList
<>(size
);
225 fOngoingStateStartTimes
= new ArrayList
<>(size
);
226 fStateValueTypes
= new ArrayList
<>(size
);
228 for (ITmfStateInterval interval
: newStateIntervals
) {
229 fOngoingStateInfo
.add(interval
.getStateValue());
230 fOngoingStateStartTimes
.add(interval
.getStartTime());
231 fStateValueTypes
.add(interval
.getStateValue().getType());
234 fRWLock
.writeLock().unlock();
239 * Add an "empty line" to both "ongoing..." vectors. This is needed so the
240 * Ongoing... tables can stay in sync with the number of attributes in the
241 * attribute tree, namely when we add sub-path attributes.
243 public void addEmptyEntry() {
244 fRWLock
.writeLock().lock();
247 * Since this is a new attribute, we suppose it was in the
248 * "null state" since the beginning (so we can have intervals
249 * covering for all timestamps). A null interval will then get added
250 * at the first state change.
252 fOngoingStateInfo
.add(TmfStateValue
.nullValue());
253 fStateValueTypes
.add(Type
.NULL
);
255 fOngoingStateStartTimes
.add(fBackend
.getStartTime());
257 fRWLock
.writeLock().unlock();
262 * Process a state change to be inserted in the history.
265 * The timestamp associated with this state change
267 * The new StateValue associated to this attribute
269 * The quark of the attribute that is being modified
270 * @throws TimeRangeException
271 * If 'eventTime' is invalid
272 * @throws AttributeNotFoundException
273 * IF 'quark' does not represent an existing attribute
274 * @throws StateValueTypeException
275 * If the state value to be inserted is of a different type of
276 * what was inserted so far for this attribute.
278 public void processStateChange(long eventTime
, ITmfStateValue value
, int quark
)
279 throws TimeRangeException
, AttributeNotFoundException
, StateValueTypeException
{
280 if (!this.fIsActive
) {
284 fRWLock
.writeLock().lock();
286 Type expectedSvType
= fStateValueTypes
.get(quark
);
287 checkValidAttribute(quark
);
290 * Make sure the state value type we're inserting is the same as the
291 * one registered for this attribute.
293 if (expectedSvType
== Type
.NULL
) {
295 * The value hasn't been used yet, set it to the value we're
296 * currently inserting (which might be null/-1 again).
298 fStateValueTypes
.set(quark
, value
.getType());
299 } else if ((value
.getType() != Type
.NULL
) && (value
.getType() != expectedSvType
)) {
301 * We authorize inserting null values in any type of attribute,
302 * but for every other types, it needs to match our
305 throw new StateValueTypeException(fBackend
.getSSID() + " Quark:" + quark
+ ", Type:" + value
.getType() + ", Expected:" + expectedSvType
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
308 if (fOngoingStateInfo
.get(quark
).equals(value
)) {
310 * This is the case where the new value and the one already
311 * present in the Builder are the same. We do not need to create
312 * an interval, we'll just keep the current one going.
317 if (fOngoingStateStartTimes
.get(quark
) < eventTime
) {
319 * These two conditions are necessary to create an interval and
320 * update ongoingStateInfo.
322 fBackend
.insertPastState(fOngoingStateStartTimes
.get(quark
),
323 eventTime
- 1, /* End Time */
324 quark
, /* attribute quark */
325 fOngoingStateInfo
.get(quark
)); /* StateValue */
327 fOngoingStateStartTimes
.set(quark
, eventTime
);
329 fOngoingStateInfo
.set(quark
, value
);
331 /* Update the Transient State's lastestTime, if needed */
332 if (fLatestTime
< eventTime
) {
333 fLatestTime
= eventTime
;
337 fRWLock
.writeLock().unlock();
342 * Run a "get state at time" query on the Transient State only.
345 * The stateInfo object in which we will put our relevant
348 * The requested timestamp
350 public void doQuery(List
<ITmfStateInterval
> stateInfo
, long t
) {
351 fRWLock
.readLock().lock();
353 if (!this.fIsActive
) {
356 if (stateInfo
.size() > fOngoingStateInfo
.size()) {
357 throw new IllegalArgumentException();
360 for (int i
= 0; i
< stateInfo
.size(); i
++) {
362 * We build a dummy interval whose end time =
363 * "current transient state end time" to put in the answer to
366 final ITmfStateInterval interval
= getIntervalAt(t
, i
);
367 if (interval
!= null) {
368 stateInfo
.set(i
, interval
);
372 fRWLock
.readLock().unlock();
377 * Close off the Transient State, used for example when we are done reading
378 * a static trace file. All the information currently contained in it will
379 * be converted to intervals and "flushed" to the state history.
382 * The timestamp to use as end time for the state history (since
383 * it may be different than the timestamp of the last state
386 public void closeTransientState(long endTime
) {
387 if (!this.fIsActive
) {
391 fRWLock
.writeLock().lock();
393 for (int i
= 0; i
< fOngoingStateInfo
.size(); i
++) {
394 if (fOngoingStateStartTimes
.get(i
) > endTime
) {
396 * Handle the cases where trace end > timestamp of last
397 * state change. This can happen when inserting "future"
403 fBackend
.insertPastState(fOngoingStateStartTimes
.get(i
),
404 endTime
, /* End Time */
405 i
, /* attribute quark */
406 fOngoingStateInfo
.get(i
)); /* StateValue */
408 } catch (TimeRangeException e
) {
410 * This shouldn't happen, since we control where the
411 * interval's start time comes from
413 throw new IllegalStateException(e
);
417 fOngoingStateInfo
.clear();
418 fOngoingStateStartTimes
.clear();
419 this.fIsActive
= false;
422 fRWLock
.writeLock().unlock();
427 * Simply returns if this Transient State is currently being used or not
429 * @return True if this transient state is active
431 public boolean isActive() {
432 return this.fIsActive
;
436 * Mark this transient state as inactive
438 public void setInactive() {
443 * Debugging method that prints the contents of the transient state
446 * The writer to which the output should be written
448 public void debugPrint(PrintWriter writer
) {
449 /* Only used for debugging, shouldn't be externalized */
450 writer
.println("------------------------------"); //$NON-NLS-1$
451 writer
.println("Info stored in the Builder:"); //$NON-NLS-1$
452 if (!this.fIsActive
) {
453 writer
.println("Builder is currently inactive"); //$NON-NLS-1$
454 writer
.println('\n');
457 writer
.println("\nAttribute\tStateValue\tValid since time"); //$NON-NLS-1$
458 for (int i
= 0; i
< fOngoingStateInfo
.size(); i
++) {
459 writer
.format("%d\t\t", i
); //$NON-NLS-1$
460 writer
.print(fOngoingStateInfo
.get(i
).toString() + "\t\t"); //$NON-NLS-1$
461 writer
.println(fOngoingStateStartTimes
.get(i
).toString());
463 writer
.println('\n');