blob: 7da87675ccb53ea773038c574ecf85fee80f493e [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2018 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
**********************************************************************/
package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.output;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.output.DataDrivenOutputEntry.IdGetter;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.output.DataDrivenOutputEntry.QuarkCallback;
import org.eclipse.tracecompass.internal.tmf.core.model.AbstractTmfTraceDataProvider;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.util.Pair;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
/**
* This data provider will return time graph models (wrapped in a response)
* based on a query filter. The models can be used afterwards by any viewer to
* draw time graphs. Model returned is for XML analysis.
*
* @author Loic Prieur-Drevon
* @author Geneviève Bastien
*/
public class DataDrivenTimeGraphDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<TimeGraphEntryModel> {
/**
* Provider unique ID.
*/
public static final String ID = "org.eclipse.tracecompass.tmf.analysis.xml.core.output.DataDrivenTimeGraphDataProvider"; //$NON-NLS-1$
private static final AtomicLong sfAtomicId = new AtomicLong();
private final List<ITmfStateSystem> fSs;
private final List<DataDrivenOutputEntry> fEntries;
private final List<DataDrivenPresentationState> fValues;
/**
* Remember the unique mappings of state system and quark to entry ID.
*/
private final Table<ITmfStateSystem, Integer, Long> fBaseQuarkToId = HashBasedTable.create();
private final Map<Long, Pair<ITmfStateSystem, Integer>> fIDToDisplayQuark = new HashMap<>();
private final IdGetter fIdGenerator = (ss, quark) -> fBaseQuarkToId.row(ss).computeIfAbsent(quark, s -> sfAtomicId.getAndIncrement());
private final QuarkCallback fQuarkCallback = (id, ss, quark, dt) -> fIDToDisplayQuark.put(id, new Pair<>(ss, quark));
private final String fId;
/**
* Constructor
*
* @param trace
* The trace this data provider is for
* @param stateSystems
* The list of state systems to build it for
* @param entries
* The entries
* @param values
* The presentation values
* @param id
* The ID of the data provider
*/
public DataDrivenTimeGraphDataProvider(ITmfTrace trace, List<ITmfStateSystem> stateSystems, List<DataDrivenOutputEntry> entries, List<DataDrivenPresentationState> values, @Nullable String id) {
super(trace);
fSs = stateSystems;
fEntries = entries;
fValues = values;
fId = (id == null) ? ID : id;
}
@Override
public final TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
List<TimeGraphEntryModel> entryList = new ArrayList<>();
boolean isComplete = true;
String traceName = String.valueOf(getTrace().getName());
for (ITmfStateSystem ss : fSs) {
isComplete &= ss.waitUntilBuilt(0);
/* Don't query empty state system */
if (ss.getNbAttributes() > 0 && ss.getStartTime() != Long.MIN_VALUE) {
long start = ss.getStartTime();
long end = ss.getCurrentEndTime();
long id = fBaseQuarkToId.row(ss).computeIfAbsent(ITmfStateSystem.ROOT_ATTRIBUTE, s -> sfAtomicId.getAndIncrement());
TimeGraphEntryModel ssEntry = new TimeGraphEntryModel(id, -1, traceName, start, end);
entryList.add(ssEntry);
for (DataDrivenOutputEntry entry : fEntries) {
entryList.addAll(entry.buildEntries(ss, ssEntry.getId(), getTrace(), -1, StringUtils.EMPTY, end, fIdGenerator, fQuarkCallback));
}
}
}
Status status = isComplete ? Status.COMPLETED : Status.RUNNING;
String msg = isComplete ? CommonStatusMessage.COMPLETED : CommonStatusMessage.RUNNING;
TmfTreeModel<@NonNull TimeGraphEntryModel> tree = new TmfTreeModel<>(Collections.emptyList(), entryList);
return new TmfModelResponse<>(tree, status, msg);
}
@Override
public @NonNull String getId() {
return fId;
}
@Override
public @NonNull TmfModelResponse<@NonNull TimeGraphModel> fetchRowModel(@NonNull Map<@NonNull String, @NonNull Object> fetchParameters, @Nullable IProgressMonitor monitor) {
// TODO server: Parameters validation should be handle separately. It
// can be either in the data provider itself or before calling it. It
// will avoid the creation of filters and the content of the map can be
// use directly.
SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters);
if (filter == null) {
return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.INCORRECT_QUERY_PARAMETERS);
}
Table<ITmfStateSystem, Integer, Long> table = HashBasedTable.create();
for (Long id : filter.getSelectedItems()) {
Pair<ITmfStateSystem, Integer> pair = fIDToDisplayQuark.get(id);
if (pair != null) {
table.put(pair.getFirst(), pair.getSecond(), id);
}
}
List<@NonNull ITimeGraphRowModel> allRows = new ArrayList<>();
try {
for (Entry<ITmfStateSystem, Map<Integer, Long>> ssEntry : table.rowMap().entrySet()) {
Collection<@NonNull ITimeGraphRowModel> rows = createRows(ssEntry.getKey(), ssEntry.getValue(), fetchParameters, monitor);
allRows.addAll(rows);
}
} catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
}
return new TmfModelResponse<>(new TimeGraphModel(allRows), Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
private Collection<ITimeGraphRowModel> createRows(ITmfStateSystem ss, Map<Integer, Long> idToDisplayQuark,
Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull Object>>> predicates = new HashMap<>();
Multimap<@NonNull Integer, @NonNull String> regexesMap = DataProviderParameterUtils.extractRegexFilter(fetchParameters);
if (regexesMap != null) {
predicates.putAll(computeRegexPredicate(regexesMap));
}
long currentEndTime = ss.getCurrentEndTime();
Map<Integer, ITimeGraphRowModel> quarkToRow = new HashMap<>(idToDisplayQuark.size());
for (Entry<Integer, Long> entry : idToDisplayQuark.entrySet()) {
quarkToRow.put(entry.getKey(), new TimeGraphRowModel(entry.getValue(), new ArrayList<>()));
}
List<Long> timesRequested = DataProviderParameterUtils.extractTimeRequested(fetchParameters);
for (ITmfStateInterval interval : ss.query2D(idToDisplayQuark.keySet(), getTimes(ss, timesRequested))) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
ITimeGraphRowModel row = quarkToRow.get(interval.getAttribute());
if (row != null) {
List<@NonNull ITimeGraphState> states = row.getStates();
ITimeGraphState timeGraphState = getStateFromInterval(interval, currentEndTime);
applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
}
}
for (ITimeGraphRowModel model : quarkToRow.values()) {
model.getStates().sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
}
return quarkToRow.values();
}
private static TimeGraphState getStateFromInterval(ITmfStateInterval statusInterval, long currentEndTime) {
long time = statusInterval.getStartTime();
long duration = Math.min(currentEndTime, statusInterval.getEndTime() + 1) - time;
Object o = statusInterval.getValue();
if (o instanceof Integer) {
return new TimeGraphState(time, duration, ((Integer) o).intValue(), String.valueOf(o));
} else if (o instanceof Long) {
long l = (long) o;
return new TimeGraphState(time, duration, (int) l, "0x" + Long.toHexString(l)); //$NON-NLS-1$
} else if (o instanceof String) {
return new TimeGraphState(time, duration, Integer.MIN_VALUE, (String) o);
} else if (o instanceof Double) {
return new TimeGraphState(time, duration, ((Double) o).intValue());
}
return new TimeGraphState(time, duration, Integer.MIN_VALUE);
}
private static Set<Long> getTimes(ITmfStateSystem key, List<Long> timesRequested) {
Set<@NonNull Long> times = new HashSet<>();
for (long t : timesRequested) {
if (key.getStartTime() <= t && t <= key.getCurrentEndTime()) {
times.add(t);
}
}
return times;
}
@Override
public @NonNull TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Override
public @NonNull TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Deprecated
@Override
public TmfModelResponse<List<TimeGraphEntryModel>> fetchTree(@NonNull TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
@NonNull Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = fetchTree(parameters, monitor);
TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel();
List<TimeGraphEntryModel> treeModel = null;
if (model != null) {
treeModel = model.getEntries();
}
return new TmfModelResponse<>(treeModel, response.getStatus(), response.getStatusMessage());
}
@Deprecated
@Override
public @NonNull TmfModelResponse<@NonNull List<@NonNull ITimeGraphRowModel>> fetchRowModel(@NonNull SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
@NonNull Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
TmfModelResponse<@NonNull TimeGraphModel> response = fetchRowModel(parameters, monitor);
TimeGraphModel model = response.getModel();
List<@NonNull ITimeGraphRowModel> rows = null;
if (model != null) {
rows = model.getRows();
}
return new TmfModelResponse<>(rows, response.getStatus(), response.getStatusMessage());
}
@Deprecated
@Override
public @NonNull TmfModelResponse<@NonNull List<@NonNull ITimeGraphArrow>> fetchArrows(@NonNull TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
return fetchArrows(parameters, monitor);
}
@Deprecated
@Override
public @NonNull TmfModelResponse<@NonNull Map<@NonNull String, @NonNull String>> fetchTooltip(@NonNull SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
return fetchTooltip(parameters, monitor);
}
/**
* Get the values
*
* TODO: Remove when support of presentation from data provider is added
*
* @return The values
*/
protected List<DataDrivenPresentationState> getValues() {
return fValues;
}
}