1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 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
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
;
15 import java
.io
.PrintWriter
;
16 import java
.util
.ArrayList
;
17 import java
.util
.List
;
18 import java
.util
.concurrent
.locks
.ReentrantReadWriteLock
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.jdt
.annotation
.Nullable
;
22 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.IStateHistoryBackend
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.TmfStateInterval
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
.Type
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
33 * The Transient State is used to build intervals from punctual state changes.
34 * It contains a "state info" vector similar to the "current state", except here
35 * we also record the start time of every state stored in it.
37 * We can then build {@link ITmfStateInterval}'s, to be inserted in a
38 * {@link IStateHistoryBackend} when we detect state changes : the "start time"
39 * of the interval will be the recorded time we have here, and the "end time"
40 * will be the timestamp of the new state-changing event we just read.
42 * @author Alexandre Montplaisir
44 public class TransientState
{
46 /* Indicates where to insert state changes that we generate */
47 @NonNull private final IStateHistoryBackend backend
;
49 private final ReentrantReadWriteLock rwl
= new ReentrantReadWriteLock(false);
51 private volatile boolean isActive
;
52 private volatile long latestTime
;
54 /* A method accessing these arrays will have to go through the lock */
55 private List
<ITmfStateValue
> ongoingStateInfo
;
56 private List
<Long
> ongoingStateStartTimes
;
57 private List
<Type
> stateValueTypes
;
63 * The back-end in which to insert the generated state intervals
65 public TransientState(@NonNull IStateHistoryBackend backend
) {
66 this.backend
= backend
;
68 ongoingStateInfo
= new ArrayList
<>();
69 ongoingStateStartTimes
= new ArrayList
<>();
70 stateValueTypes
= new ArrayList
<>();
72 latestTime
= backend
.getStartTime();
76 * Get the latest time we have seen so far.
78 * @return The latest time seen in the transient state
80 public long getLatestTime() {
85 * Retrieve the ongoing state value for a given index (attribute quark).
88 * The quark of the attribute to look for
89 * @return The corresponding state value
90 * @throws AttributeNotFoundException
91 * If the quark is invalid
93 public ITmfStateValue
getOngoingStateValue(int quark
) throws AttributeNotFoundException
{
94 rwl
.readLock().lock();
96 checkValidAttribute(quark
);
97 return ongoingStateInfo
.get(quark
);
99 rwl
.readLock().unlock();
104 * Retrieve the start time of the state in which the given attribute is in.
107 * The quark of the attribute to look for
108 * @return The start time of the current state for this attribute
109 * @throws AttributeNotFoundException
110 * If the quark is invalid
112 public long getOngoingStartTime(int quark
) throws AttributeNotFoundException
{
113 rwl
.readLock().lock();
115 checkValidAttribute(quark
);
116 return ongoingStateStartTimes
.get(quark
);
118 rwl
.readLock().unlock();
123 * Modify the current state for a given attribute. This will not update the
124 * "ongoing state start time" in any way, so be careful when using this.
127 * The quark of the attribute to modify
129 * The state value the attribute should have
130 * @throws AttributeNotFoundException
131 * If the quark is invalid
133 public void changeOngoingStateValue(int quark
, ITmfStateValue newValue
)
134 throws AttributeNotFoundException
{
135 rwl
.writeLock().lock();
137 checkValidAttribute(quark
);
138 ongoingStateInfo
.set(quark
, newValue
);
140 rwl
.writeLock().unlock();
145 * Convenience method to return the "ongoing" value for a given attribute as
146 * a dummy interval whose end time = the current latest time.
149 * The quark of the attribute
150 * @return An interval representing the current state (but whose end time is
151 * the current one, and probably not the "final" one)
152 * @throws AttributeNotFoundException
153 * If the quark is invalid
155 public ITmfStateInterval
getOngoingInterval(int quark
) throws AttributeNotFoundException
{
156 rwl
.readLock().lock();
158 checkValidAttribute(quark
);
159 return new TmfStateInterval(ongoingStateStartTimes
.get(quark
), latestTime
,
160 quark
, ongoingStateInfo
.get(quark
));
162 rwl
.readLock().unlock();
167 * Try to get the state interval valid for time/quark, if it is present in
168 * this transient state. If it is not (for example, a new value is active
169 * since after the specified timestamp) then null will be returned.
172 * The timestamp to look for
174 * The quark of the attribute to look for
175 * @return The corresponding TmfStateInterval object if we could find it in
176 * this transient state, or null if we couldn't.
179 public ITmfStateInterval
getIntervalAt(long time
, int quark
) {
180 rwl
.readLock().lock();
182 checkValidAttribute(quark
);
183 if (!isActive() || time
< ongoingStateStartTimes
.get(quark
)) {
186 return new TmfStateInterval(ongoingStateStartTimes
.get(quark
),
187 latestTime
, quark
, ongoingStateInfo
.get(quark
));
188 } catch (AttributeNotFoundException e
) {
191 rwl
.readLock().unlock();
195 private void checkValidAttribute(int quark
) throws AttributeNotFoundException
{
196 if (quark
> ongoingStateInfo
.size() - 1 || quark
< 0) {
197 throw new AttributeNotFoundException();
202 * More advanced version of {@link #changeOngoingStateValue}. Replaces the
203 * complete ongoingStateInfo in one go, and updates the
204 * ongoingStateStartTimes and #stateValuesTypes accordingly. BE VERY CAREFUL
207 * @param newStateIntervals
208 * The List of intervals that will represent the new
209 * "ongoing state". Their end times don't matter, we will only
210 * check their value and start times.
212 public void replaceOngoingState(List
<ITmfStateInterval
> newStateIntervals
) {
213 final int size
= newStateIntervals
.size();
215 rwl
.writeLock().lock();
217 ongoingStateInfo
= new ArrayList
<>(size
);
218 ongoingStateStartTimes
= new ArrayList
<>(size
);
219 stateValueTypes
= new ArrayList
<>(size
);
221 for (ITmfStateInterval interval
: newStateIntervals
) {
222 ongoingStateInfo
.add(interval
.getStateValue());
223 ongoingStateStartTimes
.add(interval
.getStartTime());
224 stateValueTypes
.add(interval
.getStateValue().getType());
227 rwl
.writeLock().unlock();
232 * Add an "empty line" to both "ongoing..." vectors. This is needed so the
233 * Ongoing... tables can stay in sync with the number of attributes in the
234 * attribute tree, namely when we add sub-path attributes.
236 public void addEmptyEntry() {
237 rwl
.writeLock().lock();
240 * Since this is a new attribute, we suppose it was in the
241 * "null state" since the beginning (so we can have intervals
242 * covering for all timestamps). A null interval will then get added
243 * at the first state change.
245 ongoingStateInfo
.add(TmfStateValue
.nullValue());
246 stateValueTypes
.add(Type
.NULL
);
248 ongoingStateStartTimes
.add(backend
.getStartTime());
250 rwl
.writeLock().unlock();
255 * Process a state change to be inserted in the history.
258 * The timestamp associated with this state change
260 * The new StateValue associated to this attribute
262 * The quark of the attribute that is being modified
263 * @throws TimeRangeException
264 * If 'eventTime' is invalid
265 * @throws AttributeNotFoundException
266 * IF 'quark' does not represent an existing attribute
267 * @throws StateValueTypeException
268 * If the state value to be inserted is of a different type of
269 * what was inserted so far for this attribute.
271 public void processStateChange(long eventTime
, ITmfStateValue value
, int quark
)
272 throws TimeRangeException
, AttributeNotFoundException
, StateValueTypeException
{
273 rwl
.writeLock().lock();
274 assert (this.isActive
);
277 Type expectedSvType
= stateValueTypes
.get(quark
);
278 checkValidAttribute(quark
);
281 * Make sure the state value type we're inserting is the same as the
282 * one registered for this attribute.
284 if (expectedSvType
== Type
.NULL
) {
286 * The value hasn't been used yet, set it to the value we're
287 * currently inserting (which might be null/-1 again).
289 stateValueTypes
.set(quark
, value
.getType());
290 } else if ((value
.getType() != Type
.NULL
) && (value
.getType() != expectedSvType
)) {
292 * We authorize inserting null values in any type of attribute,
293 * but for every other types, it needs to match our
296 throw new StateValueTypeException();
299 if (ongoingStateInfo
.get(quark
).equals(value
)) {
301 * This is the case where the new value and the one already
302 * present in the Builder are the same. We do not need to create
303 * an interval, we'll just keep the current one going.
308 if (ongoingStateStartTimes
.get(quark
) < eventTime
) {
310 * These two conditions are necessary to create an interval and
311 * update ongoingStateInfo.
313 backend
.insertPastState(ongoingStateStartTimes
.get(quark
),
314 eventTime
- 1, /* End Time */
315 quark
, /* attribute quark */
316 ongoingStateInfo
.get(quark
)); /* StateValue */
318 ongoingStateStartTimes
.set(quark
, eventTime
);
320 ongoingStateInfo
.set(quark
, value
);
322 /* Update the Transient State's lastestTime, if needed */
323 if (latestTime
< eventTime
) {
324 latestTime
= eventTime
;
328 rwl
.writeLock().unlock();
333 * Run a "get state at time" query on the Transient State only.
336 * The stateInfo object in which we will put our relevant
339 * The requested timestamp
341 public void doQuery(List
<ITmfStateInterval
> stateInfo
, long t
) {
342 rwl
.readLock().lock();
344 if (!this.isActive
) {
347 assert (stateInfo
.size() == ongoingStateInfo
.size());
349 for (int i
= 0; i
< ongoingStateInfo
.size(); i
++) {
351 * We build a dummy interval with end time = -1 to put in the
352 * answer to the query.
354 final ITmfStateInterval interval
= getIntervalAt(t
, i
);
355 if (interval
!= null) {
356 stateInfo
.set(i
, interval
);
360 rwl
.readLock().unlock();
365 * Close off the Transient State, used for example when we are done reading
366 * a static trace file. All the information currently contained in it will
367 * be converted to intervals and "flushed" to the state history.
370 * The timestamp to use as end time for the state history (since
371 * it may be different than the timestamp of the last state
374 public void closeTransientState(long endTime
) {
375 rwl
.writeLock().lock();
377 assert (this.isActive
);
379 for (int i
= 0; i
< ongoingStateInfo
.size(); i
++) {
380 if (ongoingStateStartTimes
.get(i
) > endTime
) {
382 * Handle the cases where trace end > timestamp of last
383 * state change. This can happen when inserting "future"
389 backend
.insertPastState(ongoingStateStartTimes
.get(i
),
390 endTime
, /* End Time */
391 i
, /* attribute quark */
392 ongoingStateInfo
.get(i
)); /* StateValue */
394 } catch (TimeRangeException e
) {
396 * This shouldn't happen, since we control where the
397 * interval's start time comes from
399 throw new IllegalStateException(e
);
403 ongoingStateInfo
.clear();
404 ongoingStateStartTimes
.clear();
405 this.isActive
= false;
408 rwl
.writeLock().unlock();
413 * Simply returns if this Transient State is currently being used or not
415 * @return True if this transient state is active
417 public boolean isActive() {
418 return this.isActive
;
422 * Mark this transient state as inactive
424 public void setInactive() {
429 * Debugging method that prints the contents of the transient state
432 * The writer to which the output should be written
434 public void debugPrint(PrintWriter writer
) {
435 /* Only used for debugging, shouldn't be externalized */
436 writer
.println("------------------------------"); //$NON-NLS-1$
437 writer
.println("Info stored in the Builder:"); //$NON-NLS-1$
438 if (!this.isActive
) {
439 writer
.println("Builder is currently inactive"); //$NON-NLS-1$
440 writer
.println('\n');
443 writer
.println("\nAttribute\tStateValue\tValid since time"); //$NON-NLS-1$
444 for (int i
= 0; i
< ongoingStateInfo
.size(); i
++) {
445 writer
.format("%d\t\t", i
); //$NON-NLS-1$
446 writer
.print(ongoingStateInfo
.get(i
).toString() + "\t\t"); //$NON-NLS-1$
447 writer
.println(ongoingStateStartTimes
.get(i
).toString());
449 writer
.println('\n');