blob: ec64f3bf89354ed8492b4cc643b27b1868f70ac0 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2020 É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 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.examples.core.data.provider;
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.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
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.dataprovider.X11ColorUtils;
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
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.ITimeGraphEntryModel;
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.ITmfTreeDataModel;
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
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.trace.TmfTraceUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
/**
* An example of a time graph data provider.
*
* This class is also in the developer documentation of Trace Compass. If it is
* modified here, the doc should also be updated.
*
* @author Geneviève Bastien
*/
@SuppressWarnings("restriction")
@NonNullByDefault
public class ExampleTimeGraphDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel>, IOutputStyleProvider {
/**
* Provider unique ID.
*/
public static final String ID = "org.eclipse.tracecompass.examples.timegraph.dataprovider"; //$NON-NLS-1$
private static final AtomicLong sfAtomicId = new AtomicLong();
private static final String STYLE0_NAME = "style0"; //$NON-NLS-1$
private static final String STYLE1_NAME = "style1"; //$NON-NLS-1$
private static final String STYLE2_NAME = "style2"; //$NON-NLS-1$
/* The map of basic styles */
private static final Map<String, OutputElementStyle> STATE_MAP;
/*
* A map of styles names to a style that has the basic style as parent, to
* avoid returning complete styles for each state
*/
private static final Map<String, OutputElementStyle> STYLE_MAP;
static {
/* Build three different styles to use as examples */
ImmutableMap.Builder<String, OutputElementStyle> builder = new ImmutableMap.Builder<>();
builder.put(STYLE0_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE0_NAME,
StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("blue")), //$NON-NLS-1$
StyleProperties.HEIGHT, 0.5f,
StyleProperties.OPACITY, 0.75f)));
builder.put(STYLE1_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE1_NAME,
StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("yellow")), //$NON-NLS-1$
StyleProperties.HEIGHT, 1.0f,
StyleProperties.OPACITY, 1.0f)));
builder.put(STYLE2_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE2_NAME,
StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("green")), //$NON-NLS-1$
StyleProperties.HEIGHT, 0.75f,
StyleProperties.OPACITY, 0.5f)));
STATE_MAP = builder.build();
/* build the style map too */
builder = new ImmutableMap.Builder<>();
builder.put(STYLE0_NAME, new OutputElementStyle(STYLE0_NAME));
builder.put(STYLE1_NAME, new OutputElementStyle(STYLE1_NAME));
builder.put(STYLE2_NAME, new OutputElementStyle(STYLE2_NAME));
STYLE_MAP = builder.build();
}
private final BiMap<Long, Integer> fIDToDisplayQuark = HashBiMap.create();
private ExampleStateSystemAnalysisModule fModule;
/**
* Constructor
*
* @param trace
* The trace this analysis is for
* @param module
* The scripted analysis for this data provider
*/
public ExampleTimeGraphDataProvider(ITmfTrace trace, ExampleStateSystemAnalysisModule module) {
super(trace);
fModule = module;
}
/**
* Create the time graph data provider
*
* @param trace
* The trace for which is the data provider
* @return The data provider
*/
public static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace) {
ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
return module != null ? new ExampleTimeGraphDataProvider(trace, module) : null;
}
@Override
public TmfModelResponse<TmfTreeModel<@NonNull ITimeGraphEntryModel>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
fModule.waitForInitialization();
ITmfStateSystem ss = fModule.getStateSystem();
if (ss == null) {
return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
}
boolean isComplete = ss.waitUntilBuilt(0);
long endTime = ss.getCurrentEndTime();
// Make an entry for each base quark
List<ITimeGraphEntryModel> entryList = new ArrayList<>();
for (Integer quark : ss.getQuarks("CPUs", "*")) { //$NON-NLS-1$ //$NON-NLS-2$
Long id = fIDToDisplayQuark.inverse().computeIfAbsent(quark, q -> sfAtomicId.getAndIncrement());
entryList.add(new TimeGraphEntryModel(id, -1, ss.getAttributeName(quark), ss.getStartTime(), endTime));
}
Status status = isComplete ? Status.COMPLETED : Status.RUNNING;
String msg = isComplete ? CommonStatusMessage.COMPLETED : CommonStatusMessage.RUNNING;
return new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), entryList), status, msg);
}
@Override
public @NonNull String getId() {
return ID;
}
@Override
public @NonNull TmfModelResponse<TimeGraphModel> fetchRowModel(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
ITmfStateSystem ss = fModule.getStateSystem();
if (ss == null) {
return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
}
try {
List<@NonNull ITimeGraphRowModel> rowModels = getDefaultRowModels(fetchParameters, ss, monitor);
if (rowModels == null) {
rowModels = Collections.emptyList();
}
return new TmfModelResponse<>(new TimeGraphModel(rowModels), Status.COMPLETED, CommonStatusMessage.COMPLETED);
} catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
}
}
private @Nullable List<ITimeGraphRowModel> getDefaultRowModels(Map<String, Object> fetchParameters, ITmfStateSystem ss, @Nullable IProgressMonitor monitor) throws IndexOutOfBoundsException, TimeRangeException, StateSystemDisposedException {
Map<Integer, ITimeGraphRowModel> quarkToRow = new HashMap<>();
// Prepare the quarks to display
Collection<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(fetchParameters);
if (selectedItems == null) {
// No selected items, take them all
selectedItems = fIDToDisplayQuark.keySet();
}
for (Long id : selectedItems) {
Integer quark = fIDToDisplayQuark.get(id);
if (quark != null) {
quarkToRow.put(quark, new TimeGraphRowModel(id, new ArrayList<>()));
}
}
// This regex map automatically filters or highlights the entry
// according to the global filter entered by the user
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));
}
// Query the state system to fill the states
long currentEndTime = ss.getCurrentEndTime();
for (ITmfStateInterval interval : ss.query2D(quarkToRow.keySet(), getTimes(ss, DataProviderParameterUtils.extractTimeRequested(fetchParameters)))) {
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);
// This call will compare the state with the filter predicate
applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
}
}
for (ITimeGraphRowModel model : quarkToRow.values()) {
model.getStates().sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
}
return new ArrayList<>(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 Long)) {
// Add a null state
return new TimeGraphState(time, duration, Integer.MIN_VALUE);
}
String styleName = "style" + ((Long) o) % 3; //$NON-NLS-1$
return new TimeGraphState(time, duration, String.valueOf(o), STYLE_MAP.get(styleName));
}
private static Set<Long> getTimes(ITmfStateSystem key, @Nullable List<Long> list) {
if (list == null) {
return Collections.emptySet();
}
Set<@NonNull Long> times = new HashSet<>();
for (long t : list) {
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) {
/**
* If there were arrows to be drawn, this is where they would be defined
*/
return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Override
public @NonNull TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
/**
* If there were tooltips to be drawn, this is where they would be
* defined
*/
return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Override
public TmfModelResponse<OutputStyleModel> fetchStyle(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
return new TmfModelResponse<>(new OutputStyleModel(STATE_MAP), Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Deprecated
@Override
public TmfModelResponse<List<ITimeGraphEntryModel>> fetchTree(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
TmfModelResponse<TmfTreeModel<ITimeGraphEntryModel>> modelResponse = fetchTree(parameters, monitor);
TmfTreeModel<ITimeGraphEntryModel> model = modelResponse.getModel();
return new TmfModelResponse<>(model == null ? null : model.getEntries(), modelResponse.getStatus(), modelResponse.getStatusMessage());
}
@Deprecated
@Override
public TmfModelResponse<List<ITimeGraphRowModel>> fetchRowModel(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
TmfModelResponse<TimeGraphModel> modelResponse = fetchRowModel(parameters, monitor);
TimeGraphModel model = modelResponse.getModel();
return new TmfModelResponse<>(model == null ? null : model.getRows(), modelResponse.getStatus(), modelResponse.getStatusMessage());
}
@Deprecated
@Override
public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
return fetchArrows(parameters, monitor);
}
@Deprecated
@Override
public TmfModelResponse<Map<String, String>> fetchTooltip(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
return fetchTooltip(parameters, monitor);
}
}