/*******************************************************************************
- * Copyright (c) 2012, 2015 Ericsson
+ * Copyright (c) 2012, 2016 Ericsson
* Copyright (c) 2010, 2011 École Polytechnique de Montréal
* Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
*
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.PrintWriter;
import java.nio.channels.ClosedChannelException;
+import java.util.Deque;
+import java.util.LinkedList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.internal.statesystem.core.Activator;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.CoreNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTInterval;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import com.google.common.annotations.VisibleForTesting;
+
/**
* History Tree backend for storing a state history. This is the basic version
* that runs in the same thread as the class creating it.
/**
* The history tree that sits underneath.
*/
- private final HistoryTree fSht;
+ private final @NonNull IHistoryTree fSht;
/** Indicates if the history tree construction is done */
private volatile boolean fFinishedBuilding = false;
fSsid = ssid;
final HTConfig conf = new HTConfig(newStateFile, blockSize, maxChildren,
providerVersion, startTime);
- fSht = new HistoryTree(conf);
+ fSht = initializeSHT(conf);
}
/**
* recognized, or if the version of the file does not match the
* expected providerVersion.
*/
- public HistoryTreeBackend(@NonNull String ssid, File existingStateFile, int providerVersion)
+ public HistoryTreeBackend(@NonNull String ssid, @NonNull File existingStateFile, int providerVersion)
throws IOException {
fSsid = ssid;
- fSht = new HistoryTree(existingStateFile, providerVersion);
+ fSht = initializeSHT(existingStateFile, providerVersion);
fFinishedBuilding = true;
}
+ /**
+ * New-tree initializer for the History Tree wrapped by this backend. Can be
+ * overriden to use different implementations.
+ *
+ * @param conf
+ * The HTConfig configuration object
+ * @return The new history tree
+ * @throws IOException
+ * If there was a problem during creation
+ */
+ @VisibleForTesting
+ protected @NonNull IHistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
+ return HistoryTreeFactory.createHistoryTree(conf);
+ }
+
+ /**
+ * Existing-tree initializer for the History Tree wrapped by this backend.
+ * Can be overriden to use different implementations.
+ *
+ * @param existingStateFile
+ * The file to open
+ * @param providerVersion
+ * The expected state provider version
+ * @return The history tree opened from the given file
+ * @throws IOException
+ * If there was a problem during creation
+ */
+ @VisibleForTesting
+ protected @NonNull IHistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
+ return HistoryTreeFactory.createFromFile(existingStateFile.toPath(), providerVersion);
+ }
+
/**
* Get the History Tree built by this backend.
*
+ * Note: Do not override this method. If you want to extend the class to use
+ * a different History Tree implementation, override both variants of
+ * {@link #initializeSHT} instead.
+ *
* @return The history tree
*/
- protected HistoryTree getSHT() {
+ protected final @NonNull IHistoryTree getSHT() {
return fSht;
}
@Override
public long getStartTime() {
- return fSht.getTreeStart();
+ return getSHT().getTreeStart();
}
@Override
public long getEndTime() {
- return fSht.getTreeEnd();
+ return getSHT().getTreeEnd();
}
@Override
quark, (TmfStateValue) value);
/* Start insertions at the "latest leaf" */
- fSht.insertInterval(interval);
+ getSHT().insertInterval(interval);
}
@Override
public void finishedBuilding(long endTime) {
- fSht.closeTree(endTime);
+ getSHT().closeTree(endTime);
fFinishedBuilding = true;
}
@Override
public FileInputStream supplyAttributeTreeReader() {
- return fSht.supplyATReader();
+ return getSHT().supplyATReader();
}
@Override
public File supplyAttributeTreeWriterFile() {
- return fSht.supplyATWriterFile();
+ return getSHT().supplyATWriterFile();
}
@Override
public long supplyAttributeTreeWriterFilePosition() {
- return fSht.supplyATWriterFilePos();
+ return getSHT().supplyATWriterFilePos();
}
@Override
public void removeFiles() {
- fSht.deleteFile();
+ getSHT().deleteFile();
}
@Override
public void dispose() {
if (fFinishedBuilding) {
- fSht.closeFile();
+ getSHT().closeFile();
} else {
/*
* The build is being interrupted, delete the file we partially
* built since it won't be complete, so shouldn't be re-used in the
* future (.deleteFile() will close the file first)
*/
- fSht.deleteFile();
+ getSHT().deleteFile();
}
}
throws TimeRangeException, StateSystemDisposedException {
checkValidTime(t);
+ /* Queue is a stack of nodes containing nodes intersecting t */
+ Deque<HTNode> queue = new LinkedList<>();
+
/* We start by reading the information in the root node */
- HTNode currentNode = fSht.getRootNode();
- currentNode.writeInfoFromNode(stateInfo, t);
+ queue.add(getSHT().getRootNode());
- /* Then we follow the branch down in the relevant children */
+ /* Then we follow the down in the relevant children */
try {
- while (currentNode.getNodeType() == HTNode.NodeType.CORE) {
- currentNode = fSht.selectNextChild((CoreNode) currentNode, t);
+ while (!queue.isEmpty()) {
+ HTNode currentNode = queue.pop();
+ if (currentNode.getNodeType() == HTNode.NodeType.CORE) {
+ /*Here we add the relevant children nodes for BFS*/
+ queue.addAll(getSHT().selectNextChildren((ParentNode) currentNode, t));
+ }
currentNode.writeInfoFromNode(stateInfo, t);
}
} catch (ClosedChannelException e) {
@Override
public ITmfStateInterval doSingularQuery(long t, int attributeQuark)
throws TimeRangeException, StateSystemDisposedException {
- return getRelevantInterval(t, attributeQuark);
+ try {
+ return getRelevantInterval(t, attributeQuark);
+ } catch (ClosedChannelException e) {
+ throw new StateSystemDisposedException(e);
+ }
}
private void checkValidTime(long t) {
- long treeStart = fSht.getTreeStart();
- long treeEnd = fSht.getTreeEnd();
- if (t < treeStart || t > treeEnd) {
- throw new TimeRangeException(fSsid + " Time:" + t + ", Start:" + treeStart + ", End:" + treeEnd); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ long startTime = getStartTime();
+ long endTime = getEndTime();
+ if (t < startTime || t > endTime) {
+ throw new TimeRangeException(String.format("%s Time:%d, Start:%d, End:%d", //$NON-NLS-1$
+ fSsid, t, startTime, endTime));
}
}
/**
* Inner method to find the interval in the tree containing the requested
* key/timestamp pair, wherever in which node it is.
- *
- * @param t
- * @param key
- * @return The node containing the information we want
*/
private HTInterval getRelevantInterval(long t, int key)
- throws TimeRangeException, StateSystemDisposedException {
+ throws TimeRangeException, ClosedChannelException {
checkValidTime(t);
- HTNode currentNode = fSht.getRootNode();
- HTInterval interval = currentNode.getRelevantInterval(key, t);
-
- try {
- while (interval == null && currentNode.getNodeType() == HTNode.NodeType.CORE) {
- currentNode = fSht.selectNextChild((CoreNode) currentNode, t);
- interval = currentNode.getRelevantInterval(key, t);
+ Deque<HTNode> queue = new LinkedList<>();
+ queue.add(getSHT().getRootNode());
+ HTInterval interval = null;
+ while (interval == null && !queue.isEmpty()) {
+ HTNode currentNode = queue.pop();
+ if (currentNode.getNodeType() == HTNode.NodeType.CORE) {
+ queue.addAll(getSHT().selectNextChildren((ParentNode) currentNode, t));
}
- } catch (ClosedChannelException e) {
- throw new StateSystemDisposedException(e);
+ interval = currentNode.getRelevantInterval(key, t);
}
return interval;
}
* @return The current size of the history file in bytes
*/
public long getFileSize() {
- return fSht.getFileSize();
+ return getSHT().getFileSize();
}
/**
long ret;
try {
- for (int seq = 0; seq < fSht.getNodeCount(); seq++) {
- node = fSht.readNode(seq);
+ for (int seq = 0; seq < getSHT().getNodeCount(); seq++) {
+ node = getSHT().readNode(seq);
total += node.getNodeUsagePercent();
}
} catch (ClosedChannelException e) {
Activator.getDefault().logError(e.getMessage(), e);
}
- ret = total / fSht.getNodeCount();
+ ret = total / getSHT().getNodeCount();
/* The return value should be a percentage */
- if (ret >= 0 && ret <= 100) {
+ if (ret < 0 || ret > 100) {
throw new IllegalStateException("Average node usage is not a percentage: " + ret); //$NON-NLS-1$
}
return (int) ret;
}
- @Override
- public void debugPrint(PrintWriter writer) {
- /* By default don't print out all the intervals */
- debugPrint(writer, false);
- }
-
- /**
- * The basic debugPrint method will print the tree structure, but not their
- * contents.
- *
- * This method here print the contents (the intervals) as well.
- *
- * @param writer
- * The PrintWriter to which the debug info will be written
- * @param printIntervals
- * Should we also print every contained interval individually?
- */
- public void debugPrint(PrintWriter writer, boolean printIntervals) {
- /* Only used for debugging, shouldn't be externalized */
- writer.println("------------------------------"); //$NON-NLS-1$
- writer.println("State History Tree:\n"); //$NON-NLS-1$
- writer.println(fSht.toString());
- writer.println("Average node utilization: " //$NON-NLS-1$
- + getAverageNodeUsage());
- writer.println(""); //$NON-NLS-1$
-
- fSht.debugPrintFullTree(writer, printIntervals);
- }
}