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
.io
.PrintWriter
;
22 import java
.nio
.channels
.ClosedChannelException
;
23 import java
.util
.List
;
25 import org
.eclipse
.jdt
.annotation
.NonNull
;
26 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.Activator
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
34 import com
.google
.common
.annotations
.VisibleForTesting
;
37 * History Tree backend for storing a state history. This is the basic version
38 * that runs in the same thread as the class creating it.
40 * @author Alexandre Montplaisir
42 public class HistoryTreeBackend
implements IStateHistoryBackend
{
44 private final @NonNull String fSsid
;
47 * The history tree that sits underneath.
49 private final @NonNull IHistoryTree fSht
;
51 /** Indicates if the history tree construction is done */
52 private volatile boolean fFinishedBuilding
= false;
55 * Indicates if the history tree construction is done
57 * @return if the history tree construction is done
59 protected boolean isFinishedBuilding() {
60 return fFinishedBuilding
;
64 * Sets if the history tree is finished building
66 * @param isFinishedBuilding
67 * is the history tree finished building
69 protected void setFinishedBuilding(boolean isFinishedBuilding
) {
70 fFinishedBuilding
= isFinishedBuilding
;
74 * Constructor for new history files. Use this when creating a new history
78 * The state system's ID
80 * The filename/location where to store the state history (Should
82 * @param providerVersion
83 * Version of of the state provider. We will only try to reopen
84 * existing files if this version matches the one in the
87 * The earliest time stamp that will be stored in the history
89 * The size of the blocks in the history file. This should be a
92 * The maximum number of children each core node can have
94 * Thrown if we can't create the file for some reason
96 public HistoryTreeBackend(@NonNull String ssid
,
101 int maxChildren
) throws IOException
{
103 final HTConfig conf
= new HTConfig(newStateFile
, blockSize
, maxChildren
,
104 providerVersion
, startTime
);
105 fSht
= initializeSHT(conf
);
109 * Constructor for new history files. Use this when creating a new history
110 * from scratch. This version supplies sane defaults for the configuration
114 * The state system's id
115 * @param newStateFile
116 * The filename/location where to store the state history (Should
118 * @param providerVersion
119 * Version of of the state provider. We will only try to reopen
120 * existing files if this version matches the one in the
123 * The earliest time stamp that will be stored in the history
124 * @throws IOException
125 * Thrown if we can't create the file for some reason
128 public HistoryTreeBackend(@NonNull String ssid
, File newStateFile
, int providerVersion
, long startTime
)
130 this(ssid
, newStateFile
, providerVersion
, startTime
, 64 * 1024, 50);
134 * Existing history constructor. Use this to open an existing state-file.
137 * The state system's id
138 * @param existingStateFile
139 * Filename/location of the history we want to load
140 * @param providerVersion
141 * Expected version of of the state provider plugin.
142 * @throws IOException
143 * If we can't read the file, if it doesn't exist, is not
144 * recognized, or if the version of the file does not match the
145 * expected providerVersion.
147 public HistoryTreeBackend(@NonNull String ssid
, @NonNull File existingStateFile
, int providerVersion
)
150 fSht
= initializeSHT(existingStateFile
, providerVersion
);
151 fFinishedBuilding
= true;
155 * New-tree initializer for the History Tree wrapped by this backend. Can be
156 * overriden to use different implementations.
159 * The HTConfig configuration object
160 * @return The new history tree
161 * @throws IOException
162 * If there was a problem during creation
165 protected @NonNull IHistoryTree
initializeSHT(@NonNull HTConfig conf
) throws IOException
{
166 return HistoryTreeFactory
.createHistoryTree(conf
);
170 * Existing-tree initializer for the History Tree wrapped by this backend.
171 * Can be overriden to use different implementations.
173 * @param existingStateFile
175 * @param providerVersion
176 * The expected state provider version
177 * @return The history tree opened from the given file
178 * @throws IOException
179 * If there was a problem during creation
182 protected @NonNull IHistoryTree
initializeSHT(@NonNull File existingStateFile
, int providerVersion
) throws IOException
{
183 return HistoryTreeFactory
.createFromFile(existingStateFile
.toPath(), providerVersion
);
187 * Get the History Tree built by this backend.
189 * Note: Do not override this method. If you want to extend the class to use
190 * a different History Tree implementation, override both variants of
191 * {@link #initializeSHT} instead.
193 * @return The history tree
195 protected final @NonNull IHistoryTree
getSHT() {
200 public String
getSSID() {
205 public long getStartTime() {
206 return getSHT().getTreeStart();
210 public long getEndTime() {
211 return getSHT().getTreeEnd();
215 public void insertPastState(long stateStartTime
, long stateEndTime
,
216 int quark
, ITmfStateValue value
) throws TimeRangeException
{
217 HTInterval interval
= new HTInterval(stateStartTime
, stateEndTime
,
218 quark
, (TmfStateValue
) value
);
220 /* Start insertions at the "latest leaf" */
221 getSHT().insertInterval(interval
);
225 public void finishedBuilding(long endTime
) {
226 getSHT().closeTree(endTime
);
227 fFinishedBuilding
= true;
231 public FileInputStream
supplyAttributeTreeReader() {
232 return getSHT().supplyATReader();
236 public File
supplyAttributeTreeWriterFile() {
237 return getSHT().supplyATWriterFile();
241 public long supplyAttributeTreeWriterFilePosition() {
242 return getSHT().supplyATWriterFilePos();
246 public void removeFiles() {
247 getSHT().deleteFile();
251 public void dispose() {
252 if (fFinishedBuilding
) {
253 getSHT().closeFile();
256 * The build is being interrupted, delete the file we partially
257 * built since it won't be complete, so shouldn't be re-used in the
258 * future (.deleteFile() will close the file first)
260 getSHT().deleteFile();
265 public void doQuery(List
<ITmfStateInterval
> stateInfo
, long t
)
266 throws TimeRangeException
, StateSystemDisposedException
{
269 /* We start by reading the information in the root node */
270 HTNode currentNode
= getSHT().getRootNode();
271 currentNode
.writeInfoFromNode(stateInfo
, t
);
273 /* Then we follow the branch down in the relevant children */
275 while (currentNode
.getNodeType() == HTNode
.NodeType
.CORE
) {
276 currentNode
= getSHT().selectNextChild((CoreNode
) currentNode
, t
);
277 currentNode
.writeInfoFromNode(stateInfo
, t
);
279 } catch (ClosedChannelException e
) {
280 throw new StateSystemDisposedException(e
);
284 * The stateInfo should now be filled with everything needed, we pass
285 * the control back to the State System.
290 public ITmfStateInterval
doSingularQuery(long t
, int attributeQuark
)
291 throws TimeRangeException
, StateSystemDisposedException
{
292 return getRelevantInterval(t
, attributeQuark
);
295 private void checkValidTime(long t
) {
296 long startTime
= getStartTime();
297 long endTime
= getEndTime();
298 if (t
< startTime
|| t
> endTime
) {
299 throw new TimeRangeException(String
.format("%s Time:%d, Start:%d, End:%d", //$NON-NLS-1$
300 fSsid
, t
, startTime
, endTime
));
305 * Inner method to find the interval in the tree containing the requested
306 * key/timestamp pair, wherever in which node it is.
310 * @return The node containing the information we want
312 private HTInterval
getRelevantInterval(long t
, int key
)
313 throws TimeRangeException
, StateSystemDisposedException
{
316 HTNode currentNode
= getSHT().getRootNode();
317 HTInterval interval
= currentNode
.getRelevantInterval(key
, t
);
320 while (interval
== null && currentNode
.getNodeType() == HTNode
.NodeType
.CORE
) {
321 currentNode
= getSHT().selectNextChild((CoreNode
) currentNode
, t
);
322 interval
= currentNode
.getRelevantInterval(key
, t
);
324 } catch (ClosedChannelException e
) {
325 throw new StateSystemDisposedException(e
);
331 * Return the size of the tree history file
333 * @return The current size of the history file in bytes
335 public long getFileSize() {
336 return getSHT().getFileSize();
340 * Return the average node usage as a percentage (between 0 and 100)
342 * @return Average node usage %
344 public int getAverageNodeUsage() {
350 for (int seq
= 0; seq
< getSHT().getNodeCount(); seq
++) {
351 node
= getSHT().readNode(seq
);
352 total
+= node
.getNodeUsagePercent();
354 } catch (ClosedChannelException e
) {
355 Activator
.getDefault().logError(e
.getMessage(), e
);
358 ret
= total
/ getSHT().getNodeCount();
359 /* The return value should be a percentage */
360 if (ret
< 0 || ret
> 100) {
361 throw new IllegalStateException("Average node usage is not a percentage: " + ret
); //$NON-NLS-1$
367 public void debugPrint(PrintWriter writer
) {
368 /* By default don't print out all the intervals */
369 debugPrint(writer
, false, -1);
373 * The basic debugPrint method will print the tree structure, but not their
376 * This method here print the contents (the intervals) as well.
379 * The PrintWriter to which the debug info will be written
380 * @param printIntervals
381 * Should we also print every contained interval individually?
383 * The timestamp that nodes have to intersect for intervals to be
384 * printed. A negative value will print intervals for all nodes.
385 * The timestamp only applies if printIntervals is true.
387 public void debugPrint(PrintWriter writer
, boolean printIntervals
, long ts
) {
388 /* Only used for debugging, shouldn't be externalized */
389 writer
.println("------------------------------"); //$NON-NLS-1$
390 writer
.println("State History Tree:\n"); //$NON-NLS-1$
391 writer
.println(getSHT().toString());
392 writer
.println("Average node utilization: " //$NON-NLS-1$
393 + getAverageNodeUsage());
394 writer
.println(""); //$NON-NLS-1$
396 getSHT().debugPrintFullTree(writer
, printIntervals
, ts
);