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
.TmfStateValue
;
38 * This is the core class of the Generic State System. It contains all the
39 * methods to build and query a state history. It's exposed externally through
40 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
41 * user needs read-only access or read-write access.
43 * When building, DON'T FORGET to call .closeHistory() when you are done
44 * inserting intervals, or the storage backend will have no way of knowing it
45 * can close and write itself to disk, and its thread will keep running.
50 public class StateSystem
implements ITmfStateSystemBuilder
{
52 /* References to the inner structures */
53 private final AttributeTree attributeTree
;
54 private final TransientState transState
;
55 private final IStateHistoryBackend backend
;
57 /* Latch tracking if the state history is done building or not */
58 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
60 private boolean buildCancelled
= false;
61 private boolean isDisposed
= false;
64 * New-file constructor. For when you build a state system with a new file,
65 * or if the back-end does not require a file on disk.
68 * Back-end plugin to use
70 public StateSystem(IStateHistoryBackend backend
) {
71 this.backend
= backend
;
72 this.transState
= new TransientState(backend
);
73 this.attributeTree
= new AttributeTree(this);
80 * The "state history storage" back-end to use.
82 * Put true if this is a new history started from scratch. It is
83 * used to tell the state system where to get its attribute tree.
85 * If there was a problem creating the new history file
87 public StateSystem(IStateHistoryBackend backend
, boolean newFile
)
89 this.backend
= backend
;
90 this.transState
= new TransientState(backend
);
93 attributeTree
= new AttributeTree(this);
95 /* We're opening an existing file */
96 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
97 transState
.setInactive();
98 finishedLatch
.countDown(); /* The history is already built */
103 public boolean waitUntilBuilt() {
105 finishedLatch
.await();
106 } catch (InterruptedException e
) {
109 return !buildCancelled
;
113 public synchronized void dispose() {
115 if (transState
.isActive()) {
116 transState
.setInactive();
117 buildCancelled
= true;
122 //--------------------------------------------------------------------------
123 // General methods related to the attribute tree
124 //--------------------------------------------------------------------------
127 * Method used by the attribute tree when creating new attributes, to keep
128 * the attribute count in the transient state in sync.
130 void addEmptyAttribute() {
131 transState
.addEmptyEntry();
135 public int getNbAttributes() {
136 return attributeTree
.getNbAttributes();
140 public boolean isLastAttribute(int quark
) {
141 return (quark
== getNbAttributes() - 1) ?
true : false;
145 public String
getAttributeName(int attributeQuark
) {
146 return attributeTree
.getAttributeName(attributeQuark
);
150 public String
getFullAttributePath(int attributeQuark
) {
151 return attributeTree
.getFullAttributeName(attributeQuark
);
154 //--------------------------------------------------------------------------
155 // Methods related to the storage backend
156 //--------------------------------------------------------------------------
159 public long getStartTime() {
160 return backend
.getStartTime();
164 public long getCurrentEndTime() {
165 return backend
.getEndTime();
169 public void closeHistory(long endTime
) throws TimeRangeException
{
170 File attributeTreeFile
;
171 long attributeTreeFilePos
;
172 long realEndTime
= endTime
;
174 if (realEndTime
< backend
.getEndTime()) {
176 * This can happen (empty nodes pushing the border further, etc.)
177 * but shouldn't be too big of a deal.
179 realEndTime
= backend
.getEndTime();
181 transState
.closeTransientState(realEndTime
);
182 backend
.finishedBuilding(realEndTime
);
184 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
185 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
186 if (attributeTreeFile
!= null) {
188 * If null was returned, we simply won't save the attribute tree,
191 attributeTree
.writeSelf(attributeTreeFile
, attributeTreeFilePos
);
193 finishedLatch
.countDown(); /* Mark the history as finished building */
196 //--------------------------------------------------------------------------
197 // Quark-retrieving methods
198 //--------------------------------------------------------------------------
201 public int getQuarkAbsolute(String
... attribute
)
202 throws AttributeNotFoundException
{
203 return attributeTree
.getQuarkDontAdd(-1, attribute
);
207 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
208 return attributeTree
.getQuarkAndAdd(-1, attribute
);
212 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
213 throws AttributeNotFoundException
{
214 return attributeTree
.getQuarkDontAdd(startingNodeQuark
, subPath
);
218 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
219 return attributeTree
.getQuarkAndAdd(startingNodeQuark
, subPath
);
223 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
224 throws AttributeNotFoundException
{
225 return attributeTree
.getSubAttributes(quark
, recursive
);
229 public List
<Integer
> getQuarks(String
... pattern
) {
230 List
<Integer
> quarks
= new LinkedList
<Integer
>();
231 List
<String
> prefix
= new LinkedList
<String
>();
232 List
<String
> suffix
= new LinkedList
<String
>();
233 boolean split
= false;
236 List
<Integer
> directChildren
;
237 int startingAttribute
;
239 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
240 for (String entry
: pattern
) {
241 if (entry
.equals("*")) { //$NON-NLS-1$
244 * Split was already true? This means there was more than
245 * one wildcard. This is not supported, return an empty
260 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
261 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
264 * If there was no wildcard, we'll only return the one matching
265 * attribute, if there is one.
267 if (split
== false) {
270 quark
= getQuarkAbsolute(prefixStr
);
271 } catch (AttributeNotFoundException e
) {
272 /* It's fine, we'll just return the empty List */
280 if (prefix
.size() == 0) {
282 * If 'prefix' is empty, this means the wildcard was the first
283 * element. Look for the root node's sub-attributes.
285 startingAttribute
= -1;
287 startingAttribute
= getQuarkAbsolute(prefixStr
);
289 directChildren
= attributeTree
.getSubAttributes(startingAttribute
,
291 } catch (AttributeNotFoundException e
) {
292 /* That attribute path did not exist, return the empty array */
297 * Iterate of all the sub-attributes, and only keep those who match the
298 * 'suffix' part of the initial pattern.
300 for (int childQuark
: directChildren
) {
303 matchingQuark
= getQuarkRelative(childQuark
, suffixStr
);
304 } catch (AttributeNotFoundException e
) {
307 quarks
.add(matchingQuark
);
313 //--------------------------------------------------------------------------
314 // Methods related to insertions in the history
315 //--------------------------------------------------------------------------
318 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
319 throws TimeRangeException
, AttributeNotFoundException
,
320 StateValueTypeException
{
321 transState
.processStateChange(t
, value
, attributeQuark
);
325 public void incrementAttribute(long t
, int attributeQuark
)
326 throws StateValueTypeException
, TimeRangeException
,
327 AttributeNotFoundException
{
328 int prevValue
= queryOngoingState(attributeQuark
).unboxInt();
329 if (prevValue
== -1) {
330 /* if the attribute was previously null, start counting at 0 */
333 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
338 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
339 throws TimeRangeException
, AttributeNotFoundException
,
340 StateValueTypeException
{
341 Integer stackDepth
= 0;
342 int subAttributeQuark
;
343 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
345 if (previousSV
.isNull()) {
347 * If the StateValue was null, this means this is the first time we
348 * use this attribute. Leave stackDepth at 0.
350 } else if (previousSV
.getType() == 0) {
351 /* Previous value was an integer, all is good, use it */
352 stackDepth
= previousSV
.unboxInt();
354 /* Previous state of this attribute was another type? Not good! */
355 throw new StateValueTypeException();
358 if (stackDepth
>= 10) {
360 * Limit stackDepth to 10, to avoid having Attribute Trees grow out
361 * of control due to buggy insertions
363 String message
= "Stack limit reached, not pushing"; //$NON-NLS-1$
364 throw new AttributeNotFoundException(message
);
368 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, stackDepth
.toString());
370 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
371 modifyAttribute(t
, value
, subAttributeQuark
);
375 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
376 throws AttributeNotFoundException
, TimeRangeException
,
377 StateValueTypeException
{
378 /* These are the state values of the stack-attribute itself */
379 ITmfStateValue previousSV
= queryOngoingState(attributeQuark
);
381 if (previousSV
.isNull()) {
383 * Trying to pop an empty stack. This often happens at the start of
384 * traces, for example when we see a syscall_exit, without having
385 * the corresponding syscall_entry in the trace. Just ignore
390 if (previousSV
.getType() != ITmfStateValue
.TYPE_INTEGER
) {
392 * The existing value was a string, this doesn't look like a valid
395 throw new StateValueTypeException();
398 Integer stackDepth
= previousSV
.unboxInt();
400 if (stackDepth
<= 0) {
401 /* This on the other hand should not happen... */
402 /* the case where == -1 was handled previously by .isNull() */
403 String message
= "A top-level stack attribute cannot " + //$NON-NLS-1$
404 "have a value of 0 or less (except -1/null)."; //$NON-NLS-1$
405 throw new StateValueTypeException(message
);
408 /* The attribute should already exist at this point */
409 int subAttributeQuark
= getQuarkRelative(attributeQuark
, stackDepth
.toString());
410 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
412 /* Update the state value of the stack-attribute */
413 ITmfStateValue nextSV
;
414 if (--stackDepth
== 0 ) {
415 /* Jump over "0" and store -1 (a null state value) */
416 nextSV
= TmfStateValue
.nullValue();
418 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
420 modifyAttribute(t
, nextSV
, attributeQuark
);
422 /* Delete the sub-attribute that contained the user's state value */
423 removeAttribute(t
, subAttributeQuark
);
429 public void removeAttribute(long t
, int attributeQuark
)
430 throws TimeRangeException
, AttributeNotFoundException
{
431 assert (attributeQuark
>= 0);
432 List
<Integer
> childAttributes
;
435 * "Nullify our children first, recursively. We pass 'false' because we
436 * handle the recursion ourselves.
438 childAttributes
= attributeTree
.getSubAttributes(attributeQuark
, false);
439 for (Integer childNodeQuark
: childAttributes
) {
440 assert (attributeQuark
!= childNodeQuark
);
441 removeAttribute(t
, childNodeQuark
);
443 /* Nullify ourselves */
445 transState
.processStateChange(t
, TmfStateValue
.nullValue(),
447 } catch (StateValueTypeException e
) {
449 * Will not happen since we're inserting null values only, but poor
450 * compiler has no way of knowing this...
456 //--------------------------------------------------------------------------
457 // "Current" query/update methods
458 //--------------------------------------------------------------------------
461 public ITmfStateValue
queryOngoingState(int attributeQuark
)
462 throws AttributeNotFoundException
{
463 return transState
.getOngoingStateValue(attributeQuark
);
467 public long getOngoingStartTime(int attribute
)
468 throws AttributeNotFoundException
{
469 return transState
.getOngoingStartTime(attribute
);
473 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
474 throws AttributeNotFoundException
{
475 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
478 //--------------------------------------------------------------------------
479 // Regular query methods (sent to the back-end)
480 //--------------------------------------------------------------------------
483 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
484 throws TimeRangeException
, StateSystemDisposedException
{
486 throw new StateSystemDisposedException();
489 List
<ITmfStateInterval
> stateInfo
= new ArrayList
<ITmfStateInterval
>(
490 attributeTree
.getNbAttributes());
492 /* Bring the size of the array to the current number of attributes */
493 for (int i
= 0; i
< attributeTree
.getNbAttributes(); i
++) {
497 /* Query the storage backend */
498 backend
.doQuery(stateInfo
, t
);
501 * If we are currently building the history, also query the "ongoing"
502 * states for stuff that might not yet be written to the history.
504 if (transState
.isActive()) {
505 transState
.doQuery(stateInfo
, t
);
509 * We should have previously inserted an interval for every attribute.
510 * If we do happen do see a 'null' object here, just replace it with a a
511 * dummy internal with a null value, to avoid NPE's further up.
513 for (int i
= 0; i
< stateInfo
.size(); i
++) {
514 if (stateInfo
.get(i
) == null) {
515 //logMissingInterval(i, t);
516 stateInfo
.set(i
, new TmfStateInterval(t
, t
, i
, TmfStateValue
.nullValue()));
523 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
524 throws AttributeNotFoundException
, TimeRangeException
,
525 StateSystemDisposedException
{
527 throw new StateSystemDisposedException();
530 ITmfStateInterval ret
;
531 if (transState
.hasInfoAboutStateOf(t
, attributeQuark
)) {
532 ret
= transState
.getOngoingInterval(attributeQuark
);
534 ret
= backend
.doSingularQuery(t
, attributeQuark
);
538 * Return a fake interval if we could not find anything in the history.
539 * We do NOT want to return 'null' here.
542 //logMissingInterval(attributeQuark, t);
543 return new TmfStateInterval(t
, this.getCurrentEndTime(),
544 attributeQuark
, TmfStateValue
.nullValue());
550 public ITmfStateInterval
querySingleStackTop(long t
, int stackAttributeQuark
)
551 throws StateValueTypeException
, AttributeNotFoundException
,
552 TimeRangeException
, StateSystemDisposedException
{
553 Integer curStackDepth
= querySingleState(t
, stackAttributeQuark
).getStateValue().unboxInt();
555 if (curStackDepth
== -1) {
556 /* There is nothing stored in this stack at this moment */
558 } else if (curStackDepth
< -1 || curStackDepth
== 0) {
560 * This attribute is an integer attribute, but it doesn't seem like
561 * it's used as a stack-attribute...
563 throw new StateValueTypeException();
566 int subAttribQuark
= getQuarkRelative(stackAttributeQuark
, curStackDepth
.toString());
567 ITmfStateInterval ret
= querySingleState(t
, subAttribQuark
);
572 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
573 long t1
, long t2
) throws TimeRangeException
,
574 AttributeNotFoundException
, StateSystemDisposedException
{
576 throw new StateSystemDisposedException();
579 List
<ITmfStateInterval
> intervals
;
580 ITmfStateInterval currentInterval
;
583 /* Make sure the time range makes sense */
585 throw new TimeRangeException();
588 /* Set the actual, valid end time of the range query */
589 if (t2
> this.getCurrentEndTime()) {
590 tEnd
= this.getCurrentEndTime();
595 /* Get the initial state at time T1 */
596 intervals
= new ArrayList
<ITmfStateInterval
>();
597 currentInterval
= querySingleState(t1
, attributeQuark
);
598 intervals
.add(currentInterval
);
600 /* Get the following state changes */
601 ts
= currentInterval
.getEndTime();
602 while (ts
!= -1 && ts
< tEnd
) {
603 ts
++; /* To "jump over" to the next state in the history */
604 currentInterval
= querySingleState(ts
, attributeQuark
);
605 intervals
.add(currentInterval
);
606 ts
= currentInterval
.getEndTime();
612 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
613 long t1
, long t2
, long resolution
, IProgressMonitor monitor
)
614 throws TimeRangeException
, AttributeNotFoundException
,
615 StateSystemDisposedException
{
617 throw new StateSystemDisposedException();
620 List
<ITmfStateInterval
> intervals
;
621 ITmfStateInterval currentInterval
;
624 IProgressMonitor mon
= monitor
;
626 mon
= new NullProgressMonitor();
629 /* Make sure the time range makes sense */
630 if (t2
< t1
|| resolution
<= 0) {
631 throw new TimeRangeException();
634 /* Set the actual, valid end time of the range query */
635 if (t2
> this.getCurrentEndTime()) {
636 tEnd
= this.getCurrentEndTime();
641 /* Get the initial state at time T1 */
642 intervals
= new ArrayList
<ITmfStateInterval
>();
643 currentInterval
= querySingleState(t1
, attributeQuark
);
644 intervals
.add(currentInterval
);
647 * Iterate over the "resolution points". We skip unneeded queries in the
648 * case the current interval is longer than the resolution.
650 for (ts
= t1
; (currentInterval
.getEndTime() != -1) && (ts
< tEnd
);
652 if (mon
.isCanceled()) {
655 if (ts
<= currentInterval
.getEndTime()) {
658 currentInterval
= querySingleState(ts
, attributeQuark
);
659 intervals
.add(currentInterval
);
662 /* Add the interval at t2, if it wasn't included already. */
663 if (currentInterval
.getEndTime() < tEnd
) {
664 currentInterval
= querySingleState(tEnd
, attributeQuark
);
665 intervals
.add(currentInterval
);
670 //--------------------------------------------------------------------------
672 //--------------------------------------------------------------------------
674 static void logMissingInterval(int attribute
, long timestamp
) {
675 Activator
.logInfo("No data found in history for attribute " + //$NON-NLS-1$
676 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
677 ", returning dummy interval"); //$NON-NLS-1$
681 * Print out the contents of the inner structures.
684 * The PrintWriter in which to print the output
686 public void debugPrint(PrintWriter writer
) {
687 attributeTree
.debugPrint(writer
);
688 transState
.debugPrint(writer
);
689 backend
.debugPrint(writer
);