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