| /******************************************************************************* |
| * Copyright (c) 2012, 2017 Ericsson and others |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Alexandre Montplaisir - Initial API and implementation |
| * Patrick Tasse - Add message to exceptions |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.internal.statesystem.core; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.common.core.log.TraceCompassLog; |
| import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils; |
| import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.ScopeLog; |
| import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.IntegerRangeCondition; |
| import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.TimeRangeCondition; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; |
| import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
| import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; |
| import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; |
| import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type; |
| import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; |
| |
| import com.google.common.collect.ImmutableCollection.Builder; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * 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 Alexandre Montplaisir |
| * |
| */ |
| public class StateSystem implements ITmfStateSystemBuilder { |
| |
| private static final int MAX_STACK_DEPTH = 100000; |
| private static final String PARENT = ".."; //$NON-NLS-1$ |
| private static final String WILDCARD = "*"; //$NON-NLS-1$ |
| |
| private static final @NonNull Logger LOGGER = TraceCompassLog.getLogger(StateSystem.class); |
| |
| /* References to the inner structures */ |
| 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(@NonNull IStateHistoryBackend backend) { |
| this.backend = backend; |
| this.transState = new TransientState(backend); |
| this.attributeTree = new AttributeTree(this); |
| } |
| |
| /** |
| * 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(@NonNull IStateHistoryBackend backend, boolean newFile) |
| throws IOException { |
| this.backend = backend; |
| this.transState = new TransientState(backend); |
| |
| 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 */ |
| } |
| } |
| |
| @Override |
| public String getSSID() { |
| return backend.getSSID(); |
| } |
| |
| @Override |
| public boolean isCancelled() { |
| return buildCancelled; |
| } |
| |
| @Override |
| public void waitUntilBuilt() { |
| try { |
| finishedLatch.await(); |
| } catch (InterruptedException e) { |
| // Do nothing |
| } |
| } |
| |
| @Override |
| public boolean waitUntilBuilt(long timeout) { |
| boolean ret = false; |
| try { |
| ret = finishedLatch.await(timeout, TimeUnit.MILLISECONDS); |
| } catch (InterruptedException e) { |
| // Do nothing |
| } |
| return ret; |
| } |
| |
| @Override |
| public synchronized void dispose() { |
| isDisposed = true; |
| if (transState.isActive()) { |
| transState.setInactive(); |
| buildCancelled = true; |
| } |
| backend.dispose(); |
| } |
| |
| // -------------------------------------------------------------------------- |
| // General methods related to the attribute tree |
| // -------------------------------------------------------------------------- |
| |
| /** |
| * Get the attribute tree associated with this state system. This should be |
| * the only way of accessing it (and if subclasses want to point to a |
| * different attribute tree than their own, they should only need to |
| * override this). |
| * |
| * @return The attribute tree |
| */ |
| public AttributeTree getAttributeTree() { |
| return attributeTree; |
| } |
| |
| /** |
| * Method used by the attribute tree when creating new attributes, to keep |
| * the attribute count in the transient state in sync. |
| */ |
| public void addEmptyAttribute() { |
| transState.addEmptyEntry(); |
| } |
| |
| @Override |
| public int getNbAttributes() { |
| return getAttributeTree().getNbAttributes(); |
| } |
| |
| @Override |
| public String getAttributeName(int attributeQuark) { |
| return getAttributeTree().getAttributeName(attributeQuark); |
| } |
| |
| @Override |
| public String getFullAttributePath(int attributeQuark) { |
| return getAttributeTree().getFullAttributeName(attributeQuark); |
| } |
| |
| @Override |
| public String[] getFullAttributePathArray(int attributeQuark) { |
| return getAttributeTree().getFullAttributePathArray(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! |
| */ |
| getAttributeTree().writeSelf(attributeTreeFile, attributeTreeFilePos); |
| } |
| finishedLatch.countDown(); /* Mark the history as finished building */ |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Quark-retrieving methods |
| // -------------------------------------------------------------------------- |
| |
| @Override |
| public int getQuarkAbsolute(String... attribute) |
| throws AttributeNotFoundException { |
| int quark = getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE, attribute); |
| if (quark == INVALID_ATTRIBUTE) { |
| throw new AttributeNotFoundException(getSSID() + " Path:" + Arrays.toString(attribute)); //$NON-NLS-1$ |
| } |
| return quark; |
| } |
| |
| @Override |
| public int optQuarkAbsolute(String... attribute) { |
| return getAttributeTree().getQuarkDontAdd(ROOT_ATTRIBUTE, attribute); |
| } |
| |
| @Override |
| public int getQuarkAbsoluteAndAdd(String... attribute) { |
| return getAttributeTree().getQuarkAndAdd(ROOT_ATTRIBUTE, attribute); |
| } |
| |
| @Override |
| public int getQuarkRelative(int startingNodeQuark, String... subPath) |
| throws AttributeNotFoundException { |
| int quark = getAttributeTree().getQuarkDontAdd(startingNodeQuark, subPath); |
| if (quark == INVALID_ATTRIBUTE) { |
| throw new AttributeNotFoundException(getSSID() + " Quark:" + startingNodeQuark + ", SubPath:" + Arrays.toString(subPath)); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return quark; |
| } |
| |
| @Override |
| public int optQuarkRelative(int startingNodeQuark, String... subPath) { |
| return getAttributeTree().getQuarkDontAdd(startingNodeQuark, subPath); |
| } |
| |
| @Override |
| public int getQuarkRelativeAndAdd(int startingNodeQuark, String... subPath) { |
| return getAttributeTree().getQuarkAndAdd(startingNodeQuark, subPath); |
| } |
| |
| @Override |
| public List<@NonNull Integer> getSubAttributes(int quark, boolean recursive) { |
| return getAttributeTree().getSubAttributes(quark, recursive); |
| } |
| |
| @Override |
| public List<@NonNull Integer> getSubAttributes(int quark, boolean recursive, String pattern) { |
| List<Integer> all = getSubAttributes(quark, recursive); |
| List<@NonNull Integer> ret = new LinkedList<>(); |
| Pattern regex = Pattern.compile(pattern, Pattern.MULTILINE | Pattern.DOTALL); |
| for (Integer attQuark : all) { |
| String name = getAttributeName(attQuark.intValue()); |
| if (regex.matcher(name).matches()) { |
| ret.add(attQuark); |
| } |
| } |
| return ret; |
| } |
| |
| @Override |
| public int getParentAttributeQuark(int quark) { |
| return getAttributeTree().getParentAttributeQuark(quark); |
| } |
| |
| @Override |
| public List<@NonNull Integer> getQuarks(String... pattern) { |
| return getQuarks(ROOT_ATTRIBUTE, pattern); |
| } |
| |
| @Override |
| public List<@NonNull Integer> getQuarks(int startingNodeQuark, String... pattern) { |
| Builder<@NonNull Integer> builder = ImmutableSet.builder(); |
| if (pattern.length > 0) { |
| getQuarks(builder, startingNodeQuark, Arrays.asList(pattern)); |
| } else { |
| builder.add(startingNodeQuark); |
| } |
| return builder.build().asList(); |
| } |
| |
| private void getQuarks(Builder<@NonNull Integer> builder, int quark, List<String> pattern) { |
| String element = pattern.get(0); |
| if (element == null) { |
| return; |
| } |
| List<String> remainder = pattern.subList(1, pattern.size()); |
| if (remainder.isEmpty()) { |
| if (element.equals(WILDCARD)) { |
| builder.addAll(getSubAttributes(quark, false)); |
| } else if (element.equals(PARENT)) { |
| builder.add(getParentAttributeQuark(quark)); |
| } else { |
| int subQuark = optQuarkRelative(quark, element); |
| if (subQuark != INVALID_ATTRIBUTE) { |
| builder.add(subQuark); |
| } |
| } |
| } else { |
| if (element.equals(WILDCARD)) { |
| getSubAttributes(quark, false).forEach(subquark -> getQuarks(builder, subquark, remainder)); |
| } else if (element.equals(PARENT)) { |
| getQuarks(builder, getParentAttributeQuark(quark), remainder); |
| } else { |
| int subQuark = optQuarkRelative(quark, element); |
| if (subQuark != INVALID_ATTRIBUTE) { |
| getQuarks(builder, subQuark, remainder); |
| } |
| } |
| } |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Methods related to insertions in the history |
| // -------------------------------------------------------------------------- |
| |
| @Override |
| public void modifyAttribute(long t, Object value, int attributeQuark) |
| throws TimeRangeException, StateValueTypeException { |
| transState.processStateChange(t, value, attributeQuark); |
| } |
| |
| @Override |
| public void pushAttribute(long t, Object value, int attributeQuark) |
| throws TimeRangeException, StateValueTypeException { |
| int stackDepth; |
| int subAttributeQuark; |
| Object previousSV = transState.getOngoingStateValue(attributeQuark); |
| |
| if (previousSV == null) { |
| /* |
| * If the StateValue was null, this means this is the first time we |
| * use this attribute. Leave stackDepth at 0. |
| */ |
| stackDepth = 0; |
| } else if (previousSV instanceof Integer) { |
| /* Previous value was an integer, all is good, use it */ |
| stackDepth = (int) previousSV; |
| } else { |
| /* Previous state of this attribute was another type? Not good! */ |
| throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark + ", Type:" + previousSV.getClass() + ", Expected:" + Type.INTEGER); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| if (stackDepth >= MAX_STACK_DEPTH) { |
| /* |
| * Limit stackDepth to 100000, to avoid having Attribute Trees grow |
| * out of control due to buggy insertions |
| */ |
| String message = " Stack limit reached, not pushing"; //$NON-NLS-1$ |
| throw new IllegalStateException(getSSID() + " Quark:" + attributeQuark + message); //$NON-NLS-1$ |
| } |
| |
| stackDepth++; |
| subAttributeQuark = getQuarkRelativeAndAdd(attributeQuark, String.valueOf(stackDepth)); |
| |
| modifyAttribute(t, stackDepth, attributeQuark); |
| modifyAttribute(t, value, subAttributeQuark); |
| } |
| |
| @Override |
| public ITmfStateValue popAttribute(long t, int attributeQuark) |
| throws TimeRangeException, StateValueTypeException { |
| Object pop = popAttributeObject(t, attributeQuark); |
| return pop != null ? TmfStateValue.newValue(pop) : null; |
| } |
| |
| @Override |
| public Object popAttributeObject(long t, int attributeQuark) |
| throws TimeRangeException, StateValueTypeException { |
| /* These are the state values of the stack-attribute itself */ |
| @Nullable Object previousSV = transState.getOngoingStateValue(attributeQuark); |
| |
| if (previousSV == null) { |
| /* |
| * 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; |
| } |
| |
| |
| int stackDepth = 0; |
| |
| if (previousSV instanceof Integer) { |
| stackDepth = (int) previousSV; |
| } else { |
| /* This on the other hand should not happen... */ |
| throw new StateValueTypeException(getSSID() + " Quark:" + attributeQuark + ", Stack depth:" + stackDepth); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| |
| /* The attribute should already exist at this point */ |
| int subAttributeQuark; |
| try { |
| subAttributeQuark = getQuarkRelative(attributeQuark, String.valueOf(stackDepth)); |
| } catch (AttributeNotFoundException e) { |
| String message = " Stack attribute missing sub-attribute for depth:" + stackDepth; //$NON-NLS-1$ |
| throw new IllegalStateException(getSSID() + " Quark:" + attributeQuark + message); //$NON-NLS-1$ |
| } |
| Object poppedValue = queryOngoing(subAttributeQuark); |
| |
| /* Update the state value of the stack-attribute */ |
| Integer nextSV; |
| if (--stackDepth == 0) { |
| /* Store a null state value */ |
| nextSV = null; |
| } else { |
| nextSV = 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 { |
| /* |
| * Nullify our children first, recursively. We pass 'false' because we |
| * handle the recursion ourselves. |
| */ |
| List<Integer> childAttributes = getSubAttributes(attributeQuark, false); |
| for (int childNodeQuark : childAttributes) { |
| if (attributeQuark == childNodeQuark) { |
| /* Something went very wrong when building out attribute tree */ |
| throw new IllegalStateException(); |
| } |
| removeAttribute(t, childNodeQuark); |
| } |
| /* Nullify ourselves */ |
| try { |
| transState.processStateChange(t, null, attributeQuark); |
| } catch (StateValueTypeException e) { |
| /* |
| * Will not happen since we're inserting null values only, but poor |
| * compiler has no way of knowing this... |
| */ |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| // -------------------------------------------------------------------------- |
| // "Current" query/update methods |
| // -------------------------------------------------------------------------- |
| |
| @Override |
| public ITmfStateValue queryOngoingState(int attributeQuark) { |
| return TmfStateValue.newValue(queryOngoing(attributeQuark)); |
| } |
| |
| @Override |
| public Object queryOngoing(int attributeQuark) { |
| return transState.getOngoingStateValue(attributeQuark); |
| } |
| |
| @Override |
| public List<@Nullable Object> queryOngoing() { |
| return transState.getOngoingStateValues(); |
| } |
| |
| @Override |
| public long getOngoingStartTime(int attribute) { |
| return transState.getOngoingStartTime(attribute); |
| } |
| |
| @Override |
| public void updateOngoingState(ITmfStateValue newValue, int attributeQuark) { |
| transState.changeOngoingStateValue(attributeQuark, newValue.unboxValue()); |
| } |
| |
| @Override |
| public void updateOngoingState(Object newValue, int attributeQuark) { |
| transState.changeOngoingStateValue(attributeQuark, newValue); |
| } |
| |
| /** |
| * Modify the whole "ongoing state" (state values + start times). This can |
| * be used when "seeking" a state system to a different point in the trace |
| * (and restoring the known stateInfo at this location). Use with care! |
| * |
| * @param newStateIntervals |
| * The new List of state values to use as ongoing state info |
| */ |
| protected void replaceOngoingState(@NonNull List<@NonNull ITmfStateInterval> newStateIntervals) { |
| transState.replaceOngoingState(newStateIntervals); |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Regular query methods (sent to the back-end) |
| // -------------------------------------------------------------------------- |
| |
| @Override |
| public List<ITmfStateInterval> queryFullState(long t) |
| throws TimeRangeException, StateSystemDisposedException { |
| if (isDisposed) { |
| throw new StateSystemDisposedException(); |
| } |
| |
| try (ScopeLog log = new ScopeLog(LOGGER, Level.FINER, "StateSystem:FullQuery", //$NON-NLS-1$ |
| "ssid", getSSID(), "ts", t);) { //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| final int nbAttr = getNbAttributes(); |
| List<@Nullable ITmfStateInterval> stateInfo = new ArrayList<>(nbAttr); |
| |
| /* |
| * Bring the size of the array to the current number of attributes |
| */ |
| for (int i = 0; i < nbAttr; i++) { |
| stateInfo.add(null); |
| } |
| |
| /* |
| * 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); |
| } |
| |
| /* Query the storage backend */ |
| backend.doQuery(stateInfo, t); |
| |
| /* |
| * We should have previously inserted an interval for every |
| * attribute. |
| */ |
| for (ITmfStateInterval interval : stateInfo) { |
| if (interval == null) { |
| throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$ |
| } |
| } |
| return stateInfo; |
| } |
| } |
| |
| @Override |
| public ITmfStateInterval querySingleState(long t, int attributeQuark) |
| throws TimeRangeException, StateSystemDisposedException { |
| if (isDisposed) { |
| throw new StateSystemDisposedException(); |
| } |
| try (TraceCompassLogUtils.ScopeLog log = new TraceCompassLogUtils.ScopeLog(LOGGER, Level.FINER, "StateSystem:SingleQuery", //$NON-NLS-1$ |
| "ssid", this.getSSID(), //$NON-NLS-1$ |
| "ts", t, //$NON-NLS-1$ |
| "attribute", attributeQuark)) { //$NON-NLS-1$ |
| ITmfStateInterval ret = transState.getIntervalAt(t, attributeQuark); |
| if (ret == null) { |
| /* |
| * The transient state did not have the information, let's look |
| * into the backend next. |
| */ |
| ret = backend.doSingularQuery(t, attributeQuark); |
| } |
| |
| if (ret == null) { |
| /* |
| * If we did our job correctly, there should be intervals for |
| * every possible attribute, over all the valid time range. |
| */ |
| throw new IllegalStateException("Incoherent interval storage"); //$NON-NLS-1$ |
| } |
| return ret; |
| } |
| } |
| |
| @Override |
| public Iterable<@NonNull ITmfStateInterval> query2D(Collection<@NonNull Integer> quarks, Collection<@NonNull Long> times) |
| throws StateSystemDisposedException, TimeRangeException, IndexOutOfBoundsException { |
| if (isDisposed) { |
| throw new StateSystemDisposedException(); |
| } |
| if (times.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| TimeRangeCondition timeCondition = TimeRangeCondition.forDiscreteRange(times); |
| return query2D(quarks, timeCondition, false); |
| } |
| |
| @Override |
| public Iterable<@NonNull ITmfStateInterval> query2D(Collection<@NonNull Integer> quarks, long start, long end) |
| throws StateSystemDisposedException, TimeRangeException, IndexOutOfBoundsException { |
| if (isDisposed) { |
| throw new StateSystemDisposedException(); |
| } |
| |
| boolean reverse = (start > end) ? true : false; |
| TimeRangeCondition timeCondition = TimeRangeCondition.forContinuousRange(Math.min(start, end), Math.max(start, end)); |
| return query2D(quarks, timeCondition, reverse); |
| } |
| |
| private Iterable<@NonNull ITmfStateInterval> query2D(@NonNull Collection<@NonNull Integer> quarks, TimeRangeCondition timeCondition, boolean reverse) |
| throws TimeRangeException, IndexOutOfBoundsException { |
| if (timeCondition.min() < getStartTime()) { |
| throw new TimeRangeException("Time conditions " + timeCondition.min() + " is lower than state system start time: " + getStartTime()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| if (quarks.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| IntegerRangeCondition quarkCondition = IntegerRangeCondition.forDiscreteRange(quarks); |
| if (quarkCondition.min() < 0 || quarkCondition.max() >= getNbAttributes()) { |
| throw new IndexOutOfBoundsException(); |
| } |
| |
| Iterable<@NonNull ITmfStateInterval> transStateIterable = transState.query2D(quarks, timeCondition); |
| Iterable<@NonNull ITmfStateInterval> backendIterable = backend.query2D(quarkCondition, timeCondition, reverse); |
| |
| return Iterables.concat(transStateIterable, backendIterable); |
| } |
| |
| @Override |
| public void removeFiles() { |
| backend.removeFiles(); |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Debug methods |
| // -------------------------------------------------------------------------- |
| |
| static void logMissingInterval(int attribute, long timestamp) { |
| Activator.getDefault().logInfo("No data found in history for attribute " + //$NON-NLS-1$ |
| attribute + " at time " + timestamp + //$NON-NLS-1$ |
| ", returning dummy interval"); //$NON-NLS-1$ |
| } |
| } |