tmf: Add a method to query an ongoing state's start time
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / internal / tmf / core / statesystem / StateSystem.java
index 1dbc4c16a925dc8910b4ac9f920d8a5c00c19965..cd82f921198913a07891bc704d6c6458d1203808 100644 (file)
 /*******************************************************************************
- * Copyright (c) 2012 Ericsson
+ * Copyright (c) 2012, 2013 Ericsson
  * Copyright (c) 2010, 2011 École Polytechnique de Montréal
  * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
- * 
+ *
  * 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.linuxtools.internal.tmf.core.statesystem;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.linuxtools.internal.tmf.core.Activator;
+import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.IStateHistoryBackend;
 import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
+import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
 import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
 import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
+import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
+import org.eclipse.linuxtools.tmf.core.interval.TmfStateInterval;
+import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystemBuilder;
 import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
 import org.eclipse.linuxtools.tmf.core.statevalue.TmfStateValue;
 
 /**
- * This is the base class for the StateHistorySystem. It contains all the
- * current-state-updating methods.
- * 
- * It's not abstract, as it can be used by itself: in this case, no History tree
- * will be built underneath (no information will be saved to disk) and it will
- * only be able to respond to queries to the current, latest time.
- * 
- * (See IStateSystemQuerier and IStateSystemBuilder for the Javadoc.)
- * 
+ * This is the core class of the Generic State System. It contains all the
+ * methods to build and query a state history. It's exposed externally through
+ * the IStateSystemQuerier and IStateSystemBuilder interfaces, depending if the
+ * user needs read-only access or read-write access.
+ *
+ * When building, DON'T FORGET to call .closeHistory() when you are done
+ * inserting intervals, or the storage backend will have no way of knowing it
+ * can close and write itself to disk, and its thread will keep running.
+ *
  * @author alexmont
- * 
+ *
  */
