blob: 813aab16940755bcbf6aba40f88fcf75298eba23 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 Ericsson
*
* 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
* Matthew Khouzam - Modified to use a TreeSet
* Patrick Tasse - Add message to exceptions
******************************************************************************/
package org.eclipse.tracecompass.internal.statesystem.core.backend;
import java.io.File;
import java.io.FileInputStream;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils;
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.backend.IStateHistoryBackend;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval;
import com.google.common.collect.Iterables;
/**
* State history back-end that stores its intervals in RAM only. It cannot be
* saved to disk, which means we need to rebuild it every time we re-open a
* trace. But it's relatively quick to build, so this shouldn't be a problem in
* most cases.
*
* This should only be used with very small state histories (and/or, very small
* traces). Since it's stored in standard Collections, it's limited to 2^31
* intervals.
*
* @author Alexandre Montplaisir
*/
public class InMemoryBackend implements IStateHistoryBackend {
private static final @NonNull Logger LOGGER = TraceCompassLog.getLogger(InMemoryBackend.class);
private final @NonNull String ssid;
private final NavigableSet<@NonNull ITmfStateInterval> intervals;
private final long startTime;
private volatile long latestTime;
/**
* Constructor
*
* @param ssid
* The state system's ID
* @param startTime
* The start time of this interval store
*/
public InMemoryBackend(@NonNull String ssid, long startTime) {
this.ssid = ssid;
this.startTime = startTime;
this.latestTime = startTime;
/**
* We need to compare the end time and the attribute, because we can
* have 2 intervals with the same end time (for different attributes).
* And TreeSet needs a unique "key" per element.
*/
this.intervals = new TreeSet<>(Comparator
.comparing(ITmfStateInterval::getEndTime)
.thenComparing(ITmfStateInterval::getAttribute));
}
@Override
public String getSSID() {
return ssid;
}
@Override
public long getStartTime() {
return startTime;
}
@Override
public long getEndTime() {
return latestTime;
}
@Override
public void insertPastState(long stateStartTime, long stateEndTime,
int quark, Object value) throws TimeRangeException {
/* Make sure the passed start/end times make sense */
if (stateStartTime > stateEndTime || stateStartTime < startTime) {
throw new TimeRangeException(ssid + " Interval Start:" + stateStartTime + ", Interval End:" + stateEndTime + ", Backend Start:" + startTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
ITmfStateInterval interval = new TmfStateInterval(stateStartTime, stateEndTime, quark, value);
/* Add the interval into the tree */
synchronized (intervals) {
intervals.add(interval);
}
/* Update the "latest seen time" */
if (stateEndTime > latestTime) {
latestTime = stateEndTime;
}
}
@Override
public void doQuery(List<ITmfStateInterval> currentStateInfo, long t)
throws TimeRangeException {
if (!checkValidTime(t)) {
throw new TimeRangeException(ssid + " Time:" + t + ", Start:" + startTime + ", End:" + latestTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/*
* The intervals are sorted by end time, so we can binary search to get
* the first possible interval, then only compare their start times.
*/
synchronized (intervals) {
Iterator<ITmfStateInterval> iter = searchforEndTime(intervals, 0, t).iterator();
for (int modCount = 0; iter.hasNext() && modCount < currentStateInfo.size();) {
ITmfStateInterval entry = iter.next();
final long entryStartTime = entry.getStartTime();
if (entryStartTime <= t) {
/* Add this interval to the returned values */
currentStateInfo.set(entry.getAttribute(), entry);
modCount++;
}
}
}
}
@Override
public ITmfStateInterval doSingularQuery(long t, int attributeQuark)
throws TimeRangeException {
if (!checkValidTime(t)) {
throw new TimeRangeException(ssid + " Time:" + t + ", Start:" + startTime + ", End:" + latestTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/*
* The intervals are sorted by end time, so we can binary search to get
* the first possible interval, then only compare their start times.
*/
synchronized (intervals) {
Iterable<ITmfStateInterval> iter = searchforEndTime(intervals, attributeQuark, t);
for (ITmfStateInterval entry : iter) {
final boolean attributeMatches = (entry.getAttribute() == attributeQuark);
final long entryStartTime = entry.getStartTime();
if (attributeMatches) {
if (entryStartTime <= t) {
/* This is the droid we are looking for */
return entry;
}
}
}
}
return null;
}
private boolean checkValidTime(long t) {
return (t >= startTime && t <= latestTime);
}
@Override
public void finishedBuilding(long endTime) throws TimeRangeException {
/* Nothing to do */
}
@Override
public FileInputStream supplyAttributeTreeReader() {
/* Saving to disk not supported */
return null;
}
@Override
public File supplyAttributeTreeWriterFile() {
/* Saving to disk not supported */
return null;
}
@Override
public long supplyAttributeTreeWriterFilePosition() {
/* Saving to disk not supported */
return -1;
}
@Override
public void removeFiles() {
/* Nothing to do */
}
@Override
public void dispose() {
/* Nothing to do */
}
private static Iterable<@NonNull ITmfStateInterval> searchforEndTime(NavigableSet<@NonNull ITmfStateInterval> tree, int quark, long time) {
ITmfStateInterval dummyInterval = new TmfStateInterval(-1, time, quark, (Object) null);
return tree.tailSet(dummyInterval);
}
@Override
public Iterable<@NonNull ITmfStateInterval> query2D(IntegerRangeCondition quarks, TimeRangeCondition times)
throws TimeRangeException {
try (TraceCompassLogUtils.ScopeLog log = new TraceCompassLogUtils.ScopeLog(LOGGER, Level.FINER, "InMemoryBackend:query2D", //$NON-NLS-1$
"ssid", getSSID(), //$NON-NLS-1$
"quarks", quarks, //$NON-NLS-1$
"times", times)) { //$NON-NLS-1$
synchronized (intervals) {
return Iterables.filter(searchforEndTime(intervals, quarks.min(), times.min()),
interval -> quarks.test(interval.getAttribute())
&& times.intersects(interval.getStartTime(), interval.getEndTime()));
}
}
}
}