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