ss: add 2D iterator queries to the SS API
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HistoryTreeBackend.java
CommitLineData
a52fde77 1/*******************************************************************************
e8aa3325 2 * Copyright (c) 2012, 2016 Ericsson
a52fde77
AM
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 *
e13bd4cd
PT
11 * Contributors:
12 * Alexandre Montplaisir - Initial API and implementation
13 * Patrick Tasse - Add message to exceptions
a52fde77
AM
14 *******************************************************************************/
15
0306a843 16package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
a52fde77
AM
17
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.IOException;
1ff6f3de 21import java.nio.channels.ClosedChannelException;
5d279031 22import java.util.Collections;
1ff6f3de
GB
23import java.util.Deque;
24import java.util.LinkedList;
a52fde77 25import java.util.List;
441a6e7f 26import java.util.logging.Logger;
a52fde77 27
b2f62cb5 28import org.eclipse.jdt.annotation.NonNull;
441a6e7f 29import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
5d279031
LPD
30import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.IntegerRangeCondition;
31import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.TimeRangeCondition;
1ff6f3de 32import org.eclipse.tracecompass.internal.statesystem.core.Activator;
e894a508
AM
33import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
34import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
35import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
36import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
37import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
38import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
a52fde77 39
068641fa 40import com.google.common.annotations.VisibleForTesting;
5d279031 41import com.google.common.collect.Iterables;
068641fa 42
a52fde77
AM
43/**
44 * History Tree backend for storing a state history. This is the basic version
45 * that runs in the same thread as the class creating it.
5df842b3 46 *
360f899e 47 * @author Alexandre Montplaisir
a52fde77
AM
48 */
49public class HistoryTreeBackend implements IStateHistoryBackend {
50
441a6e7f
GB
51 private static final Logger LOGGER = TraceCompassLog.getLogger(HistoryTreeBackend.class);
52
0e9b2f07 53 private final @NonNull String fSsid;
b2f62cb5 54
bcec0116
AM
55 /**
56 * The history tree that sits underneath.
bcec0116 57 */
1ff6f3de 58 private final @NonNull IHistoryTree fSht;
6f4e8ec0 59
1a4205d9 60 /** Indicates if the history tree construction is done */
d740e018
MK
61 private volatile boolean fFinishedBuilding = false;
62
63 /**
64 * Indicates if the history tree construction is done
65 *
66 * @return if the history tree construction is done
67 */
68 protected boolean isFinishedBuilding() {
69 return fFinishedBuilding;
70 }
71
72 /**
73 * Sets if the history tree is finished building
74 *
75 * @param isFinishedBuilding
76 * is the history tree finished building
77 */
78 protected void setFinishedBuilding(boolean isFinishedBuilding) {
0e9b2f07 79 fFinishedBuilding = isFinishedBuilding;
d740e018 80 }
1a4205d9 81
a52fde77 82 /**
7453b40e 83 * Constructor for new history files. Use this when creating a new history
a52fde77 84 * from scratch.
5df842b3 85 *
b2f62cb5
AM
86 * @param ssid
87 * The state system's ID
a52fde77
AM
88 * @param newStateFile
89 * The filename/location where to store the state history (Should
90 * end in .ht)
a96cc6be
AM
91 * @param providerVersion
92 * Version of of the state provider. We will only try to reopen
93 * existing files if this version matches the one in the
94 * framework.
a52fde77
AM
95 * @param startTime
96 * The earliest time stamp that will be stored in the history
f6d24a71
AM
97 * @param blockSize
98 * The size of the blocks in the history file. This should be a
99 * multiple of 4096.
100 * @param maxChildren
101 * The maximum number of children each core node can have
a52fde77
AM
102 * @throws IOException
103 * Thrown if we can't create the file for some reason
104 */
f6d24a71 105 public HistoryTreeBackend(@NonNull String ssid,
1ff6f3de 106 File newStateFile,
f6d24a71
AM
107 int providerVersion,
108 long startTime,
109 int blockSize,
110 int maxChildren) throws IOException {
0e9b2f07 111 fSsid = ssid;
1ff6f3de
GB
112 final HTConfig conf = new HTConfig(newStateFile, blockSize, maxChildren,
113 providerVersion, startTime);
114 fSht = initializeSHT(conf);
a52fde77
AM
115 }
116
117 /**
7453b40e 118 * Constructor for new history files. Use this when creating a new history
a52fde77
AM
119 * from scratch. This version supplies sane defaults for the configuration
120 * parameters.
5df842b3 121 *
b2f62cb5
AM
122 * @param ssid
123 * The state system's id
a52fde77
AM
124 * @param newStateFile
125 * The filename/location where to store the state history (Should
126 * end in .ht)
a96cc6be
AM
127 * @param providerVersion
128 * Version of of the state provider. We will only try to reopen
129 * existing files if this version matches the one in the
130 * framework.
a52fde77
AM
131 * @param startTime
132 * The earliest time stamp that will be stored in the history
133 * @throws IOException
134 * Thrown if we can't create the file for some reason
dbc7991d 135 * @since 1.0
a52fde77 136 */
1ff6f3de 137 public HistoryTreeBackend(@NonNull String ssid, File newStateFile, int providerVersion, long startTime)
a52fde77 138 throws IOException {
f6d24a71 139 this(ssid, newStateFile, providerVersion, startTime, 64 * 1024, 50);
a52fde77
AM
140 }
141
142 /**
143 * Existing history constructor. Use this to open an existing state-file.
5df842b3 144 *
b2f62cb5
AM
145 * @param ssid
146 * The state system's id
0d9a6d76 147 * @param existingStateFile
b255ae4b 148 * Filename/location of the history we want to load
a96cc6be
AM
149 * @param providerVersion
150 * Expected version of of the state provider plugin.
b255ae4b 151 * @throws IOException
a96cc6be
AM
152 * If we can't read the file, if it doesn't exist, is not
153 * recognized, or if the version of the file does not match the
154 * expected providerVersion.
a52fde77 155 */
068641fa 156 public HistoryTreeBackend(@NonNull String ssid, @NonNull File existingStateFile, int providerVersion)
a96cc6be 157 throws IOException {
0e9b2f07 158 fSsid = ssid;
068641fa 159 fSht = initializeSHT(existingStateFile, providerVersion);
d740e018 160 fFinishedBuilding = true;
a52fde77
AM
161 }
162
068641fa 163 /**
1ff6f3de
GB
164 * New-tree initializer for the History Tree wrapped by this backend. Can be
165 * overriden to use different implementations.
068641fa 166 *
1ff6f3de
GB
167 * @param conf
168 * The HTConfig configuration object
068641fa
GB
169 * @return The new history tree
170 * @throws IOException
1ff6f3de 171 * If there was a problem during creation
068641fa
GB
172 */
173 @VisibleForTesting
1ff6f3de
GB
174 protected @NonNull IHistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
175 return HistoryTreeFactory.createHistoryTree(conf);
068641fa
GB
176 }
177
178 /**
179 * Existing-tree initializer for the History Tree wrapped by this backend.
180 * Can be overriden to use different implementations.
181 *
182 * @param existingStateFile
183 * The file to open
184 * @param providerVersion
185 * The expected state provider version
186 * @return The history tree opened from the given file
187 * @throws IOException
188 * If there was a problem during creation
189 */
190 @VisibleForTesting
1ff6f3de 191 protected @NonNull IHistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
9a4c098d 192 return HistoryTreeFactory.createFromFile(existingStateFile.toPath(), providerVersion);
068641fa
GB
193 }
194
e62a23a9
AM
195 /**
196 * Get the History Tree built by this backend.
197 *
068641fa
GB
198 * Note: Do not override this method. If you want to extend the class to use
199 * a different History Tree implementation, override both variants of
200 * {@link #initializeSHT} instead.
201 *
e62a23a9
AM
202 * @return The history tree
203 */
1ff6f3de 204 protected final @NonNull IHistoryTree getSHT() {
0e9b2f07 205 return fSht;
e62a23a9
AM
206 }
207
b2f62cb5
AM
208 @Override
209 public String getSSID() {
0e9b2f07 210 return fSsid;
b2f62cb5
AM
211 }
212
a52fde77
AM
213 @Override
214 public long getStartTime() {
068641fa 215 return getSHT().getTreeStart();
a52fde77
AM
216 }
217
218 @Override
219 public long getEndTime() {
068641fa 220 return getSHT().getTreeEnd();
a52fde77
AM
221 }
222
223 @Override
224 public void insertPastState(long stateStartTime, long stateEndTime,
225 int quark, ITmfStateValue value) throws TimeRangeException {
1ff6f3de 226 HTInterval interval = new HTInterval(stateStartTime, stateEndTime,
a52fde77
AM
227 quark, (TmfStateValue) value);
228
229 /* Start insertions at the "latest leaf" */
1ff6f3de 230 getSHT().insertInterval(interval);
a52fde77
AM
231 }
232
233 @Override
b33c7369 234 public void finishedBuilding(long endTime) {
068641fa 235 getSHT().closeTree(endTime);
d740e018 236 fFinishedBuilding = true;
a52fde77
AM
237 }
238
239 @Override
240 public FileInputStream supplyAttributeTreeReader() {
068641fa 241 return getSHT().supplyATReader();
a52fde77
AM
242 }
243
244 @Override
245 public File supplyAttributeTreeWriterFile() {
068641fa 246 return getSHT().supplyATWriterFile();
a52fde77
AM
247 }
248
249 @Override
250 public long supplyAttributeTreeWriterFilePosition() {
068641fa 251 return getSHT().supplyATWriterFilePos();
a52fde77
AM
252 }
253
36bf82a2
AM
254 @Override
255 public void removeFiles() {
068641fa 256 getSHT().deleteFile();
36bf82a2
AM
257 }
258
1a4205d9
AM
259 @Override
260 public void dispose() {
d740e018 261 if (fFinishedBuilding) {
441a6e7f 262 LOGGER.info(() -> "[HistoryTreeBackend:ClosingFile] size=" + getSHT().getFileSize()); //$NON-NLS-1$
068641fa 263 getSHT().closeFile();
1a4205d9
AM
264 } else {
265 /*
266 * The build is being interrupted, delete the file we partially
267 * built since it won't be complete, so shouldn't be re-used in the
268 * future (.deleteFile() will close the file first)
269 */
068641fa 270 getSHT().deleteFile();
1a4205d9
AM
271 }
272 }
273
a52fde77
AM
274 @Override
275 public void doQuery(List<ITmfStateInterval> stateInfo, long t)
3b7f5abe 276 throws TimeRangeException, StateSystemDisposedException {
e13bd4cd 277 checkValidTime(t);
a52fde77 278
1ff6f3de
GB
279 /* Queue is a stack of nodes containing nodes intersecting t */
280 Deque<Integer> queue = new LinkedList<>();
281
282 /* We start by reading the information in the root node */
283 queue.add(getSHT().getRootNode().getSequenceNumber());
284
285 /* Then we follow the down in the relevant children */
286 try {
287 while (!queue.isEmpty()) {
288 int sequenceNumber = queue.pop();
289 HTNode currentNode = getSHT().readNode(sequenceNumber);
290 if (currentNode.getNodeType() == HTNode.NodeType.CORE) {
291 /* Here we add the relevant children nodes for BFS */
292 queue.addAll(((ParentNode) currentNode).selectNextChildren(t));
293 }
294 currentNode.writeInfoFromNode(stateInfo, t);
295 }
296 } catch (ClosedChannelException e) {
297 throw new StateSystemDisposedException(e);
298 }
a52fde77 299
1ff6f3de
GB
300 /*
301 * The stateInfo should now be filled with everything needed, we pass
302 * the control back to the State System.
303 */
a52fde77
AM
304 }
305
306 @Override
307 public ITmfStateInterval doSingularQuery(long t, int attributeQuark)
3b7f5abe 308 throws TimeRangeException, StateSystemDisposedException {
1ff6f3de
GB
309 try {
310 return getRelevantInterval(t, attributeQuark);
311 } catch (ClosedChannelException e) {
312 throw new StateSystemDisposedException(e);
313 }
a52fde77
AM
314 }
315
e13bd4cd 316 private void checkValidTime(long t) {
e8aa3325
PT
317 long startTime = getStartTime();
318 long endTime = getEndTime();
319 if (t < startTime || t > endTime) {
320 throw new TimeRangeException(String.format("%s Time:%d, Start:%d, End:%d", //$NON-NLS-1$
321 fSsid, t, startTime, endTime));
e13bd4cd 322 }
a52fde77
AM
323 }
324
1ff6f3de
GB
325 /**
326 * Inner method to find the interval in the tree containing the requested
327 * key/timestamp pair, wherever in which node it is.
328 */
329 private HTInterval getRelevantInterval(long t, int key)
330 throws TimeRangeException, ClosedChannelException {
331 checkValidTime(t);
332
333 Deque<Integer> queue = new LinkedList<>();
334 queue.add(getSHT().getRootNode().getSequenceNumber());
335 HTInterval interval = null;
336 while (interval == null && !queue.isEmpty()) {
337 int sequenceNumber = queue.pop();
338 HTNode currentNode = getSHT().readNode(sequenceNumber);
339 if (currentNode.getNodeType() == HTNode.NodeType.CORE) {
340 /* Here we add the relevant children nodes for BFS */
341 queue.addAll(((ParentNode) currentNode).selectNextChildren(t));
342 }
343 interval = currentNode.getRelevantInterval(key, t);
344 }
345 return interval;
346 }
347
5d279031
LPD
348 @Override
349 public Iterable<@NonNull ITmfStateInterval> query2D(IntegerRangeCondition quarks, TimeRangeCondition times)
350 throws TimeRangeException {
351 /* Get a flattened Iterable of nodes that match the conditions */
352 Iterable<HTNode> nodes = flatten(getSHT().getRootNode(), quarks, times);
353 /*
354 * Transform them into the iterables over their intervals that match the
355 * conditions.
356 */
357 Iterable<Iterable<HTInterval>> iterables = Iterables.transform(nodes,
358 n -> n.iterable2D(quarks, times));
359 return Iterables.concat(iterables);
360 }
361
362 private Iterable<HTNode> flatten(HTNode node, IntegerRangeCondition quarks, TimeRangeCondition times) {
363 if (node.getNodeType() == HTNode.NodeType.LEAF) {
364 return Collections.singleton(node);
365 }
366 ParentNode parent = (ParentNode) node;
367 /* Reduce the condition to this node's bounds. */
368 if (node.getNodeStart() > node.getNodeEnd()) {
369 return Collections.emptyList();
370 }
371 TimeRangeCondition subTimes = times.subCondition(node.getNodeStart(), node.getNodeEnd());
372 /*
373 * Transform the children's sequence numbers into the children's
374 * flattened subtrees.
375 */
376 Iterable<Iterable<HTNode>> children = Iterables.transform(parent.selectNextChildren2D(quarks, subTimes), seqNum -> {
377 try {
378 /* Recursive call to flatten children */
379 return flatten(getSHT().readNode(seqNum), quarks, subTimes);
380 } catch (ClosedChannelException e) {
381 return null;
382 }
383 });
384 /* BFS */
385 return Iterables.concat(Collections.singleton(node), Iterables.concat(children));
386 }
387
a52fde77
AM
388 /**
389 * Return the size of the tree history file
5df842b3 390 *
a52fde77
AM
391 * @return The current size of the history file in bytes
392 */
393 public long getFileSize() {
068641fa 394 return getSHT().getFileSize();
a52fde77
AM
395 }
396
a52fde77
AM
397 /**
398 * Return the average node usage as a percentage (between 0 and 100)
5df842b3 399 *
a52fde77
AM
400 * @return Average node usage %
401 */
402 public int getAverageNodeUsage() {
1ff6f3de
GB
403 HTNode node;
404 long total = 0;
405 long ret;
406
407 try {
408 for (int seq = 0; seq < getSHT().getNodeCount(); seq++) {
409 node = getSHT().readNode(seq);
410 total += node.getNodeUsagePercent();
411 }
412 } catch (ClosedChannelException e) {
413 Activator.getDefault().logError(e.getMessage(), e);
414 }
415
416 ret = total / getSHT().getNodeCount();
417 /* The return value should be a percentage */
418 if (ret < 0 || ret > 100) {
419 throw new IllegalStateException("Average node usage is not a percentage: " + ret); //$NON-NLS-1$
420 }
421 return (int) ret;
a52fde77
AM
422 }
423
a52fde77 424}
This page took 0.11095 seconds and 5 git commands to generate.