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