blob: 4cc0a7af6cce0c8b5c0629206bcc22a124fea642 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2018 École Polytechnique de Montréal 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
*******************************************************************************/
package org.eclipse.tracecompass.internal.analysis.graph.ui.criticalpath.view;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Phaser;
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.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.tracecompass.analysis.graph.core.criticalpath.CriticalPathModule;
import org.eclipse.tracecompass.internal.analysis.graph.core.dataprovider.CriticalPathDataProvider;
import org.eclipse.tracecompass.internal.analysis.graph.core.dataprovider.CriticalPathEntry;
import org.eclipse.tracecompass.internal.provisional.tmf.ui.widgets.timegraph.BaseDataProviderTimeGraphPresentationProvider;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
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.ITimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.signal.TmfEndSynchSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfStartAnalysisSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfStartSynchSignal;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.views.timegraph.BaseDataProviderTimeGraphView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeLinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
import org.eclipse.ui.IWorkbenchActionConstants;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
/**
* The Critical Path view
*
* @author Geneviève Bastien
* @author Francis Giraldeau
*/
public class CriticalPathView extends BaseDataProviderTimeGraphView {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
/** View ID */
public static final String ID = "org.eclipse.linuxtools.tmf.analysis.graph.ui.criticalpath.view.criticalpathview"; //$NON-NLS-1$
private static final double NANOINV = 10E-9;
private static final String COLUMN_PROCESS = Messages.getMessage(Messages.CriticalFlowView_columnProcess);
private static final String COLUMN_ELAPSED = Messages.getMessage(Messages.CriticalFlowView_columnElapsed);
private static final String COLUMN_PERCENT = Messages.getMessage(Messages.CriticalFlowView_columnPercent);
private static final String[] COLUMN_NAMES = new String[] {
COLUMN_PROCESS,
COLUMN_ELAPSED,
COLUMN_PERCENT
};
private static final String[] FILTER_COLUMN_NAMES = new String[] {
COLUMN_PROCESS
};
private Phaser fPhaser = new Phaser();
private class CriticalPathTreeLabelProvider extends TreeLabelProvider {
@Override
public String getColumnText(@Nullable Object element, int columnIndex) {
if (!(element instanceof TimeGraphEntry)) {
return StringUtils.EMPTY;
}
TimeGraphEntry entry = (TimeGraphEntry) element;
ITmfTreeDataModel model = entry.getEntryModel();
if (columnIndex == 0) {
return entry.getName();
} else if (columnIndex == 1 && model instanceof CriticalPathEntry) {
return String.format("%.9f", ((CriticalPathEntry) model).getSum() * NANOINV); //$NON-NLS-1$
} else if (columnIndex == 2 && model instanceof CriticalPathEntry) {
double percent = ((CriticalPathEntry) model).getPercent();
return String.format("%.2f", percent * 100); //$NON-NLS-1$
}
return StringUtils.EMPTY;
}
}
private class CriticalPathEntryComparator implements Comparator<ITimeGraphEntry> {
@Override
public int compare(@Nullable ITimeGraphEntry o1, @Nullable ITimeGraphEntry o2) {
if ((o1 instanceof TimeGraphEntry) && (o2 instanceof TimeGraphEntry)) {
ITmfTreeDataModel model1 = ((TimeGraphEntry) o1).getEntryModel();
ITmfTreeDataModel model2 = ((TimeGraphEntry) o2).getEntryModel();
if (model1 instanceof CriticalPathEntry && model2 instanceof CriticalPathEntry) {
return Long.compare(((CriticalPathEntry) model2).getSum(), ((CriticalPathEntry) model1).getSum());
}
}
return 0;
}
}
/**
* Constructor
*/
public CriticalPathView() {
super(ID, new BaseDataProviderTimeGraphPresentationProvider(), CriticalPathDataProvider.ID);
setTreeColumns(COLUMN_NAMES);
setFilterColumns(FILTER_COLUMN_NAMES);
setTreeLabelProvider(new CriticalPathTreeLabelProvider());
setEntryComparator(new CriticalPathEntryComparator());
}
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
@Override
protected List<ITimeEvent> createTimeEvents(TimeGraphEntry entry, List<ITimeGraphState> values) {
// need to override to not have transparent events in the gaps.
return Lists.transform(values, state -> createTimeEvent(entry, state));
}
@Override
protected List<ILinkEvent> getLinkList(long startTime, long endTime, long resolution, IProgressMonitor monitor) {
List<@NonNull TimeGraphEntry> traceEntries = getEntryList(getTrace());
if (traceEntries == null) {
return Collections.emptyList();
}
List<@NonNull ILinkEvent> linkList = new ArrayList<>();
TimeQueryFilter queryFilter = new TimeQueryFilter(startTime, endTime, 2);
/*
* group entries by critical path data provider as several hosts may refer to
* the same data provider
*/
Table<ITimeGraphDataProvider<?>, Long, TimeGraphEntry> table = HashBasedTable.create();
for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
for (TimeGraphEntry entry : Utils.flatten(traceEntry)) {
table.put(traceEntry.getProvider(), entry.getEntryModel().getId(), entry);
}
}
for (Map.Entry<ITimeGraphDataProvider<?>, Map<Long, TimeGraphEntry>> entry : table.rowMap().entrySet()) {
ITimeGraphDataProvider<?> provider = entry.getKey();
Map<Long, TimeGraphEntry> map = entry.getValue();
TmfModelResponse<List<ITimeGraphArrow>> response = provider.fetchArrows(FetchParametersUtils.timeQueryToMap(queryFilter), monitor);
List<ITimeGraphArrow> model = response.getModel();
if (monitor.isCanceled()) {
return null;
}
if (model != null) {
for (ITimeGraphArrow arrow : model) {
ITimeGraphEntry src = map.get(arrow.getSourceId());
ITimeGraphEntry dst = map.get(arrow.getDestinationId());
if (src != null && dst != null) {
linkList.add(new TimeLinkEvent(src, dst, arrow.getStartTime(), arrow.getDuration(), arrow.getValue()));
}
}
}
}
return linkList;
}
/**
* Signal handler for analysis started, we need to rebuilt the entry list with
* updated statistics values for the current graph worker of the critical path
* module.
*
* @param signal
* The signal
*/
@TmfSignalHandler
public void analysisStarted(TmfStartAnalysisSignal signal) {
IAnalysisModule analysis = signal.getAnalysisModule();
if (analysis instanceof CriticalPathModule) {
CriticalPathModule criticalPath = (CriticalPathModule) analysis;
/*
* We need to wait for CriticalPathDataProviderFactory to have
* received this signal. Create a new phaser and register, and wait
* for the end synch signal to arrive and advance in a new thread.
*/
fPhaser = new Phaser();
fPhaser.register();
new Thread() {
@Override
public void run() {
fPhaser.awaitAdvance(0);
Collection<ITmfTrace> traces = TmfTraceManager.getTraceSetWithExperiment(getTrace());
if (traces.contains(criticalPath.getTrace())) {
rebuild();
}
}
}.start();
}
}
/**
* Handler for the start synch signal
*
* @param signal
* Incoming signal
*/
@TmfSignalHandler
public void startSynch(TmfStartSynchSignal signal) {
fPhaser.register();
}
/**
* Handler for the end synch signal
*
* @param signal
* Incoming signal
*/
@TmfSignalHandler
public void endSynch(TmfEndSynchSignal signal) {
fPhaser.arriveAndDeregister();
}
@Override
protected void fillLocalToolBar(@Nullable IToolBarManager manager) {
super.fillLocalToolBar(manager);
if (manager == null) {
return;
}
IAction followArrowBwdAction = getTimeGraphViewer().getFollowArrowBwdAction();
followArrowBwdAction.setText(Messages.CriticalPathView_followArrowBwdText);
followArrowBwdAction.setToolTipText(Messages.CriticalPathView_followArrowBwdText);
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, followArrowBwdAction);
IAction followArrowFwdAction = getTimeGraphViewer().getFollowArrowFwdAction();
followArrowFwdAction.setText(Messages.CriticalPathView_followArrowFwdText);
followArrowFwdAction.setToolTipText(Messages.CriticalPathView_followArrowFwdText);
manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, followArrowFwdAction);
}
@Override
protected Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) {
// we need the critical path module to run on the experiment, not the traces.
return trace != null ? Collections.singleton(trace) : Collections.emptyList();
}
}