1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 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
;
22 import java
.util
.concurrent
.TimeUnit
;
24 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
25 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.Activator
;
28 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.IStateHistoryBackend
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.TmfStateInterval
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystemBuilder
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
37 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
.Type
;
38 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
41 * This is the core class of the Generic State System. It contains all the
42 * methods to build and query a state history. It's exposed externally through
43 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
44 * user needs read-only access or read-write access.
46 * When building, DON'T FORGET to call .closeHistory() when you are done
47 * inserting intervals, or the storage backend will have no way of knowing it
48 * can close and write itself to disk, and its thread will keep running.
53 public class StateSystem
implements ITmfStateSystemBuilder
{
55 private final String ssid
;
57 /* References to the inner structures */
58 private final AttributeTree attributeTree
;
59 private final TransientState transState
;
60 private final IStateHistoryBackend backend
;
62 /* Latch tracking if the state history is done building or not */
63 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
65 private boolean buildCancelled
= false;
66 private boolean isDisposed
= false;
69 * New-file constructor. For when you build a state system with a new file,
70 * or if the back-end does not require a file on disk.
73 * The ID of this statesystem. It should be unique.
75 * Back-end plugin to use
77 public StateSystem(@NonNull String ssid
, @NonNull IStateHistoryBackend backend
) {
79 this.backend
= backend
;
80 this.transState
= new TransientState(backend
);
81 this.attributeTree
= new AttributeTree(this);
88 * The ID of this statesystem. It should be unique.
90 * The "state history storage" back-end to use.
92 * Put true if this is a new history started from scratch. It is
93 * used to tell the state system where to get its attribute tree.
95 * If there was a problem creating the new history file
97 public StateSystem(@NonNull String ssid
, @NonNull IStateHistoryBackend backend
, boolean newFile
)
100 this.backend
= backend
;
101 this.transState
= new TransientState(backend
);
104 attributeTree
= new AttributeTree(this);
106 /* We're opening an existing file */
107 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
108 transState
.setInactive();
109 finishedLatch
.countDown(); /* The history is already built */
114 public String
getSSID() {
119 public boolean isCancelled() {
120 return buildCancelled
;
124 public void waitUntilBuilt() {
126 finishedLatch
.await();
127 } catch (InterruptedException e
) {
133 public boolean waitUntilBuilt(long timeout
) {
136 ret
= finishedLatch
.await(timeout
, TimeUnit
.MILLISECONDS
);
137 } catch (InterruptedException e
) {
144 public synchronized void dispose() {
146 if (transState
.isActive()) {
147 transState
.setInactive();
148 buildCancelled
= true;
153 //--------------------------------------------------------------------------
154 // General methods related to the attribute tree
155 //--------------------------------------------------------------------------
158 * Get the attribute tree associated with this state system. This should be
159 * the only way of accessing it (and if subclasses want to point to a
160 * different attribute tree than their own, they should only need to
163 * @return The attribute tree
165 public AttributeTree
getAttributeTree() {
166 return attributeTree
;
170 * Method used by the attribute tree when creating new attributes, to keep
171 * the attribute count in the transient state in sync.
173 protected void addEmptyAttribute() {
174 transState
.addEmptyEntry();
178 public int getNbAttributes() {
179 return getAttributeTree().getNbAttributes();
183 public String
getAttributeName(int attributeQuark
) {
184 return getAttributeTree().getAttributeName(attributeQuark
);
188 public String
getFullAttributePath(int attributeQuark
) {
189 return getAttributeTree().getFullAttributeName(attributeQuark
);
192 //--------------------------------------------------------------------------
193 // Methods related to the storage backend
194 //--------------------------------------------------------------------------
197 public long getStartTime() {
198 return backend
.getStartTime();
202 public long getCurrentEndTime() {
203 return backend
.getEndTime();
207 public void closeHistory(long endTime
) throws TimeRangeException
{
208 File attributeTreeFile
;
209 long attributeTreeFilePos
;
210 long realEndTime
= endTime
;
212 if (realEndTime
< backend
.getEndTime()) {
214 * This can happen (empty nodes pushing the border further, etc.)
215 * but shouldn't be too big of a deal.
217 realEndTime
= backend
.getEndTime();
219 transState
.closeTransientState(realEndTime
);
220 backend
.finishedBuilding(realEndTime
);
222 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
223 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
224 if (attributeTreeFile
!= null) {
226 * If null was returned, we simply won't save the attribute tree,
229 getAttributeTree().writeSelf(attributeTreeFile
, attributeTreeFilePos
);
231 finishedLatch
.countDown(); /* Mark the history as finished building */
234 //--------------------------------------------------------------------------
235 // Quark-retrieving methods
236 //--------------------------------------------------------------------------
239 public int getQuarkAbsolute(String
... attribute
)
240 throws AttributeNotFoundException
{
241 return getAttributeTree().getQuarkDontAdd(-1, attribute
);
245 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
246 return getAttributeTree().getQuarkAndAdd(-1, attribute
);
250 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
251 throws AttributeNotFoundException
{
252 return getAttributeTree().getQuarkDontAdd(startingNodeQuark
, subPath
);
256 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
257 return getAttributeTree().getQuarkAndAdd(startingNodeQuark
, subPath
);
261 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
262 throws AttributeNotFoundException
{
263 return getAttributeTree().getSubAttributes(quark
, recursive
);
267 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
, String pattern
)
268 throws AttributeNotFoundException
{
269 List
<Integer
> all
= getSubAttributes(quark
, recursive
);
270 List
<Integer
> ret
= new LinkedList
<>();
271 for (Integer attQuark
: all
) {
272 String name
= getAttributeName(attQuark
.intValue());
273 if (name
.matches(pattern
)) {
281 public int getParentAttributeQuark(int quark
) {
282 return getAttributeTree().getParentAttributeQuark(quark
);
286 public List
<Integer
> getQuarks(String
... pattern
) {
287 List
<Integer
> quarks
= new LinkedList
<>();
288 List
<String
> prefix
= new LinkedList
<>();
289 List
<String
> suffix
= new LinkedList
<>();
290 boolean split
= false;
293 List
<Integer
> directChildren
;
294 int startingAttribute
;
296 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
297 for (String entry
: pattern
) {
298 if (entry
.equals("*")) { //$NON-NLS-1$
301 * Split was already true? This means there was more than
302 * one wildcard. This is not supported, return an empty
317 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
318 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
321 * If there was no wildcard, we'll only return the one matching
322 * attribute, if there is one.
327 quark
= getQuarkAbsolute(prefixStr
);
328 } catch (AttributeNotFoundException e
) {
329 /* It's fine, we'll just return the empty List */
337 if (prefix
.size() == 0) {
339 * If 'prefix' is empty, this means the wildcard was the first
340 * element. Look for the root node's sub-attributes.
342 startingAttribute
= -1;
344 startingAttribute
= getQuarkAbsolute(prefixStr
);
346 directChildren
= getSubAttributes(startingAttribute
, false);
347 } catch (AttributeNotFoundException e
) {
348 /* That attribute path did not exist, return the empty array */
353 * Iterate of all the sub-attributes, and only keep those who match the
354 * 'suffix' part of the initial pattern.
356 for (int childQuark
: directChildren
) {
359 matchingQuark
= getQuarkRelative(childQuark
, suffixStr
);
360 } catch (AttributeNotFoundException e
) {
363 quarks
.add(matchingQuark
);
369 //--------------------------------------------------------------------------
370 // Methods related to insertions in the history
371 //--------------------------------------------------------------------------
374 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
375 throws TimeRangeException
, AttributeNotFoundException
,
376 StateValueTypeException
{
377 transState
.processStateChange(t
, value
, attributeQuark
);
381 public void incrementAttribute(long t
, int attributeQuark
)
382 throws StateValueTypeException
, TimeRangeException
,
383 AttributeNotFoundException
{
384 ITmfStateValue stateValue
= queryOngoingState(attributeQuark
);
386 /* if the attribute was previously null, start counting at 0 */
387 if (!stateValue
.isNull()) {
388 prevValue
= stateValue
.unboxInt();
390 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
395 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
396 throws TimeRangeException
, AttributeNotFoundException
,
397 StateValueTypeException
{
399 int subAttributeQuark
;
400 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
402 if (previousSV
.isNull()) {
404 * If the StateValue was null, this means this is the first time we
405 * use this attribute. Leave stackDepth at 0.
408 } else if (previousSV
.getType() == Type
.INTEGER
) {
409 /* Previous value was an integer, all is good, use it */
410 stackDepth
= previousSV
.unboxInt();
412 /* Previous state of this attribute was another type? Not good! */
413 throw new StateValueTypeException();
416 if (stackDepth
>= 100000) {
418 * Limit stackDepth to 100000, to avoid having Attribute Trees grow out
419 * of control due to buggy insertions
421 String message
= "Stack limit reached, not pushing"; //$NON-NLS-1$
422 throw new AttributeNotFoundException(message
);
426 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, String
.valueOf(stackDepth
));
428 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
429 modifyAttribute(t
, value
, subAttributeQuark
);
433 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
434 throws AttributeNotFoundException
, TimeRangeException
,
435 StateValueTypeException
{
436 /* These are the state values of the stack-attribute itself */
437 ITmfStateValue previousSV
= queryOngoingState(attributeQuark
);
439 if (previousSV
.isNull()) {
441 * Trying to pop an empty stack. This often happens at the start of
442 * traces, for example when we see a syscall_exit, without having
443 * the corresponding syscall_entry in the trace. Just ignore
448 if (previousSV
.getType() != Type
.INTEGER
) {
450 * The existing value was not an integer (which is expected for
451 * stack tops), this doesn't look like a valid stack attribute.
453 throw new StateValueTypeException();
456 int stackDepth
= previousSV
.unboxInt();
458 if (stackDepth
<= 0) {
459 /* This on the other hand should not happen... */
460 String message
= "A top-level stack attribute cannot " + //$NON-NLS-1$
461 "have a value of 0 or less."; //$NON-NLS-1$
462 throw new StateValueTypeException(message
);
465 /* The attribute should already exist at this point */
466 int subAttributeQuark
= getQuarkRelative(attributeQuark
, String
.valueOf(stackDepth
));
467 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
469 /* Update the state value of the stack-attribute */
470 ITmfStateValue nextSV
;
471 if (--stackDepth
== 0 ) {
472 /* Store a null state value */
473 nextSV
= TmfStateValue
.nullValue();
475 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
477 modifyAttribute(t
, nextSV
, attributeQuark
);
479 /* Delete the sub-attribute that contained the user's state value */
480 removeAttribute(t
, subAttributeQuark
);
486 public void removeAttribute(long t
, int attributeQuark
)
487 throws TimeRangeException
, AttributeNotFoundException
{
488 assert (attributeQuark
>= 0);
489 List
<Integer
> childAttributes
;
492 * "Nullify our children first, recursively. We pass 'false' because we
493 * handle the recursion ourselves.
495 childAttributes
= getSubAttributes(attributeQuark
, false);
496 for (int childNodeQuark
: childAttributes
) {
497 assert (attributeQuark
!= childNodeQuark
);
498 removeAttribute(t
, childNodeQuark
);
500 /* Nullify ourselves */
502 transState
.processStateChange(t
, TmfStateValue
.nullValue(),
504 } catch (StateValueTypeException e
) {
506 * Will not happen since we're inserting null values only, but poor
507 * compiler has no way of knowing this...
509 throw new IllegalStateException(e
);
513 //--------------------------------------------------------------------------
514 // "Current" query/update methods
515 //--------------------------------------------------------------------------
518 public ITmfStateValue
queryOngoingState(int attributeQuark
)
519 throws AttributeNotFoundException
{
520 return transState
.getOngoingStateValue(attributeQuark
);
524 public long getOngoingStartTime(int attribute
)
525 throws AttributeNotFoundException
{
526 return transState
.getOngoingStartTime(attribute
);
530 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
531 throws AttributeNotFoundException
{
532 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
536 * Modify the whole "ongoing state" (state values + start times). This can
537 * be used when "seeking" a state system to a different point in the trace
538 * (and restoring the known stateInfo at this location). Use with care!
540 * @param newStateIntervals
541 * The new List of state values to use as ongoing state info
543 protected void replaceOngoingState(List
<ITmfStateInterval
> newStateIntervals
) {
544 transState
.replaceOngoingState(newStateIntervals
);
547 //--------------------------------------------------------------------------
548 // Regular query methods (sent to the back-end)
549 //--------------------------------------------------------------------------
552 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
553 throws TimeRangeException
, StateSystemDisposedException
{
555 throw new StateSystemDisposedException();
558 List
<ITmfStateInterval
> stateInfo
= new ArrayList
<>(getNbAttributes());
560 /* Bring the size of the array to the current number of attributes */
561 for (int i
= 0; i
< getNbAttributes(); i
++) {
565 /* Query the storage backend */
566 backend
.doQuery(stateInfo
, t
);
569 * If we are currently building the history, also query the "ongoing"
570 * states for stuff that might not yet be written to the history.
572 if (transState
.isActive()) {
573 transState
.doQuery(stateInfo
, t
);
577 * We should have previously inserted an interval for every attribute.
578 * If we do happen do see a 'null' object here, just replace it with a a
579 * dummy internal with a null value, to avoid NPE's further up.
581 for (int i
= 0; i
< stateInfo
.size(); i
++) {
582 if (stateInfo
.get(i
) == null) {
583 stateInfo
.set(i
, new TmfStateInterval(t
, t
, i
, TmfStateValue
.nullValue()));
590 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
591 throws AttributeNotFoundException
, TimeRangeException
,
592 StateSystemDisposedException
{
594 throw new StateSystemDisposedException();
597 ITmfStateInterval ret
= transState
.getIntervalAt(t
, attributeQuark
);
600 * The transient state did not have the information, let's look into
603 ret
= backend
.doSingularQuery(t
, attributeQuark
);
607 * Return a fake interval if we could not find anything in the history.
608 * We do NOT want to return 'null' here.
611 return new TmfStateInterval(t
, this.getCurrentEndTime(),
612 attributeQuark
, TmfStateValue
.nullValue());
618 public ITmfStateInterval
querySingleStackTop(long t
, int stackAttributeQuark
)
619 throws StateValueTypeException
, AttributeNotFoundException
,
620 TimeRangeException
, StateSystemDisposedException
{
621 ITmfStateValue curStackStateValue
= querySingleState(t
, stackAttributeQuark
).getStateValue();
623 if (curStackStateValue
.isNull()) {
624 /* There is nothing stored in this stack at this moment */
627 int curStackDepth
= curStackStateValue
.unboxInt();
628 if (curStackDepth
<= 0) {
630 * This attribute is an integer attribute, but it doesn't seem like
631 * it's used as a stack-attribute...
633 throw new StateValueTypeException();
636 int subAttribQuark
= getQuarkRelative(stackAttributeQuark
, String
.valueOf(curStackDepth
));
637 return querySingleState(t
, subAttribQuark
);
641 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
642 long t1
, long t2
) throws TimeRangeException
,
643 AttributeNotFoundException
, StateSystemDisposedException
{
645 throw new StateSystemDisposedException();
648 List
<ITmfStateInterval
> intervals
;
649 ITmfStateInterval currentInterval
;
652 /* Make sure the time range makes sense */
654 throw new TimeRangeException();
657 /* Set the actual, valid end time of the range query */
658 if (t2
> this.getCurrentEndTime()) {
659 tEnd
= this.getCurrentEndTime();
664 /* Get the initial state at time T1 */
665 intervals
= new ArrayList
<>();
666 currentInterval
= querySingleState(t1
, attributeQuark
);
667 intervals
.add(currentInterval
);
669 /* Get the following state changes */
670 ts
= currentInterval
.getEndTime();
671 while (ts
!= -1 && ts
< tEnd
) {
672 ts
++; /* To "jump over" to the next state in the history */
673 currentInterval
= querySingleState(ts
, attributeQuark
);
674 intervals
.add(currentInterval
);
675 ts
= currentInterval
.getEndTime();
681 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
682 long t1
, long t2
, long resolution
, IProgressMonitor monitor
)
683 throws TimeRangeException
, AttributeNotFoundException
,
684 StateSystemDisposedException
{
686 throw new StateSystemDisposedException();
689 List
<ITmfStateInterval
> intervals
;
690 ITmfStateInterval currentInterval
;
693 IProgressMonitor mon
= monitor
;
695 mon
= new NullProgressMonitor();
698 /* Make sure the time range makes sense */
699 if (t2
< t1
|| resolution
<= 0) {
700 throw new TimeRangeException();
703 /* Set the actual, valid end time of the range query */
704 if (t2
> this.getCurrentEndTime()) {
705 tEnd
= this.getCurrentEndTime();
710 /* Get the initial state at time T1 */
711 intervals
= new ArrayList
<>();
712 currentInterval
= querySingleState(t1
, attributeQuark
);
713 intervals
.add(currentInterval
);
716 * Iterate over the "resolution points". We skip unneeded queries in the
717 * case the current interval is longer than the resolution.
719 for (ts
= t1
; (currentInterval
.getEndTime() != -1) && (ts
< tEnd
);
721 if (mon
.isCanceled()) {
724 if (ts
<= currentInterval
.getEndTime()) {
727 currentInterval
= querySingleState(ts
, attributeQuark
);
728 intervals
.add(currentInterval
);
731 /* Add the interval at t2, if it wasn't included already. */
732 if (currentInterval
.getEndTime() < tEnd
) {
733 currentInterval
= querySingleState(tEnd
, attributeQuark
);
734 intervals
.add(currentInterval
);
739 //--------------------------------------------------------------------------
741 //--------------------------------------------------------------------------
743 static void logMissingInterval(int attribute
, long timestamp
) {
744 Activator
.logInfo("No data found in history for attribute " + //$NON-NLS-1$
745 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
746 ", returning dummy interval"); //$NON-NLS-1$
750 * Print out the contents of the inner structures.
753 * The PrintWriter in which to print the output
755 public void debugPrint(PrintWriter writer
) {
756 getAttributeTree().debugPrint(writer
);
757 transState
.debugPrint(writer
);
758 backend
.debugPrint(writer
);