-public class StateSystem {
+public class StateSystem implements ITmfStateSystemBuilder {
 
     /* References to the inner structures */
-    protected AttributeTree attributeTree;
-    protected TransientState transState;
+    private final AttributeTree attributeTree;
+    private final TransientState transState;
+    private final IStateHistoryBackend backend;
+
+    /* Latch tracking if the state history is done building or not */
+    private final CountDownLatch finishedLatch = new CountDownLatch(1);
+
+    private boolean buildCancelled = false;
+    private boolean isDisposed = false;
+
+    /**
+     * New-file constructor. For when you build a state system with a new file,
+     * or if the back-end does not require a file on disk.
+     *
+     * @param backend
+     *            Back-end plugin to use
+     */
+    public StateSystem(IStateHistoryBackend backend) {
+        this.backend = backend;
+        this.transState = new TransientState(backend);
+        this.attributeTree = new AttributeTree(this);
+    }
 
     /**
-     * Constructor. No configuration needed!
+     * General constructor
+     *
+     * @param backend
+     *            The "state history storage" back-end to use.
+     * @param newFile
+     *            Put true if this is a new history started from scratch. It is
+     *            used to tell the state system where to get its attribute tree.
+     * @throws IOException
+     *             If there was a problem creating the new history file
      */
-    public StateSystem() {
-        attributeTree = new AttributeTree(this);
+    public StateSystem(IStateHistoryBackend backend, boolean newFile)
+            throws IOException {
+        this.backend = backend;
+        this.transState = new TransientState(backend);
 
-        /* This will tell the builder to discard the intervals */
-        transState = new TransientState(null);
+        if (newFile) {
+            attributeTree = new AttributeTree(this);
+        } else {
+            /* We're opening an existing file */
+            this.attributeTree = new AttributeTree(this, backend.supplyAttributeTreeReader());
+            transState.setInactive();
+            finishedLatch.countDown(); /* The history is already built */
+        }
     }
 
-    public int getNbAttributes() {
-        return attributeTree.getNbAttributes();
+    @Override
+    public boolean waitUntilBuilt() {
+        try {
+            finishedLatch.await();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return !buildCancelled;
     }
 
+    @Override
+    public synchronized void dispose() {
+        isDisposed = true;
+        if (transState.isActive()) {
+            transState.setInactive();
+            buildCancelled = true;
+        }
+        backend.dispose();
+    }
+
+    //--------------------------------------------------------------------------
+    //        General methods related to the attribute tree
+    //--------------------------------------------------------------------------
+
     /**
-     * @name Quark-retrieving methods
+     * Method used by the attribute tree when creating new attributes, to keep
+     * the attribute count in the transient state in sync.
      */
+    void addEmptyAttribute() {
+        transState.addEmptyEntry();
+    }
+
+    @Override
+    public int getNbAttributes() {
+        return attributeTree.getNbAttributes();
+    }
+
+    @Override
+    public boolean isLastAttribute(int quark) {
+        return (quark == getNbAttributes() - 1) ? true : false;
+    }
+
+    @Override
+    public String getAttributeName(int attributeQuark) {
+        return attributeTree.getAttributeName(attributeQuark);
+    }
+
+    @Override
+    public String getFullAttributePath(int attributeQuark) {
+        return attributeTree.getFullAttributeName(attributeQuark);
+    }
+
+    //--------------------------------------------------------------------------
+    //        Methods related to the storage backend
+    //--------------------------------------------------------------------------
+
+    @Override
+    public long getStartTime() {
+        return backend.getStartTime();
+    }
+
+    @Override
+    public long getCurrentEndTime() {
+        return backend.getEndTime();
+    }
+
+    @Override
+    public void closeHistory(long endTime) throws TimeRangeException {
+        File attributeTreeFile;
+        long attributeTreeFilePos;
+        long realEndTime = endTime;
 
+        if (realEndTime < backend.getEndTime()) {
+            /*
+             * This can happen (empty nodes pushing the border further, etc.)
+             * but shouldn't be too big of a deal.
+             */
+            realEndTime = backend.getEndTime();
+        }
+        transState.closeTransientState(realEndTime);
+        backend.finishedBuilding(realEndTime);
+
+        attributeTreeFile = backend.supplyAttributeTreeWriterFile();
+        attributeTreeFilePos = backend.supplyAttributeTreeWriterFilePosition();
+        if (attributeTreeFile != null) {
+            /*
+             * If null was returned, we simply won't save the attribute tree,
+             * too bad!
+             */
+            attributeTree.writeSelf(attributeTreeFile, attributeTreeFilePos);
+        }
+        finishedLatch.countDown(); /* Mark the history as finished building */
+    }
+
+    //--------------------------------------------------------------------------
+    //        Quark-retrieving methods
+    //--------------------------------------------------------------------------
+
+    @Override
     public int getQuarkAbsolute(String... attribute)
             throws AttributeNotFoundException {
         return attributeTree.getQuarkDontAdd(-1, attribute);
     }
 
+    @Override
     public int getQuarkAbsoluteAndAdd(String... attribute) {
         return attributeTree.getQuarkAndAdd(-1, attribute);
     }
 
+    @Override
     public int getQuarkRelative(int startingNodeQuark, String... subPath)
             throws AttributeNotFoundException {
         return attributeTree.getQuarkDontAdd(startingNodeQuark, subPath);
     }
 
+    @Override
     public int getQuarkRelativeAndAdd(int startingNodeQuark, String... subPath) {
         return attributeTree.getQuarkAndAdd(startingNodeQuark, subPath);
     }
 
+    @Override
     public List<Integer> getSubAttributes(int quark, boolean recursive)
             throws AttributeNotFoundException {
         return attributeTree.getSubAttributes(quark, recursive);
     }
 
+    @Override
     public List<Integer> getQuarks(String... pattern) {
         List<Integer> quarks = new LinkedList<Integer>();
         List<String> prefix = new LinkedList<String>();
@@ -166,25 +310,31 @@ public class StateSystem {
         return quarks;
     }
 
-    /**
-     * @name External methods related to insertions in the history -
-     */
+    //--------------------------------------------------------------------------
+    //        Methods related to insertions in the history
+    //--------------------------------------------------------------------------
 
+    @Override
     public void modifyAttribute(long t, ITmfStateValue value, int attributeQuark)
             throws TimeRangeException, AttributeNotFoundException,
             StateValueTypeException {
         transState.processStateChange(t, value, attributeQuark);
     }
 
+    @Override
     public void incrementAttribute(long t, int attributeQuark)
             throws StateValueTypeException, TimeRangeException,
             AttributeNotFoundException {
         int prevValue = queryOngoingState(attributeQuark).unboxInt();
-        /* prevValue should be == 0 if the attribute wasn't existing before */
+        if (prevValue == -1) {
+            /* if the attribute was previously null, start counting at 0 */
+            prevValue = 0;
+        }
         modifyAttribute(t, TmfStateValue.newValueInt(prevValue + 1),
                 attributeQuark);
     }
 
+    @Override
     public void pushAttribute(long t, ITmfStateValue value, int attributeQuark)
             throws TimeRangeException, AttributeNotFoundException,
             StateValueTypeException {
@@ -215,26 +365,29 @@ public class StateSystem {
         }
 
         stackDepth++;
-        subAttributeQuark = getQuarkRelativeAndAdd(attributeQuark,
-                stackDepth.toString());
+        subAttributeQuark = getQuarkRelativeAndAdd(attributeQuark, stackDepth.toString());
 
-        modifyAttribute(t, TmfStateValue.newValueInt(stackDepth),
-                attributeQuark);
+        modifyAttribute(t, TmfStateValue.newValueInt(stackDepth), attributeQuark);
         modifyAttribute(t, value, subAttributeQuark);
     }
 
-    public void popAttribute(long t, int attributeQuark)
+    @Override
+    public ITmfStateValue popAttribute(long t, int attributeQuark)
             throws AttributeNotFoundException, TimeRangeException,
             StateValueTypeException {
-        Integer stackDepth;
-        int subAttributeQuark;
-        ITmfStateValue previousSV = transState.getOngoingStateValue(attributeQuark);
+        /* These are the state values of the stack-attribute itself */
+        ITmfStateValue previousSV = queryOngoingState(attributeQuark);
 
         if (previousSV.isNull()) {
-            /* Same as if stackDepth == 0, see below */
-            return;
+            /*
+             * Trying to pop an empty stack. This often happens at the start of
+             * traces, for example when we see a syscall_exit, without having
+             * the corresponding syscall_entry in the trace. Just ignore
+             * silently.
+             */
+            return null;
         }
-        if (previousSV.getType() != 0) {
+        if (previousSV.getType() != ITmfStateValue.TYPE_INTEGER) {
             /*
              * The existing value was a string, this doesn't look like a valid
              * stack attribute.
@@ -242,35 +395,37 @@ public class StateSystem {
             throw new StateValueTypeException();
         }
 
-        stackDepth = previousSV.unboxInt();
-
-        if (stackDepth == 0) {
-            /*
-             * Trying to pop an empty stack. This often happens at the start of
-             * traces, for example when we see a syscall_exit, without having
-             * the corresponding syscall_entry in the trace. Just ignore
-             * silently.
-             */
-            return;
-        }
+        Integer stackDepth = previousSV.unboxInt();
 
-        if (stackDepth < 0) {
+        if (stackDepth <= 0) {
             /* This on the other hand should not happen... */
-            String message = "A top-level stack attribute " + //$NON-NLS-1$
-                    "cannot have a negative integer value."; //$NON-NLS-1$
+            /* the case where == -1 was handled previously by .isNull() */
+            String message = "A top-level stack attribute cannot " + //$NON-NLS-1$
+                    "have a value of 0 or less (except -1/null)."; //$NON-NLS-1$
             throw new StateValueTypeException(message);
         }
 
-        /* The attribute should already exist... */
-        subAttributeQuark = getQuarkRelative(attributeQuark,
-                stackDepth.toString());
+        /* The attribute should already exist at this point */
+        int subAttributeQuark = getQuarkRelative(attributeQuark, stackDepth.toString());
+        ITmfStateValue poppedValue = queryOngoingState(subAttributeQuark);
 
-        stackDepth--;
-        modifyAttribute(t, TmfStateValue.newValueInt(stackDepth),
-                attributeQuark);
+        /* Update the state value of the stack-attribute */
+        ITmfStateValue nextSV;
+        if (--stackDepth == 0 ) {
+            /* Jump over "0" and store -1 (a null state value) */
+            nextSV = TmfStateValue.nullValue();
+        } else {
+            nextSV = TmfStateValue.newValueInt(stackDepth);
+        }
+        modifyAttribute(t, nextSV, attributeQuark);
+
+        /* Delete the sub-attribute that contained the user's state value */
         removeAttribute(t, subAttributeQuark);
+
+        return poppedValue;
     }
 
+    @Override
     public void removeAttribute(long t, int attributeQuark)
             throws TimeRangeException, AttributeNotFoundException {
         assert (attributeQuark >= 0);
@@ -298,37 +453,240 @@ public class StateSystem {
         }
     }
 
-    /**
-     * @name "Current" query/update methods -
-     */
+    //--------------------------------------------------------------------------
+    //        "Current" query/update methods
+    //--------------------------------------------------------------------------
 
+    @Override
     public ITmfStateValue queryOngoingState(int attributeQuark)
             throws AttributeNotFoundException {
         return transState.getOngoingStateValue(attributeQuark);
     }
 
+    @Override
+    public long getOngoingStartTime(int attribute)
+            throws AttributeNotFoundException {
+        return transState.getOngoingStartTime(attribute);
+    }
+
+    @Override
     public void updateOngoingState(ITmfStateValue newValue, int attributeQuark)
             throws AttributeNotFoundException {
         transState.changeOngoingStateValue(attributeQuark, newValue);
     }
 
-    public String getAttributeName(int attributeQuark) {
-        return attributeTree.getAttributeName(attributeQuark);
+    //--------------------------------------------------------------------------
+    //        Regular query methods (sent to the back-end)
+    //--------------------------------------------------------------------------
+
+    @Override
+    public synchronized List<ITmfStateInterval> queryFullState(long t)
+            throws TimeRangeException, StateSystemDisposedException {
+        if (isDisposed) {
+            throw new StateSystemDisposedException();
+        }
+
+        List<ITmfStateInterval> stateInfo = new ArrayList<ITmfStateInterval>(
+                attributeTree.getNbAttributes());
+
+        /* Bring the size of the array to the current number of attributes */
+        for (int i = 0; i < attributeTree.getNbAttributes(); i++) {
+            stateInfo.add(null);
+        }
+
+        /* Query the storage backend */
+        backend.doQuery(stateInfo, t);
+
+        /*
+         * If we are currently building the history, also query the "ongoing"
+         * states for stuff that might not yet be written to the history.
+         */
+        if (transState.isActive()) {
+            transState.doQuery(stateInfo, t);
+        }
+
+        /*
+         * We should have previously inserted an interval for every attribute.
+         * If we do happen do see a 'null' object here, just replace it with a a
+         * dummy internal with a null value, to avoid NPE's further up.
+         */
+        for (int i = 0; i < stateInfo.size(); i++) {
+            if (stateInfo.get(i) == null) {
+                //logMissingInterval(i, t);
+                stateInfo.set(i, new TmfStateInterval(t, t, i, TmfStateValue.nullValue()));
+            }
+        }
+        return stateInfo;
     }
 
-    public String getFullAttributePath(int attributeQuark) {
-        return attributeTree.getFullAttributeName(attributeQuark);
+    @Override
+    public ITmfStateInterval querySingleState(long t, int attributeQuark)
+            throws AttributeNotFoundException, TimeRangeException,
+            StateSystemDisposedException {
+        if (isDisposed) {
+            throw new StateSystemDisposedException();
+        }
+
+        ITmfStateInterval ret;
+        if (transState.hasInfoAboutStateOf(t, attributeQuark)) {
+            ret = transState.getOngoingInterval(attributeQuark);
+        } else {
+            ret = backend.doSingularQuery(t, attributeQuark);
+        }
+
+        /*
+         * Return a fake interval if we could not find anything in the history.
+         * We do NOT want to return 'null' here.
+         */
+        if (ret == null) {
+            //logMissingInterval(attributeQuark, t);
+            return new TmfStateInterval(t, this.getCurrentEndTime(),
+                    attributeQuark, TmfStateValue.nullValue());
+        }
+        return ret;
+    }
+
+    @Override
+    public ITmfStateInterval querySingleStackTop(long t, int stackAttributeQuark)
+            throws StateValueTypeException, AttributeNotFoundException,
+            TimeRangeException, StateSystemDisposedException {
+        Integer curStackDepth = querySingleState(t, stackAttributeQuark).getStateValue().unboxInt();
+
+        if (curStackDepth == -1) {
+            /* There is nothing stored in this stack at this moment */
+            return null;
+        } else if (curStackDepth < -1 || curStackDepth == 0) {
+            /*
+             * This attribute is an integer attribute, but it doesn't seem like
+             * it's used as a stack-attribute...
+             */
+            throw new StateValueTypeException();
+        }
+
+        int subAttribQuark = getQuarkRelative(stackAttributeQuark, curStackDepth.toString());
+        ITmfStateInterval ret = querySingleState(t, subAttribQuark);
+        return ret;
+    }
+
+    @Override
+    public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
+            long t1, long t2) throws TimeRangeException,
+            AttributeNotFoundException, StateSystemDisposedException {
+        if (isDisposed) {
+            throw new StateSystemDisposedException();
+        }
+
+        List<ITmfStateInterval> intervals;
+        ITmfStateInterval currentInterval;
+        long ts, tEnd;
+
+        /* Make sure the time range makes sense */
+        if (t2 <= t1) {
+            throw new TimeRangeException();
+        }
+
+        /* Set the actual, valid end time of the range query */
+        if (t2 > this.getCurrentEndTime()) {
+            tEnd = this.getCurrentEndTime();
+        } else {
+            tEnd = t2;
+        }
+
+        /* Get the initial state at time T1 */
+        intervals = new ArrayList<ITmfStateInterval>();
+        currentInterval = querySingleState(t1, attributeQuark);
+        intervals.add(currentInterval);
+
+        /* Get the following state changes */
+        ts = currentInterval.getEndTime();
+        while (ts != -1 && ts < tEnd) {
+            ts++; /* To "jump over" to the next state in the history */
+            currentInterval = querySingleState(ts, attributeQuark);
+            intervals.add(currentInterval);
+            ts = currentInterval.getEndTime();
+        }
+        return intervals;
+    }
+
+    @Override
+    public List<ITmfStateInterval> queryHistoryRange(int attributeQuark,
+            long t1, long t2, long resolution, IProgressMonitor monitor)
+            throws TimeRangeException, AttributeNotFoundException,
+            StateSystemDisposedException {
+        if (isDisposed) {
+            throw new StateSystemDisposedException();
+        }
+
+        List<ITmfStateInterval> intervals;
+        ITmfStateInterval currentInterval;
+        long ts, tEnd;
+
+        IProgressMonitor mon = monitor;
+        if (mon == null) {
+            mon = new NullProgressMonitor();
+        }
+
+        /* Make sure the time range makes sense */
+        if (t2 < t1 || resolution <= 0) {
+            throw new TimeRangeException();
+        }
+
+        /* Set the actual, valid end time of the range query */
+        if (t2 > this.getCurrentEndTime()) {
+            tEnd = this.getCurrentEndTime();
+        } else {
+            tEnd = t2;
+        }
+
+        /* Get the initial state at time T1 */
+        intervals = new ArrayList<ITmfStateInterval>();
+        currentInterval = querySingleState(t1, attributeQuark);
+        intervals.add(currentInterval);
+
+        /*
+         * Iterate over the "resolution points". We skip unneeded queries in the
+         * case the current interval is longer than the resolution.
+         */
+        for (ts = t1; (currentInterval.getEndTime() != -1) && (ts < tEnd);
+                ts += resolution) {
+            if (mon.isCanceled()) {
+                return intervals;
+            }
+            if (ts <= currentInterval.getEndTime()) {
+                continue;
+            }
+            currentInterval = querySingleState(ts, attributeQuark);
+            intervals.add(currentInterval);
+        }
+
+        /* Add the interval at t2, if it wasn't included already. */
+        if (currentInterval.getEndTime() < tEnd) {
+            currentInterval = querySingleState(tEnd, attributeQuark);
+            intervals.add(currentInterval);
+        }
+        return intervals;
+    }
+
+    //--------------------------------------------------------------------------
+    //        Debug methods
+    //--------------------------------------------------------------------------
+
+    static void logMissingInterval(int attribute, long timestamp) {
+        Activator.logInfo("No data found in history for attribute " + //$NON-NLS-1$
+                attribute + " at time " + timestamp + //$NON-NLS-1$
+                ", returning dummy interval"); //$NON-NLS-1$
     }
 
     /**
      * Print out the contents of the inner structures.
-     * 
+     *
      * @param writer
      *            The PrintWriter in which to print the output
      */
     public void debugPrint(PrintWriter writer) {
         attributeTree.debugPrint(writer);
         transState.debugPrint(writer);
+        backend.debugPrint(writer);
     }
 
-}
\ No newline at end of file
+}
This page took 0.030173 seconds and 5 git commands to generate.