1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
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
12 * Alexandre Montplaisir - Initial API and implementation
13 * Patrick Tasse - Add message to exceptions
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
;
19 import java
.io
.FileInputStream
;
20 import java
.io
.IOException
;
21 import java
.nio
.channels
.ClosedChannelException
;
22 import java
.util
.Deque
;
23 import java
.util
.LinkedList
;
24 import java
.util
.List
;
25 import java
.util
.logging
.Logger
;
27 import org
.eclipse
.jdt
.annotation
.NonNull
;
28 import org
.eclipse
.tracecompass
.common
.core
.log
.TraceCompassLog
;
29 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.Activator
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
37 import com
.google
.common
.annotations
.VisibleForTesting
;
40 * History Tree backend for storing a state history. This is the basic version
41 * that runs in the same thread as the class creating it.
43 * @author Alexandre Montplaisir
45 public class HistoryTreeBackend
implements IStateHistoryBackend
{
47 private static final Logger LOGGER
= TraceCompassLog
.getLogger(HistoryTreeBackend
.class);
49 private final @NonNull String fSsid
;
52 * The history tree that sits underneath.
54 private final @NonNull IHistoryTree fSht
;
56 /** Indicates if the history tree construction is done */
57 private volatile boolean fFinishedBuilding
= false;
60 * Indicates if the history tree construction is done
62 * @return if the history tree construction is done
64 protected boolean isFinishedBuilding() {
65 return fFinishedBuilding
;
69 * Sets if the history tree is finished building
71 * @param isFinishedBuilding
72 * is the history tree finished building
74 protected void setFinishedBuilding(boolean isFinishedBuilding
) {
75 fFinishedBuilding
= isFinishedBuilding
;
79 * Constructor for new history files. Use this when creating a new history
83 * The state system's ID
85 * The filename/location where to store the state history (Should
87 * @param providerVersion
88 * Version of of the state provider. We will only try to reopen
89 * existing files if this version matches the one in the
92 * The earliest time stamp that will be stored in the history
94 * The size of the blocks in the history file. This should be a
97 * The maximum number of children each core node can have
99 * Thrown if we can't create the file for some reason
101 public HistoryTreeBackend(@NonNull String ssid
,
106 int maxChildren
) throws IOException
{
108 final HTConfig conf
= new HTConfig(newStateFile
, blockSize
, maxChildren
,
109 providerVersion
, startTime
);
110 fSht
= initializeSHT(conf
);
114 * Constructor for new history files. Use this when creating a new history
115 * from scratch. This version supplies sane defaults for the configuration
119 * The state system's id
120 * @param newStateFile
121 * The filename/location where to store the state history (Should
123 * @param providerVersion
124 * Version of of the state provider. We will only try to reopen
125 * existing files if this version matches the one in the
128 * The earliest time stamp that will be stored in the history
129 * @throws IOException
130 * Thrown if we can't create the file for some reason
133 public HistoryTreeBackend(@NonNull String ssid
, File newStateFile
, int providerVersion
, long startTime
)
135 this(ssid
, newStateFile
, providerVersion
, startTime
, 64 * 1024, 50);
139 * Existing history constructor. Use this to open an existing state-file.
142 * The state system's id
143 * @param existingStateFile
144 * Filename/location of the history we want to load
145 * @param providerVersion
146 * Expected version of of the state provider plugin.
147 * @throws IOException
148 * If we can't read the file, if it doesn't exist, is not
149 * recognized, or if the version of the file does not match the
150 * expected providerVersion.
152 public HistoryTreeBackend(@NonNull String ssid
, @NonNull File existingStateFile
, int providerVersion
)
155 fSht
= initializeSHT(existingStateFile
, providerVersion
);
156 fFinishedBuilding
= true;
160 * New-tree initializer for the History Tree wrapped by this backend. Can be
161 * overriden to use different implementations.
164 * The HTConfig configuration object
165 * @return The new history tree
166 * @throws IOException
167 * If there was a problem during creation
170 protected @NonNull IHistoryTree
initializeSHT(@NonNull HTConfig conf
) throws IOException
{
171 return HistoryTreeFactory
.createHistoryTree(conf
);
175 * Existing-tree initializer for the History Tree wrapped by this backend.
176 * Can be overriden to use different implementations.
178 * @param existingStateFile
180 * @param providerVersion
181 * The expected state provider version
182 * @return The history tree opened from the given file
183 * @throws IOException
184 * If there was a problem during creation
187 protected @NonNull IHistoryTree
initializeSHT(@NonNull File existingStateFile
, int providerVersion
) throws IOException
{
188 return HistoryTreeFactory
.createFromFile(existingStateFile
.toPath(), providerVersion
);
192 * Get the History Tree built by this backend.
194 * Note: Do not override this method. If you want to extend the class to use
195 * a different History Tree implementation, override both variants of
196 * {@link #initializeSHT} instead.
198 * @return The history tree
200 protected final @NonNull IHistoryTree
getSHT() {
205 public String
getSSID() {
210 public long getStartTime() {
211 return getSHT().getTreeStart();
215 public long getEndTime() {
216 return getSHT().getTreeEnd();
220 public void insertPastState(long stateStartTime
, long stateEndTime
,
221 int quark
, ITmfStateValue value
) throws TimeRangeException
{
222 HTInterval interval
= new HTInterval(stateStartTime
, stateEndTime
,
223 quark
, (TmfStateValue
) value
);
225 /* Start insertions at the "latest leaf" */
226 getSHT().insertInterval(interval
);
230 public void finishedBuilding(long endTime
) {
231 getSHT().closeTree(endTime
);
232 fFinishedBuilding
= true;
236 public FileInputStream
supplyAttributeTreeReader() {
237 return getSHT().supplyATReader();
241 public File
supplyAttributeTreeWriterFile() {
242 return getSHT().supplyATWriterFile();
246 public long supplyAttributeTreeWriterFilePosition() {
247 return getSHT().supplyATWriterFilePos();
251 public void removeFiles() {
252 getSHT().deleteFile();
256 public void dispose() {
257 if (fFinishedBuilding
) {
258 LOGGER
.info(() -> "[HistoryTreeBackend:ClosingFile] size=" + getSHT().getFileSize()); //$NON-NLS-1$
259 getSHT().closeFile();
262 * The build is being interrupted, delete the file we partially
263 * built since it won't be complete, so shouldn't be re-used in the
264 * future (.deleteFile() will close the file first)
266 getSHT().deleteFile();
271 public void doQuery(List
<ITmfStateInterval
> stateInfo
, long t
)
272 throws TimeRangeException
, StateSystemDisposedException
{
275 /* Queue is a stack of nodes containing nodes intersecting t */
276 Deque
<Integer
> queue
= new LinkedList
<>();
278 /* We start by reading the information in the root node */
279 queue
.add(getSHT().getRootNode().getSequenceNumber());
281 /* Then we follow the down in the relevant children */
283 while (!queue
.isEmpty()) {
284 int sequenceNumber
= queue
.pop();
285 HTNode currentNode
= getSHT().readNode(sequenceNumber
);
286 if (currentNode
.getNodeType() == HTNode
.NodeType
.CORE
) {
287 /* Here we add the relevant children nodes for BFS */
288 queue
.addAll(((ParentNode
) currentNode
).selectNextChildren(t
));
290 currentNode
.writeInfoFromNode(stateInfo
, t
);
292 } catch (ClosedChannelException e
) {
293 throw new StateSystemDisposedException(e
);
297 * The stateInfo should now be filled with everything needed, we pass
298 * the control back to the State System.
303 public ITmfStateInterval
doSingularQuery(long t
, int attributeQuark
)
304 throws TimeRangeException
, StateSystemDisposedException
{
306 return getRelevantInterval(t
, attributeQuark
);
307 } catch (ClosedChannelException e
) {
308 throw new StateSystemDisposedException(e
);
312 private void checkValidTime(long t
) {
313 long startTime
= getStartTime();
314 long endTime
= getEndTime();
315 if (t
< startTime
|| t
> endTime
) {
316 throw new TimeRangeException(String
.format("%s Time:%d, Start:%d, End:%d", //$NON-NLS-1$
317 fSsid
, t
, startTime
, endTime
));
322 * Inner method to find the interval in the tree containing the requested
323 * key/timestamp pair, wherever in which node it is.
325 private HTInterval
getRelevantInterval(long t
, int key
)
326 throws TimeRangeException
, ClosedChannelException
{
329 Deque
<Integer
> queue
= new LinkedList
<>();
330 queue
.add(getSHT().getRootNode().getSequenceNumber());
331 HTInterval interval
= null;
332 while (interval
== null && !queue
.isEmpty()) {
333 int sequenceNumber
= queue
.pop();
334 HTNode currentNode
= getSHT().readNode(sequenceNumber
);
335 if (currentNode
.getNodeType() == HTNode
.NodeType
.CORE
) {
336 /* Here we add the relevant children nodes for BFS */
337 queue
.addAll(((ParentNode
) currentNode
).selectNextChildren(t
));
339 interval
= currentNode
.getRelevantInterval(key
, t
);
345 * Return the size of the tree history file
347 * @return The current size of the history file in bytes
349 public long getFileSize() {
350 return getSHT().getFileSize();
354 * Return the average node usage as a percentage (between 0 and 100)
356 * @return Average node usage %
358 public int getAverageNodeUsage() {
364 for (int seq
= 0; seq
< getSHT().getNodeCount(); seq
++) {
365 node
= getSHT().readNode(seq
);
366 total
+= node
.getNodeUsagePercent();
368 } catch (ClosedChannelException e
) {
369 Activator
.getDefault().logError(e
.getMessage(), e
);
372 ret
= total
/ getSHT().getNodeCount();
373 /* The return value should be a percentage */
374 if (ret
< 0 || ret
> 100) {
375 throw new IllegalStateException("Average node usage is not a percentage: " + ret
); //$NON-NLS-1$