1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 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
;
19 import java
.io
.IOException
;
20 import java
.io
.PrintWriter
;
21 import java
.util
.ArrayList
;
22 import java
.util
.Arrays
;
23 import java
.util
.LinkedList
;
24 import java
.util
.List
;
25 import java
.util
.concurrent
.CountDownLatch
;
26 import java
.util
.concurrent
.TimeUnit
;
27 import java
.util
.logging
.Logger
;
29 import org
.eclipse
.jdt
.annotation
.NonNull
;
30 import org
.eclipse
.jdt
.annotation
.Nullable
;
31 import org
.eclipse
.tracecompass
.common
.core
.log
.TraceCompassLog
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
37 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
38 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
39 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
40 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
.Type
;
41 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
43 import com
.google
.common
.collect
.ImmutableCollection
.Builder
;
44 import com
.google
.common
.collect
.ImmutableSet
;
47 * This is the core class of the Generic State System. It contains all the
48 * methods to build and query a state history. It's exposed externally through
49 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
50 * user needs read-only access or read-write access.
52 * When building, DON'T FORGET to call .closeHistory() when you are done
53 * inserting intervals, or the storage backend will have no way of knowing it
54 * can close and write itself to disk, and its thread will keep running.
59 public class StateSystem
implements ITmfStateSystemBuilder
{
61 private static final String PARENT
= ".."; //$NON-NLS-1$
62 private static final String WILDCARD
= "*"; //$NON-NLS-1$
64 private static final Logger LOGGER
= TraceCompassLog
.getLogger(StateSystem
.class);
66 /* References to the inner structures */
67 private final AttributeTree attributeTree
;
68 private final TransientState transState
;
69 private final IStateHistoryBackend backend
;
71 /* Latch tracking if the state history is done building or not */
72 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
74 private boolean buildCancelled
= false;
75 private boolean isDisposed
= false;
78 * New-file constructor. For when you build a state system with a new file,
79 * or if the back-end does not require a file on disk.
82 * Back-end plugin to use
84 public StateSystem(@NonNull IStateHistoryBackend backend
) {
85 this.backend
= backend
;
86 this.transState
= new TransientState(backend
);
87 this.attributeTree
= new AttributeTree(this);
94 * The "state history storage" back-end to use.
96 * Put true if this is a new history started from scratch. It is
97 * used to tell the state system where to get its attribute tree.
99 * If there was a problem creating the new history file
101 public StateSystem(@NonNull IStateHistoryBackend backend
, boolean newFile
)
103 this.backend
= backend
;
104 this.transState
= new TransientState(backend
);
107 attributeTree
= new AttributeTree(this);
109 /* We're opening an existing file */
110 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
111 transState
.setInactive();
112 finishedLatch
.countDown(); /* The history is already built */
117 public String
getSSID() {
118 return backend
.getSSID();
122 public boolean isCancelled() {
123 return buildCancelled
;
127 public void waitUntilBuilt() {
129 finishedLatch
.await();
130 } catch (InterruptedException e
) {
136 public boolean waitUntilBuilt(long timeout
) {
139 ret
= finishedLatch
.await(timeout
, TimeUnit
.MILLISECONDS
);
140 } catch (InterruptedException e
) {
147 public synchronized void dispose() {
149 if (transState
.isActive()) {
150 transState
.setInactive();
151 buildCancelled
= true;
156 //--------------------------------------------------------------------------
157 // General methods related to the attribute tree
158 //--------------------------------------------------------------------------
161 * Get the attribute tree associated with this state system. This should be
162 * the only way of accessing it (and if subclasses want to point to a
163 * different attribute tree than their own, they should only need to
166 * @return The attribute tree
168 public AttributeTree
getAttributeTree() {
169 return attributeTree
;
173 * Method used by the attribute tree when creating new attributes, to keep
174 * the attribute count in the transient state in sync.
176 public void addEmptyAttribute() {
177 transState
.addEmptyEntry();
181 public int getNbAttributes() {
182 return getAttributeTree().getNbAttributes();
186 public String
getAttributeName(int attributeQuark
) {
187 return getAttributeTree().getAttributeName(attributeQuark
);
191 public String
getFullAttributePath(int attributeQuark
) {
192 return getAttributeTree().getFullAttributeName(attributeQuark
);
196 public String
[] getFullAttributePathArray(int attributeQuark
) {
197 return getAttributeTree().getFullAttributePathArray(attributeQuark
);
200 //--------------------------------------------------------------------------
201 // Methods related to the storage backend
202 //--------------------------------------------------------------------------
205 public long getStartTime() {
206 return backend
.getStartTime();
210 public long getCurrentEndTime() {
211 return backend
.getEndTime();
215 public void closeHistory(long endTime
) throws TimeRangeException
{
216 File attributeTreeFile
;
217 long attributeTreeFilePos
;
218 long realEndTime
= endTime
;
220 if (realEndTime
< backend
.getEndTime()) {
222 * This can happen (empty nodes pushing the border further, etc.)
223 * but shouldn't be too big of a deal.
225 realEndTime
= backend
.getEndTime();
227 transState
.closeTransientState(realEndTime
);
228 backend
.finishedBuilding(realEndTime
);
230 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
231 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
232 if (attributeTreeFile
!= null) {
234 * If null was returned, we simply won't save the attribute tree,
237 getAttributeTree().writeSelf(attributeTreeFile
, attributeTreeFilePos
);
239 finishedLatch
.countDown(); /* Mark the history as finished building */
242 //--------------------------------------------------------------------------
243 // Quark-retrieving methods
244 //--------------------------------------------------------------------------
247 public int getQuarkAbsolute(String
... attribute
)
248 throws AttributeNotFoundException
{
249 int quark
= getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE
, attribute
);
250 if (quark
== INVALID_ATTRIBUTE
) {
251 throw new AttributeNotFoundException(getSSID() + " Path:" + Arrays
.toString(attribute
)); //$NON-NLS-1$
257 public int optQuarkAbsolute(String
... attribute
) {
258 return getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE
, attribute
);
262 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
263 return getAttributeTree().getQuarkAndAdd(ROOT_ATTRIBUTE
, attribute
);
267 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
268 throws AttributeNotFoundException
{
269 int quark
= getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
270 if (quark
== INVALID_ATTRIBUTE
) {
271 throw new AttributeNotFoundException(getSSID() + " Quark:" + startingNodeQuark
+ ", SubPath:" + Arrays
.toString(subPath
)); //$NON-NLS-1$ //$NON-NLS-2$
277 public int optQuarkRelative(int startingNodeQuark
, String
... subPath
) {
278 return getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
282 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
283 return getAttributeTree().getQuarkAndAdd(startingNodeQuark
, subPath
);
287 public List
<@NonNull Integer
> getSubAttributes(int quark
, boolean recursive
) {
288 return getAttributeTree().getSubAttributes(quark
, recursive
);
292 public List
<@NonNull Integer
> getSubAttributes(int quark
, boolean recursive
, String pattern
) {
293 List
<Integer
> all
= getSubAttributes(quark
, recursive
);
294 List
<@NonNull Integer
> ret
= new LinkedList
<>();
295 for (Integer attQuark
: all
) {
296 String name
= getAttributeName(attQuark
.intValue());
297 if (name
.matches(pattern
)) {
305 public int getParentAttributeQuark(int quark
) {
306 return getAttributeTree().getParentAttributeQuark(quark
);
310 public List
<@NonNull Integer
> getQuarks(String
... pattern
) {
311 return getQuarks(ROOT_ATTRIBUTE
, pattern
);
315 public List
<@NonNull Integer
> getQuarks(int startingNodeQuark
, String
... pattern
) {
316 Builder
<@NonNull Integer
> builder
= ImmutableSet
.builder();
317 if (pattern
.length
> 0) {
318 getQuarks(builder
, startingNodeQuark
, Arrays
.asList(pattern
));
320 builder
.add(startingNodeQuark
);
322 return builder
.build().asList();
325 private void getQuarks(Builder
<@NonNull Integer
> builder
, int quark
, List
<String
> pattern
) {
326 String element
= pattern
.get(0);
327 if (element
== null) {
330 List
<String
> remainder
= pattern
.subList(1, pattern
.size());
331 if (remainder
.isEmpty()) {
332 if (element
.equals(WILDCARD
)) {
333 builder
.addAll(getSubAttributes(quark
, false));
334 } else if (element
.equals(PARENT
)){
335 builder
.add(getParentAttributeQuark(quark
));
337 int subQuark
= optQuarkRelative(quark
, element
);
338 if (subQuark
!= INVALID_ATTRIBUTE
) {
339 builder
.add(subQuark
);
343 if (element
.equals(WILDCARD
)) {
344 for (@NonNull Integer subquark
: getSubAttributes(quark
, false)) {
345 getQuarks(builder
, subquark
, remainder
);
347 } else if (element
.equals(PARENT
)){
348 getQuarks(builder
, getParentAttributeQuark(quark
), remainder
);
350 int subQuark
= optQuarkRelative(quark
, element
);
351 if (subQuark
!= INVALID_ATTRIBUTE
) {
352 getQuarks(builder
, subQuark
, remainder
);
358 //--------------------------------------------------------------------------
359 // Methods related to insertions in the history
360 //--------------------------------------------------------------------------
363 public void modifyAttribute(long t
, @NonNull ITmfStateValue value
, int attributeQuark
)
364 throws TimeRangeException
, StateValueTypeException
{
365 transState
.processStateChange(t
, value
, attributeQuark
);
370 public void incrementAttribute(long t
, int attributeQuark
)
371 throws StateValueTypeException
, TimeRangeException
{
372 ITmfStateValue stateValue
= queryOngoingState(attributeQuark
);
374 /* if the attribute was previously null, start counting at 0 */
375 if (!stateValue
.isNull()) {
376 prevValue
= stateValue
.unboxInt();
378 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
383 public void pushAttribute(long t
, @NonNull ITmfStateValue value
, int attributeQuark
)
384 throws TimeRangeException
, StateValueTypeException
{
386 int subAttributeQuark
;
387 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
389 if (previousSV
.isNull()) {
391 * If the StateValue was null, this means this is the first time we
392 * use this attribute. Leave stackDepth at 0.
395 } else if (previousSV
.getType() == Type
.INTEGER
) {
396 /* Previous value was an integer, all is good, use it */
397 stackDepth
= previousSV
.unboxInt();
399 /* Previous state of this attribute was another type? Not good! */
400 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Type:" + previousSV
.getType() + ", Expected:" + Type
.INTEGER
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
403 if (stackDepth
>= 100000) {
405 * Limit stackDepth to 100000, to avoid having Attribute Trees grow
406 * out of control due to buggy insertions
408 String message
= " Stack limit reached, not pushing"; //$NON-NLS-1$
409 throw new IllegalStateException(getSSID() + " Quark:" + attributeQuark
+ message
); //$NON-NLS-1$
413 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, String
.valueOf(stackDepth
));
415 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
416 modifyAttribute(t
, value
, subAttributeQuark
);
420 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
421 throws TimeRangeException
, StateValueTypeException
{
422 /* These are the state values of the stack-attribute itself */
423 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
425 if (previousSV
.isNull()) {
427 * Trying to pop an empty stack. This often happens at the start of
428 * traces, for example when we see a syscall_exit, without having
429 * the corresponding syscall_entry in the trace. Just ignore
434 if (previousSV
.getType() != Type
.INTEGER
) {
436 * The existing value was not an integer (which is expected for
437 * stack tops), this doesn't look like a valid stack attribute.
439 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Type:" + previousSV
.getType() + ", Expected:" + Type
.INTEGER
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
442 int stackDepth
= previousSV
.unboxInt();
444 if (stackDepth
<= 0) {
445 /* This on the other hand should not happen... */
446 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Stack depth:" + stackDepth
); //$NON-NLS-1$//$NON-NLS-2$
449 /* The attribute should already exist at this point */
450 int subAttributeQuark
;
452 subAttributeQuark
= getQuarkRelative(attributeQuark
, String
.valueOf(stackDepth
));
453 } catch (AttributeNotFoundException e
) {
454 String message
= " Stack attribute missing sub-attribute for depth:" + stackDepth
; //$NON-NLS-1$
455 throw new IllegalStateException(getSSID() + " Quark:" + attributeQuark
+ message
); //$NON-NLS-1$
457 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
459 /* Update the state value of the stack-attribute */
460 ITmfStateValue nextSV
;
461 if (--stackDepth
== 0) {
462 /* Store a null state value */
463 nextSV
= TmfStateValue
.nullValue();
465 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
467 modifyAttribute(t
, nextSV
, attributeQuark
);
469 /* Delete the sub-attribute that contained the user's state value */
470 removeAttribute(t
, subAttributeQuark
);
476 public void removeAttribute(long t
, int attributeQuark
)
477 throws TimeRangeException
{
479 * Nullify our children first, recursively. We pass 'false' because we
480 * handle the recursion ourselves.
482 List
<Integer
> childAttributes
= getSubAttributes(attributeQuark
, false);
483 for (int childNodeQuark
: childAttributes
) {
484 if (attributeQuark
== childNodeQuark
) {
485 /* Something went very wrong when building out attribute tree */
486 throw new IllegalStateException();
488 removeAttribute(t
, childNodeQuark
);
490 /* Nullify ourselves */
492 transState
.processStateChange(t
, TmfStateValue
.nullValue(), attributeQuark
);
493 } catch (StateValueTypeException e
) {
495 * Will not happen since we're inserting null values only, but poor
496 * compiler has no way of knowing this...
498 throw new IllegalStateException(e
);
502 //--------------------------------------------------------------------------
503 // "Current" query/update methods
504 //--------------------------------------------------------------------------
507 public ITmfStateValue
queryOngoingState(int attributeQuark
) {
508 return transState
.getOngoingStateValue(attributeQuark
);
512 public long getOngoingStartTime(int attribute
) {
513 return transState
.getOngoingStartTime(attribute
);
517 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
) {
518 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
522 * Modify the whole "ongoing state" (state values + start times). This can
523 * be used when "seeking" a state system to a different point in the trace
524 * (and restoring the known stateInfo at this location). Use with care!
526 * @param newStateIntervals
527 * The new List of state values to use as ongoing state info
529 protected void replaceOngoingState(@NonNull List
<@NonNull ITmfStateInterval
> newStateIntervals
) {
530 transState
.replaceOngoingState(newStateIntervals
);
533 //--------------------------------------------------------------------------
534 // Regular query methods (sent to the back-end)
535 //--------------------------------------------------------------------------
538 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
539 throws TimeRangeException
, StateSystemDisposedException
{
541 throw new StateSystemDisposedException();
544 LOGGER
.info(() -> "[StateSystem:FullQueryStart] ssid=" + this.getSSID() + ", ts=" + t
); //$NON-NLS-1$//$NON-NLS-2$
546 final int nbAttr
= getNbAttributes();
547 List
<@Nullable ITmfStateInterval
> stateInfo
= new ArrayList
<>(nbAttr
);
549 /* Bring the size of the array to the current number of attributes */
550 for (int i
= 0; i
< nbAttr
; i
++) {
555 * If we are currently building the history, also query the "ongoing"
556 * states for stuff that might not yet be written to the history.
558 if (transState
.isActive()) {
559 transState
.doQuery(stateInfo
, t
);
562 /* Query the storage backend */
563 backend
.doQuery(stateInfo
, t
);
566 * We should have previously inserted an interval for every attribute.
568 for (ITmfStateInterval interval
: stateInfo
) {
569 if (interval
== null) {
570 throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$
573 LOGGER
.info(() -> "[StateSystem:FullQueryEnd]"); //$NON-NLS-1$
578 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
579 throws TimeRangeException
, StateSystemDisposedException
{
581 throw new StateSystemDisposedException();
584 LOGGER
.info(() -> "[StateSystem:SingleQueryStart] ssid=" + this.getSSID() + ", ts=" + t
+ ", attribute=" + attributeQuark
); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
586 ITmfStateInterval ret
= transState
.getIntervalAt(t
, attributeQuark
);
589 * The transient state did not have the information, let's look into
592 ret
= backend
.doSingularQuery(t
, attributeQuark
);
597 * If we did our job correctly, there should be intervals for every
598 * possible attribute, over all the valid time range.
600 throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$
602 LOGGER
.info(() -> "[StateSystem:SingleQueryEnd]"); //$NON-NLS-1$
606 //--------------------------------------------------------------------------
608 //--------------------------------------------------------------------------
610 static void logMissingInterval(int attribute
, long timestamp
) {
611 Activator
.getDefault().logInfo("No data found in history for attribute " + //$NON-NLS-1$
612 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
613 ", returning dummy interval"); //$NON-NLS-1$
617 * Print out the contents of the inner structures.
620 * The PrintWriter in which to print the output
622 public void debugPrint(@NonNull PrintWriter writer
) {
623 getAttributeTree().debugPrint(writer
);
624 transState
.debugPrint(writer
);
625 backend
.debugPrint(writer
);