Fix previous commit
[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++;
324 subAttributeQuark = getQuarkRelativeAndAdd(attributeQuark,
325 stackDepth.toString());
326
327 modifyAttribute(t, TmfStateValue.newValueInt(stackDepth),
328 attributeQuark);
90a25ebe 329 modifyAttribute(t, value, subAttributeQuark);
a52fde77
AM
330 }
331
8d1346f0 332 @Override
a52fde77
AM
333 public void popAttribute(long t, int attributeQuark)
334 throws AttributeNotFoundException, TimeRangeException,
335 StateValueTypeException {
a52fde77
AM
336 Integer stackDepth;
337 int subAttributeQuark;
338 ITmfStateValue previousSV = transState.getOngoingStateValue(attributeQuark);
339
340 if (previousSV.isNull()) {
90a25ebe
AM
341 /* Same as if stackDepth == 0, see below */
342 return;
343 }
344 if (previousSV.getType() != 0) {
a52fde77 345 /*
90a25ebe
AM
346 * The existing value was a string, this doesn't look like a valid
347 * stack attribute.
a52fde77 348 */
90a25ebe 349 throw new StateValueTypeException();
a52fde77
AM
350 }
351
90a25ebe
AM
352 stackDepth = previousSV.unboxInt();
353
a52fde77
AM
354 if (stackDepth == 0) {
355 /*
356 * Trying to pop an empty stack. This often happens at the start of
357 * traces, for example when we see a syscall_exit, without having
358 * the corresponding syscall_entry in the trace. Just ignore
359 * silently.
360 */
361 return;
90a25ebe
AM
362 }
363
364 if (stackDepth < 0) {
a52fde77 365 /* This on the other hand should not happen... */
90a25ebe
AM
366 String message = "A top-level stack attribute " + //$NON-NLS-1$
367 "cannot have a negative integer value."; //$NON-NLS-1$
368 throw new StateValueTypeException(message);
a52fde77
AM
369 }
370
371 /* The attribute should already exist... */
372 subAttributeQuark = getQuarkRelative(attributeQuark,
373 stackDepth.toString());
374
375 stackDepth--;
376 modifyAttribute(t, TmfStateValue.newValueInt(stackDepth),
377 attributeQuark);
378 removeAttribute(t, subAttributeQuark);
379 }
380
8d1346f0 381 @Override
a52fde77
AM
382 public void removeAttribute(long t, int attributeQuark)
383 throws TimeRangeException, AttributeNotFoundException {
384 assert (attributeQuark >= 0);
c66426fd
AM
385 List<Integer> childAttributes;
386
387 /*
388 * "Nullify our children first, recursively. We pass 'false' because we
389 * handle the recursion ourselves.
390 */
391 childAttributes = attributeTree.getSubAttributes(attributeQuark, false);
a52fde77
AM
392 for (Integer childNodeQuark : childAttributes) {
393 assert (attributeQuark != childNodeQuark);
394 removeAttribute(t, childNodeQuark);
395 }
396 /* Nullify ourselves */
7e0b2b56
AM
397 try {
398 transState.processStateChange(t, TmfStateValue.nullValue(),
399 attributeQuark);
400 } catch (StateValueTypeException e) {
50678114
AM
401 /*
402 * Will not happen since we're inserting null values only, but poor
403 * compiler has no way of knowing this...
7e0b2b56
AM
404 */
405 e.printStackTrace();
406 }
a52fde77
AM
407 }
408
8d1346f0
AM
409 //--------------------------------------------------------------------------
410 // "Current" query/update methods
411 //--------------------------------------------------------------------------
a52fde77 412
8d1346f0 413 @Override
a52fde77
AM
414 public ITmfStateValue queryOngoingState(int attributeQuark)
415 throws AttributeNotFoundException {
416 return transState.getOngoingStateValue(attributeQuark);
417 }
418
8d1346f0 419 @Override
a52fde77
AM
420 public void updateOngoingState(ITmfStateValue newValue, int attributeQuark)
421 throws AttributeNotFoundException {
422 transState.changeOngoingStateValue(attributeQuark, newValue);
423 }
424
8d1346f0
AM
425
426
427 //--------------------------------------------------------------------------
428 // Regular query methods (sent to the back-end)
429 //--------------------------------------------------------------------------
430
431 @Override
432 public synchronized List<ITmfStateInterval> queryFullState(long t)
433 throws TimeRangeException {
434 List<ITmfStateInterval> stateInfo = new ArrayList<ITmfStateInterval>(
435 attributeTree.getNbAttributes());
436
437 /* Bring the size of the array to the current number of attributes */
438 for (int i = 0; i < attributeTree.getNbAttributes(); i++) {
439 stateInfo.add(null);
440 }
441
442 /* Query the storage backend */
443 backend.doQuery(stateInfo, t);
444
445 /*
446 * If we are currently building the history, also query the "ongoing"
447 * states for stuff that might not yet be written to the history.
448 */
449 if (transState.isActive()) {
450 transState.doQuery(stateInfo, t);
451 }
452
453 /*
454 * We should have previously inserted an interval for every attribute.
455 * If we do happen do see a 'null' object here, just replace it with a a
456 * dummy internal with a null value, to avoid NPE's further up.
457 */
458 for (int i = 0; i < stateInfo.size(); i++) {
459 if (stateInfo.get(i) == null) {
460 //logMissingInterval(i, t);
461 stateInfo.set(i, new TmfStateInterval(t, t, i, TmfStateValue.nullValue()));
462 }
463 }
464 return stateInfo;
50678114
AM
465 }
466
8d1346f0
AM
467 @Override
468 public ITmfStateInterval querySingleState(long t, int attributeQuark)
469 throws AttributeNotFoundException, TimeRangeException {
470 ITmfStateInterval ret;
471
472 if (transState.hasInfoAboutStateOf(t, attributeQuark)) {
473 ret = transState.getOngoingInterval(attributeQuark);
474 } else {
475 ret = backend.doSingularQuery(t, attributeQuark);
476 }
477
478 /*
479 * Return a fake interval if we could not find anything in the history.
480 * We do NOT want to return 'null' here.
481 */
482 if (ret == null) {
483 //logMissingInterval(attributeQuark, t);
484 return new TmfStateInterval(t, this.getCurrentEndTime(),
485 attributeQuark, TmfStateValue.nullValue());
486 }
487 return ret;
488 }
489
490 @Override
491 public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
492 long t1, long t2) throws TimeRangeException,
493 AttributeNotFoundException {
494 List<ITmfStateInterval> intervals;
495 ITmfStateInterval currentInterval;
496 long ts, tEnd;
497
498 /* Make sure the time range makes sense */
499 if (t2 <= t1) {
500 throw new TimeRangeException();
501 }
502
503 /* Set the actual, valid end time of the range query */
504 if (t2 > this.getCurrentEndTime()) {
505 tEnd = this.getCurrentEndTime();
506 } else {
507 tEnd = t2;
508 }
509
510 /* Get the initial state at time T1 */
511 intervals = new ArrayList<ITmfStateInterval>();
512 currentInterval = querySingleState(t1, attributeQuark);
513 intervals.add(currentInterval);
514
515 /* Get the following state changes */
516 ts = currentInterval.getEndTime();
517 while (ts != -1 && ts < tEnd) {
518 ts++; /* To "jump over" to the next state in the history */
519 currentInterval = querySingleState(ts, attributeQuark);
520 intervals.add(currentInterval);
521 ts = currentInterval.getEndTime();
522 }
523 return intervals;
524 }
525
526 @Override
527 public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
b5a8d0cc
AM
528 long t1, long t2, long resolution, IProgressMonitor monitor)
529 throws TimeRangeException, AttributeNotFoundException {
8d1346f0
AM
530 List<ITmfStateInterval> intervals;
531 ITmfStateInterval currentInterval;
532 long ts, tEnd;
533
b5a8d0cc
AM
534 if (monitor == null) {
535 monitor = new NullProgressMonitor();
536 }
537
8d1346f0
AM
538 /* Make sure the time range makes sense */
539 if (t2 < t1 || resolution <= 0) {
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 /*
556 * Iterate over the "resolution points". We skip unneeded queries in the
557 * case the current interval is longer than the resolution.
558 */
559 for (ts = t1; (currentInterval.getEndTime() != -1) && (ts < tEnd);
560 ts += resolution) {
561 if (monitor.isCanceled()) {
562 return intervals;
563 }
564 if (ts <= currentInterval.getEndTime()) {
565 continue;
566 }
567 currentInterval = querySingleState(ts, attributeQuark);
568 intervals.add(currentInterval);
569 }
570
571 /* Add the interval at t2, if it wasn't included already. */
572 if (currentInterval.getEndTime() < tEnd) {
573 currentInterval = querySingleState(tEnd, attributeQuark);
574 intervals.add(currentInterval);
575 }
576 return intervals;
577 }
578
579 //--------------------------------------------------------------------------
580 // Debug methods
581 //--------------------------------------------------------------------------
582
583 static void logMissingInterval(int attribute, long timestamp) {
584 Tracer.traceInfo("No data found in history for attribute " + //$NON-NLS-1$
585 attribute + " at time " + timestamp + //$NON-NLS-1$
586 ", returning dummy interval"); //$NON-NLS-1$
a52fde77
AM
587 }
588
589 /**
590 * Print out the contents of the inner structures.
5df842b3 591 *
a52fde77
AM
592 * @param writer
593 * The PrintWriter in which to print the output
594 */
595 public void debugPrint(PrintWriter writer) {
596 attributeTree.debugPrint(writer);
597 transState.debugPrint(writer);
8d1346f0 598 backend.debugPrint(writer);
a52fde77
AM
599 }
600
8d1346f0 601}
This page took 0.109965 seconds and 5 git commands to generate.