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
;
16 import java
.io
.IOException
;
17 import java
.io
.PrintWriter
;
18 import java
.util
.ArrayList
;
19 import java
.util
.LinkedList
;
20 import java
.util
.List
;
21 import java
.util
.concurrent
.CountDownLatch
;
23 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
24 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
25 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.Activator
;
26 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.IStateHistoryBackend
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.TmfStateInterval
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystemBuilder
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
.Type
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
39 * This is the core class of the Generic State System. It contains all the
40 * methods to build and query a state history. It's exposed externally through
41 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
42 * user needs read-only access or read-write access.
44 * When building, DON'T FORGET to call .closeHistory() when you are done
45 * inserting intervals, or the storage backend will have no way of knowing it
46 * can close and write itself to disk, and its thread will keep running.
51 public class StateSystem
implements ITmfStateSystemBuilder
{
53 /* References to the inner structures */
54 private final AttributeTree attributeTree
;
55 private final TransientState transState
;
56 private final IStateHistoryBackend backend
;
58 /* Latch tracking if the state history is done building or not */
59 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
61 private boolean buildCancelled
= false;
62 private boolean isDisposed
= false;
65 * New-file constructor. For when you build a state system with a new file,
66 * or if the back-end does not require a file on disk.
69 * Back-end plugin to use
71 public StateSystem(IStateHistoryBackend backend
) {
72 this.backend
= backend
;
73 this.transState
= new TransientState(backend
);
74 this.attributeTree
= new AttributeTree(this);
81 * The "state history storage" back-end to use.
83 * Put true if this is a new history started from scratch. It is
84 * used to tell the state system where to get its attribute tree.
86 * If there was a problem creating the new history file
88 public StateSystem(IStateHistoryBackend backend
, boolean newFile
)
90 this.backend
= backend
;
91 this.transState
= new TransientState(backend
);
94 attributeTree
= new AttributeTree(this);
96 /* We're opening an existing file */
97 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
98 transState
.setInactive();
99 finishedLatch
.countDown(); /* The history is already built */
104 public boolean waitUntilBuilt() {
106 finishedLatch
.await();
107 } catch (InterruptedException e
) {
110 return !buildCancelled
;
114 public synchronized void dispose() {
116 if (transState
.isActive()) {
117 transState
.setInactive();
118 buildCancelled
= true;
123 //--------------------------------------------------------------------------
124 // General methods related to the attribute tree
125 //--------------------------------------------------------------------------
128 * Get the attribute tree associated with this state system. This should be
129 * the only way of accessing it (and if subclasses want to point to a
130 * different attribute tree than their own, they should only need to
133 * @return The attribute tree
135 public AttributeTree
getAttributeTree() {
136 return attributeTree
;
140 * Method used by the attribute tree when creating new attributes, to keep
141 * the attribute count in the transient state in sync.
143 protected void addEmptyAttribute() {
144 transState
.addEmptyEntry();
148 public int getNbAttributes() {
149 return getAttributeTree().getNbAttributes();
153 public String
getAttributeName(int attributeQuark
) {
154 return getAttributeTree().getAttributeName(attributeQuark
);
158 public String
getFullAttributePath(int attributeQuark
) {
159 return getAttributeTree().getFullAttributeName(attributeQuark
);
162 //--------------------------------------------------------------------------
163 // Methods related to the storage backend
164 //--------------------------------------------------------------------------
167 public long getStartTime() {
168 return backend
.getStartTime();
172 public long getCurrentEndTime() {
173 return backend
.getEndTime();
177 public void closeHistory(long endTime
) throws TimeRangeException
{
178 File attributeTreeFile
;
179 long attributeTreeFilePos
;
180 long realEndTime
= endTime
;
182 if (realEndTime
< backend
.getEndTime()) {
184 * This can happen (empty nodes pushing the border further, etc.)
185 * but shouldn't be too big of a deal.
187 realEndTime
= backend
.getEndTime();
189 transState
.closeTransientState(realEndTime
);
190 backend
.finishedBuilding(realEndTime
);
192 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
193 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
194 if (attributeTreeFile
!= null) {
196 * If null was returned, we simply won't save the attribute tree,
199 getAttributeTree().writeSelf(attributeTreeFile
, attributeTreeFilePos
);
201 finishedLatch
.countDown(); /* Mark the history as finished building */
204 //--------------------------------------------------------------------------
205 // Quark-retrieving methods
206 //--------------------------------------------------------------------------
209 public int getQuarkAbsolute(String
... attribute
)
210 throws AttributeNotFoundException
{
211 return getAttributeTree().getQuarkDontAdd(-1, attribute
);
215 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
216 return getAttributeTree().getQuarkAndAdd(-1, attribute
);
220 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
221 throws AttributeNotFoundException
{
222 return getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
226 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
227 return getAttributeTree().getQuarkAndAdd(startingNodeQuark
, subPath
);
231 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
232 throws AttributeNotFoundException
{
233 return getAttributeTree().getSubAttributes(quark
, recursive
);
237 public List
<Integer
> getQuarks(String
... pattern
) {
238 List
<Integer
> quarks
= new LinkedList
<Integer
>();
239 List
<String
> prefix
= new LinkedList
<String
>();
240 List
<String
> suffix
= new LinkedList
<String
>();
241 boolean split
= false;
244 List
<Integer
> directChildren
;
245 int startingAttribute
;
247 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
248 for (String entry
: pattern
) {
249 if (entry
.equals("*")) { //$NON-NLS-1$
252 * Split was already true? This means there was more than
253 * one wildcard. This is not supported, return an empty
268 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
269 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
272 * If there was no wildcard, we'll only return the one matching
273 * attribute, if there is one.
278 quark
= getQuarkAbsolute(prefixStr
);
279 } catch (AttributeNotFoundException e
) {
280 /* It's fine, we'll just return the empty List */
288 if (prefix
.size() == 0) {
290 * If 'prefix' is empty, this means the wildcard was the first
291 * element. Look for the root node's sub-attributes.
293 startingAttribute
= -1;
295 startingAttribute
= getQuarkAbsolute(prefixStr
);
297 directChildren
= getSubAttributes(startingAttribute
, false);
298 } catch (AttributeNotFoundException e
) {
299 /* That attribute path did not exist, return the empty array */
304 * Iterate of all the sub-attributes, and only keep those who match the
305 * 'suffix' part of the initial pattern.
307 for (int childQuark
: directChildren
) {
310 matchingQuark
= getQuarkRelative(childQuark
, suffixStr
);
311 } catch (AttributeNotFoundException e
) {
314 quarks
.add(matchingQuark
);
320 //--------------------------------------------------------------------------
321 // Methods related to insertions in the history
322 //--------------------------------------------------------------------------
325 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
326 throws TimeRangeException
, AttributeNotFoundException
,
327 StateValueTypeException
{
328 transState
.processStateChange(t
, value
, attributeQuark
);
332 public void incrementAttribute(long t
, int attributeQuark
)
333 throws StateValueTypeException
, TimeRangeException
,
334 AttributeNotFoundException
{
335 ITmfStateValue stateValue
= queryOngoingState(attributeQuark
);
337 /* if the attribute was previously null, start counting at 0 */
338 if (!stateValue
.isNull()) {
339 prevValue
= stateValue
.unboxInt();
341 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
346 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
347 throws TimeRangeException
, AttributeNotFoundException
,
348 StateValueTypeException
{
350 int subAttributeQuark
;
351 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
353 if (previousSV
.isNull()) {
355 * If the StateValue was null, this means this is the first time we
356 * use this attribute. Leave stackDepth at 0.
359 } else if (previousSV
.getType() == Type
.INTEGER
) {
360 /* Previous value was an integer, all is good, use it */
361 stackDepth
= previousSV
.unboxInt();
363 /* Previous state of this attribute was another type? Not good! */
364 throw new StateValueTypeException();
367 if (stackDepth
>= 100000) {
369 * Limit stackDepth to 100000, to avoid having Attribute Trees grow out
370 * of control due to buggy insertions
372 String message
= "Stack limit reached, not pushing"; //$NON-NLS-1$
373 throw new AttributeNotFoundException(message
);
377 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, stackDepth
.toString());
379 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
380 modifyAttribute(t
, value
, subAttributeQuark
);
384 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
385 throws AttributeNotFoundException
, TimeRangeException
,
386 StateValueTypeException
{
387 /* These are the state values of the stack-attribute itself */
388 ITmfStateValue previousSV
= queryOngoingState(attributeQuark
);
390 if (previousSV
.isNull()) {
392 * Trying to pop an empty stack. This often happens at the start of
393 * traces, for example when we see a syscall_exit, without having
394 * the corresponding syscall_entry in the trace. Just ignore
399 if (previousSV
.getType() != Type
.INTEGER
) {
401 * The existing value was not an integer (which is expected for
402 * stack tops), this doesn't look like a valid stack attribute.
404 throw new StateValueTypeException();
407 Integer stackDepth
= previousSV
.unboxInt();
409 if (stackDepth
<= 0) {
410 /* This on the other hand should not happen... */
411 String message
= "A top-level stack attribute cannot " + //$NON-NLS-1$
412 "have a value of 0 or less."; //$NON-NLS-1$
413 throw new StateValueTypeException(message
);
416 /* The attribute should already exist at this point */
417 int subAttributeQuark
= getQuarkRelative(attributeQuark
, stackDepth
.toString());
418 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
420 /* Update the state value of the stack-attribute */
421 ITmfStateValue nextSV
;
422 if (--stackDepth
== 0 ) {
423 /* Store a null state value */
424 nextSV
= TmfStateValue
.nullValue();
426 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
428 modifyAttribute(t
, nextSV
, attributeQuark
);
430 /* Delete the sub-attribute that contained the user's state value */
431 removeAttribute(t
, subAttributeQuark
);
437 public void removeAttribute(long t
, int attributeQuark
)
438 throws TimeRangeException
, AttributeNotFoundException
{
439 assert (attributeQuark
>= 0);
440 List
<Integer
> childAttributes
;
443 * "Nullify our children first, recursively. We pass 'false' because we
444 * handle the recursion ourselves.
446 childAttributes
= getSubAttributes(attributeQuark
, false);
447 for (Integer childNodeQuark
: childAttributes
) {
448 assert (attributeQuark
!= childNodeQuark
);
449 removeAttribute(t
, childNodeQuark
);
451 /* Nullify ourselves */
453 transState
.processStateChange(t
, TmfStateValue
.nullValue(),
455 } catch (StateValueTypeException e
) {
457 * Will not happen since we're inserting null values only, but poor
458 * compiler has no way of knowing this...
460 throw new IllegalStateException(e
);
464 //--------------------------------------------------------------------------
465 // "Current" query/update methods
466 //--------------------------------------------------------------------------
469 public ITmfStateValue
queryOngoingState(int attributeQuark
)
470 throws AttributeNotFoundException
{
471 return transState
.getOngoingStateValue(attributeQuark
);
475 public long getOngoingStartTime(int attribute
)
476 throws AttributeNotFoundException
{
477 return transState
.getOngoingStartTime(attribute
);
481 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
482 throws AttributeNotFoundException
{
483 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
487 * Modify the whole "ongoing state" (state values + start times). This can
488 * be used when "seeking" a state system to a different point in the trace
489 * (and restoring the known stateInfo at this location). Use with care!
491 * @param newStateIntervals
492 * The new List of state values to use as ongoing state info
494 protected void replaceOngoingState(List
<ITmfStateInterval
> newStateIntervals
) {
495 transState
.replaceOngoingState(newStateIntervals
);
498 //--------------------------------------------------------------------------
499 // Regular query methods (sent to the back-end)
500 //--------------------------------------------------------------------------
503 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
504 throws TimeRangeException
, StateSystemDisposedException
{
506 throw new StateSystemDisposedException();
509 List
<ITmfStateInterval
> stateInfo
= new ArrayList
<ITmfStateInterval
>(getNbAttributes());
511 /* Bring the size of the array to the current number of attributes */
512 for (int i
= 0; i
< getNbAttributes(); i
++) {
516 /* Query the storage backend */
517 backend
.doQuery(stateInfo
, t
);
520 * If we are currently building the history, also query the "ongoing"
521 * states for stuff that might not yet be written to the history.
523 if (transState
.isActive()) {
524 transState
.doQuery(stateInfo
, t
);
528 * We should have previously inserted an interval for every attribute.
529 * If we do happen do see a 'null' object here, just replace it with a a
530 * dummy internal with a null value, to avoid NPE's further up.
532 for (int i
= 0; i
< stateInfo
.size(); i
++) {
533 if (stateInfo
.get(i
) == null) {
534 stateInfo
.set(i
, new TmfStateInterval(t
, t
, i
, TmfStateValue
.nullValue()));
541 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
542 throws AttributeNotFoundException
, TimeRangeException
,
543 StateSystemDisposedException
{
545 throw new StateSystemDisposedException();
548 ITmfStateInterval ret
;
549 if (transState
.hasInfoAboutStateOf(t
, attributeQuark
)) {
550 ret
= transState
.getOngoingInterval(attributeQuark
);
552 ret
= backend
.doSingularQuery(t
, attributeQuark
);
556 * Return a fake interval if we could not find anything in the history.
557 * We do NOT want to return 'null' here.
560 return new TmfStateInterval(t
, this.getCurrentEndTime(),
561 attributeQuark
, TmfStateValue
.nullValue());
567 public ITmfStateInterval
querySingleStackTop(long t
, int stackAttributeQuark
)
568 throws StateValueTypeException
, AttributeNotFoundException
,
569 TimeRangeException
, StateSystemDisposedException
{
570 ITmfStateValue curStackStateValue
= querySingleState(t
, stackAttributeQuark
).getStateValue();
572 if (curStackStateValue
.isNull()) {
573 /* There is nothing stored in this stack at this moment */
576 Integer curStackDepth
= curStackStateValue
.unboxInt();
577 if (curStackDepth
<= 0) {
579 * This attribute is an integer attribute, but it doesn't seem like
580 * it's used as a stack-attribute...
582 throw new StateValueTypeException();
585 int subAttribQuark
= getQuarkRelative(stackAttributeQuark
, curStackDepth
.toString());
586 return querySingleState(t
, subAttribQuark
);
590 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
591 long t1
, long t2
) throws TimeRangeException
,
592 AttributeNotFoundException
, StateSystemDisposedException
{
594 throw new StateSystemDisposedException();
597 List
<ITmfStateInterval
> intervals
;
598 ITmfStateInterval currentInterval
;
601 /* Make sure the time range makes sense */
603 throw new TimeRangeException();
606 /* Set the actual, valid end time of the range query */
607 if (t2
> this.getCurrentEndTime()) {
608 tEnd
= this.getCurrentEndTime();
613 /* Get the initial state at time T1 */
614 intervals
= new ArrayList
<ITmfStateInterval
>();
615 currentInterval
= querySingleState(t1
, attributeQuark
);
616 intervals
.add(currentInterval
);
618 /* Get the following state changes */
619 ts
= currentInterval
.getEndTime();
620 while (ts
!= -1 && ts
< tEnd
) {
621 ts
++; /* To "jump over" to the next state in the history */
622 currentInterval
= querySingleState(ts
, attributeQuark
);
623 intervals
.add(currentInterval
);
624 ts
= currentInterval
.getEndTime();
630 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
631 long t1
, long t2
, long resolution
, IProgressMonitor monitor
)
632 throws TimeRangeException
, AttributeNotFoundException
,
633 StateSystemDisposedException
{
635 throw new StateSystemDisposedException();
638 List
<ITmfStateInterval
> intervals
;
639 ITmfStateInterval currentInterval
;
642 IProgressMonitor mon
= monitor
;
644 mon
= new NullProgressMonitor();
647 /* Make sure the time range makes sense */
648 if (t2
< t1
|| resolution
<= 0) {
649 throw new TimeRangeException();
652 /* Set the actual, valid end time of the range query */
653 if (t2
> this.getCurrentEndTime()) {
654 tEnd
= this.getCurrentEndTime();
659 /* Get the initial state at time T1 */
660 intervals
= new ArrayList
<ITmfStateInterval
>();
661 currentInterval
= querySingleState(t1
, attributeQuark
);
662 intervals
.add(currentInterval
);
665 * Iterate over the "resolution points". We skip unneeded queries in the
666 * case the current interval is longer than the resolution.
668 for (ts
= t1
; (currentInterval
.getEndTime() != -1) && (ts
< tEnd
);
670 if (mon
.isCanceled()) {
673 if (ts
<= currentInterval
.getEndTime()) {
676 currentInterval
= querySingleState(ts
, attributeQuark
);
677 intervals
.add(currentInterval
);
680 /* Add the interval at t2, if it wasn't included already. */
681 if (currentInterval
.getEndTime() < tEnd
) {
682 currentInterval
= querySingleState(tEnd
, attributeQuark
);
683 intervals
.add(currentInterval
);
688 //--------------------------------------------------------------------------
690 //--------------------------------------------------------------------------
692 static void logMissingInterval(int attribute
, long timestamp
) {
693 Activator
.logInfo("No data found in history for attribute " + //$NON-NLS-1$
694 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
695 ", returning dummy interval"); //$NON-NLS-1$
699 * Print out the contents of the inner structures.
702 * The PrintWriter in which to print the output
704 public void debugPrint(PrintWriter writer
) {
705 getAttributeTree().debugPrint(writer
);
706 transState
.debugPrint(writer
);
707 backend
.debugPrint(writer
);