1 /*******************************************************************************
2 * Copyright (c) 2012 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
.tmf
.core
.exceptions
.AttributeNotFoundException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.TmfStateInterval
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystemBuilder
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
37 * This is the core class of the Generic State System. It contains all the
38 * methods to build and query a state history. It's exposed externally through
39 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
40 * user needs read-only access or read-write access.
42 * When building, DON'T FORGET to call .closeHistory() when you are done
43 * inserting intervals, or the storage backend will have no way of knowing it
44 * can close and write itself to disk, and its thread will keep running.
49 public class StateSystem
implements ITmfStateSystemBuilder
{
51 /* References to the inner structures */
52 private final AttributeTree attributeTree
;
53 private final TransientState transState
;
54 private final IStateHistoryBackend backend
;
56 /* Latch tracking if the state history is done building or not */
57 private final CountDownLatch finishedLatch
= new CountDownLatch(1);
59 private boolean buildCancelled
= false;
60 private boolean isDisposed
= false;
66 * The "state history storage" backend to use.
68 * Put true if this is a new history started from scratch. It is
69 * used to tell the state system where to get its attribute tree.
71 * If there was a problem creating the new history file
73 public StateSystem(IStateHistoryBackend backend
, boolean newFile
)
75 this.backend
= backend
;
76 this.transState
= new TransientState(backend
);
79 attributeTree
= new AttributeTree(this);
81 /* We're opening an existing file */
82 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
83 transState
.setInactive();
84 finishedLatch
.countDown(); /* The history is already built */
89 public boolean waitUntilBuilt() {
91 finishedLatch
.await();
92 } catch (InterruptedException e
) {
95 return !buildCancelled
;
99 public synchronized void dispose() {
101 if (transState
.isActive()) {
102 transState
.setInactive();
103 buildCancelled
= true;
108 //--------------------------------------------------------------------------
109 // General methods related to the attribute tree
110 //--------------------------------------------------------------------------
113 * Method used by the attribute tree when creating new attributes, to keep
114 * the attribute count in the transient state in sync.
116 void addEmptyAttribute() {
117 transState
.addEmptyEntry();
121 public int getNbAttributes() {
122 return attributeTree
.getNbAttributes();
126 public boolean isLastAttribute(int quark
) {
127 return (quark
== getNbAttributes() - 1) ?
true : false;
131 public String
getAttributeName(int attributeQuark
) {
132 return attributeTree
.getAttributeName(attributeQuark
);
136 public String
getFullAttributePath(int attributeQuark
) {
137 return attributeTree
.getFullAttributeName(attributeQuark
);
140 //--------------------------------------------------------------------------
141 // Methods related to the storage backend
142 //--------------------------------------------------------------------------
145 public long getStartTime() {
146 return backend
.getStartTime();
150 public long getCurrentEndTime() {
151 return backend
.getEndTime();
155 public void closeHistory(long endTime
) throws TimeRangeException
{
156 File attributeTreeFile
;
157 long attributeTreeFilePos
;
158 long realEndTime
= endTime
;
160 if (realEndTime
< backend
.getEndTime()) {
162 * This can happen (empty nodes pushing the border further, etc.)
163 * but shouldn't be too big of a deal.
165 realEndTime
= backend
.getEndTime();
167 transState
.closeTransientState(realEndTime
);
168 backend
.finishedBuilding(realEndTime
);
170 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
171 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
172 if (attributeTreeFile
!= null) {
174 * If null was returned, we simply won't save the attribute tree,
177 attributeTree
.writeSelf(attributeTreeFile
, attributeTreeFilePos
);
179 finishedLatch
.countDown(); /* Mark the history as finished building */
182 //--------------------------------------------------------------------------
183 // Quark-retrieving methods
184 //--------------------------------------------------------------------------
187 public int getQuarkAbsolute(String
... attribute
)
188 throws AttributeNotFoundException
{
189 return attributeTree
.getQuarkDontAdd(-1, attribute
);
193 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
194 return attributeTree
.getQuarkAndAdd(-1, attribute
);
198 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
199 throws AttributeNotFoundException
{
200 return attributeTree
.getQuarkDontAdd(startingNodeQuark
, subPath
);
204 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
205 return attributeTree
.getQuarkAndAdd(startingNodeQuark
, subPath
);
209 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
210 throws AttributeNotFoundException
{
211 return attributeTree
.getSubAttributes(quark
, recursive
);
215 public List
<Integer
> getQuarks(String
... pattern
) {
216 List
<Integer
> quarks
= new LinkedList
<Integer
>();
217 List
<String
> prefix
= new LinkedList
<String
>();
218 List
<String
> suffix
= new LinkedList
<String
>();
219 boolean split
= false;
222 List
<Integer
> directChildren
;
223 int startingAttribute
;
225 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
226 for (String entry
: pattern
) {
227 if (entry
.equals("*")) { //$NON-NLS-1$
230 * Split was already true? This means there was more than
231 * one wildcard. This is not supported, return an empty
246 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
247 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
250 * If there was no wildcard, we'll only return the one matching
251 * attribute, if there is one.
253 if (split
== false) {
256 quark
= getQuarkAbsolute(prefixStr
);
257 } catch (AttributeNotFoundException e
) {
258 /* It's fine, we'll just return the empty List */
266 if (prefix
.size() == 0) {
268 * If 'prefix' is empty, this means the wildcard was the first
269 * element. Look for the root node's sub-attributes.
271 startingAttribute
= -1;
273 startingAttribute
= getQuarkAbsolute(prefixStr
);
275 directChildren
= attributeTree
.getSubAttributes(startingAttribute
,
277 } catch (AttributeNotFoundException e
) {
278 /* That attribute path did not exist, return the empty array */
283 * Iterate of all the sub-attributes, and only keep those who match the
284 * 'suffix' part of the initial pattern.
286 for (int childQuark
: directChildren
) {
289 matchingQuark
= getQuarkRelative(childQuark
, suffixStr
);
290 } catch (AttributeNotFoundException e
) {
293 quarks
.add(matchingQuark
);
299 //--------------------------------------------------------------------------
300 // Methods related to insertions in the history
301 //--------------------------------------------------------------------------
304 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
305 throws TimeRangeException
, AttributeNotFoundException
,
306 StateValueTypeException
{
307 transState
.processStateChange(t
, value
, attributeQuark
);
311 public void incrementAttribute(long t
, int attributeQuark
)
312 throws StateValueTypeException
, TimeRangeException
,
313 AttributeNotFoundException
{
314 int prevValue
= queryOngoingState(attributeQuark
).unboxInt();
315 if (prevValue
== -1) {
316 /* if the attribute was previously null, start counting at 0 */
319 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
324 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
325 throws TimeRangeException
, AttributeNotFoundException
,
326 StateValueTypeException
{
327 Integer stackDepth
= 0;
328 int subAttributeQuark
;
329 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
331 if (previousSV
.isNull()) {
333 * If the StateValue was null, this means this is the first time we
334 * use this attribute. Leave stackDepth at 0.
336 } else if (previousSV
.getType() == 0) {
337 /* Previous value was an integer, all is good, use it */
338 stackDepth
= previousSV
.unboxInt();
340 /* Previous state of this attribute was another type? Not good! */
341 throw new StateValueTypeException();
344 if (stackDepth
>= 10) {
346 * Limit stackDepth to 10, to avoid having Attribute Trees grow out
347 * of control due to buggy insertions
349 String message
= "Stack limit reached, not pushing"; //$NON-NLS-1$
350 throw new AttributeNotFoundException(message
);
354 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
, stackDepth
.toString());
356 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
), attributeQuark
);
357 modifyAttribute(t
, value
, subAttributeQuark
);
361 public ITmfStateValue
popAttribute(long t
, int attributeQuark
)
362 throws AttributeNotFoundException
, TimeRangeException
,
363 StateValueTypeException
{
364 /* These are the state values of the stack-attribute itself */
365 ITmfStateValue previousSV
= queryOngoingState(attributeQuark
);
367 if (previousSV
.isNull()) {
369 * Trying to pop an empty stack. This often happens at the start of
370 * traces, for example when we see a syscall_exit, without having
371 * the corresponding syscall_entry in the trace. Just ignore
376 if (previousSV
.getType() != ITmfStateValue
.TYPE_INTEGER
) {
378 * The existing value was a string, this doesn't look like a valid
381 throw new StateValueTypeException();
384 Integer stackDepth
= previousSV
.unboxInt();
386 if (stackDepth
<= 0) {
387 /* This on the other hand should not happen... */
388 /* the case where == -1 was handled previously by .isNull() */
389 String message
= "A top-level stack attribute cannot " + //$NON-NLS-1$
390 "have a value of 0 or less (except -1/null)."; //$NON-NLS-1$
391 throw new StateValueTypeException(message
);
394 /* The attribute should already exist at this point */
395 int subAttributeQuark
= getQuarkRelative(attributeQuark
, stackDepth
.toString());
396 ITmfStateValue poppedValue
= queryOngoingState(subAttributeQuark
);
398 /* Update the state value of the stack-attribute */
399 ITmfStateValue nextSV
;
400 if (--stackDepth
== 0 ) {
401 /* Jump over "0" and store -1 (a null state value) */
402 nextSV
= TmfStateValue
.nullValue();
404 nextSV
= TmfStateValue
.newValueInt(stackDepth
);
406 modifyAttribute(t
, nextSV
, attributeQuark
);
408 /* Delete the sub-attribute that contained the user's state value */
409 removeAttribute(t
, subAttributeQuark
);
415 public void removeAttribute(long t
, int attributeQuark
)
416 throws TimeRangeException
, AttributeNotFoundException
{
417 assert (attributeQuark
>= 0);
418 List
<Integer
> childAttributes
;
421 * "Nullify our children first, recursively. We pass 'false' because we
422 * handle the recursion ourselves.
424 childAttributes
= attributeTree
.getSubAttributes(attributeQuark
, false);
425 for (Integer childNodeQuark
: childAttributes
) {
426 assert (attributeQuark
!= childNodeQuark
);
427 removeAttribute(t
, childNodeQuark
);
429 /* Nullify ourselves */
431 transState
.processStateChange(t
, TmfStateValue
.nullValue(),
433 } catch (StateValueTypeException e
) {
435 * Will not happen since we're inserting null values only, but poor
436 * compiler has no way of knowing this...
442 //--------------------------------------------------------------------------
443 // "Current" query/update methods
444 //--------------------------------------------------------------------------
447 public ITmfStateValue
queryOngoingState(int attributeQuark
)
448 throws AttributeNotFoundException
{
449 return transState
.getOngoingStateValue(attributeQuark
);
453 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
454 throws AttributeNotFoundException
{
455 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
460 //--------------------------------------------------------------------------
461 // Regular query methods (sent to the back-end)
462 //--------------------------------------------------------------------------
465 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
466 throws TimeRangeException
, StateSystemDisposedException
{
468 throw new StateSystemDisposedException();
471 List
<ITmfStateInterval
> stateInfo
= new ArrayList
<ITmfStateInterval
>(
472 attributeTree
.getNbAttributes());
474 /* Bring the size of the array to the current number of attributes */
475 for (int i
= 0; i
< attributeTree
.getNbAttributes(); i
++) {
479 /* Query the storage backend */
480 backend
.doQuery(stateInfo
, t
);
483 * If we are currently building the history, also query the "ongoing"
484 * states for stuff that might not yet be written to the history.
486 if (transState
.isActive()) {
487 transState
.doQuery(stateInfo
, t
);
491 * We should have previously inserted an interval for every attribute.
492 * If we do happen do see a 'null' object here, just replace it with a a
493 * dummy internal with a null value, to avoid NPE's further up.
495 for (int i
= 0; i
< stateInfo
.size(); i
++) {
496 if (stateInfo
.get(i
) == null) {
497 //logMissingInterval(i, t);
498 stateInfo
.set(i
, new TmfStateInterval(t
, t
, i
, TmfStateValue
.nullValue()));
505 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
506 throws AttributeNotFoundException
, TimeRangeException
,
507 StateSystemDisposedException
{
509 throw new StateSystemDisposedException();
512 ITmfStateInterval ret
;
513 if (transState
.hasInfoAboutStateOf(t
, attributeQuark
)) {
514 ret
= transState
.getOngoingInterval(attributeQuark
);
516 ret
= backend
.doSingularQuery(t
, attributeQuark
);
520 * Return a fake interval if we could not find anything in the history.
521 * We do NOT want to return 'null' here.
524 //logMissingInterval(attributeQuark, t);
525 return new TmfStateInterval(t
, this.getCurrentEndTime(),
526 attributeQuark
, TmfStateValue
.nullValue());
532 public ITmfStateInterval
querySingleStackTop(long t
, int stackAttributeQuark
)
533 throws StateValueTypeException
, AttributeNotFoundException
,
534 TimeRangeException
, StateSystemDisposedException
{
535 Integer curStackDepth
= querySingleState(t
, stackAttributeQuark
).getStateValue().unboxInt();
537 if (curStackDepth
== -1) {
538 /* There is nothing stored in this stack at this moment */
540 } else if (curStackDepth
< -1 || curStackDepth
== 0) {
542 * This attribute is an integer attribute, but it doesn't seem like
543 * it's used as a stack-attribute...
545 throw new StateValueTypeException();
548 int subAttribQuark
= getQuarkRelative(stackAttributeQuark
, curStackDepth
.toString());
549 ITmfStateInterval ret
= querySingleState(t
, subAttribQuark
);
554 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
555 long t1
, long t2
) throws TimeRangeException
,
556 AttributeNotFoundException
, StateSystemDisposedException
{
558 throw new StateSystemDisposedException();
561 List
<ITmfStateInterval
> intervals
;
562 ITmfStateInterval currentInterval
;
565 /* Make sure the time range makes sense */
567 throw new TimeRangeException();
570 /* Set the actual, valid end time of the range query */
571 if (t2
> this.getCurrentEndTime()) {
572 tEnd
= this.getCurrentEndTime();
577 /* Get the initial state at time T1 */
578 intervals
= new ArrayList
<ITmfStateInterval
>();
579 currentInterval
= querySingleState(t1
, attributeQuark
);
580 intervals
.add(currentInterval
);
582 /* Get the following state changes */
583 ts
= currentInterval
.getEndTime();
584 while (ts
!= -1 && ts
< tEnd
) {
585 ts
++; /* To "jump over" to the next state in the history */
586 currentInterval
= querySingleState(ts
, attributeQuark
);
587 intervals
.add(currentInterval
);
588 ts
= currentInterval
.getEndTime();
594 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
595 long t1
, long t2
, long resolution
, IProgressMonitor monitor
)
596 throws TimeRangeException
, AttributeNotFoundException
,
597 StateSystemDisposedException
{
599 throw new StateSystemDisposedException();
602 List
<ITmfStateInterval
> intervals
;
603 ITmfStateInterval currentInterval
;
606 IProgressMonitor mon
= monitor
;
608 mon
= new NullProgressMonitor();
611 /* Make sure the time range makes sense */
612 if (t2
< t1
|| resolution
<= 0) {
613 throw new TimeRangeException();
616 /* Set the actual, valid end time of the range query */
617 if (t2
> this.getCurrentEndTime()) {
618 tEnd
= this.getCurrentEndTime();
623 /* Get the initial state at time T1 */
624 intervals
= new ArrayList
<ITmfStateInterval
>();
625 currentInterval
= querySingleState(t1
, attributeQuark
);
626 intervals
.add(currentInterval
);
629 * Iterate over the "resolution points". We skip unneeded queries in the
630 * case the current interval is longer than the resolution.
632 for (ts
= t1
; (currentInterval
.getEndTime() != -1) && (ts
< tEnd
);
634 if (mon
.isCanceled()) {
637 if (ts
<= currentInterval
.getEndTime()) {
640 currentInterval
= querySingleState(ts
, attributeQuark
);
641 intervals
.add(currentInterval
);
644 /* Add the interval at t2, if it wasn't included already. */
645 if (currentInterval
.getEndTime() < tEnd
) {
646 currentInterval
= querySingleState(tEnd
, attributeQuark
);
647 intervals
.add(currentInterval
);
652 //--------------------------------------------------------------------------
654 //--------------------------------------------------------------------------
656 static void logMissingInterval(int attribute
, long timestamp
) {
657 Activator
.logInfo("No data found in history for attribute " + //$NON-NLS-1$
658 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
659 ", returning dummy interval"); //$NON-NLS-1$
663 * Print out the contents of the inner structures.
666 * The PrintWriter in which to print the output
668 public void debugPrint(PrintWriter writer
) {
669 attributeTree
.debugPrint(writer
);
670 transState
.debugPrint(writer
);
671 backend
.debugPrint(writer
);