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
;
22 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
23 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
24 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.Tracer
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.TmfStateInterval
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateSystemBuilder
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateSystemQuerier2
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
36 * This is the core class of the Generic State System. It contains all the
37 * methods to build and query a state history. It's exposed externally through
38 * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
39 * user needs read-only access or read-write access.
41 * When building, DON'T FORGET to call .closeHistory() when you are done
42 * inserting intervals, or the storage backend will have no way of knowing it
43 * can close and write itself to disk, and its thread will keep running.
48 public class StateSystem
implements IStateSystemBuilder
, IStateSystemQuerier2
{
50 /* References to the inner structures */
51 private final AttributeTree attributeTree
;
52 private final TransientState transState
;
53 private final IStateHistoryBackend backend
;
59 * The "state history storage" backend to use.
61 * Put true if this is a new history started from scratch. It is
62 * used to tell the state system where to get its attribute tree.
64 * If there was a problem creating the new history file
66 public StateSystem(IStateHistoryBackend backend
, boolean newFile
)
68 this.backend
= backend
;
69 this.transState
= new TransientState(backend
);
72 attributeTree
= new AttributeTree(this);
74 /* We're opening an existing file */
75 this.attributeTree
= new AttributeTree(this, backend
.supplyAttributeTreeReader());
76 transState
.setInactive();
80 //--------------------------------------------------------------------------
81 // General methods related to the attribute tree
82 //--------------------------------------------------------------------------
85 * Method used by the attribute tree when creating new attributes, to keep
86 * the attribute count in the transient state in sync.
88 void addEmptyAttribute() {
89 transState
.addEmptyEntry();
93 public int getNbAttributes() {
94 return attributeTree
.getNbAttributes();
98 public String
getAttributeName(int attributeQuark
) {
99 return attributeTree
.getAttributeName(attributeQuark
);
103 public String
getFullAttributePath(int attributeQuark
) {
104 return attributeTree
.getFullAttributeName(attributeQuark
);
107 //--------------------------------------------------------------------------
108 // Methods related to the storage backend
109 //--------------------------------------------------------------------------
112 public long getStartTime() {
113 return backend
.getStartTime();
117 public long getCurrentEndTime() {
118 return backend
.getEndTime();
122 public void closeHistory(long endTime
) throws TimeRangeException
{
123 File attributeTreeFile
;
124 long attributeTreeFilePos
;
125 long realEndTime
= endTime
;
127 if (realEndTime
< backend
.getEndTime()) {
129 * This can happen (empty nodes pushing the border further, etc.)
130 * but shouldn't be too big of a deal.
132 realEndTime
= backend
.getEndTime();
134 transState
.closeTransientState(realEndTime
);
135 backend
.finishedBuilding(realEndTime
);
137 attributeTreeFile
= backend
.supplyAttributeTreeWriterFile();
138 attributeTreeFilePos
= backend
.supplyAttributeTreeWriterFilePosition();
139 if (attributeTreeFile
!= null) {
141 * If null was returned, we simply won't save the attribute tree,
144 attributeTree
.writeSelf(attributeTreeFile
, attributeTreeFilePos
);
148 //--------------------------------------------------------------------------
149 // Quark-retrieving methods
150 //--------------------------------------------------------------------------
153 public int getQuarkAbsolute(String
... attribute
)
154 throws AttributeNotFoundException
{
155 return attributeTree
.getQuarkDontAdd(-1, attribute
);
159 public int getQuarkAbsoluteAndAdd(String
... attribute
) {
160 return attributeTree
.getQuarkAndAdd(-1, attribute
);
164 public int getQuarkRelative(int startingNodeQuark
, String
... subPath
)
165 throws AttributeNotFoundException
{
166 return attributeTree
.getQuarkDontAdd(startingNodeQuark
, subPath
);
170 public int getQuarkRelativeAndAdd(int startingNodeQuark
, String
... subPath
) {
171 return attributeTree
.getQuarkAndAdd(startingNodeQuark
, subPath
);
175 public List
<Integer
> getSubAttributes(int quark
, boolean recursive
)
176 throws AttributeNotFoundException
{
177 return attributeTree
.getSubAttributes(quark
, recursive
);
181 public List
<Integer
> getQuarks(String
... pattern
) {
182 List
<Integer
> quarks
= new LinkedList
<Integer
>();
183 List
<String
> prefix
= new LinkedList
<String
>();
184 List
<String
> suffix
= new LinkedList
<String
>();
185 boolean split
= false;
188 List
<Integer
> directChildren
;
189 int startingAttribute
;
191 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
192 for (String entry
: pattern
) {
193 if (entry
.equals("*")) { //$NON-NLS-1$
196 * Split was already true? This means there was more than
197 * one wildcard. This is not supported, return an empty
212 prefixStr
= prefix
.toArray(new String
[prefix
.size()]);
213 suffixStr
= suffix
.toArray(new String
[suffix
.size()]);
216 * If there was no wildcard, we'll only return the one matching
217 * attribute, if there is one.
219 if (split
== false) {
222 quark
= getQuarkAbsolute(prefixStr
);
223 } catch (AttributeNotFoundException e
) {
224 /* It's fine, we'll just return the empty List */
232 if (prefix
.size() == 0) {
234 * If 'prefix' is empty, this means the wildcard was the first
235 * element. Look for the root node's sub-attributes.
237 startingAttribute
= -1;
239 startingAttribute
= getQuarkAbsolute(prefixStr
);
241 directChildren
= attributeTree
.getSubAttributes(startingAttribute
,
243 } catch (AttributeNotFoundException e
) {
244 /* That attribute path did not exist, return the empty array */
249 * Iterate of all the sub-attributes, and only keep those who match the
250 * 'suffix' part of the initial pattern.
252 for (int childQuark
: directChildren
) {
255 matchingQuark
= getQuarkRelative(childQuark
, suffixStr
);
256 } catch (AttributeNotFoundException e
) {
259 quarks
.add(matchingQuark
);
265 //--------------------------------------------------------------------------
266 // Methods related to insertions in the history
267 //--------------------------------------------------------------------------
270 public void modifyAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
271 throws TimeRangeException
, AttributeNotFoundException
,
272 StateValueTypeException
{
273 transState
.processStateChange(t
, value
, attributeQuark
);
277 public void incrementAttribute(long t
, int attributeQuark
)
278 throws StateValueTypeException
, TimeRangeException
,
279 AttributeNotFoundException
{
280 int prevValue
= queryOngoingState(attributeQuark
).unboxInt();
281 if (prevValue
== -1) {
282 /* if the attribute was previously null, start counting at 0 */
285 modifyAttribute(t
, TmfStateValue
.newValueInt(prevValue
+ 1),
290 public void pushAttribute(long t
, ITmfStateValue value
, int attributeQuark
)
291 throws TimeRangeException
, AttributeNotFoundException
,
292 StateValueTypeException
{
293 Integer stackDepth
= 0;
294 int subAttributeQuark
;
295 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
297 if (previousSV
.isNull()) {
299 * If the StateValue was null, this means this is the first time we
300 * use this attribute. Leave stackDepth at 0.
302 } else if (previousSV
.getType() == 0) {
303 /* Previous value was an integer, all is good, use it */
304 stackDepth
= previousSV
.unboxInt();
306 /* Previous state of this attribute was another type? Not good! */
307 throw new StateValueTypeException();
310 if (stackDepth
>= 10) {
312 * Limit stackDepth to 10, to avoid having Attribute Trees grow out
313 * of control due to buggy insertions
315 String message
= "Stack limit reached, not pushing"; //$NON-NLS-1$
316 throw new AttributeNotFoundException(message
);
320 subAttributeQuark
= getQuarkRelativeAndAdd(attributeQuark
,
321 stackDepth
.toString());
323 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
),
325 modifyAttribute(t
, value
, subAttributeQuark
);
329 public void popAttribute(long t
, int attributeQuark
)
330 throws AttributeNotFoundException
, TimeRangeException
,
331 StateValueTypeException
{
333 int subAttributeQuark
;
334 ITmfStateValue previousSV
= transState
.getOngoingStateValue(attributeQuark
);
336 if (previousSV
.isNull()) {
337 /* Same as if stackDepth == 0, see below */
340 if (previousSV
.getType() != 0) {
342 * The existing value was a string, this doesn't look like a valid
345 throw new StateValueTypeException();
348 stackDepth
= previousSV
.unboxInt();
350 if (stackDepth
== 0) {
352 * Trying to pop an empty stack. This often happens at the start of
353 * traces, for example when we see a syscall_exit, without having
354 * the corresponding syscall_entry in the trace. Just ignore
360 if (stackDepth
< 0) {
361 /* This on the other hand should not happen... */
362 String message
= "A top-level stack attribute " + //$NON-NLS-1$
363 "cannot have a negative integer value."; //$NON-NLS-1$
364 throw new StateValueTypeException(message
);
367 /* The attribute should already exist... */
368 subAttributeQuark
= getQuarkRelative(attributeQuark
,
369 stackDepth
.toString());
372 modifyAttribute(t
, TmfStateValue
.newValueInt(stackDepth
),
374 removeAttribute(t
, subAttributeQuark
);
378 public void removeAttribute(long t
, int attributeQuark
)
379 throws TimeRangeException
, AttributeNotFoundException
{
380 assert (attributeQuark
>= 0);
381 List
<Integer
> childAttributes
;
384 * "Nullify our children first, recursively. We pass 'false' because we
385 * handle the recursion ourselves.
387 childAttributes
= attributeTree
.getSubAttributes(attributeQuark
, false);
388 for (Integer childNodeQuark
: childAttributes
) {
389 assert (attributeQuark
!= childNodeQuark
);
390 removeAttribute(t
, childNodeQuark
);
392 /* Nullify ourselves */
394 transState
.processStateChange(t
, TmfStateValue
.nullValue(),
396 } catch (StateValueTypeException e
) {
398 * Will not happen since we're inserting null values only, but poor
399 * compiler has no way of knowing this...
405 //--------------------------------------------------------------------------
406 // "Current" query/update methods
407 //--------------------------------------------------------------------------
410 public ITmfStateValue
queryOngoingState(int attributeQuark
)
411 throws AttributeNotFoundException
{
412 return transState
.getOngoingStateValue(attributeQuark
);
416 public void updateOngoingState(ITmfStateValue newValue
, int attributeQuark
)
417 throws AttributeNotFoundException
{
418 transState
.changeOngoingStateValue(attributeQuark
, newValue
);
423 //--------------------------------------------------------------------------
424 // Regular query methods (sent to the back-end)
425 //--------------------------------------------------------------------------
428 public synchronized List
<ITmfStateInterval
> queryFullState(long t
)
429 throws TimeRangeException
{
430 List
<ITmfStateInterval
> stateInfo
= new ArrayList
<ITmfStateInterval
>(
431 attributeTree
.getNbAttributes());
433 /* Bring the size of the array to the current number of attributes */
434 for (int i
= 0; i
< attributeTree
.getNbAttributes(); i
++) {
438 /* Query the storage backend */
439 backend
.doQuery(stateInfo
, t
);
442 * If we are currently building the history, also query the "ongoing"
443 * states for stuff that might not yet be written to the history.
445 if (transState
.isActive()) {
446 transState
.doQuery(stateInfo
, t
);
450 * We should have previously inserted an interval for every attribute.
451 * If we do happen do see a 'null' object here, just replace it with a a
452 * dummy internal with a null value, to avoid NPE's further up.
454 for (int i
= 0; i
< stateInfo
.size(); i
++) {
455 if (stateInfo
.get(i
) == null) {
456 //logMissingInterval(i, t);
457 stateInfo
.set(i
, new TmfStateInterval(t
, t
, i
, TmfStateValue
.nullValue()));
464 public ITmfStateInterval
querySingleState(long t
, int attributeQuark
)
465 throws AttributeNotFoundException
, TimeRangeException
{
466 ITmfStateInterval ret
;
468 if (transState
.hasInfoAboutStateOf(t
, attributeQuark
)) {
469 ret
= transState
.getOngoingInterval(attributeQuark
);
471 ret
= backend
.doSingularQuery(t
, attributeQuark
);
475 * Return a fake interval if we could not find anything in the history.
476 * We do NOT want to return 'null' here.
479 //logMissingInterval(attributeQuark, t);
480 return new TmfStateInterval(t
, this.getCurrentEndTime(),
481 attributeQuark
, TmfStateValue
.nullValue());
487 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
488 long t1
, long t2
) throws TimeRangeException
,
489 AttributeNotFoundException
{
490 List
<ITmfStateInterval
> intervals
;
491 ITmfStateInterval currentInterval
;
494 /* Make sure the time range makes sense */
496 throw new TimeRangeException();
499 /* Set the actual, valid end time of the range query */
500 if (t2
> this.getCurrentEndTime()) {
501 tEnd
= this.getCurrentEndTime();
506 /* Get the initial state at time T1 */
507 intervals
= new ArrayList
<ITmfStateInterval
>();
508 currentInterval
= querySingleState(t1
, attributeQuark
);
509 intervals
.add(currentInterval
);
511 /* Get the following state changes */
512 ts
= currentInterval
.getEndTime();
513 while (ts
!= -1 && ts
< tEnd
) {
514 ts
++; /* To "jump over" to the next state in the history */
515 currentInterval
= querySingleState(ts
, attributeQuark
);
516 intervals
.add(currentInterval
);
517 ts
= currentInterval
.getEndTime();
523 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
524 long t1
, long t2
, long resolution
) throws TimeRangeException
,
525 AttributeNotFoundException
{
526 return queryHistoryRange(attributeQuark
, t1
, t2
, resolution
, new NullProgressMonitor());
530 public List
<ITmfStateInterval
> queryHistoryRange(int attributeQuark
,
531 long t1
, long t2
, long resolution
, IProgressMonitor monitor
) throws TimeRangeException
,
532 AttributeNotFoundException
{
533 List
<ITmfStateInterval
> intervals
;
534 ITmfStateInterval currentInterval
;
537 /* Make sure the time range makes sense */
538 if (t2
< t1
|| resolution
<= 0) {
539 throw new TimeRangeException();
542 /* Set the actual, valid end time of the range query */
543 if (t2
> this.getCurrentEndTime()) {
544 tEnd
= this.getCurrentEndTime();
549 /* Get the initial state at time T1 */
550 intervals
= new ArrayList
<ITmfStateInterval
>();
551 currentInterval
= querySingleState(t1
, attributeQuark
);
552 intervals
.add(currentInterval
);
555 * Iterate over the "resolution points". We skip unneeded queries in the
556 * case the current interval is longer than the resolution.
558 for (ts
= t1
; (currentInterval
.getEndTime() != -1) && (ts
< tEnd
);
560 if (monitor
.isCanceled()) {
563 if (ts
<= currentInterval
.getEndTime()) {
566 currentInterval
= querySingleState(ts
, attributeQuark
);
567 intervals
.add(currentInterval
);
570 /* Add the interval at t2, if it wasn't included already. */
571 if (currentInterval
.getEndTime() < tEnd
) {
572 currentInterval
= querySingleState(tEnd
, attributeQuark
);
573 intervals
.add(currentInterval
);
578 //--------------------------------------------------------------------------
580 //--------------------------------------------------------------------------
582 static void logMissingInterval(int attribute
, long timestamp
) {
583 Tracer
.traceInfo("No data found in history for attribute " + //$NON-NLS-1$
584 attribute
+ " at time " + timestamp
+ //$NON-NLS-1$
585 ", returning dummy interval"); //$NON-NLS-1$
589 * Print out the contents of the inner structures.
592 * The PrintWriter in which to print the output
594 public void debugPrint(PrintWriter writer
) {
595 attributeTree
.debugPrint(writer
);
596 transState
.debugPrint(writer
);
597 backend
.debugPrint(writer
);