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
;
28 import org
.eclipse
.jdt
.annotation
.NonNull
;
29 import org
.eclipse
.jdt
.annotation
.Nullable
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
37 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
38 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
.Type
;
39 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
42 * This is the core class of the Generic State System. It contains all the
43 * methods to build and query a state history. It's exposed externally through
44 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
45 * user needs read-only access or read-write access.
47 * When building, DON'T FORGET to call .closeHistory() when you are done
48 * inserting intervals, or the storage backend will have no way of knowing it
49 * can close and write itself to disk, and its thread will keep running.
54 public class StateSystem
implements ITmfStateSystemBuilder
{
56 /* References to the inner structures */
57 private final AttributeTree attributeTree
;
58 private final TransientState transState
;
59 private final IStateHistoryBackend backend
;
61 /* Latch tracking if the state history is done building or not */
62 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
64 private boolean buildCancelled
= false;
65 private boolean isDisposed
= false;
68 * New-file constructor. For when you build a state system with a new file,
69 * or if the back-end does not require a file on disk.
72 * Back-end plugin to use
74 public StateSystem(@NonNull IStateHistoryBackend backend
) {
75 this.backend
= backend
;
76 this.transState
= new TransientState(backend
);
77 this.attributeTree
= new AttributeTree(this);
84 * The "state history storage" back-end to use.
86 * Put true if this is a new history started from scratch. It is
87 * used to tell the state system where to get its attribute tree.
89 * If there was a problem creating the new history file
91 public StateSystem(@NonNull IStateHistoryBackend backend
, boolean newFile
)
93 this.backend
= backend
;
94 this.transState
= new TransientState(backend
);
97 attributeTree
= new AttributeTree(this);
99 /* We're opening an existing file */
100 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
101 transState
.setInactive();
102 finishedLatch
.countDown(); /* The history is already built */
107 public String
getSSID() {
108 return backend
.getSSID();
112 public boolean isCancelled() {
113 return buildCancelled
;
117 public void waitUntilBuilt() {
119 finishedLatch
.await();
120 } catch (InterruptedException e
) {
126 public boolean waitUntilBuilt(long timeout
) {
129 ret
= finishedLatch
.await(timeout
, TimeUnit
.MILLISECONDS
);
130 } catch (InterruptedException e
) {
137 public synchronized void dispose() {
139 if (transState
.isActive()) {
140 transState
.setInactive();
141 buildCancelled
= true;
146 //--------------------------------------------------------------------------
147 // General methods related to the attribute tree
148 //--------------------------------------------------------------------------
151 * Get the attribute tree associated with this state system. This should be
152 * the only way of accessing it (and if subclasses want to point to a
153 * different attribute tree than their own, they should only need to
156 * @return The attribute tree
158 public AttributeTree
getAttributeTree() {
159 return attributeTree
;
163 * Method used by the attribute tree when creating new attributes, to keep
164 * the attribute count in the transient state in sync.
166 public void addEmptyAttribute() {
167 transState
.addEmptyEntry();
171 public int getNbAttributes() {
172 return getAttributeTree().getNbAttributes();
176 public String
getAttributeName(int attributeQuark
) {
177 return getAttributeTree().getAttributeName(attributeQuark
);
181 public String
getFullAttributePath(int attributeQuark
) {
182 return getAttributeTree().getFullAttributeName(attributeQuark
);
186 public String
[] getFullAttributePathArray(int attributeQuark
) {
187 return getAttributeTree().getFullAttributePathArray(attributeQuark
);
190 //--------------------------------------------------------------------------
191 // Methods related to the storage backend
192 //--------------------------------------------------------------------------
195 public long getStartTime() {
196 return backend
.getStartTime();
200 public long getCurrentEndTime() {
201 return backend
.getEndTime();
205 public void closeHistory(long endTime
) throws TimeRangeException
{
206 File attributeTreeFile
;
207 long attributeTreeFilePos
;
208 long realEndTime
= endTime
;
210 if (realEndTime
< backend
.getEndTime()) {
212 * This can happen (empty nodes pushing the border further, etc.)
213 * but shouldn't be too big of a deal.
215 realEndTime
= backend
.getEndTime();
217 transState
.closeTransientState(realEndTime
);
218 backend
.finishedBuilding(realEndTime
);
220 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
221 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
222 if (attributeTreeFile
!= null) {
224 * If null was returned, we simply won't save the attribute tree,
227 getAttributeTree().writeSelf(attributeTreeFile
, attributeTreeFilePos
);
229 finishedLatch
.countDown(); /* Mark the history as finished building */
232 //--------------------------------------------------------------------------
233 // Quark-retrieving methods
234 //--------------------------------------------------------------------------
237 public int getQuarkAbsolute(String
... attribute
)
238 throws AttributeNotFoundException
{
239 int quark
= getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE
, attribute
);
240 if (quark
== INVALID_ATTRIBUTE
) {
241 throw new AttributeNotFoundException(getSSID() + " Path:" + Arrays
.toString(attribute
)); //$NON-NLS-1$
247 public int optQuarkAbsolute(String
... attribute
) {
248 return getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE
, attribute
);
252 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
253 return getAttributeTree().getQuarkAndAdd(ROOT_ATTRIBUTE
, attribute
);
257 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
258 throws AttributeNotFoundException
{
259 int quark
= getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
260 if (quark
== INVALID_ATTRIBUTE
) {
261 throw new AttributeNotFoundException(getSSID() + " Quark:" + startingNodeQuark
+ ", SubPath:" + Arrays
.toString(subPath
)); //$NON-NLS-1$ //$NON-NLS-2$
267 public int optQuarkRelative(int startingNodeQuark
, String
... subPath
) {
268 return getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
272 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
273 return getAttributeTree().getQuarkAndAdd(startingNodeQuark
, subPath
);
277 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
278 throws AttributeNotFoundException
{
279 return getAttributeTree().getSubAttributes(quark
, recursive
);
283 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
, String pattern
)
284 throws AttributeNotFoundException
{
285 List
<Integer
> all
= getSubAttributes(quark
, recursive
);
286 List
<Integer
> ret
= new LinkedList
<>();
287 for (Integer attQuark
: all
) {
288 String name
= getAttributeName(attQuark
.intValue());
289 if (name
.matches(pattern
)) {
297 public int getParentAttributeQuark(int quark
) {
298 return getAttributeTree().getParentAttributeQuark(quark
);
302 public List
<@NonNull Integer
> getQuarks(String
... pattern
) {
303 List
<@NonNull Integer
> quarks
= new LinkedList
<>();
304 List
<String
> prefix
= new LinkedList
<>();
305 List
<String
> suffix
= new LinkedList
<>();
306 boolean split
= false;
309 List
<Integer
> directChildren
;
310 int startingAttribute
;
312 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
313 for (String entry
: pattern
) {
314 if (entry
.equals("*")) { //$NON-NLS-1$
317 * Split was already true? This means there was more than
318 * one wildcard. This is not supported, return an empty
333 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
334 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
337 * If there was no wildcard, we'll only return the one matching
338 * attribute, if there is one.
343 quark
= getQuarkAbsolute(prefixStr
);
344 } catch (AttributeNotFoundException e
) {
345 /* It's fine, we'll just return the empty List */
352 if (prefix
.isEmpty()) {
354 * If 'prefix' is empty, this means the wildcard was the first
355 * element. Look for the root node's sub-attributes.
357 startingAttribute
= ROOT_ATTRIBUTE
;
359 startingAttribute
= optQuarkAbsolute(prefixStr
);
360 if (startingAttribute
== INVALID_ATTRIBUTE
) {
361 /* That attribute path did not exist, return the empty array */
366 directChildren
= getSubAttributes(startingAttribute
, false);
367 } catch (AttributeNotFoundException e
) {
368 /* Should not happen, starting attribute is a valid quark */
369 throw new IllegalStateException();
373 * Iterate of all the sub-attributes, and only keep those who match the
374 * 'suffix' part of the initial pattern.
376 for (int childQuark
: directChildren
) {
377 int matchingQuark
= optQuarkRelative(childQuark
, suffixStr
);
378 if (matchingQuark
!= INVALID_ATTRIBUTE
) {
379 quarks
.add(matchingQuark
);
386 //--------------------------------------------------------------------------
387 // Methods related to insertions in the history
388 //--------------------------------------------------------------------------
391 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
392 throws TimeRangeException
, AttributeNotFoundException
,
393 StateValueTypeException
{
396 * TODO Replace with @NonNull parameter (will require fixing all the
399 throw new IllegalArgumentException();
401 transState
.processStateChange(t
, value
, attributeQuark
);
405 public void incrementAttribute(long t
, int attributeQuark
)
406 throws StateValueTypeException
, TimeRangeException
,
407 AttributeNotFoundException
{
408 ITmfStateValue stateValue
= queryOngoingState(attributeQuark
);
410 /* if the attribute was previously null, start counting at 0 */
411 if (!stateValue
.isNull()) {
412 prevValue
= stateValue
.unboxInt();
414 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
419 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
420 throws TimeRangeException
, AttributeNotFoundException
,
421 StateValueTypeException
{
423 int subAttributeQuark
;
424 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
426 if (previousSV
.isNull()) {
428 * If the StateValue was null, this means this is the first time we
429 * use this attribute. Leave stackDepth at 0.
432 } else if (previousSV
.getType() == Type
.INTEGER
) {
433 /* Previous value was an integer, all is good, use it */
434 stackDepth
= previousSV
.unboxInt();
436 /* Previous state of this attribute was another type? Not good! */
437 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Type:" + previousSV
.getType() + ", Expected:" + Type
.INTEGER
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
440 if (stackDepth
>= 100000) {
442 * Limit stackDepth to 100000, to avoid having Attribute Trees grow
443 * out of control due to buggy insertions
445 String message
= " Stack limit reached, not pushing"; //$NON-NLS-1$
446 throw new AttributeNotFoundException(getSSID() + " Quark:" + attributeQuark
+ message
); //$NON-NLS-1$
450 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, String
.valueOf(stackDepth
));
452 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
453 modifyAttribute(t
, value
, subAttributeQuark
);
457 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
458 throws AttributeNotFoundException
, TimeRangeException
,
459 StateValueTypeException
{
460 /* These are the state values of the stack-attribute itself */
461 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
463 if (previousSV
.isNull()) {
465 * Trying to pop an empty stack. This often happens at the start of
466 * traces, for example when we see a syscall_exit, without having
467 * the corresponding syscall_entry in the trace. Just ignore
472 if (previousSV
.getType() != Type
.INTEGER
) {
474 * The existing value was not an integer (which is expected for
475 * stack tops), this doesn't look like a valid stack attribute.
477 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Type:" + previousSV
.getType() + ", Expected:" + Type
.INTEGER
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
480 int stackDepth
= previousSV
.unboxInt();
482 if (stackDepth
<= 0) {
483 /* This on the other hand should not happen... */
484 throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark
+ ", Stack depth:" + stackDepth
); //$NON-NLS-1$//$NON-NLS-2$
487 /* The attribute should already exist at this point */
488 int subAttributeQuark
= getQuarkRelative(attributeQuark
, String
.valueOf(stackDepth
));
489 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
491 /* Update the state value of the stack-attribute */
492 ITmfStateValue nextSV
;
493 if (--stackDepth
== 0) {
494 /* Store a null state value */
495 nextSV
= TmfStateValue
.nullValue();
497 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
499 modifyAttribute(t
, nextSV
, attributeQuark
);
501 /* Delete the sub-attribute that contained the user's state value */
502 removeAttribute(t
, subAttributeQuark
);
508 public void removeAttribute(long t
, int attributeQuark
)
509 throws TimeRangeException
, AttributeNotFoundException
{
510 if (attributeQuark
< 0) {
511 throw new IllegalArgumentException();
515 * Nullify our children first, recursively. We pass 'false' because we
516 * handle the recursion ourselves.
518 List
<Integer
> childAttributes
= getSubAttributes(attributeQuark
, false);
519 for (int childNodeQuark
: childAttributes
) {
520 if (attributeQuark
== childNodeQuark
) {
521 /* Something went very wrong when building out attribute tree */
522 throw new IllegalStateException();
524 removeAttribute(t
, childNodeQuark
);
526 /* Nullify ourselves */
528 transState
.processStateChange(t
, TmfStateValue
.nullValue(), attributeQuark
);
529 } catch (StateValueTypeException e
) {
531 * Will not happen since we're inserting null values only, but poor
532 * compiler has no way of knowing this...
534 throw new IllegalStateException(e
);
538 //--------------------------------------------------------------------------
539 // "Current" query/update methods
540 //--------------------------------------------------------------------------
543 public ITmfStateValue
queryOngoingState(int attributeQuark
)
544 throws AttributeNotFoundException
{
545 return transState
.getOngoingStateValue(attributeQuark
);
549 public long getOngoingStartTime(int attribute
)
550 throws AttributeNotFoundException
{
551 return transState
.getOngoingStartTime(attribute
);
555 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
556 throws AttributeNotFoundException
{
557 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
561 * Modify the whole "ongoing state" (state values + start times). This can
562 * be used when "seeking" a state system to a different point in the trace
563 * (and restoring the known stateInfo at this location). Use with care!
565 * @param newStateIntervals
566 * The new List of state values to use as ongoing state info
568 protected void replaceOngoingState(@NonNull List
<@NonNull ITmfStateInterval
> newStateIntervals
) {
569 transState
.replaceOngoingState(newStateIntervals
);
572 //--------------------------------------------------------------------------
573 // Regular query methods (sent to the back-end)
574 //--------------------------------------------------------------------------
577 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
578 throws TimeRangeException
, StateSystemDisposedException
{
580 throw new StateSystemDisposedException();
583 final int nbAttr
= getNbAttributes();
584 List
<@Nullable ITmfStateInterval
> stateInfo
= new ArrayList
<>(nbAttr
);
586 /* Bring the size of the array to the current number of attributes */
587 for (int i
= 0; i
< nbAttr
; i
++) {
592 * If we are currently building the history, also query the "ongoing"
593 * states for stuff that might not yet be written to the history.
595 if (transState
.isActive()) {
596 transState
.doQuery(stateInfo
, t
);
599 /* Query the storage backend */
600 backend
.doQuery(stateInfo
, t
);
603 * We should have previously inserted an interval for every attribute.
605 for (ITmfStateInterval interval
: stateInfo
) {
606 if (interval
== null) {
607 throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$
614 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
615 throws AttributeNotFoundException
, TimeRangeException
,
616 StateSystemDisposedException
{
618 throw new StateSystemDisposedException();
621 ITmfStateInterval ret
= transState
.getIntervalAt(t
, attributeQuark
);
624 * The transient state did not have the information, let's look into
627 ret
= backend
.doSingularQuery(t
, attributeQuark
);
632 * If we did our job correctly, there should be intervals for every
633 * possible attribute, over all the valid time range.
635 throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$
640 //--------------------------------------------------------------------------
642 //--------------------------------------------------------------------------
644 static void logMissingInterval(int attribute
, long timestamp
) {
645 Activator
.getDefault().logInfo("No data found in history for attribute " + //$NON-NLS-1$
646 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
647 ", returning dummy interval"); //$NON-NLS-1$
651 * Print out the contents of the inner structures.
654 * The PrintWriter in which to print the output
656 public void debugPrint(@NonNull PrintWriter writer
) {
657 getAttributeTree().debugPrint(writer
);
658 transState
.debugPrint(writer
);
659 backend
.debugPrint(writer
);