blob: 15475884ee23302c4f8bb623de6c41e787b1ea43 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 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:
* Patrick Tasse - Initial API and implementation
* Bernd Hufmann - Updated signal handling
* Marc-Andre Laperle - Map from binary file
* Mikael Ferland - Support multiple symbol providers for a trace
*******************************************************************************/
package org.eclipse.tracecompass.analysis.profiling.ui.views.flamechart;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider.CallStackDataProvider;
import org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider.CallStackEntryModel;
import org.eclipse.tracecompass.internal.analysis.profiling.ui.Activator;
import org.eclipse.tracecompass.internal.analysis.profiling.ui.views.flamechart.Messages;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
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.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
import org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProviderPreferencePage;
import org.eclipse.tracecompass.tmf.ui.symbols.SymbolProviderConfigDialog;
import org.eclipse.tracecompass.tmf.ui.views.timegraph.BaseDataProviderTimeGraphView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NamedTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* Main implementation for the Call Stack view
*
* @author Patrick Tasse
*/
public class FlameChartView extends BaseDataProviderTimeGraphView {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
/** View ID. */
public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.callstack"; //$NON-NLS-1$
private static final String[] COLUMN_NAMES = new String[] {
Messages.CallStackView_FunctionColumn,
Messages.CallStackView_PidTidColumn,
Messages.CallStackView_DepthColumn,
Messages.CallStackView_EntryTimeColumn,
Messages.CallStackView_ExitTimeColumn,
Messages.CallStackView_DurationColumn
};
private static final Comparator<ITimeGraphEntry> NAME_COMPARATOR = Comparator.comparing(ITimeGraphEntry::getName);
private static final Comparator<ITimeGraphEntry> THREAD_ID_COMPARATOR = (o1, o2) -> {
if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
TimeGraphEntry t1 = (TimeGraphEntry) o1;
TimeGraphEntry t2 = (TimeGraphEntry) o2;
ITimeGraphEntryModel model1 = t1.getModel();
ITimeGraphEntryModel model2 = t2.getModel();
if (model1 instanceof CallStackEntryModel && model2 instanceof CallStackEntryModel) {
CallStackEntryModel m1 = (CallStackEntryModel) model1;
CallStackEntryModel m2 = (CallStackEntryModel) model2;
if (m1.getStackLevel() == 0 && m2.getStackLevel() == 0) {
return Long.compare(m1.getPid(), m2.getPid());
}
}
}
return 0;
};
private static final Comparator<ITimeGraphEntry> DEPTH_COMPARATOR = (o1, o2) -> {
if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
TimeGraphEntry t1 = (TimeGraphEntry) o1;
TimeGraphEntry t2 = (TimeGraphEntry) o2;
ITimeGraphEntryModel model1 = t1.getModel();
ITimeGraphEntryModel model2 = t2.getModel();
if (model1 instanceof CallStackEntryModel && model2 instanceof CallStackEntryModel) {
return Integer.compare(((CallStackEntryModel) model1).getStackLevel(), ((CallStackEntryModel) model2).getStackLevel());
}
}
return 0;
};
@SuppressWarnings("unchecked")
private static final Comparator<ITimeGraphEntry>[] COMPARATORS = new Comparator[] {
Comparator.comparing(ITimeGraphEntry::getName),
THREAD_ID_COMPARATOR,
DEPTH_COMPARATOR,
Comparator.comparingLong(ITimeGraphEntry::getStartTime),
Comparator.comparingLong(ITimeGraphEntry::getEndTime)
};
private static final String[] FILTER_COLUMN_NAMES = new String[] {
Messages.CallStackView_ThreadColumn
};
private static final Image PROCESS_IMAGE = Objects.requireNonNull(Activator.getDefault()).getImageFromPath("icons/obj16/process_obj.gif"); //$NON-NLS-1$
private static final Image THREAD_IMAGE = Objects.requireNonNull(Activator.getDefault()).getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$
private static final Image STACKFRAME_IMAGE = Objects.requireNonNull(Activator.getDefault()).getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$
private static final String IMPORT_BINARY_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
// The next event action
private @Nullable Action fNextEventAction;
// The previous event action
private @Nullable Action fPrevEventAction;
// The next item action
private @Nullable Action fNextItemAction;
// The previous item action
private @Nullable Action fPreviousItemAction;
// The action to import a binary file mapping */
private @Nullable Action fConfigureSymbolsAction;
// When set to true, syncToTime() will select the first call stack entry
// whose current state start time exactly matches the sync time.
private boolean fSyncSelection = false;
private final Map<Long, ITimeGraphState> fFunctions = new HashMap<>();
// ------------------------------------------------------------------------
// Classes
// ------------------------------------------------------------------------
private class CallStackComparator implements Comparator<ITimeGraphEntry> {
@Override
public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
TimeGraphEntry t1 = (TimeGraphEntry) o1;
TimeGraphEntry t2 = (TimeGraphEntry) o2;
ITimeGraphEntryModel model1 = t1.getModel();
ITimeGraphEntryModel model2 = t2.getModel();
CallStackEntryModel m1 = (CallStackEntryModel) model1;
CallStackEntryModel m2 = (CallStackEntryModel) model2;
if (m1.getStackLevel() == 0 && m2.getStackLevel() == 0) {
return NAME_COMPARATOR.compare(t1, t2);
} else if (m1.getStackLevel() == -1 && m2.getStackLevel() == -1) {
return Integer.compare(m1.getPid(), m2.getPid());
}
}
return 0;
}
}
private class CallStackTreeLabelProvider extends TreeLabelProvider {
@Override
public @Nullable Image getColumnImage(@Nullable Object element, int columnIndex) {
if (columnIndex == 0 && element instanceof TimeGraphEntry) {
TimeGraphEntry entry = (TimeGraphEntry) element;
ITimeGraphEntryModel entryModel = entry.getModel();
if (entryModel instanceof CallStackEntryModel) {
CallStackEntryModel callStackEntryModel = (CallStackEntryModel) entryModel;
if (callStackEntryModel.getStackLevel() == CallStackEntryModel.PROCESS) {
return PROCESS_IMAGE;
} else if (callStackEntryModel.getStackLevel() == CallStackEntryModel.THREAD) {
return THREAD_IMAGE;
} else if (fFunctions.containsKey(entryModel.getId())) {
return STACKFRAME_IMAGE;
}
}
}
return null;
}
@Override
public String getColumnText(@Nullable Object element, int columnIndex) {
if (element instanceof TraceEntry && columnIndex == 0) {
return String.valueOf(((TraceEntry) element).getName());
} else if (element instanceof TimeGraphEntry) {
TimeGraphEntry entry = (TimeGraphEntry) element;
ITimeGraphEntryModel model = entry.getModel();
ITimeGraphState function = fFunctions.get(model.getId());
if (columnIndex == 0 && (!(model instanceof CallStackEntryModel) ||
(model instanceof CallStackEntryModel && ((CallStackEntryModel) model).getStackLevel() <= 0))) {
// trace, process, threads
return String.valueOf(entry.getName());
}
if (function != null) {
if (columnIndex == 0) {
// functions
return String.valueOf(function.getLabel());
} else if (columnIndex == 2 && model instanceof CallStackEntryModel) {
return Integer.toString(((CallStackEntryModel) model).getStackLevel());
} else if (columnIndex == 3) {
return String.valueOf(TmfTimestampFormat.getDefaulTimeFormat().format(function.getStartTime()));
} else if (columnIndex == 4) {
return String.valueOf(TmfTimestampFormat.getDefaulTimeFormat().format(function.getStartTime() + function.getDuration()));
} else if (columnIndex == 5) {
return String.valueOf(TmfTimestampFormat.getDefaulIntervalFormat().format(function.getDuration()));
}
} else if (model instanceof CallStackEntryModel) {
CallStackEntryModel callStackEntryModel = (CallStackEntryModel) model;
if (columnIndex == 1 && callStackEntryModel.getStackLevel() <= 0 && callStackEntryModel.getPid() >= 0) {
return Integer.toString(callStackEntryModel.getPid());
} else if (columnIndex == 3 && callStackEntryModel.getStackLevel() <= 0) {
return String.valueOf(TmfTimestampFormat.getDefaulTimeFormat().format(model.getStartTime()));
} else if (columnIndex == 4 && callStackEntryModel.getStackLevel() <= 0) {
return String.valueOf(TmfTimestampFormat.getDefaulTimeFormat().format(model.getEndTime()));
}
}
}
return ""; //$NON-NLS-1$
}
}
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Default constructor
*/
public FlameChartView() {
this(ID, new CallStackPresentationProvider(), CallStackDataProvider.ID);
}
/**
* Custom constructor, used for extending the callstack view with a custom
* presentation provider or data provider.
*
* @param id
* The ID of the view
* @param presentationProvider
* the presentation provider
* @param dataProviderID
* the data provider id
* @since 3.3
*/
public FlameChartView(String id, TimeGraphPresentationProvider presentationProvider, String dataProviderID) {
super(id, presentationProvider, dataProviderID);
setTreeColumns(COLUMN_NAMES, COMPARATORS, 0);
setTreeLabelProvider(new CallStackTreeLabelProvider());
setEntryComparator(new CallStackComparator());
setFilterColumns(FILTER_COLUMN_NAMES);
setFilterLabelProvider(new CallStackTreeLabelProvider());
}
// ------------------------------------------------------------------------
// ViewPart
// ------------------------------------------------------------------------
@Override
public void createPartControl(@Nullable Composite parent) {
super.createPartControl(parent);
getTimeGraphViewer().addTimeListener(event -> synchingToTime(event.getBeginTime()));
getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(@Nullable MouseEvent event) {
ITimeGraphEntry selection = getTimeGraphViewer().getSelection();
if (!(selection instanceof TimeGraphEntry)) {
// also null checks
return;
}
ITimeGraphState function = fFunctions.get(((TimeGraphEntry) selection).getModel().getId());
if (function != null) {
long entryTime = function.getStartTime();
long exitTime = entryTime + function.getDuration();
TmfTimeRange range = new TmfTimeRange(TmfTimestamp.fromNanos(entryTime), TmfTimestamp.fromNanos(exitTime));
broadcast(new TmfWindowRangeUpdatedSignal(FlameChartView.this, range, getTrace()));
getTimeGraphViewer().setStartFinishTime(entryTime, exitTime);
startZoomThread(entryTime, exitTime);
}
}
});
getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(@Nullable MouseEvent e) {
TimeGraphControl timeGraphControl = getTimeGraphViewer().getTimeGraphControl();
ISelection selection = timeGraphControl.getSelection();
if (selection instanceof IStructuredSelection) {
for (Object object : ((IStructuredSelection) selection).toList()) {
if (object instanceof NamedTimeEvent) {
NamedTimeEvent event = (NamedTimeEvent) object;
long startTime = event.getTime();
long endTime = startTime + event.getDuration();
TmfTimeRange range = new TmfTimeRange(TmfTimestamp.fromNanos(startTime), TmfTimestamp.fromNanos(endTime));
broadcast(new TmfWindowRangeUpdatedSignal(FlameChartView.this, range, getTrace()));
getTimeGraphViewer().setStartFinishTime(startTime, endTime);
startZoomThread(startTime, endTime);
break;
}
}
}
}
});
IEditorPart editor = getSite().getPage().getActiveEditor();
if (editor instanceof ITmfTraceEditor) {
ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
if (trace != null) {
traceSelected(new TmfTraceSelectedSignal(this, trace));
}
}
}
/**
* Handler for the selection range signal.
*
* @param signal
* The incoming signal
* @since 1.0
*/
@Override
@TmfSignalHandler
public void selectionRangeUpdated(final @Nullable TmfSelectionRangeUpdatedSignal signal) {
fSyncSelection = true;
super.selectionRangeUpdated(signal);
}
/**
* @since 2.0
*/
@Override
@TmfSignalHandler
public void windowRangeUpdated(final @Nullable TmfWindowRangeUpdatedSignal signal) {
if (signal == null || signal.getSource() == this) {
return;
}
super.windowRangeUpdated(signal);
}
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
/**
* @since 2.0
*/
@Override
protected void rebuild() {
super.rebuild();
updateConfigureSymbolsAction();
}
@Override
protected void buildEntryList(final ITmfTrace trace, final ITmfTrace parentTrace, final IProgressMonitor monitor) {
CallStackDataProvider provider = DataProviderManager.getInstance().getDataProvider(trace,
getProviderId(), CallStackDataProvider.class);
if (provider == null) {
addUnavailableEntry(trace, parentTrace);
return;
}
provider.resetFunctionNames(monitor);
super.buildEntryList(trace, parentTrace, monitor);
}
private void addUnavailableEntry(ITmfTrace trace, ITmfTrace parentTrace) {
String name = Messages.CallStackView_StackInfoNotAvailable + ' ' + '(' + trace.getName() + ')';
TimeGraphEntry unavailableEntry = new TimeGraphEntry(name, 0, 0) {
@Override
public boolean hasTimeEvents() {
return false;
}
};
addToEntryList(parentTrace, Collections.singletonList(unavailableEntry));
if (parentTrace == getTrace()) {
refresh();
}
}
@Override
protected TimeEvent createTimeEvent(@Nullable TimeGraphEntry entry, @Nullable ITimeGraphState state) {
if (state == null) {
throw new NullPointerException("state should not be null when creating time event"); //$NON-NLS-1$
}
if (state.getValue() == Integer.MIN_VALUE) {
return new NullTimeEvent(entry, state.getStartTime(), state.getDuration());
}
String label = state.getLabel();
if (label != null) {
return new NamedTimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), label, state.getActiveProperties());
}
return new TimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), state.getActiveProperties());
}
/**
* @since 1.2
*/
@Override
protected void synchingToTime(final long time) {
List<TimeGraphEntry> traceEntries = getEntryList(getTrace());
if (traceEntries != null) {
for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
Iterable<TimeGraphEntry> unfiltered = Utils.flatten(traceEntry);
Map<Long, TimeGraphEntry> map = Maps.uniqueIndex(unfiltered, e -> e.getModel().getId());
// use time -1 as a lower bound for the end of Time events to be included.
SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(time - 1, time, 2, map.keySet());
TmfModelResponse<@NonNull TimeGraphModel> response = traceEntry.getProvider().fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(filter), null);
TimeGraphModel model = response.getModel();
if (model != null) {
for (ITimeGraphRowModel row : model.getRows()) {
syncToRow(row, time, map);
}
}
}
}
fSyncSelection = false;
if (Display.getCurrent() != null) {
getTimeGraphViewer().refresh();
}
}
private void syncToRow(ITimeGraphRowModel rowModel, long time, Map<Long, TimeGraphEntry> entryMap) {
long id = rowModel.getEntryID();
List<@NonNull ITimeGraphState> list = rowModel.getStates();
if (!list.isEmpty()) {
ITimeGraphState event = list.get(0);
if (event.getStartTime() + event.getDuration() <= time && list.size() > 1) {
/*
* get the second time graph state as passing time - 1 as a first argument to
* the filter will get the previous state, if time is the beginning of an event
*/
event = list.get(1);
}
if (event.getLabel() != null) {
fFunctions.put(id, event);
} else {
fFunctions.remove(id);
}
if (fSyncSelection && time == event.getStartTime()) {
TimeGraphEntry entry = entryMap.get(id);
if (entry != null) {
fSyncSelection = false;
Display.getDefault().asyncExec(() -> {
getTimeGraphViewer().setSelection(entry, true);
getTimeGraphViewer().getTimeGraphControl().fireSelectionChanged();
});
}
}
} else {
fFunctions.remove(id);
}
}
private void makeActions() {
Action previousItemAction = getTimeGraphViewer().getPreviousItemAction();
fPreviousItemAction = previousItemAction;
previousItemAction.setText(Messages.FlameChartView_PreviousItemActionNameText);
previousItemAction.setToolTipText(Messages.FlameChartView_PreviousItemActionToolTipText);
Action nextItemAction = getTimeGraphViewer().getNextItemAction();
fNextItemAction = nextItemAction;
nextItemAction.setText(Messages.FlameChartView_NextItemActionNameText);
nextItemAction.setToolTipText(Messages.FlameChartView_NextItemActionToolTipText);
}
/**
* @since 1.2
*/
@Override
protected void fillLocalToolBar(@Nullable IToolBarManager manager) {
if (manager == null) {
return;
}
makeActions();
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getConfigureSymbolsAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getShowFilterDialogAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getResetScaleAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getPreviousEventAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getNextEventAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getToggleBookmarkAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getPreviousMarkerAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getNextMarkerAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fPreviousItemAction);
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fNextItemAction);
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getZoomInAction());
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, getTimeGraphViewer().getZoomOutAction());
}
/**
* Get the the next event action.
*
* @return The action object
*/
private Action getNextEventAction() {
Action nextAction = fNextEventAction;
if (nextAction == null) {
Action superNextAction = getTimeGraphViewer().getNextEventAction();
nextAction = new Action() {
@Override
public void run() {
TimeGraphViewer viewer = getTimeGraphViewer();
ITimeGraphEntry entry = viewer.getSelection();
if (entry instanceof TimeGraphEntry) {
TimeGraphEntry callStackEntry = (TimeGraphEntry) entry;
ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = getProvider(callStackEntry);
long selectionBegin = viewer.getSelectionBegin();
SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(selectionBegin, Long.MAX_VALUE, 2, Collections.singleton(callStackEntry.getModel().getId()));
TmfModelResponse<@NonNull TimeGraphModel> response = provider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(filter), null);
TimeGraphModel model = response.getModel();
if (model == null || model.getRows().size() != 1) {
return;
}
List<@NonNull ITimeGraphState> row = model.getRows().get(0).getStates();
if (row.size() != 1) {
return;
}
ITimeGraphState stackInterval = row.get(0);
if (stackInterval.getStartTime() <= selectionBegin && selectionBegin <= stackInterval.getStartTime() + stackInterval.getDuration()) {
viewer.setSelectedTimeNotify(stackInterval.getStartTime() + stackInterval.getDuration() + 1, true);
} else {
viewer.setSelectedTimeNotify(stackInterval.getStartTime(), true);
}
int stackLevel = stackInterval.getValue();
ITimeGraphEntry selectedEntry = callStackEntry.getParent().getChildren().get(Integer.max(0, stackLevel - 1));
viewer.setSelection(selectedEntry, true);
viewer.getTimeGraphControl().fireSelectionChanged();
startZoomThread(viewer.getTime0(), viewer.getTime1());
}
}
};
nextAction.setText(superNextAction.getText());
nextAction.setToolTipText(superNextAction.getToolTipText());
nextAction.setImageDescriptor(superNextAction.getImageDescriptor());
fNextEventAction = nextAction;
}
return nextAction;
}
/**
* Get the previous event action.
*
* @return The Action object
*/
private Action getPreviousEventAction() {
Action prevAction = fPrevEventAction;
if (prevAction == null) {
Action superPrevAction = getTimeGraphViewer().getPreviousEventAction();
prevAction = new Action() {
@Override
public void run() {
TimeGraphViewer viewer = getTimeGraphViewer();
ITimeGraphEntry entry = viewer.getSelection();
if (entry instanceof TimeGraphEntry) {
TimeGraphEntry callStackEntry = (TimeGraphEntry) entry;
ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = getProvider(callStackEntry);
long selectionBegin = viewer.getSelectionBegin();
SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(Lists.newArrayList(Long.MIN_VALUE, selectionBegin), Collections.singleton(callStackEntry.getModel().getId()));
TmfModelResponse<@NonNull TimeGraphModel> response = provider.fetchRowModel(FetchParametersUtils.selectionTimeQueryToMap(filter), null);
TimeGraphModel model = response.getModel();
if (model == null || model.getRows().size() != 1) {
return;
}
List<@NonNull ITimeGraphState> row = model.getRows().get(0).getStates();
if (row.size() != 1) {
return;
}
ITimeGraphState stackInterval = row.get(0);
viewer.setSelectedTimeNotify(stackInterval.getStartTime(), true);
int stackLevel = stackInterval.getValue();
ITimeGraphEntry selectedEntry = callStackEntry.getParent().getChildren().get(Integer.max(0, stackLevel - 1));
viewer.setSelection(selectedEntry, true);
viewer.getTimeGraphControl().fireSelectionChanged();
startZoomThread(viewer.getTime0(), viewer.getTime1());
}
}
};
prevAction.setText(superPrevAction.getText());
prevAction.setToolTipText(superPrevAction.getToolTipText());
prevAction.setImageDescriptor(superPrevAction.getImageDescriptor());
fPrevEventAction = prevAction;
}
return prevAction;
}
// ------------------------------------------------------------------------
// Methods related to function name mapping
// ------------------------------------------------------------------------
private Action getConfigureSymbolsAction() {
if (fConfigureSymbolsAction != null) {
return fConfigureSymbolsAction;
}
Action configureSymbolsAction = new Action(Messages.CallStackView_ConfigureSymbolProvidersText) {
@Override
public void run() {
SymbolProviderConfigDialog dialog = new SymbolProviderConfigDialog(getSite().getShell(), getProviderPages());
if (dialog.open() == IDialogConstants.OK_ID) {
List<TimeGraphEntry> traceEntries = getEntryList(getTrace());
if (traceEntries != null) {
for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = traceEntry.getProvider();
if (provider instanceof CallStackDataProvider) {
((CallStackDataProvider) provider).resetFunctionNames(new NullProgressMonitor());
}
// reset full and zoomed events here
Iterable<TimeGraphEntry> flatten = Utils.flatten(traceEntry);
flatten.forEach(e -> e.setSampling(null));
// recompute full events
long start = traceEntry.getStartTime();
long end = traceEntry.getEndTime();
final long resolution = Long.max(1, (end - start) / getDisplayWidth());
zoomEntries(flatten, start, end, resolution, new NullProgressMonitor());
}
// zoomed events will be retriggered by refreshing
refresh();
}
synchingToTime(getTimeGraphViewer().getSelectionBegin());
}
}
};
configureSymbolsAction.setToolTipText(Messages.CallStackView_ConfigureSymbolProvidersTooltip);
configureSymbolsAction.setImageDescriptor(Objects.requireNonNull(Activator.getDefault()).getImageDescripterFromPath(IMPORT_BINARY_ICON_PATH));
/*
* The updateConfigureSymbolsAction() method (called by refresh()) will set the
* action to true if applicable after the symbol provider has been properly
* loaded.
*/
configureSymbolsAction.setEnabled(false);
fConfigureSymbolsAction = configureSymbolsAction;
return configureSymbolsAction;
}
/**
* @return an array of {@link ISymbolProviderPreferencePage} that will configure
* the current traces
*/
private ISymbolProviderPreferencePage[] getProviderPages() {
List<ISymbolProviderPreferencePage> pages = new ArrayList<>();
ITmfTrace trace = getTrace();
if (trace != null) {
for (ITmfTrace subTrace : getTracesToBuild(trace)) {
Collection<@NonNull ISymbolProvider> symbolProviders = SymbolProviderManager.getInstance().getSymbolProviders(subTrace);
for (org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProvider provider : Iterables.filter(symbolProviders, org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProvider.class)) {
ISymbolProviderPreferencePage page = provider.createPreferencePage();
if (page != null) {
pages.add(page);
}
}
}
}
return pages.toArray(new ISymbolProviderPreferencePage[pages.size()]);
}
/**
* Update the enable status of the configure symbols action
*/
private void updateConfigureSymbolsAction() {
ISymbolProviderPreferencePage[] providerPages = getProviderPages();
getConfigureSymbolsAction().setEnabled(providerPages.length > 0);
}
@TmfSignalHandler
@Override
public void traceClosed(@Nullable TmfTraceClosedSignal signal) {
List<@NonNull TimeGraphEntry> traceEntries = getEntryList(Objects.requireNonNull(signal).getTrace());
if (traceEntries != null) {
/*
* remove functions associated to the trace's entries.
*/
Iterable<TimeGraphEntry> all = Iterables.concat(Iterables.transform(traceEntries, Utils::flatten));
all.forEach(entry -> fFunctions.remove(entry.getModel().getId()));
}
super.traceClosed(signal);
}
}