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