| /******************************************************************************* |
| * Copyright (c) 2012, 2018 Ericsson, É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 |
| * |
| * Contributors: |
| * Patrick Tasse - Initial API and implementation |
| * Geneviève Bastien - Move code to provide base classes for time graph views |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.resources; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.function.Function; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
| import org.eclipse.tracecompass.analysis.os.linux.core.signals.TmfCpuSelectedSignal; |
| import org.eclipse.tracecompass.analysis.os.linux.core.signals.TmfThreadSelectedSignal; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.core.resourcesstatus.ResourcesEntryModel; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.core.resourcesstatus.ResourcesEntryModel.Type; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.core.resourcesstatus.ResourcesStatusDataProvider; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Messages; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.FollowCpuAction; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.FollowThreadAction; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.UnfollowCpuAction; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.UnfollowThreadAction; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty; |
| import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel; |
| import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext; |
| 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.ITimeGraphEntry; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NamedTimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry; |
| |
| import com.google.common.collect.Multimap; |
| |
| /** |
| * Main implementation for the LTTng 2.0 kernel Resource view |
| * |
| * @author Patrick Tasse |
| */ |
| public class ResourcesView extends BaseDataProviderTimeGraphView { |
| |
| /** View ID. */ |
| public static final @NonNull String ID = "org.eclipse.tracecompass.analysis.os.linux.views.resources"; //$NON-NLS-1$ |
| |
| /** ID of the followed CPU in the map data in {@link TmfTraceContext} */ |
| public static final @NonNull String RESOURCES_FOLLOW_CPU = ID + ".FOLLOW_CPU"; //$NON-NLS-1$ |
| |
| /** |
| * ID of the followed Current Thread in the map data in |
| * {@link TmfTraceContext} |
| * |
| * @deprecated Selected thread should be matched with its host. Use |
| * {@link HostThread#SELECTED_HOST_THREAD_KEY} instead, with a |
| * value of type {@link HostThread} |
| */ |
| @Deprecated |
| public static final @NonNull String RESOURCES_FOLLOW_CURRENT_THREAD = ID + ".FOLLOW_CURRENT_THREAD"; //$NON-NLS-1$ |
| |
| private static final String EMPTY_STRING = ""; //$NON-NLS-1$ |
| |
| private String fFollowedThread = EMPTY_STRING; |
| |
| private static final String[] FILTER_COLUMN_NAMES = new String[] { |
| Messages.ResourcesView_stateTypeName |
| }; |
| |
| /** |
| * CPU group entries (Current Thread, CPU, Frequency, separator) first, then IRQ, then SoftIRQ. |
| * Numerical order second, then CPU group entries in their correct order. |
| */ |
| private static final @NonNull List<Type> CPU_GROUP_ORDER = Arrays.asList(Type.CURRENT_THREAD, Type.CPU, Type.FREQUENCY, Type.GROUP); |
| |
| private static final Comparator<ResourcesEntryModel> COMPARATOR = Comparator |
| .comparing((Function<ResourcesEntryModel, Type>) entry -> CPU_GROUP_ORDER.contains(entry.getType()) ? Type.GROUP : entry.getType()) |
| .thenComparing(ResourcesEntryModel::getResourceId) |
| .thenComparing(entry -> CPU_GROUP_ORDER.indexOf(entry.getType())); |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Default constructor |
| */ |
| public ResourcesView() { |
| super(ID, new ResourcesPresentationProvider(), ResourcesStatusDataProvider.ID); |
| setFilterColumns(FILTER_COLUMN_NAMES); |
| setFilterLabelProvider(new ResourcesFilterLabelProvider()); |
| setEntryComparator(new ResourcesEntryComparator()); |
| setAutoExpandLevel(1); |
| } |
| |
| private static class ResourcesEntryComparator implements Comparator<ITimeGraphEntry> { |
| @Override |
| public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { |
| if (o1 instanceof TraceEntry && o2 instanceof TraceEntry) { |
| /* sort trace entries alphabetically */ |
| return o1.getName().compareTo(o2.getName()); |
| } else if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) { |
| ITmfTreeDataModel model1 = ((TimeGraphEntry) o1).getEntryModel(); |
| ITmfTreeDataModel model2 = ((TimeGraphEntry) o2).getEntryModel(); |
| /* sort resource entries by their defined order */ |
| if (model1 instanceof ResourcesEntryModel && model2 instanceof ResourcesEntryModel) { |
| return COMPARATOR.compare((ResourcesEntryModel) model1, (ResourcesEntryModel) model2); |
| } |
| } |
| return 0; |
| } |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| protected void fillTimeGraphEntryContextMenu(@NonNull IMenuManager menuManager) { |
| ISelection selection = getSite().getSelectionProvider().getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection sSel = (IStructuredSelection) selection; |
| if (sSel.getFirstElement() instanceof TimeGraphEntry) { |
| TimeGraphEntry resourcesEntry = (TimeGraphEntry) sSel.getFirstElement(); |
| ITmfTreeDataModel model = resourcesEntry.getEntryModel(); |
| if (model instanceof ResourcesEntryModel) { |
| ResourcesEntryModel resourcesModel = (ResourcesEntryModel) model; |
| Type type = resourcesModel.getType(); |
| if (type == Type.CPU || type == Type.CURRENT_THREAD) { |
| ITmfTrace trace = getTrace(resourcesEntry); |
| TmfTraceContext ctx = TmfTraceManager.getInstance().getTraceContext(trace); |
| Integer data = (Integer) ctx.getData(RESOURCES_FOLLOW_CPU); |
| int cpu = data != null ? data.intValue() : -1; |
| if (cpu >= 0) { |
| menuManager.add(new UnfollowCpuAction(ResourcesView.this, resourcesModel.getResourceId(), trace)); |
| } else { |
| menuManager.add(new FollowCpuAction(ResourcesView.this, resourcesModel.getResourceId(), trace)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager) { |
| ISelection selection = getSite().getSelectionProvider().getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection sSel = (IStructuredSelection) selection; |
| if (sSel.getFirstElement() instanceof TimeGraphEntry) { |
| TimeGraphEntry resourcesEntry = (TimeGraphEntry) sSel.getFirstElement(); |
| ITmfTreeDataModel model = resourcesEntry.getEntryModel(); |
| if (sSel.toArray()[1] instanceof NamedTimeEvent && ((ResourcesEntryModel) model).getType() == Type.CURRENT_THREAD) { |
| ITmfTrace trace = getTrace(resourcesEntry); |
| NamedTimeEvent event = (NamedTimeEvent) sSel.toArray()[1]; |
| TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext(); |
| HostThread data = (HostThread) ctx.getData(HostThread.SELECTED_HOST_THREAD_KEY); |
| if (data != null) { |
| menuManager.add(new UnfollowThreadAction(ResourcesView.this)); |
| } else { |
| menuManager.add(new FollowThreadAction(ResourcesView.this, null, event.getValue(), trace)); |
| } |
| } |
| } |
| } |
| super.fillTimeEventContextMenu(menuManager); |
| } |
| |
| private static class ResourcesFilterLabelProvider extends TreeLabelProvider { |
| @Override |
| public String getColumnText(Object element, int columnIndex) { |
| if (columnIndex == 0 && element instanceof TimeGraphEntry) { |
| return ((TimeGraphEntry) element).getName(); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Internal |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| protected String getNextText() { |
| return Messages.ResourcesView_nextResourceActionNameText; |
| } |
| |
| @Override |
| protected String getNextTooltip() { |
| return Messages.ResourcesView_nextResourceActionToolTipText; |
| } |
| |
| @Override |
| protected String getPrevText() { |
| return Messages.ResourcesView_previousResourceActionNameText; |
| } |
| |
| @Override |
| protected String getPrevTooltip() { |
| return Messages.ResourcesView_previousResourceActionToolTipText; |
| } |
| |
| private void setFollowedThread(String regex) { |
| fFollowedThread = regex; |
| } |
| |
| private void removeFollowedThread() { |
| fFollowedThread = EMPTY_STRING; |
| } |
| |
| @Override |
| protected @NonNull Multimap<@NonNull Integer, @NonNull String> getRegexes() { |
| Multimap<@NonNull Integer, @NonNull String> regexes = super.getRegexes(); |
| if (!fFollowedThread.isEmpty()) { |
| regexes.put(IFilterProperty.BOUND, fFollowedThread); |
| } else { |
| regexes.removeAll(IFilterProperty.BOUND); |
| } |
| return regexes; |
| } |
| |
| /** |
| * Signal handler for a cpu selected signal. |
| * |
| * @param signal |
| * the cpu selected signal |
| * @since 2.0 |
| */ |
| @TmfSignalHandler |
| public void listenToCpu(TmfCpuSelectedSignal signal) { |
| int data = signal.getCore() >= 0 ? signal.getCore() : -1; |
| ITmfTrace trace = getTrace(); |
| if (trace == null) { |
| return; |
| } |
| TmfTraceManager.getInstance().updateTraceContext(trace, |
| builder -> builder.setData(RESOURCES_FOLLOW_CPU, data)); |
| } |
| |
| /** |
| * Signal handler for a thread selected signal. |
| * |
| * @param signal |
| * the thread selected signal |
| * @since 2.0 |
| */ |
| @TmfSignalHandler |
| public void listenToCurrentThread(TmfThreadSelectedSignal signal) { |
| HostThread data = signal.getThreadId() >= 0 ? signal.getHostThread() : null; |
| ITmfTrace trace = getTrace(); |
| if (trace == null) { |
| return; |
| } |
| |
| TmfTraceManager.getInstance().updateTraceContext(trace, |
| builder -> builder.setData(HostThread.SELECTED_HOST_THREAD_KEY, data)); |
| if (data != null) { |
| setFollowedThread("Current_thread==" + data.getTid() + " || TID==" + data.getTid()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| removeFollowedThread(); |
| } |
| restartZoomThread(); |
| } |
| } |