tmf: Enforce non-null state sytem backends
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / internal / tmf / core / statesystem / StateSystem.java
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>
5 *
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
10 *
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.internal.tmf.core.statesystem;
14
15 import java.io.File;
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;
23
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;
39
40 /**
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.
45 *
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.
49 *
50 * @author alexmont
51 *
52 */
53 public class StateSystem implements ITmfStateSystemBuilder {
54
55 /* References to the inner structures */
56 private final AttributeTree attributeTree;
57 private final TransientState transState;
58 private final IStateHistoryBackend backend;
59
60 /* Latch tracking if the state history is done building or not */
61 private final CountDownLatch finishedLatch = new CountDownLatch(1);
62
63 private boolean buildCancelled = false;
64 private boolean isDisposed = false;
65
66 /**
67 * New-file constructor. For when you build a state system with a new file,
68 * or if the back-end does not require a file on disk.
69 *
70 * @param backend
71 * Back-end plugin to use
72 */
73 public StateSystem(@NonNull IStateHistoryBackend backend) {
74 this.backend = backend;
75 this.transState = new TransientState(backend);
76 this.attributeTree = new AttributeTree(this);
77 }
78
79 /**
80 * General constructor
81 *
82 * @param backend
83 * The "state history storage" back-end to use.
84 * @param newFile
85 * Put true if this is a new history started from scratch. It is
86 * used to tell the state system where to get its attribute tree.
87 * @throws IOException
88 * If there was a problem creating the new history file
89 */
90 public StateSystem(@NonNull IStateHistoryBackend backend, boolean newFile)
91 throws IOException {
92 this.backend = backend;
93 this.transState = new TransientState(backend);
94
95 if (newFile) {
96 attributeTree = new AttributeTree(this);
97 } else {
98 /* We're opening an existing file */
99 this.attributeTree = new AttributeTree(this, backend.supplyAttributeTreeReader());
100 transState.setInactive();
101 finishedLatch.countDown(); /* The history is already built */
102 }
103 }
104
105 @Override
106 public boolean isCancelled() {
107 return buildCancelled;
108 }
109
110 @Override
111 public void waitUntilBuilt() {
112 try {
113 finishedLatch.await();
114 } catch (InterruptedException e) {
115 e.printStackTrace();
116 }
117 }
118
119 @Override
120 public boolean waitUntilBuilt(long timeout) {
121 boolean ret = false;
122 try {
123 ret = finishedLatch.await(timeout, TimeUnit.MILLISECONDS);
124 } catch (InterruptedException e) {
125 e.printStackTrace();
126 }
127 return ret;
128 }
129
130 @Override
131 public synchronized void dispose() {
132 isDisposed = true;
133 if (transState.isActive()) {
134 transState.setInactive();
135 buildCancelled = true;
136 }
137 backend.dispose();
138 }
139
140 //--------------------------------------------------------------------------
141 // General methods related to the attribute tree
142 //--------------------------------------------------------------------------
143
144 /**
145 * Get the attribute tree associated with this state system. This should be
146 * the only way of accessing it (and if subclasses want to point to a
147 * different attribute tree than their own, they should only need to
148 * override this).
149 *
150 * @return The attribute tree
151 */
152 public AttributeTree getAttributeTree() {
153 return attributeTree;
154 }
155
156 /**
157 * Method used by the attribute tree when creating new attributes, to keep
158 * the attribute count in the transient state in sync.
159 */
160 protected void addEmptyAttribute() {
161 transState.addEmptyEntry();
162 }
163
164 @Override
165 public int getNbAttributes() {
166 return getAttributeTree().getNbAttributes();
167 }
168
169 @Override
170 public String getAttributeName(int attributeQuark) {
171 return getAttributeTree().getAttributeName(attributeQuark);
172 }
173
174 @Override
175 public String getFullAttributePath(int attributeQuark) {
176 return getAttributeTree().getFullAttributeName(attributeQuark);
177 }
178
179 //--------------------------------------------------------------------------
180 // Methods related to the storage backend
181 //--------------------------------------------------------------------------
182
183 @Override
184 public long getStartTime() {
185 return backend.getStartTime();
186 }
187
188 @Override
189 public long getCurrentEndTime() {
190 return backend.getEndTime();
191 }
192
193 @Override
194 public void closeHistory(long endTime) throws TimeRangeException {
195 File attributeTreeFile;
196 long attributeTreeFilePos;
197 long realEndTime = endTime;
198
199 if (realEndTime < backend.getEndTime()) {
200 /*
201 * This can happen (empty nodes pushing the border further, etc.)
202 * but shouldn't be too big of a deal.
203 */
204 realEndTime = backend.getEndTime();
205 }
206 transState.closeTransientState(realEndTime);
207 backend.finishedBuilding(realEndTime);
208
209 attributeTreeFile = backend.supplyAttributeTreeWriterFile();
210 attributeTreeFilePos = backend.supplyAttributeTreeWriterFilePosition();
211 if (attributeTreeFile != null) {
212 /*
213 * If null was returned, we simply won't save the attribute tree,
214 * too bad!
215 */
216 getAttributeTree().writeSelf(attributeTreeFile, attributeTreeFilePos);
217 }
218 finishedLatch.countDown(); /* Mark the history as finished building */
219 }
220
221 //--------------------------------------------------------------------------
222 // Quark-retrieving methods
223 //--------------------------------------------------------------------------
224
225 @Override
226 public int getQuarkAbsolute(String... attribute)
227 throws AttributeNotFoundException {
228 return getAttributeTree().getQuarkDontAdd(-1, attribute);
229 }
230
231 @Override
232 public int getQuarkAbsoluteAndAdd(String... attribute) {
233 return getAttributeTree().getQuarkAndAdd(-1, attribute);
234 }
235
236 @Override
237 public int getQuarkRelative(int startingNodeQuark, String... subPath)
238 throws AttributeNotFoundException {
239 return getAttributeTree().getQuarkDontAdd(startingNodeQuark, subPath);
240 }
241
242 @Override
243 public int getQuarkRelativeAndAdd(int startingNodeQuark, String... subPath) {
244 return getAttributeTree().getQuarkAndAdd(startingNodeQuark, subPath);
245 }
246
247 @Override
248 public List<Integer> getSubAttributes(int quark, boolean recursive)
249 throws AttributeNotFoundException {
250 return getAttributeTree().getSubAttributes(quark, recursive);
251 }
252
253 @Override
254 public List<Integer> getQuarks(String... pattern) {
255 List<Integer> quarks = new LinkedList<>();
256 List<String> prefix = new LinkedList<>();
257 List<String> suffix = new LinkedList<>();
258 boolean split = false;
259 String[] prefixStr;
260 String[] suffixStr;
261 List<Integer> directChildren;
262 int startingAttribute;
263
264 /* Fill the "prefix" and "suffix" parts of the pattern around the '*' */
265 for (String entry : pattern) {
266 if (entry.equals("*")) { //$NON-NLS-1$
267 if (split) {
268 /*
269 * Split was already true? This means there was more than
270 * one wildcard. This is not supported, return an empty
271 * list.
272 */
273 return quarks;
274 }
275 split = true;
276 continue;
277 }
278
279 if (split) {
280 suffix.add(entry);
281 } else {
282 prefix.add(entry);
283 }
284 }
285 prefixStr = prefix.toArray(new String[prefix.size()]);
286 suffixStr = suffix.toArray(new String[suffix.size()]);
287
288 /*
289 * If there was no wildcard, we'll only return the one matching
290 * attribute, if there is one.
291 */
292 if (!split) {
293 int quark;
294 try {
295 quark = getQuarkAbsolute(prefixStr);
296 } catch (AttributeNotFoundException e) {
297 /* It's fine, we'll just return the empty List */
298 return quarks;
299 }
300 quarks.add(quark);
301 return quarks;
302 }
303
304 try {
305 if (prefix.size() == 0) {
306 /*
307 * If 'prefix' is empty, this means the wildcard was the first
308 * element. Look for the root node's sub-attributes.
309 */
310 startingAttribute = -1;
311 } else {
312 startingAttribute = getQuarkAbsolute(prefixStr);
313 }
314 directChildren = getSubAttributes(startingAttribute, false);
315 } catch (AttributeNotFoundException e) {
316 /* That attribute path did not exist, return the empty array */
317 return quarks;
318 }
319
320 /*
321 * Iterate of all the sub-attributes, and only keep those who match the
322 * 'suffix' part of the initial pattern.
323 */
324 for (int childQuark : directChildren) {
325 int matchingQuark;
326 try {
327 matchingQuark = getQuarkRelative(childQuark, suffixStr);
328 } catch (AttributeNotFoundException e) {
329 continue;
330 }
331 quarks.add(matchingQuark);
332 }
333
334 return quarks;
335 }
336
337 //--------------------------------------------------------------------------
338 // Methods related to insertions in the history
339 //--------------------------------------------------------------------------
340
341 @Override
342 public void modifyAttribute(long t, ITmfStateValue value, int attributeQuark)
343 throws TimeRangeException, AttributeNotFoundException,
344 StateValueTypeException {
345 transState.processStateChange(t, value, attributeQuark);
346 }
347
348 @Override
349 public void incrementAttribute(long t, int attributeQuark)
350 throws StateValueTypeException, TimeRangeException,
351 AttributeNotFoundException {
352 ITmfStateValue stateValue = queryOngoingState(attributeQuark);
353 int prevValue = 0;
354 /* if the attribute was previously null, start counting at 0 */
355 if (!stateValue.isNull()) {
356 prevValue = stateValue.unboxInt();
357 }
358 modifyAttribute(t, TmfStateValue.newValueInt(prevValue + 1),
359 attributeQuark);
360 }
361
362 @Override
363 public void pushAttribute(long t, ITmfStateValue value, int attributeQuark)
364 throws TimeRangeException, AttributeNotFoundException,
365 StateValueTypeException {
366 Integer stackDepth;
367 int subAttributeQuark;
368 ITmfStateValue previousSV = transState.getOngoingStateValue(attributeQuark);
369
370 if (previousSV.isNull()) {
371 /*
372 * If the StateValue was null, this means this is the first time we
373 * use this attribute. Leave stackDepth at 0.
374 */
375 stackDepth = 0;
376 } else if (previousSV.getType() == Type.INTEGER) {
377 /* Previous value was an integer, all is good, use it */
378 stackDepth = previousSV.unboxInt();
379 } else {
380 /* Previous state of this attribute was another type? Not good! */
381 throw new StateValueTypeException();
382 }
383
384 if (stackDepth >= 100000) {
385 /*
386 * Limit stackDepth to 100000, to avoid having Attribute Trees grow out
387 * of control due to buggy insertions
388 */
389 String message = "Stack limit reached, not pushing"; //$NON-NLS-1$
390 throw new AttributeNotFoundException(message);
391 }
392
393 stackDepth++;
394 subAttributeQuark = getQuarkRelativeAndAdd(attributeQuark, stackDepth.toString());
395
396 modifyAttribute(t, TmfStateValue.newValueInt(stackDepth), attributeQuark);
397 modifyAttribute(t, value, subAttributeQuark);
398 }
399
400 @Override
401 public ITmfStateValue popAttribute(long t, int attributeQuark)
402 throws AttributeNotFoundException, TimeRangeException,
403 StateValueTypeException {
404 /* These are the state values of the stack-attribute itself */
405 ITmfStateValue previousSV = queryOngoingState(attributeQuark);
406
407 if (previousSV.isNull()) {
408 /*
409 * Trying to pop an empty stack. This often happens at the start of
410 * traces, for example when we see a syscall_exit, without having
411 * the corresponding syscall_entry in the trace. Just ignore
412 * silently.
413 */
414 return null;
415 }
416 if (previousSV.getType() != Type.INTEGER) {
417 /*
418 * The existing value was not an integer (which is expected for
419 * stack tops), this doesn't look like a valid stack attribute.
420 */
421 throw new StateValueTypeException();
422 }
423
424 Integer stackDepth = previousSV.unboxInt();
425
426 if (stackDepth <= 0) {
427 /* This on the other hand should not happen... */
428 String message = "A top-level stack attribute cannot " + //$NON-NLS-1$
429 "have a value of 0 or less."; //$NON-NLS-1$
430 throw new StateValueTypeException(message);
431 }
432
433 /* The attribute should already exist at this point */
434 int subAttributeQuark = getQuarkRelative(attributeQuark, stackDepth.toString());
435 ITmfStateValue poppedValue = queryOngoingState(subAttributeQuark);
436
437 /* Update the state value of the stack-attribute */
438 ITmfStateValue nextSV;
439 if (--stackDepth == 0 ) {
440 /* Store a null state value */
441 nextSV = TmfStateValue.nullValue();
442 } else {
443 nextSV = TmfStateValue.newValueInt(stackDepth);
444 }
445 modifyAttribute(t, nextSV, attributeQuark);
446
447 /* Delete the sub-attribute that contained the user's state value */
448 removeAttribute(t, subAttributeQuark);
449
450 return poppedValue;
451 }
452
453 @Override
454 public void removeAttribute(long t, int attributeQuark)
455 throws TimeRangeException, AttributeNotFoundException {
456 assert (attributeQuark >= 0);
457 List<Integer> childAttributes;
458
459 /*
460 * "Nullify our children first, recursively. We pass 'false' because we
461 * handle the recursion ourselves.
462 */
463 childAttributes = getSubAttributes(attributeQuark, false);
464 for (Integer childNodeQuark : childAttributes) {
465 assert (attributeQuark != childNodeQuark);
466 removeAttribute(t, childNodeQuark);
467 }
468 /* Nullify ourselves */
469 try {
470 transState.processStateChange(t, TmfStateValue.nullValue(),
471 attributeQuark);
472 } catch (StateValueTypeException e) {
473 /*
474 * Will not happen since we're inserting null values only, but poor
475 * compiler has no way of knowing this...
476 */
477 throw new IllegalStateException(e);
478 }
479 }
480
481 //--------------------------------------------------------------------------
482 // "Current" query/update methods
483 //--------------------------------------------------------------------------
484
485 @Override
486 public ITmfStateValue queryOngoingState(int attributeQuark)
487 throws AttributeNotFoundException {
488 return transState.getOngoingStateValue(attributeQuark);
489 }
490
491 @Override
492 public long getOngoingStartTime(int attribute)
493 throws AttributeNotFoundException {
494 return transState.getOngoingStartTime(attribute);
495 }
496
497 @Override
498 public void updateOngoingState(ITmfStateValue newValue, int attributeQuark)
499 throws AttributeNotFoundException {
500 transState.changeOngoingStateValue(attributeQuark, newValue);
501 }
502
503 /**
504 * Modify the whole "ongoing state" (state values + start times). This can
505 * be used when "seeking" a state system to a different point in the trace
506 * (and restoring the known stateInfo at this location). Use with care!
507 *
508 * @param newStateIntervals
509 * The new List of state values to use as ongoing state info
510 */
511 protected void replaceOngoingState(List<ITmfStateInterval> newStateIntervals) {
512 transState.replaceOngoingState(newStateIntervals);
513 }
514
515 //--------------------------------------------------------------------------
516 // Regular query methods (sent to the back-end)
517 //--------------------------------------------------------------------------
518
519 @Override
520 public synchronized List<ITmfStateInterval> queryFullState(long t)
521 throws TimeRangeException, StateSystemDisposedException {
522 if (isDisposed) {
523 throw new StateSystemDisposedException();
524 }
525
526 List<ITmfStateInterval> stateInfo = new ArrayList<>(getNbAttributes());
527
528 /* Bring the size of the array to the current number of attributes */
529 for (int i = 0; i < getNbAttributes(); i++) {
530 stateInfo.add(null);
531 }
532
533 /* Query the storage backend */
534 backend.doQuery(stateInfo, t);
535
536 /*
537 * If we are currently building the history, also query the "ongoing"
538 * states for stuff that might not yet be written to the history.
539 */
540 if (transState.isActive()) {
541 transState.doQuery(stateInfo, t);
542 }
543
544 /*
545 * We should have previously inserted an interval for every attribute.
546 * If we do happen do see a 'null' object here, just replace it with a a
547 * dummy internal with a null value, to avoid NPE's further up.
548 */
549 for (int i = 0; i < stateInfo.size(); i++) {
550 if (stateInfo.get(i) == null) {
551 stateInfo.set(i, new TmfStateInterval(t, t, i, TmfStateValue.nullValue()));
552 }
553 }
554 return stateInfo;
555 }
556
557 @Override
558 public ITmfStateInterval querySingleState(long t, int attributeQuark)
559 throws AttributeNotFoundException, TimeRangeException,
560 StateSystemDisposedException {
561 if (isDisposed) {
562 throw new StateSystemDisposedException();
563 }
564
565 ITmfStateInterval ret;
566 if (transState.hasInfoAboutStateOf(t, attributeQuark)) {
567 ret = transState.getOngoingInterval(attributeQuark);
568 } else {
569 ret = backend.doSingularQuery(t, attributeQuark);
570 }
571
572 /*
573 * Return a fake interval if we could not find anything in the history.
574 * We do NOT want to return 'null' here.
575 */
576 if (ret == null) {
577 return new TmfStateInterval(t, this.getCurrentEndTime(),
578 attributeQuark, TmfStateValue.nullValue());
579 }
580 return ret;
581 }
582
583 @Override
584 public ITmfStateInterval querySingleStackTop(long t, int stackAttributeQuark)
585 throws StateValueTypeException, AttributeNotFoundException,
586 TimeRangeException, StateSystemDisposedException {
587 ITmfStateValue curStackStateValue = querySingleState(t, stackAttributeQuark).getStateValue();
588
589 if (curStackStateValue.isNull()) {
590 /* There is nothing stored in this stack at this moment */
591 return null;
592 }
593 Integer curStackDepth = curStackStateValue.unboxInt();
594 if (curStackDepth <= 0) {
595 /*
596 * This attribute is an integer attribute, but it doesn't seem like
597 * it's used as a stack-attribute...
598 */
599 throw new StateValueTypeException();
600 }
601
602 int subAttribQuark = getQuarkRelative(stackAttributeQuark, curStackDepth.toString());
603 return querySingleState(t, subAttribQuark);
604 }
605
606 @Override
607 public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
608 long t1, long t2) throws TimeRangeException,
609 AttributeNotFoundException, StateSystemDisposedException {
610 if (isDisposed) {
611 throw new StateSystemDisposedException();
612 }
613
614 List<ITmfStateInterval> intervals;
615 ITmfStateInterval currentInterval;
616 long ts, tEnd;
617
618 /* Make sure the time range makes sense */
619 if (t2 <= t1) {
620 throw new TimeRangeException();
621 }
622
623 /* Set the actual, valid end time of the range query */
624 if (t2 > this.getCurrentEndTime()) {
625 tEnd = this.getCurrentEndTime();
626 } else {
627 tEnd = t2;
628 }
629
630 /* Get the initial state at time T1 */
631 intervals = new ArrayList<>();
632 currentInterval = querySingleState(t1, attributeQuark);
633 intervals.add(currentInterval);
634
635 /* Get the following state changes */
636 ts = currentInterval.getEndTime();
637 while (ts != -1 && ts < tEnd) {
638 ts++; /* To "jump over" to the next state in the history */
639 currentInterval = querySingleState(ts, attributeQuark);
640 intervals.add(currentInterval);
641 ts = currentInterval.getEndTime();
642 }
643 return intervals;
644 }
645
646 @Override
647 public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
648 long t1, long t2, long resolution, IProgressMonitor monitor)
649 throws TimeRangeException, AttributeNotFoundException,
650 StateSystemDisposedException {
651 if (isDisposed) {
652 throw new StateSystemDisposedException();
653 }
654
655 List<ITmfStateInterval> intervals;
656 ITmfStateInterval currentInterval;
657 long ts, tEnd;
658
659 IProgressMonitor mon = monitor;
660 if (mon == null) {
661 mon = new NullProgressMonitor();
662 }
663
664 /* Make sure the time range makes sense */
665 if (t2 < t1 || resolution <= 0) {
666 throw new TimeRangeException();
667 }
668
669 /* Set the actual, valid end time of the range query */
670 if (t2 > this.getCurrentEndTime()) {
671 tEnd = this.getCurrentEndTime();
672 } else {
673 tEnd = t2;
674 }
675
676 /* Get the initial state at time T1 */
677 intervals = new ArrayList<>();
678 currentInterval = querySingleState(t1, attributeQuark);
679 intervals.add(currentInterval);
680
681 /*
682 * Iterate over the "resolution points". We skip unneeded queries in the
683 * case the current interval is longer than the resolution.
684 */
685 for (ts = t1; (currentInterval.getEndTime() != -1) && (ts < tEnd);
686 ts += resolution) {
687 if (mon.isCanceled()) {
688 return intervals;
689 }
690 if (ts <= currentInterval.getEndTime()) {
691 continue;
692 }
693 currentInterval = querySingleState(ts, attributeQuark);
694 intervals.add(currentInterval);
695 }
696
697 /* Add the interval at t2, if it wasn't included already. */
698 if (currentInterval.getEndTime() < tEnd) {
699 currentInterval = querySingleState(tEnd, attributeQuark);
700 intervals.add(currentInterval);
701 }
702 return intervals;
703 }
704
705 //--------------------------------------------------------------------------
706 // Debug methods
707 //--------------------------------------------------------------------------
708
709 static void logMissingInterval(int attribute, long timestamp) {
710 Activator.logInfo("No data found in history for attribute " + //$NON-NLS-1$
711 attribute + " at time " + timestamp + //$NON-NLS-1$
712 ", returning dummy interval"); //$NON-NLS-1$
713 }
714
715 /**
716 * Print out the contents of the inner structures.
717 *
718 * @param writer
719 * The PrintWriter in which to print the output
720 */
721 public void debugPrint(PrintWriter writer) {
722 getAttributeTree().debugPrint(writer);
723 transState.debugPrint(writer);
724 backend.debugPrint(writer);
725 }
726
727 }
This page took 0.048416 seconds and 5 git commands to generate.