ss: Extract an history tree interface
authorGeneviève Bastien <gbastien+lttng@versatic.net>
Mon, 11 Jul 2016 01:51:30 +0000 (21:51 -0400)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Mon, 19 Sep 2016 20:04:46 +0000 (16:04 -0400)
This is a second step towards supporting multiple types of SHTs

Change-Id: I294ae4991207fac30700ef803f529f3f14e4417b
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Reviewed-on: https://git.eclipse.org/r/77007
Reviewed-by: Hudson CI
12 files changed:
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/historytree/HistoryTreeTest.java
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/historytree/HistoryTreeWithBackendTest.java
statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeBackendStub.java
statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeClassicStub.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeStub.java [deleted file]
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HTConfig.java
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HT_IO.java
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTree.java [deleted file]
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTreeBackend.java
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTreeClassic.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTreeFactory.java
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/IHistoryTree.java [new file with mode: 0644]

index c90f7142a19d4e8fccfd32ebb866a9f4694dde2c..13b759c353749fe1c14f617dbc09ff8d86e58fcf 100644 (file)
@@ -22,9 +22,9 @@ import org.eclipse.jdt.annotation.Nullable;
 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.internal.statesystem.core.backend.historytree.IHistoryTree;
 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
-import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeStub;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeClassicStub;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -38,7 +38,7 @@ public class HistoryTreeTest {
 
 
     /* Minimal allowed blocksize */
-    private static final int BLOCK_SIZE = HistoryTree.TREE_HEADER_SIZE;
+    private static final int BLOCK_SIZE = HistoryTreeClassicStub.MINIMUM_BLOCK_SIZE;
 
     private static final HTInterval NULL_INTERVAL = new HTInterval(10, 20, 1, TmfStateValue.nullValue());
 
@@ -84,8 +84,8 @@ public class HistoryTreeTest {
      *            The max number of children per node in the tree (tree config
      *            option)
      */
-    private HistoryTreeStub setupSmallTree(int maxChildren) {
-        HistoryTreeStub ht = null;
+    private HistoryTreeClassicStub setupSmallTree(int maxChildren) {
+        HistoryTreeClassicStub ht = null;
         try {
             File newFile = fTempFile;
             assertNotNull(newFile);
@@ -94,7 +94,7 @@ public class HistoryTreeTest {
                     maxChildren, /* Number of children */
                     1, /* Provider version */
                     1); /* Start time */
-            ht = new HistoryTreeStub(config);
+            ht = new HistoryTreeClassicStub(config);
 
         } catch (IOException e) {
             fail(e.getMessage());
@@ -107,11 +107,11 @@ public class HistoryTreeTest {
     /**
      * Setup a history tree with config MAX_CHILDREN = 3.
      */
-    private HistoryTreeStub setupSmallTree() {
+    private HistoryTreeClassicStub setupSmallTree() {
         return setupSmallTree(3);
     }
 
-    private static long fillValues(HistoryTree ht, TmfStateValue value, int nbValues, long start) {
+    private static long fillValues(IHistoryTree ht, TmfStateValue value, int nbValues, long start) {
         for (int i = 0; i < nbValues; i++) {
             ht.insertInterval(new HTInterval(start + i, start + i + 1, 1, value));
         }
@@ -130,7 +130,7 @@ public class HistoryTreeTest {
      *         greater than or equal to this to make sure the intervals go in
      *         the leaf node.
      */
-    private static long fillNextLeafNode(HistoryTreeStub ht, long leafNodeStart) {
+    private static long fillNextLeafNode(HistoryTreeClassicStub ht, long leafNodeStart) {
         int prevCount = ht.getNodeCount();
         int prevDepth = ht.getDepth();
 
@@ -156,7 +156,7 @@ public class HistoryTreeTest {
      */
     @Test
     public void testSequentialFill() {
-        HistoryTreeStub ht = setupSmallTree();
+        HistoryTreeClassicStub ht = setupSmallTree();
 
         HTNode node = ht.getLatestLeaf();
         assertEquals(0, node.getNodeUsagePercent());
@@ -197,7 +197,7 @@ public class HistoryTreeTest {
      */
     @Test
     public void testDepth() {
-        HistoryTreeStub ht = setupSmallTree();
+        HistoryTreeClassicStub ht = setupSmallTree();
 
         /* Fill a first node */
         HTNode node = ht.getLatestLeaf();
@@ -266,7 +266,7 @@ public class HistoryTreeTest {
         /* Represents the start time of the current leaf node */
         long start = 1;
 
-        HistoryTreeStub ht = setupSmallTree(2);
+        HistoryTreeClassicStub ht = setupSmallTree(2);
         start = fillNextLeafNode(ht, start);
 
         List<HTNode> branch = ht.getLatestBranch();
index 27f57ff9e5e958141ea38f8224c442ddd300e741..85ca8dcf7de3e28a7e7b8f80dabf79505e4786a8 100644 (file)
@@ -13,22 +13,28 @@ import static org.junit.Assert.fail;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.eclipse.tracecompass.common.core.NonNullUtils;
 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.internal.statesystem.core.backend.historytree.HistoryTreeBackend;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
 import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeBackendStub;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeBackendStub.HistoryTreeType;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 /**
  * Test the {@link HistoryTreeBackend}-specific behavior and its interactions
- * with the {@link HistoryTree} class
+ * with the {@link IHistoryTree} classes.
  *
  * @author Geneviève Bastien
  */
+@RunWith(Parameterized.class)
 public class HistoryTreeWithBackendTest {
 
     /** State system ID */
@@ -41,6 +47,28 @@ public class HistoryTreeWithBackendTest {
     /** Default block size */
     private static final int BLOCK_SIZE = 4096;
 
+    private final HistoryTreeType fHtType;
+
+    /**
+     * @return The arrays of parameters
+     */
+    @Parameters(name = "{0}")
+    public static Iterable<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { HistoryTreeType.CLASSIC }
+        });
+    }
+
+    /**
+     * Constructor
+     *
+     * @param htType
+     *            The type of history tree to use
+     */
+    public HistoryTreeWithBackendTest(HistoryTreeType htType) {
+        fHtType = htType;
+    }
+
     /**
      * Test the behavior of the history tree after at least a depth of 3
      */
@@ -53,6 +81,7 @@ public class HistoryTreeWithBackendTest {
 
             long startTime = 1;
             File historyTreeFile = NonNullUtils.checkNotNull(File.createTempFile("HistoryTreeBackendTest", ".ht"));
+            HistoryTreeBackendStub.setTreeType(fHtType);
             HistoryTreeBackendStub backend = new HistoryTreeBackendStub(SSID, historyTreeFile, PROVIDER_VERSION, startTime, BLOCK_SIZE, MAX_CHILDREN);
 
             int duration = nbAttr;
@@ -64,8 +93,8 @@ public class HistoryTreeWithBackendTest {
             backend.insertPastState(interval.getStartTime(), interval.getEndTime(), interval.getAttribute(), interval.getStateValue());
 
             /*
-             * insert cascading intervals to fill 2 levels of history
-             * tree, so that we start another branch
+             * insert cascading intervals to fill 2 levels of history tree, so
+             * that we start another branch
              */
             while (backend.getHistoryTree().getDepth() < depthToRead) {
                 backend.insertPastState(
index 4405a55e29ef9059df79dde302d78b4d816ce0b2..0aa13f6a5b26d0a6b936388def196b20cd199980 100644 (file)
@@ -13,17 +13,42 @@ import java.io.File;
 import java.io.IOException;
 
 import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
 import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeBackend;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
 
 /**
  * Stub class for the {@link HistoryTreeBackend}. It creates a
- * {@link HistoryTreeStub} to grant access to some protected methods.
+ * {@link HistoryTreeClassicStub} to grant access to some protected methods.
  *
  * @author Geneviève Bastien
  */
 public class HistoryTreeBackendStub extends HistoryTreeBackend {
 
+    private static HistoryTreeType HT_TYPE = HistoryTreeType.CLASSIC;
+
+    /**
+     * Sets the type of tree to build. Since the history tree is initialized in
+     * the parent's constructor, this stub class needs to know the type of tree
+     * to build.
+     *
+     * @param htType
+     *            The type of history tree to build for this backend
+     */
+    public static void setTreeType(HistoryTreeType htType) {
+        HT_TYPE = htType;
+    }
+
+    /**
+     * Enumeration of all history tree types implemented. This will be used to
+     * create the right type of history tree
+     */
+    public enum HistoryTreeType {
+        /**
+         * The classic history tree
+         */
+        CLASSIC
+    }
+
     /**
      * Constructor for new history files. Use this when creating a new history
      * from scratch.
@@ -76,13 +101,23 @@ public class HistoryTreeBackendStub extends HistoryTreeBackend {
     }
 
     @Override
-    protected HistoryTree initializeSHT(HTConfig conf) throws IOException {
-        return new HistoryTreeStub(conf);
+    protected IHistoryTree initializeSHT(HTConfig conf) throws IOException {
+        switch (HT_TYPE) {
+        case CLASSIC:
+            return new HistoryTreeClassicStub(conf);
+        default:
+            return new HistoryTreeClassicStub(conf);
+        }
     }
 
     @Override
-    protected HistoryTree initializeSHT(File existingStateFile, int providerVersion) throws IOException {
-        return new HistoryTreeStub(existingStateFile, providerVersion);
+    protected IHistoryTree initializeSHT(File existingStateFile, int providerVersion) throws IOException {
+        switch (HT_TYPE) {
+        case CLASSIC:
+            return new HistoryTreeClassicStub(existingStateFile, providerVersion);
+        default:
+            return new HistoryTreeClassicStub(existingStateFile, providerVersion);
+        }
     }
 
     /**
@@ -90,8 +125,8 @@ public class HistoryTreeBackendStub extends HistoryTreeBackend {
      *
      * @return The history tree
      */
-    public HistoryTreeStub getHistoryTree() {
-        return (HistoryTreeStub) super.getSHT();
+    public HistoryTreeClassicStub getHistoryTree() {
+        return (HistoryTreeClassicStub) super.getSHT();
     }
 
 }
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeClassicStub.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeClassicStub.java
new file mode 100644 (file)
index 0000000..b46894d
--- /dev/null
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2015 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.stubs.backend;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.util.List;
+
+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.HTNode;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeClassic;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Stub class to unit test the history tree. You can set the size of the
+ * interval section before using the tree, in order to fine-tune the test.
+ *
+ * Note to developers: This tree is not meant to be used with a backend. It just
+ * exposes some info from the history tree.
+ *
+ * @author Geneviève Bastien
+ */
+public class HistoryTreeClassicStub extends HistoryTreeClassic {
+
+    /**
+     * Minimum size a block of this tree should have
+     */
+    public static final int MINIMUM_BLOCK_SIZE = IHistoryTree.TREE_HEADER_SIZE;
+
+    /**
+     * Constructor for this history tree stub
+     *
+     * @param conf
+     *            The config to use for this History Tree.
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
+     */
+    public HistoryTreeClassicStub(HTConfig conf) throws IOException {
+        super(conf);
+    }
+
+    /**
+     * "Reader" constructor : instantiate a SHTree from an existing tree file on
+     * disk
+     *
+     * @param existingStateFile
+     *            Path/filename of the history-file we are to open
+     * @param expProviderVersion
+     *            The expected version of the state provider
+     * @throws IOException
+     *             If an error happens reading the file
+     */
+    public HistoryTreeClassicStub(File existingStateFile, int expProviderVersion) throws IOException {
+        super(existingStateFile, expProviderVersion);
+    }
+
+    @Override
+    public List<HTNode> getLatestBranch() {
+        return checkNotNull(super.getLatestBranch());
+    }
+
+    /**
+     * Get the latest leaf of the tree
+     *
+     * @return The current leaf node of the tree
+     */
+    public HTNode getLatestLeaf() {
+        List<HTNode> latest = getLatestBranch();
+        return Iterables.getLast(latest);
+    }
+
+    /**
+     * Get the node from the latest branch at a given position, 0 being the root
+     * and <size of latest branch - 1> being a leaf node.
+     *
+     * @param pos
+     *            The position at which to return the node
+     * @return The node at position pos
+     */
+    public HTNode getNodeAt(int pos) {
+        List<HTNode> latest = getLatestBranch();
+        return latest.get(pos);
+    }
+
+    /**
+     * Get the depth of the tree
+     *
+     * @return The depth of the tree
+     */
+    public int getDepth() {
+        return getLatestBranch().size();
+    }
+
+    private void assertChildrenIntegrity(CoreNode node) {
+        try {
+            /*
+             * Test that this node's start and end times match the start of the
+             * first child and the end of the last child, respectively
+             */
+            if (node.getNbChildren() > 0) {
+                HTNode childNode = getNode(node.getChild(0));
+                assertEquals("Equals start time of parent " + node.getSequenceNumber() + " and first child " + childNode.getSequenceNumber(),
+                        node.getNodeStart(), childNode.getNodeStart());
+                if (node.isOnDisk()) {
+                    childNode = getNode(node.getLatestChild());
+                    assertEquals("Equals end time of parent " + node.getSequenceNumber() + " and last child " + childNode.getSequenceNumber(),
+                            node.getNodeEnd(), childNode.getNodeEnd());
+                }
+            }
+
+            /*
+             * Test that the childStartTimes[] array matches the real nodes'
+             * start times
+             *
+             * Also test that children range is within the parent's range
+             */
+            for (int i = 0; i < node.getNbChildren(); i++) {
+                HTNode childNode = getNode(node.getChild(i));
+                assertEquals("Start time in parent " + node.getSequenceNumber() + " of child at index " + i,
+                        childNode.getNodeStart(), node.getChildStart(i));
+                assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
+                        node.getNodeStart() <= childNode.getNodeStart());
+                if (node.isOnDisk() && childNode.isOnDisk()) {
+                    assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
+                            childNode.getNodeEnd() <= childNode.getNodeEnd());
+                }
+            }
+
+        } catch (ClosedChannelException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Debugging method to make sure all intervals contained in the given node
+     * have valid start and end times.
+     *
+     * @param node
+     *            The node to check
+     */
+    private void assertNodeIntegrity(HTNode node) {
+        if (node instanceof CoreNode) {
+            assertChildrenIntegrity((CoreNode) node);
+        }
+
+        /* Check that all intervals are within the node's range */
+        // TODO: Get the intervals of a node
+
+    }
+
+    /**
+     * Check the integrity of all the nodes in the tree. Calls
+     * {@link #assertNodeIntegrity} for every node in the tree.
+     */
+    public void assertIntegrity() {
+        try {
+            for (int i = 0; i < getNodeCount(); i++) {
+                assertNodeIntegrity(getNode(i));
+            }
+        } catch (ClosedChannelException e) {
+            fail(e.getMessage());
+        }
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeStub.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/backend/HistoryTreeStub.java
deleted file mode 100644 (file)
index 9c5937a..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 École Polytechnique de Montréal
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.statesystem.core.tests.stubs.backend;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
-import java.util.List;
-
-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.HTNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
-
-import com.google.common.collect.Iterables;
-
-/**
- * Stub class to unit test the history tree. You can set the size of the
- * interval section before using the tree, in order to fine-tune the test.
- *
- * Note to developers: This tree is not meant to be used with a backend. It just
- * exposes some info from the history tree.
- *
- * @author Geneviève Bastien
- */
-public class HistoryTreeStub extends HistoryTree {
-
-    /**
-     * Constructor for this history tree stub
-     *
-     * @param conf
-     *            The config to use for this History Tree.
-     * @throws IOException
-     *             If an error happens trying to open/write to the file
-     *             specified in the config
-     */
-    public HistoryTreeStub(HTConfig conf) throws IOException {
-        super(conf);
-    }
-
-    /**
-     * "Reader" constructor : instantiate a SHTree from an existing tree file on
-     * disk
-     *
-     * @param existingStateFile
-     *            Path/filename of the history-file we are to open
-     * @param expProviderVersion
-     *            The expected version of the state provider
-     * @throws IOException
-     *             If an error happens reading the file
-     */
-    public HistoryTreeStub(File existingStateFile, int expProviderVersion) throws IOException {
-        super(existingStateFile, expProviderVersion);
-    }
-
-    @Override
-    public List<HTNode> getLatestBranch() {
-        return checkNotNull(super.getLatestBranch());
-    }
-
-    /**
-     * Get the latest leaf of the tree
-     *
-     * @return The current leaf node of the tree
-     */
-    public HTNode getLatestLeaf() {
-        List<HTNode> latest = getLatestBranch();
-        return Iterables.getLast(latest);
-    }
-
-    /**
-     * Get the node from the latest branch at a given position, 0 being the root
-     * and <size of latest branch - 1> being a leaf node.
-     *
-     * @param pos
-     *            The position at which to return the node
-     * @return The node at position pos
-     */
-    public HTNode getNodeAt(int pos) {
-        List<HTNode> latest = getLatestBranch();
-        return latest.get(pos);
-    }
-
-    /**
-     * Get the depth of the tree
-     *
-     * @return The depth of the tree
-     */
-    public int getDepth() {
-        return getLatestBranch().size();
-    }
-
-    private void assertChildrenIntegrity(CoreNode node) {
-        try {
-            /*
-             * Test that this node's start and end times match the start of the
-             * first child and the end of the last child, respectively
-             */
-            if (node.getNbChildren() > 0) {
-                HTNode childNode = getNode(node.getChild(0));
-                assertEquals("Equals start time of parent " + node.getSequenceNumber() + " and first child " + childNode.getSequenceNumber(),
-                        node.getNodeStart(), childNode.getNodeStart());
-                if (node.isOnDisk()) {
-                    childNode = getNode(node.getLatestChild());
-                    assertEquals("Equals end time of parent " + node.getSequenceNumber() + " and last child " + childNode.getSequenceNumber(),
-                            node.getNodeEnd(), childNode.getNodeEnd());
-                }
-            }
-
-            /*
-             * Test that the childStartTimes[] array matches the real nodes'
-             * start times
-             *
-             * Also test that children range is within the parent's range
-             */
-            for (int i = 0; i < node.getNbChildren(); i++) {
-                HTNode childNode = getNode(node.getChild(i));
-                assertEquals("Start time in parent " + node.getSequenceNumber() + " of child at index " + i,
-                        childNode.getNodeStart(), node.getChildStart(i));
-                assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
-                        node.getNodeStart() <= childNode.getNodeStart());
-                if (node.isOnDisk() && childNode.isOnDisk()) {
-                    assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
-                            childNode.getNodeEnd() <= childNode.getNodeEnd());
-                }
-            }
-
-        } catch (ClosedChannelException e) {
-            fail(e.getMessage());
-        }
-    }
-
-    /**
-     * Debugging method to make sure all intervals contained in the given node
-     * have valid start and end times.
-     *
-     * @param node
-     *            The node to check
-     */
-    private void assertNodeIntegrity(HTNode node) {
-        if (node instanceof CoreNode) {
-            assertChildrenIntegrity((CoreNode) node);
-        }
-
-        /* Check that all intervals are within the node's range */
-        // TODO: Get the intervals of a node
-
-    }
-
-    /**
-     * Check the integrity of all the nodes in the tree. Calls
-     * {@link #assertNodeIntegrity} for every node in the tree.
-     */
-    public void assertIntegrity() {
-        try {
-            for (int i = 0; i < getNodeCount(); i++) {
-                assertNodeIntegrity(getNode(i));
-            }
-        } catch (ClosedChannelException e) {
-            fail(e.getMessage());
-        }
-    }
-
-}
index 3340254b829e3fabd306ecf28e5205164fb52432..597df81238390f55e61e99f4759789213dd97a54 100644 (file)
@@ -15,7 +15,7 @@ package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
 import java.io.File;
 
 /**
- * Configuration object for the {@link HistoryTree}.
+ * Configuration object for the {@link IHistoryTree}.
  *
  * @author Alexandre Montplaisir
  */
index ef04a424e4de3aff0c9be4eee2bf1f9e584f11d5..c69c51b6e18e97f63f71f39f957021cce83d9a43 100644 (file)
@@ -270,7 +270,7 @@ class HT_IO {
          * Cast to (long) is needed to make sure the result is a long too and
          * doesn't get truncated
          */
-        fc.position(HistoryTree.TREE_HEADER_SIZE
+        fc.position(IHistoryTree.TREE_HEADER_SIZE
                 + ((long) seqNumber) * fConfig.getBlockSize());
     }
 
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTree.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTree.java
deleted file mode 100644 (file)
index d675866..0000000
+++ /dev/null
@@ -1,800 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
- *
- * All rights reserved. This program and the accompanying materials are
- * made available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *   Alexandre Montplaisir - Initial API and implementation
- *   Florian Wininger - Add Extension and Leaf Node
- *   Patrick Tasse - Add message to exceptions
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.internal.statesystem.core.Activator;
-import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
-import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Meta-container for the History Tree. This structure contains all the
- * high-level data relevant to the tree.
- *
- * @author Alexandre Montplaisir
- */
-public class HistoryTree {
-
-    /**
-     * Size of the "tree header" in the tree-file The nodes will use this offset
-     * to know where they should be in the file. This should always be a
-     * multiple of 4K.
-     */
-    public static final int TREE_HEADER_SIZE = 4096;
-
-    /**
-     * The magic number for this file format. Package-private for the factory
-     * class.
-     */
-    static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
-
-    /** File format version. Increment when breaking compatibility. */
-    private static final int FILE_VERSION = 7;
-
-    // ------------------------------------------------------------------------
-    // Tree-specific configuration
-    // ------------------------------------------------------------------------
-
-    /** Container for all the configuration constants */
-    private final HTConfig fConfig;
-
-    /** Reader/writer object */
-    private final HT_IO fTreeIO;
-
-    // ------------------------------------------------------------------------
-    // Variable Fields (will change throughout the existence of the SHT)
-    // ------------------------------------------------------------------------
-
-    /** Latest timestamp found in the tree (at any given moment) */
-    private long fTreeEnd;
-
-    /** The total number of nodes that exists in this tree */
-    private int fNodeCount;
-
-    /** "Cache" to keep the active nodes in memory */
-    private final @NonNull List<@NonNull HTNode> fLatestBranch;
-
-    // ------------------------------------------------------------------------
-    // Constructors/"Destructors"
-    // ------------------------------------------------------------------------
-
-    /**
-     * Create a new State History from scratch, using a {@link HTConfig} object
-     * for configuration.
-     *
-     * @param conf
-     *            The config to use for this History Tree.
-     * @throws IOException
-     *             If an error happens trying to open/write to the file
-     *             specified in the config
-     */
-    public HistoryTree(HTConfig conf) throws IOException {
-        /*
-         * Simple check to make sure we have enough place in the 0th block for
-         * the tree configuration
-         */
-        if (conf.getBlockSize() < TREE_HEADER_SIZE) {
-            throw new IllegalArgumentException();
-        }
-
-        fConfig = conf;
-        fTreeEnd = conf.getTreeStart();
-        fNodeCount = 0;
-        fLatestBranch = Collections.synchronizedList(new ArrayList<>());
-
-        /* Prepare the IO object */
-        fTreeIO = new HT_IO(fConfig, true);
-
-        /* Add the first node to the tree */
-        LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
-        fLatestBranch.add(firstNode);
-    }
-
-    /**
-     * "Reader" constructor : instantiate a SHTree from an existing tree file on
-     * disk
-     *
-     * @param existingStateFile
-     *            Path/filename of the history-file we are to open
-     * @param expProviderVersion
-     *            The expected version of the state provider
-     * @throws IOException
-     *             If an error happens reading the file
-     */
-    public HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
-        /*
-         * Open the file ourselves, get the tree header information we need,
-         * then pass on the descriptor to the TreeIO object.
-         */
-        int rootNodeSeqNb, res;
-        int bs, maxc;
-        long startTime;
-
-        /* Java I/O mumbo jumbo... */
-        if (!existingStateFile.exists()) {
-            throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
-        }
-        if (existingStateFile.length() <= 0) {
-            throw new IOException("Empty target file"); //$NON-NLS-1$
-        }
-
-        try (FileInputStream fis = new FileInputStream(existingStateFile);
-                FileChannel fc = fis.getChannel();) {
-
-            ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
-
-            buffer.order(ByteOrder.LITTLE_ENDIAN);
-            buffer.clear();
-            fc.read(buffer);
-            buffer.flip();
-
-            /*
-             * Check the magic number to make sure we're opening the right type
-             * of file
-             */
-            res = buffer.getInt();
-            if (res != HISTORY_FILE_MAGIC_NUMBER) {
-                throw new IOException("Wrong magic number"); //$NON-NLS-1$
-            }
-
-            res = buffer.getInt(); /* File format version number */
-            if (res != FILE_VERSION) {
-                throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
-            }
-
-            res = buffer.getInt(); /* Event handler's version number */
-            if (res != expProviderVersion &&
-                    expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
-                /*
-                 * The existing history was built using an event handler that
-                 * doesn't match the current one in the framework.
-                 *
-                 * Information could be all wrong. Instead of keeping an
-                 * incorrect history file, a rebuild is done.
-                 */
-                throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
-            }
-
-            bs = buffer.getInt(); /* Block Size */
-            maxc = buffer.getInt(); /* Max nb of children per node */
-
-            fNodeCount = buffer.getInt();
-            rootNodeSeqNb = buffer.getInt();
-            startTime = buffer.getLong();
-
-            fConfig = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
-        }
-
-        /*
-         * FIXME We close fis here and the TreeIO will then reopen the same
-         * file, not extremely elegant. But how to pass the information here to
-         * the SHT otherwise?
-         */
-        fTreeIO = new HT_IO(fConfig, false);
-
-        fLatestBranch = buildLatestBranch(rootNodeSeqNb);
-        fTreeEnd = getRootNode().getNodeEnd();
-
-        /*
-         * Make sure the history start time we read previously is consistent
-         * with was is actually in the root node.
-         */
-        if (startTime != getRootNode().getNodeStart()) {
-            throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
-                    "history file, it might be corrupted."); //$NON-NLS-1$
-        }
-    }
-
-    /**
-     * Rebuild the latestBranch "cache" object by reading the nodes from disk
-     * (When we are opening an existing file on disk and want to append to it,
-     * for example).
-     *
-     * @param rootNodeSeqNb
-     *            The sequence number of the root node, so we know where to
-     *            start
-     * @throws ClosedChannelException
-     */
-    private @NonNull List<@NonNull HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
-        List<@NonNull HTNode> list = new ArrayList<>();
-
-        HTNode nextChildNode = fTreeIO.readNode(rootNodeSeqNb);
-        list.add(nextChildNode);
-
-        /* Follow the last branch up to the leaf */
-        while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
-            nextChildNode = fTreeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
-            list.add(nextChildNode);
-        }
-        return Collections.synchronizedList(list);
-    }
-
-    /**
-     * "Save" the tree to disk. This method will cause the treeIO object to
-     * commit all nodes to disk and then return the RandomAccessFile descriptor
-     * so the Tree object can save its configuration into the header of the
-     * file.
-     *
-     * @param requestedEndTime
-     *            The greatest timestamp present in the history tree
-     */
-    public void closeTree(long requestedEndTime) {
-        /* This is an important operation, queries can wait */
-        synchronized (fLatestBranch) {
-            /*
-             * Work-around the "empty branches" that get created when the root
-             * node becomes full. Overwrite the tree's end time with the
-             * original wanted end-time, to ensure no queries are sent into
-             * those empty nodes.
-             *
-             * This won't be needed once extended nodes are implemented.
-             */
-            fTreeEnd = requestedEndTime;
-
-            /* Close off the latest branch of the tree */
-            for (int i = 0; i < fLatestBranch.size(); i++) {
-                fLatestBranch.get(i).closeThisNode(fTreeEnd);
-                fTreeIO.writeNode(fLatestBranch.get(i));
-            }
-
-            try (FileChannel fc = fTreeIO.getFcOut();) {
-                ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
-                buffer.order(ByteOrder.LITTLE_ENDIAN);
-                buffer.clear();
-
-                /* Save the config of the tree to the header of the file */
-                fc.position(0);
-
-                buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
-
-                buffer.putInt(FILE_VERSION);
-                buffer.putInt(fConfig.getProviderVersion());
-
-                buffer.putInt(fConfig.getBlockSize());
-                buffer.putInt(fConfig.getMaxChildren());
-
-                buffer.putInt(fNodeCount);
-
-                /* root node seq. nb */
-                buffer.putInt(fLatestBranch.get(0).getSequenceNumber());
-
-                /* start time of this history */
-                buffer.putLong(fLatestBranch.get(0).getNodeStart());
-
-                buffer.flip();
-                int res = fc.write(buffer);
-                assert (res <= TREE_HEADER_SIZE);
-                /* done writing the file header */
-
-            } catch (IOException e) {
-                /*
-                 * If we were able to write so far, there should not be any
-                 * problem at this point...
-                 */
-                throw new RuntimeException("State system write error"); //$NON-NLS-1$
-            }
-        }
-    }
-
-    // ------------------------------------------------------------------------
-    // Accessors
-    // ------------------------------------------------------------------------
-
-    /**
-     * Get the start time of this tree.
-     *
-     * @return The start time
-     */
-    public long getTreeStart() {
-        return fConfig.getTreeStart();
-    }
-
-    /**
-     * Get the current end time of this tree.
-     *
-     * @return The end time
-     */
-    public long getTreeEnd() {
-        return fTreeEnd;
-    }
-
-    /**
-     * Get the number of nodes in this tree.
-     *
-     * @return The number of nodes
-     */
-    public int getNodeCount() {
-        return fNodeCount;
-    }
-
-    /**
-     * Get the current root node of this tree
-     *
-     * @return The root node
-     */
-    public HTNode getRootNode() {
-        return fLatestBranch.get(0);
-    }
-
-    /**
-     * Return the latest branch of the tree. That branch is immutable. Used for
-     * unit testing and debugging.
-     *
-     * @return The immutable latest branch
-     */
-    @VisibleForTesting
-    protected List<@NonNull HTNode> getLatestBranch() {
-        return ImmutableList.copyOf(fLatestBranch);
-    }
-
-    /**
-     * Read a node at sequence number
-     *
-     * @param seqNum
-     *            The sequence number of the node to read
-     * @return The HTNode object
-     * @throws ClosedChannelException
-     *             Exception thrown when reading the node
-     */
-    @VisibleForTesting
-    protected @NonNull HTNode getNode(int seqNum) throws ClosedChannelException {
-        // First, check in the latest branch if the node is there
-        for (HTNode node : fLatestBranch) {
-            if (node.getSequenceNumber() == seqNum) {
-                return node;
-            }
-        }
-        return fTreeIO.readNode(seqNum);
-    }
-
-    // ------------------------------------------------------------------------
-    // HT_IO interface
-    // ------------------------------------------------------------------------
-
-    /**
-     * Return the FileInputStream reader with which we will read an attribute
-     * tree (it will be sought to the correct position).
-     *
-     * @return The FileInputStream indicating the file and position from which
-     *         the attribute tree can be read.
-     */
-    public FileInputStream supplyATReader() {
-        return fTreeIO.supplyATReader(getNodeCount());
-    }
-
-    /**
-     * Return the file to which we will write the attribute tree.
-     *
-     * @return The file to which we will write the attribute tree
-     */
-    public File supplyATWriterFile() {
-        return fConfig.getStateFile();
-    }
-
-    /**
-     * Return the position in the file (given by {@link #supplyATWriterFile})
-     * where to start writing the attribute tree.
-     *
-     * @return The position in the file where to start writing
-     */
-    public long supplyATWriterFilePos() {
-        return HistoryTree.TREE_HEADER_SIZE
-                + ((long) getNodeCount() * fConfig.getBlockSize());
-    }
-
-    /**
-     * Read a node from the tree.
-     *
-     * @param seqNumber
-     *            The sequence number of the node to read
-     * @return The node
-     * @throws ClosedChannelException
-     *             If the tree IO is unavailable
-     */
-    public HTNode readNode(int seqNumber) throws ClosedChannelException {
-        /* Try to read the node from memory */
-        synchronized (fLatestBranch) {
-            for (HTNode node : fLatestBranch) {
-                if (node.getSequenceNumber() == seqNumber) {
-                    return node;
-                }
-            }
-        }
-
-        /* Read the node from disk */
-        return fTreeIO.readNode(seqNumber);
-    }
-
-    /**
-     * Write a node object to the history file.
-     *
-     * @param node
-     *            The node to write to disk
-     */
-    public void writeNode(HTNode node) {
-        fTreeIO.writeNode(node);
-    }
-
-    /**
-     * Close the history file.
-     */
-    public void closeFile() {
-        fTreeIO.closeFile();
-    }
-
-    /**
-     * Delete the history file.
-     */
-    public void deleteFile() {
-        fTreeIO.deleteFile();
-    }
-
-    // ------------------------------------------------------------------------
-    // Operations
-    // ------------------------------------------------------------------------
-
-    /**
-     * Insert an interval in the tree.
-     *
-     * @param interval
-     *            The interval to be inserted
-     * @throws TimeRangeException
-     *             If the start of end time of the interval are invalid
-     */
-    public void insertInterval(HTInterval interval) throws TimeRangeException {
-        if (interval.getStartTime() < fConfig.getTreeStart()) {
-            throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + fConfig.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-        tryInsertAtNode(interval, fLatestBranch.size() - 1);
-    }
-
-    /**
-     * Inner method to find in which node we should add the interval.
-     *
-     * @param interval
-     *            The interval to add to the tree
-     * @param indexOfNode
-     *            The index *in the latestBranch* where we are trying the
-     *            insertion
-     */
-    private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
-        HTNode targetNode = fLatestBranch.get(indexOfNode);
-
-        /* Verify if there is enough room in this node to store this interval */
-        if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
-            /* Nope, not enough room. Insert in a new sibling instead. */
-            addSiblingNode(indexOfNode);
-            tryInsertAtNode(interval, fLatestBranch.size() - 1);
-            return;
-        }
-
-        /* Make sure the interval time range fits this node */
-        if (interval.getStartTime() < targetNode.getNodeStart()) {
-            /*
-             * No, this interval starts before the startTime of this node. We
-             * need to check recursively in parents if it can fit.
-             */
-            tryInsertAtNode(interval, indexOfNode - 1);
-            return;
-        }
-
-        /*
-         * Ok, there is room, and the interval fits in this time slot. Let's add
-         * it.
-         */
-        targetNode.addInterval(interval);
-
-        /* Update treeEnd if needed */
-        if (interval.getEndTime() > fTreeEnd) {
-            fTreeEnd = interval.getEndTime();
-        }
-    }
-
-    /**
-     * Method to add a sibling to any node in the latest branch. This will add
-     * children back down to the leaf level, if needed.
-     *
-     * @param indexOfNode
-     *            The index in latestBranch where we start adding
-     */
-    private void addSiblingNode(int indexOfNode) {
-        synchronized (fLatestBranch) {
-            final long splitTime = fTreeEnd;
-
-            if (indexOfNode >= fLatestBranch.size()) {
-                /*
-                 * We need to make sure (indexOfNode - 1) doesn't get the last
-                 * node in the branch, because that one is a Leaf Node.
-                 */
-                throw new IllegalStateException();
-            }
-
-            /* Check if we need to add a new root node */
-            if (indexOfNode == 0) {
-                addNewRootNode();
-                return;
-            }
-
-            /* Check if we can indeed add a child to the target parent */
-            if (((CoreNode) fLatestBranch.get(indexOfNode - 1)).getNbChildren() == fConfig.getMaxChildren()) {
-                /* If not, add a branch starting one level higher instead */
-                addSiblingNode(indexOfNode - 1);
-                return;
-            }
-
-            /* Split off the new branch from the old one */
-            for (int i = indexOfNode; i < fLatestBranch.size(); i++) {
-                fLatestBranch.get(i).closeThisNode(splitTime);
-                fTreeIO.writeNode(fLatestBranch.get(i));
-
-                CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
-                HTNode newNode;
-
-                switch (fLatestBranch.get(i).getNodeType()) {
-                case CORE:
-                    newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
-                    break;
-                case LEAF:
-                    newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
-                    break;
-                default:
-                    throw new IllegalStateException();
-                }
-
-                prevNode.linkNewChild(newNode);
-                fLatestBranch.set(i, newNode);
-            }
-        }
-    }
-
-    /**
-     * Similar to the previous method, except here we rebuild a completely new
-     * latestBranch
-     */
-    private void addNewRootNode() {
-        final long splitTime = fTreeEnd;
-
-        HTNode oldRootNode = fLatestBranch.get(0);
-        CoreNode newRootNode = initNewCoreNode(-1, fConfig.getTreeStart());
-
-        /* Tell the old root node that it isn't root anymore */
-        oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
-
-        /* Close off the whole current latestBranch */
-
-        for (int i = 0; i < fLatestBranch.size(); i++) {
-            fLatestBranch.get(i).closeThisNode(splitTime);
-            fTreeIO.writeNode(fLatestBranch.get(i));
-        }
-
-        /* Link the new root to its first child (the previous root node) */
-        newRootNode.linkNewChild(oldRootNode);
-
-        /* Rebuild a new latestBranch */
-        int depth = fLatestBranch.size();
-        fLatestBranch.clear();
-        fLatestBranch.add(newRootNode);
-
-        // Create new coreNode
-        for (int i = 1; i < depth; i++) {
-            CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
-            CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
-                    splitTime + 1);
-            prevNode.linkNewChild(newNode);
-            fLatestBranch.add(newNode);
-        }
-
-        // Create the new leafNode
-        CoreNode prevNode = (CoreNode) fLatestBranch.get(depth - 1);
-        LeafNode newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
-        prevNode.linkNewChild(newNode);
-        fLatestBranch.add(newNode);
-    }
-
-    /**
-     * Add a new empty core node to the tree.
-     *
-     * @param parentSeqNumber
-     *            Sequence number of this node's parent
-     * @param startTime
-     *            Start time of the new node
-     * @return The newly created node
-     */
-    private @NonNull CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
-        CoreNode newNode = new CoreNode(fConfig, fNodeCount, parentSeqNumber,
-                startTime);
-        fNodeCount++;
-        return newNode;
-    }
-
-    /**
-     * Add a new empty leaf node to the tree.
-     *
-     * @param parentSeqNumber
-     *            Sequence number of this node's parent
-     * @param startTime
-     *            Start time of the new node
-     * @return The newly created node
-     */
-    private @NonNull LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
-        LeafNode newNode = new LeafNode(fConfig, fNodeCount, parentSeqNumber,
-                startTime);
-        fNodeCount++;
-        return newNode;
-    }
-
-    /**
-     * Inner method to select the next child of the current node intersecting
-     * the given timestamp. Useful for moving down the tree following one
-     * branch.
-     *
-     * @param currentNode
-     *            The node on which the request is made
-     * @param t
-     *            The timestamp to choose which child is the next one
-     * @return The child node intersecting t
-     * @throws ClosedChannelException
-     *             If the file channel was closed while we were reading the tree
-     */
-    public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
-        assert (currentNode.getNbChildren() > 0);
-        int potentialNextSeqNb = currentNode.getSequenceNumber();
-
-        for (int i = 0; i < currentNode.getNbChildren(); i++) {
-            if (t >= currentNode.getChildStart(i)) {
-                potentialNextSeqNb = currentNode.getChild(i);
-            } else {
-                break;
-            }
-        }
-
-        /*
-         * Once we exit this loop, we should have found a children to follow. If
-         * we didn't, there's a problem.
-         */
-        if (potentialNextSeqNb == currentNode.getSequenceNumber()) {
-            throw new IllegalStateException("No next child node found"); //$NON-NLS-1$
-        }
-
-        /*
-         * Since this code path is quite performance-critical, avoid iterating
-         * through the whole latestBranch array if we know for sure the next
-         * node has to be on disk
-         */
-        if (currentNode.isOnDisk()) {
-            return fTreeIO.readNode(potentialNextSeqNb);
-        }
-        return readNode(potentialNextSeqNb);
-    }
-
-    /**
-     * Get the current size of the history file.
-     *
-     * @return The history file size
-     */
-    public long getFileSize() {
-        return fConfig.getStateFile().length();
-    }
-
-    // ------------------------------------------------------------------------
-    // Test/debugging methods
-    // ------------------------------------------------------------------------
-
-    /* Only used for debugging, shouldn't be externalized */
-    @SuppressWarnings("nls")
-    @Override
-    public String toString() {
-        return "Information on the current tree:\n\n" + "Blocksize: "
-                + fConfig.getBlockSize() + "\n" + "Max nb. of children per node: "
-                + fConfig.getMaxChildren() + "\n" + "Number of nodes: " + fNodeCount
-                + "\n" + "Depth of the tree: " + fLatestBranch.size() + "\n"
-                + "Size of the treefile: " + getFileSize() + "\n"
-                + "Root node has sequence number: "
-                + fLatestBranch.get(0).getSequenceNumber() + "\n"
-                + "'Latest leaf' has sequence number: "
-                + fLatestBranch.get(fLatestBranch.size() - 1).getSequenceNumber();
-    }
-
-    /**
-     * Start at currentNode and print the contents of all its children, in
-     * pre-order. Give the root node in parameter to visit the whole tree, and
-     * have a nice overview.
-     */
-    /* Only used for debugging, shouldn't be externalized */
-    @SuppressWarnings("nls")
-    private void preOrderPrint(PrintWriter writer, boolean printIntervals,
-            HTNode currentNode, int curDepth, long ts) {
-
-        writer.println(currentNode.toString());
-        /* Print intervals only if timestamp is negative or within the range of the node */
-        if (printIntervals &&
-                (ts <= 0 ||
-                (ts >= currentNode.getNodeStart() && ts <= currentNode.getNodeEnd()))) {
-            currentNode.debugPrintIntervals(writer);
-        }
-
-        switch (currentNode.getNodeType()) {
-        case LEAF:
-            /* Stop if it's the leaf node */
-            return;
-
-        case CORE:
-            try {
-                final CoreNode node = (CoreNode) currentNode;
-                /* Print the extensions, if any */
-                int extension = node.getExtensionSequenceNumber();
-                while (extension != -1) {
-                    HTNode nextNode = fTreeIO.readNode(extension);
-                    preOrderPrint(writer, printIntervals, nextNode, curDepth, ts);
-                }
-
-                /* Print the child nodes */
-                for (int i = 0; i < node.getNbChildren(); i++) {
-                    HTNode nextNode = fTreeIO.readNode(node.getChild(i));
-                    for (int j = 0; j < curDepth; j++) {
-                        writer.print("  ");
-                    }
-                    writer.print("+-");
-                    preOrderPrint(writer, printIntervals, nextNode, curDepth + 1, ts);
-                }
-            } catch (ClosedChannelException e) {
-                Activator.getDefault().logError(e.getMessage());
-            }
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    /**
-     * Print out the full tree for debugging purposes
-     *
-     * @param writer
-     *            PrintWriter in which to write the output
-     * @param printIntervals
-     *            Flag to enable full output of the interval information
-     * @param ts
-     *            The timestamp that nodes have to intersect for intervals to be
-     *            printed. A negative value will print intervals for all nodes.
-     *            The timestamp only applies if printIntervals is true.
-     */
-    public void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts) {
-        /* Only used for debugging, shouldn't be externalized */
-
-        preOrderPrint(writer, false, fLatestBranch.get(0), 0, ts);
-
-        if (printIntervals) {
-            preOrderPrint(writer, true, fLatestBranch.get(0), 0, ts);
-        }
-        writer.println('\n');
-    }
-
-}
index 75cea1a518c0d58b7bffe30d5a2808572603d73c..344c3b0f11c7cce3d034f2973a9d2195d555c0d1 100644 (file)
@@ -46,7 +46,7 @@ public class HistoryTreeBackend implements IStateHistoryBackend {
     /**
      * The history tree that sits underneath.
      */
-    private final @NonNull HistoryTree fSht;
+    private final @NonNull IHistoryTree fSht;
 
     /** Indicates if the history tree construction is done */
     private volatile boolean fFinishedBuilding = false;
@@ -162,7 +162,7 @@ public class HistoryTreeBackend implements IStateHistoryBackend {
      *             If there was a problem during creation
      */
     @VisibleForTesting
-    protected @NonNull HistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
+    protected @NonNull IHistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
         return HistoryTreeFactory.createHistoryTree(conf);
     }
 
@@ -179,7 +179,7 @@ public class HistoryTreeBackend implements IStateHistoryBackend {
      *             If there was a problem during creation
      */
     @VisibleForTesting
-    protected @NonNull HistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
+    protected @NonNull IHistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
         return HistoryTreeFactory.createFromFile(existingStateFile.toPath(), providerVersion);
     }
 
@@ -192,7 +192,7 @@ public class HistoryTreeBackend implements IStateHistoryBackend {
      *
      * @return The history tree
      */
-    protected final @NonNull HistoryTree getSHT() {
+    protected final @NonNull IHistoryTree getSHT() {
         return fSht;
     }
 
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTreeClassic.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HistoryTreeClassic.java
new file mode 100644 (file)
index 0000000..8bf229d
--- /dev/null
@@ -0,0 +1,703 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Montplaisir - Initial API and implementation
+ *   Florian Wininger - Add Extension and Leaf Node
+ *   Patrick Tasse - Add message to exceptions
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.statesystem.core.Activator;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Meta-container for the History Tree. This structure contains all the
+ * high-level data relevant to the tree.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class HistoryTreeClassic implements IHistoryTree {
+
+    /**
+     * The magic number for this file format. Package-private for the factory
+     * class.
+     */
+    static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
+
+    /** File format version. Increment when breaking compatibility. */
+    private static final int FILE_VERSION = 7;
+
+    // ------------------------------------------------------------------------
+    // Tree-specific configuration
+    // ------------------------------------------------------------------------
+
+    /** Container for all the configuration constants */
+    private final HTConfig fConfig;
+
+    /** Reader/writer object */
+    private final HT_IO fTreeIO;
+
+    // ------------------------------------------------------------------------
+    // Variable Fields (will change throughout the existence of the SHT)
+    // ------------------------------------------------------------------------
+
+    /** Latest timestamp found in the tree (at any given moment) */
+    private long fTreeEnd;
+
+    /** The total number of nodes that exists in this tree */
+    private int fNodeCount;
+
+    /** "Cache" to keep the active nodes in memory */
+    private final @NonNull List<@NonNull HTNode> fLatestBranch;
+
+    // ------------------------------------------------------------------------
+    // Constructors/"Destructors"
+    // ------------------------------------------------------------------------
+
+    /**
+     * Create a new State History from scratch, using a {@link HTConfig} object
+     * for configuration.
+     *
+     * @param conf
+     *            The config to use for this History Tree.
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
+     */
+    public HistoryTreeClassic(HTConfig conf) throws IOException {
+        /*
+         * Simple check to make sure we have enough place in the 0th block for
+         * the tree configuration
+         */
+        if (conf.getBlockSize() < TREE_HEADER_SIZE) {
+            throw new IllegalArgumentException();
+        }
+
+        fConfig = conf;
+        fTreeEnd = conf.getTreeStart();
+        fNodeCount = 0;
+        fLatestBranch = Collections.synchronizedList(new ArrayList<>());
+
+        /* Prepare the IO object */
+        fTreeIO = new HT_IO(fConfig, true);
+
+        /* Add the first node to the tree */
+        LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
+        fLatestBranch.add(firstNode);
+    }
+
+    /**
+     * "Reader" constructor : instantiate a SHTree from an existing tree file on
+     * disk
+     *
+     * @param existingStateFile
+     *            Path/filename of the history-file we are to open
+     * @param expProviderVersion
+     *            The expected version of the state provider
+     * @throws IOException
+     *             If an error happens reading the file
+     */
+    public HistoryTreeClassic(File existingStateFile, int expProviderVersion) throws IOException {
+        /*
+         * Open the file ourselves, get the tree header information we need,
+         * then pass on the descriptor to the TreeIO object.
+         */
+        int rootNodeSeqNb, res;
+        int bs, maxc;
+        long startTime;
+
+        /* Java I/O mumbo jumbo... */
+        if (!existingStateFile.exists()) {
+            throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
+        }
+        if (existingStateFile.length() <= 0) {
+            throw new IOException("Empty target file"); //$NON-NLS-1$
+        }
+
+        try (FileInputStream fis = new FileInputStream(existingStateFile);
+                FileChannel fc = fis.getChannel();) {
+
+            ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
+
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+            buffer.clear();
+            fc.read(buffer);
+            buffer.flip();
+
+            /*
+             * Check the magic number to make sure we're opening the right type
+             * of file
+             */
+            res = buffer.getInt();
+            if (res != HISTORY_FILE_MAGIC_NUMBER) {
+                throw new IOException("Wrong magic number"); //$NON-NLS-1$
+            }
+
+            res = buffer.getInt(); /* File format version number */
+            if (res != FILE_VERSION) {
+                throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
+            }
+
+            res = buffer.getInt(); /* Event handler's version number */
+            if (res != expProviderVersion &&
+                    expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
+                /*
+                 * The existing history was built using an event handler that
+                 * doesn't match the current one in the framework.
+                 *
+                 * Information could be all wrong. Instead of keeping an
+                 * incorrect history file, a rebuild is done.
+                 */
+                throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
+            }
+
+            bs = buffer.getInt(); /* Block Size */
+            maxc = buffer.getInt(); /* Max nb of children per node */
+
+            fNodeCount = buffer.getInt();
+            rootNodeSeqNb = buffer.getInt();
+            startTime = buffer.getLong();
+
+            fConfig = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
+        }
+
+        /*
+         * FIXME We close fis here and the TreeIO will then reopen the same
+         * file, not extremely elegant. But how to pass the information here to
+         * the SHT otherwise?
+         */
+        fTreeIO = new HT_IO(fConfig, false);
+
+        fLatestBranch = buildLatestBranch(rootNodeSeqNb);
+        fTreeEnd = getRootNode().getNodeEnd();
+
+        /*
+         * Make sure the history start time we read previously is consistent
+         * with was is actually in the root node.
+         */
+        if (startTime != getRootNode().getNodeStart()) {
+            throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
+                    "history file, it might be corrupted."); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Rebuild the latestBranch "cache" object by reading the nodes from disk
+     * (When we are opening an existing file on disk and want to append to it,
+     * for example).
+     *
+     * @param rootNodeSeqNb
+     *            The sequence number of the root node, so we know where to
+     *            start
+     * @throws ClosedChannelException
+     */
+    private @NonNull List<@NonNull HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
+        List<@NonNull HTNode> list = new ArrayList<>();
+
+        HTNode nextChildNode = fTreeIO.readNode(rootNodeSeqNb);
+        list.add(nextChildNode);
+
+        /* Follow the last branch up to the leaf */
+        while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
+            nextChildNode = fTreeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
+            list.add(nextChildNode);
+        }
+        return Collections.synchronizedList(list);
+    }
+
+    @Override
+    public void closeTree(long requestedEndTime) {
+        /* This is an important operation, queries can wait */
+        synchronized (fLatestBranch) {
+            /*
+             * Work-around the "empty branches" that get created when the root
+             * node becomes full. Overwrite the tree's end time with the
+             * original wanted end-time, to ensure no queries are sent into
+             * those empty nodes.
+             *
+             * This won't be needed once extended nodes are implemented.
+             */
+            fTreeEnd = requestedEndTime;
+
+            /* Close off the latest branch of the tree */
+            for (int i = 0; i < fLatestBranch.size(); i++) {
+                fLatestBranch.get(i).closeThisNode(fTreeEnd);
+                fTreeIO.writeNode(fLatestBranch.get(i));
+            }
+
+            try (FileChannel fc = fTreeIO.getFcOut();) {
+                ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
+                buffer.order(ByteOrder.LITTLE_ENDIAN);
+                buffer.clear();
+
+                /* Save the config of the tree to the header of the file */
+                fc.position(0);
+
+                buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
+
+                buffer.putInt(FILE_VERSION);
+                buffer.putInt(fConfig.getProviderVersion());
+
+                buffer.putInt(fConfig.getBlockSize());
+                buffer.putInt(fConfig.getMaxChildren());
+
+                buffer.putInt(fNodeCount);
+
+                /* root node seq. nb */
+                buffer.putInt(fLatestBranch.get(0).getSequenceNumber());
+
+                /* start time of this history */
+                buffer.putLong(fLatestBranch.get(0).getNodeStart());
+
+                buffer.flip();
+                int res = fc.write(buffer);
+                assert (res <= TREE_HEADER_SIZE);
+                /* done writing the file header */
+
+            } catch (IOException e) {
+                /*
+                 * If we were able to write so far, there should not be any
+                 * problem at this point...
+                 */
+                throw new RuntimeException("State system write error"); //$NON-NLS-1$
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    @Override
+    public long getTreeStart() {
+        return fConfig.getTreeStart();
+    }
+
+    @Override
+    public long getTreeEnd() {
+        return fTreeEnd;
+    }
+
+    @Override
+    public int getNodeCount() {
+        return fNodeCount;
+    }
+
+    @Override
+    public HTNode getRootNode() {
+        return fLatestBranch.get(0);
+    }
+
+    /**
+     * Return the latest branch of the tree. That branch is immutable. Used for
+     * unit testing and debugging.
+     *
+     * @return The immutable latest branch
+     */
+    @VisibleForTesting
+    protected List<@NonNull HTNode> getLatestBranch() {
+        return ImmutableList.copyOf(fLatestBranch);
+    }
+
+    /**
+     * Read a node at sequence number
+     *
+     * @param seqNum
+     *            The sequence number of the node to read
+     * @return The HTNode object
+     * @throws ClosedChannelException
+     *             Exception thrown when reading the node
+     */
+    @VisibleForTesting
+    protected @NonNull HTNode getNode(int seqNum) throws ClosedChannelException {
+        // First, check in the latest branch if the node is there
+        for (HTNode node : fLatestBranch) {
+            if (node.getSequenceNumber() == seqNum) {
+                return node;
+            }
+        }
+        return fTreeIO.readNode(seqNum);
+    }
+
+    // ------------------------------------------------------------------------
+    // HT_IO interface
+    // ------------------------------------------------------------------------
+
+    @Override
+    public FileInputStream supplyATReader() {
+        return fTreeIO.supplyATReader(getNodeCount());
+    }
+
+    @Override
+    public File supplyATWriterFile() {
+        return fConfig.getStateFile();
+    }
+
+    @Override
+    public long supplyATWriterFilePos() {
+        return IHistoryTree.TREE_HEADER_SIZE
+                + ((long) getNodeCount() * fConfig.getBlockSize());
+    }
+
+    @Override
+    public HTNode readNode(int seqNumber) throws ClosedChannelException {
+        /* Try to read the node from memory */
+        synchronized (fLatestBranch) {
+            for (HTNode node : fLatestBranch) {
+                if (node.getSequenceNumber() == seqNumber) {
+                    return node;
+                }
+            }
+        }
+
+        /* Read the node from disk */
+        return fTreeIO.readNode(seqNumber);
+    }
+
+    @Override
+    public void writeNode(HTNode node) {
+        fTreeIO.writeNode(node);
+    }
+
+    @Override
+    public void closeFile() {
+        fTreeIO.closeFile();
+    }
+
+    @Override
+    public void deleteFile() {
+        fTreeIO.deleteFile();
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void insertInterval(HTInterval interval) throws TimeRangeException {
+        if (interval.getStartTime() < fConfig.getTreeStart()) {
+            throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + fConfig.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        tryInsertAtNode(interval, fLatestBranch.size() - 1);
+    }
+
+    /**
+     * Inner method to find in which node we should add the interval.
+     *
+     * @param interval
+     *            The interval to add to the tree
+     * @param indexOfNode
+     *            The index *in the latestBranch* where we are trying the
+     *            insertion
+     */
+    private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
+        HTNode targetNode = fLatestBranch.get(indexOfNode);
+
+        /* Verify if there is enough room in this node to store this interval */
+        if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
+            /* Nope, not enough room. Insert in a new sibling instead. */
+            addSiblingNode(indexOfNode);
+            tryInsertAtNode(interval, fLatestBranch.size() - 1);
+            return;
+        }
+
+        /* Make sure the interval time range fits this node */
+        if (interval.getStartTime() < targetNode.getNodeStart()) {
+            /*
+             * No, this interval starts before the startTime of this node. We
+             * need to check recursively in parents if it can fit.
+             */
+            tryInsertAtNode(interval, indexOfNode - 1);
+            return;
+        }
+
+        /*
+         * Ok, there is room, and the interval fits in this time slot. Let's add
+         * it.
+         */
+        targetNode.addInterval(interval);
+
+        /* Update treeEnd if needed */
+        if (interval.getEndTime() > fTreeEnd) {
+            fTreeEnd = interval.getEndTime();
+        }
+    }
+
+    /**
+     * Method to add a sibling to any node in the latest branch. This will add
+     * children back down to the leaf level, if needed.
+     *
+     * @param indexOfNode
+     *            The index in latestBranch where we start adding
+     */
+    private void addSiblingNode(int indexOfNode) {
+        synchronized (fLatestBranch) {
+            final long splitTime = fTreeEnd;
+
+            if (indexOfNode >= fLatestBranch.size()) {
+                /*
+                 * We need to make sure (indexOfNode - 1) doesn't get the last
+                 * node in the branch, because that one is a Leaf Node.
+                 */
+                throw new IllegalStateException();
+            }
+
+            /* Check if we need to add a new root node */
+            if (indexOfNode == 0) {
+                addNewRootNode();
+                return;
+            }
+
+            /* Check if we can indeed add a child to the target parent */
+            if (((CoreNode) fLatestBranch.get(indexOfNode - 1)).getNbChildren() == fConfig.getMaxChildren()) {
+                /* If not, add a branch starting one level higher instead */
+                addSiblingNode(indexOfNode - 1);
+                return;
+            }
+
+            /* Split off the new branch from the old one */
+            for (int i = indexOfNode; i < fLatestBranch.size(); i++) {
+                fLatestBranch.get(i).closeThisNode(splitTime);
+                fTreeIO.writeNode(fLatestBranch.get(i));
+
+                CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
+                HTNode newNode;
+
+                switch (fLatestBranch.get(i).getNodeType()) {
+                case CORE:
+                    newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
+                    break;
+                case LEAF:
+                    newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
+                    break;
+                default:
+                    throw new IllegalStateException();
+                }
+
+                prevNode.linkNewChild(newNode);
+                fLatestBranch.set(i, newNode);
+            }
+        }
+    }
+
+    /**
+     * Similar to the previous method, except here we rebuild a completely new
+     * latestBranch
+     */
+    private void addNewRootNode() {
+        final long splitTime = fTreeEnd;
+
+        HTNode oldRootNode = fLatestBranch.get(0);
+        CoreNode newRootNode = initNewCoreNode(-1, fConfig.getTreeStart());
+
+        /* Tell the old root node that it isn't root anymore */
+        oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
+
+        /* Close off the whole current latestBranch */
+
+        for (int i = 0; i < fLatestBranch.size(); i++) {
+            fLatestBranch.get(i).closeThisNode(splitTime);
+            fTreeIO.writeNode(fLatestBranch.get(i));
+        }
+
+        /* Link the new root to its first child (the previous root node) */
+        newRootNode.linkNewChild(oldRootNode);
+
+        /* Rebuild a new latestBranch */
+        int depth = fLatestBranch.size();
+        fLatestBranch.clear();
+        fLatestBranch.add(newRootNode);
+
+        // Create new coreNode
+        for (int i = 1; i < depth; i++) {
+            CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
+            CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
+                    splitTime + 1);
+            prevNode.linkNewChild(newNode);
+            fLatestBranch.add(newNode);
+        }
+
+        // Create the new leafNode
+        CoreNode prevNode = (CoreNode) fLatestBranch.get(depth - 1);
+        LeafNode newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
+        prevNode.linkNewChild(newNode);
+        fLatestBranch.add(newNode);
+    }
+
+    /**
+     * Add a new empty core node to the tree.
+     *
+     * @param parentSeqNumber
+     *            Sequence number of this node's parent
+     * @param startTime
+     *            Start time of the new node
+     * @return The newly created node
+     */
+    private @NonNull CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
+        CoreNode newNode = new CoreNode(fConfig, fNodeCount, parentSeqNumber,
+                startTime);
+        fNodeCount++;
+        return newNode;
+    }
+
+    /**
+     * Add a new empty leaf node to the tree.
+     *
+     * @param parentSeqNumber
+     *            Sequence number of this node's parent
+     * @param startTime
+     *            Start time of the new node
+     * @return The newly created node
+     */
+    private @NonNull LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
+        LeafNode newNode = new LeafNode(fConfig, fNodeCount, parentSeqNumber,
+                startTime);
+        fNodeCount++;
+        return newNode;
+    }
+
+    @Override
+    public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
+        assert (currentNode.getNbChildren() > 0);
+        int potentialNextSeqNb = currentNode.getSequenceNumber();
+
+        for (int i = 0; i < currentNode.getNbChildren(); i++) {
+            if (t >= currentNode.getChildStart(i)) {
+                potentialNextSeqNb = currentNode.getChild(i);
+            } else {
+                break;
+            }
+        }
+
+        /*
+         * Once we exit this loop, we should have found a children to follow. If
+         * we didn't, there's a problem.
+         */
+        if (potentialNextSeqNb == currentNode.getSequenceNumber()) {
+            throw new IllegalStateException("No next child node found"); //$NON-NLS-1$
+        }
+
+        /*
+         * Since this code path is quite performance-critical, avoid iterating
+         * through the whole latestBranch array if we know for sure the next
+         * node has to be on disk
+         */
+        if (currentNode.isOnDisk()) {
+            return fTreeIO.readNode(potentialNextSeqNb);
+        }
+        return readNode(potentialNextSeqNb);
+    }
+
+    @Override
+    public long getFileSize() {
+        return fConfig.getStateFile().length();
+    }
+
+    // ------------------------------------------------------------------------
+    // Test/debugging methods
+    // ------------------------------------------------------------------------
+
+    /* Only used for debugging, shouldn't be externalized */
+    @SuppressWarnings("nls")
+    @Override
+    public String toString() {
+        return "Information on the current tree:\n\n" + "Blocksize: "
+                + fConfig.getBlockSize() + "\n" + "Max nb. of children per node: "
+                + fConfig.getMaxChildren() + "\n" + "Number of nodes: " + fNodeCount
+                + "\n" + "Depth of the tree: " + fLatestBranch.size() + "\n"
+                + "Size of the treefile: " + getFileSize() + "\n"
+                + "Root node has sequence number: "
+                + fLatestBranch.get(0).getSequenceNumber() + "\n"
+                + "'Latest leaf' has sequence number: "
+                + fLatestBranch.get(fLatestBranch.size() - 1).getSequenceNumber();
+    }
+
+    /**
+     * Start at currentNode and print the contents of all its children, in
+     * pre-order. Give the root node in parameter to visit the whole tree, and
+     * have a nice overview.
+     */
+    /* Only used for debugging, shouldn't be externalized */
+    @SuppressWarnings("nls")
+    private void preOrderPrint(PrintWriter writer, boolean printIntervals,
+            HTNode currentNode, int curDepth, long ts) {
+
+        writer.println(currentNode.toString());
+        /* Print intervals only if timestamp is negative or within the range of the node */
+        if (printIntervals &&
+                (ts <= 0 ||
+                (ts >= currentNode.getNodeStart() && ts <= currentNode.getNodeEnd()))) {
+            currentNode.debugPrintIntervals(writer);
+        }
+
+        switch (currentNode.getNodeType()) {
+        case LEAF:
+            /* Stop if it's the leaf node */
+            return;
+
+        case CORE:
+            try {
+                final CoreNode node = (CoreNode) currentNode;
+                /* Print the extensions, if any */
+                int extension = node.getExtensionSequenceNumber();
+                while (extension != -1) {
+                    HTNode nextNode = fTreeIO.readNode(extension);
+                    preOrderPrint(writer, printIntervals, nextNode, curDepth, ts);
+                }
+
+                /* Print the child nodes */
+                for (int i = 0; i < node.getNbChildren(); i++) {
+                    HTNode nextNode = fTreeIO.readNode(node.getChild(i));
+                    for (int j = 0; j < curDepth; j++) {
+                        writer.print("  ");
+                    }
+                    writer.print("+-");
+                    preOrderPrint(writer, printIntervals, nextNode, curDepth + 1, ts);
+                }
+            } catch (ClosedChannelException e) {
+                Activator.getDefault().logError(e.getMessage());
+            }
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    @Override
+    public void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts) {
+        /* Only used for debugging, shouldn't be externalized */
+
+        preOrderPrint(writer, false, fLatestBranch.get(0), 0, ts);
+
+        if (printIntervals) {
+            preOrderPrint(writer, true, fLatestBranch.get(0), 0, ts);
+        }
+        writer.println('\n');
+    }
+
+}
index bdf9bde3c439f64fc319c67c293c0168f0cd8b6d..61aae2307c0a6119b7c56865fb567810b0d45fab 100644 (file)
@@ -41,8 +41,8 @@ public final class HistoryTreeFactory {
      *             If an error happens trying to open/write to the file
      *             specified in the config
      */
-    public static HistoryTree createHistoryTree(HTConfig conf) throws IOException {
-        return new HistoryTree(conf);
+    public static IHistoryTree createHistoryTree(HTConfig conf) throws IOException {
+        return new HistoryTreeClassic(conf);
     }
 
     /**
@@ -57,8 +57,7 @@ public final class HistoryTreeFactory {
      * @throws IOException
      *             If an error happens reading the file
      */
-    public static HistoryTree createFromFile(Path existingStateFile, int expectedProviderVersion) throws IOException {
-
+    public static IHistoryTree createFromFile(Path existingStateFile, int expectedProviderVersion) throws IOException {
         /*
          * Check the file exists and has a positive length. These verifications
          * will also be done in the HT's constructor.
@@ -84,8 +83,8 @@ public final class HistoryTreeFactory {
          */
         int magicNumber = buffer.getInt();
         switch (magicNumber) {
-        case HistoryTree.HISTORY_FILE_MAGIC_NUMBER:
-            return new HistoryTree(existingStateFile.toFile(), expectedProviderVersion);
+        case HistoryTreeClassic.HISTORY_FILE_MAGIC_NUMBER:
+            return new HistoryTreeClassic(existingStateFile.toFile(), expectedProviderVersion);
         default:
             throw new IOException("Not a known history tree file"); //$NON-NLS-1$
         }
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/IHistoryTree.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/IHistoryTree.java
new file mode 100644 (file)
index 0000000..f58d9ae
--- /dev/null
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+import java.nio.channels.ClosedChannelException;
+
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+
+/**
+ * Meta-container for the History Tree. This structure contains all the
+ * high-level data relevant to the tree.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface IHistoryTree {
+
+    /**
+     * Size of the "tree header" in the tree-file The nodes will use this offset
+     * to know where they should be in the file. This should always be a
+     * multiple of 4K.
+     */
+    int TREE_HEADER_SIZE = 4096;
+
+    /**
+     * "Save" the tree to disk. This method will cause the treeIO object to
+     * commit all nodes to disk and then return the RandomAccessFile descriptor
+     * so the Tree object can save its configuration into the header of the
+     * file.
+     *
+     * @param requestedEndTime
+     *            The greatest timestamp present in the history tree
+     */
+    void closeTree(long requestedEndTime);
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the start time of this tree.
+     *
+     * @return The start time
+     */
+    long getTreeStart();
+
+    /**
+     * Get the current end time of this tree.
+     *
+     * @return The end time
+     */
+    long getTreeEnd();
+
+    /**
+     * Get the number of nodes in this tree.
+     *
+     * @return The number of nodes
+     */
+    int getNodeCount();
+
+    /**
+     * Get the current root node of this tree
+     *
+     * @return The root node
+     */
+    HTNode getRootNode();
+
+    // ------------------------------------------------------------------------
+    // HT_IO interface
+    // ------------------------------------------------------------------------
+
+    /**
+     * Return the FileInputStream reader with which we will read an attribute
+     * tree (it will be sought to the correct position).
+     *
+     * @return The FileInputStream indicating the file and position from which
+     *         the attribute tree can be read.
+     */
+    FileInputStream supplyATReader();
+
+    /**
+     * Return the file to which we will write the attribute tree.
+     *
+     * @return The file to which we will write the attribute tree
+     */
+    File supplyATWriterFile();
+
+    /**
+     * Return the position in the file (given by {@link #supplyATWriterFile})
+     * where to start writing the attribute tree.
+     *
+     * @return The position in the file where to start writing
+     */
+    long supplyATWriterFilePos();
+
+    /**
+     * Read a node from the tree.
+     *
+     * @param seqNumber
+     *            The sequence number of the node to read
+     * @return The node
+     * @throws ClosedChannelException
+     *             If the tree IO is unavailable
+     */
+    HTNode readNode(int seqNumber) throws ClosedChannelException;
+
+    /**
+     * Write a node object to the history file.
+     *
+     * @param node
+     *            The node to write to disk
+     */
+    void writeNode(HTNode node);
+
+    /**
+     * Close the history file.
+     */
+    void closeFile();
+
+    /**
+     * Delete the history file.
+     */
+    void deleteFile();
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Insert an interval in the tree.
+     *
+     * @param interval
+     *            The interval to be inserted
+     * @throws TimeRangeException
+     *             If the start of end time of the interval are invalid
+     */
+    void insertInterval(HTInterval interval) throws TimeRangeException;
+
+    /**
+     * Inner method to select the next child of the current node intersecting
+     * the given timestamp. Useful for moving down the tree following one
+     * branch.
+     *
+     * @param currentNode
+     *            The node on which the request is made
+     * @param t
+     *            The timestamp to choose which child is the next one
+     * @return The child node intersecting t
+     * @throws ClosedChannelException
+     *             If the file channel was closed while we were reading the tree
+     */
+    HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException;
+
+    /**
+     * Get the current size of the history file.
+     *
+     * @return The history file size
+     */
+    long getFileSize();
+
+    /**
+     * Print out the full tree for debugging purposes
+     *
+     * @param writer
+     *            PrintWriter in which to write the output
+     * @param printIntervals
+     *            Flag to enable full output of the interval information
+     * @param ts
+     *            The timestamp that nodes have to intersect for intervals to be
+     *            printed. A negative value will print intervals for all nodes.
+     *            The timestamp only applies if printIntervals is true.
+     */
+    void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts);
+
+}
This page took 0.073779 seconds and 5 git commands to generate.