| /******************************************************************************* |
| * Copyright (c) 2016, 2019 EfficiOS Inc., 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.analysis.os.linux.ui.views.controlflow.filters; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ThreadEntryModel; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ThreadStatusDataProvider; |
| import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.ControlFlowView; |
| 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.filters.TimeQueryFilter; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel; |
| import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel; |
| import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| 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.TimeGraphEntry; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Range; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * Provide active threads filtering for the Control Flow view. |
| * |
| * The Active Thread filter can be used in two modes: Show threads running on a |
| * range of CPUs or show all threads considered active |
| * |
| * @author Jonathan Rajotte Julien |
| * |
| */ |
| public class ActiveThreadsFilter extends ViewerFilter { |
| |
| /** The filtering CPU ranges */ |
| private final @NonNull List<Range<Long>> fCpuRanges; |
| /** The local cache for On CPU filtering */ |
| private @NonNull Map<ITmfTrace, Set<Long>> fCachedOnCpusThreadForTimeRange = new HashMap<>(); |
| /** The local cache for Active Threads filtering */ |
| private @NonNull Map<ITmfTrace, Set<Long>> fCachedActiveThreadForTimeRange = new HashMap<>(); |
| /** The cached time range */ |
| private TmfTimeRange fCachedTimeRange; |
| /** Whether the filter is enabled */ |
| private boolean fEnabled = false; |
| /** Whether the filter is an On CPU filter */ |
| private boolean fCpuRangesBasedFiltering = false; |
| /** The Trace */ |
| private final @Nullable ITmfTrace fTrace; |
| |
| /** |
| * Create an Active Threads filter with CPU ranges criteria. |
| * |
| * @param cpuRanges |
| * The CPU ranges for the filter if any. |
| * @param cpuRangesBasedFiltering |
| * Whether or not to filter based on CPU ranges. |
| * @param trace |
| * The trace the filter is for |
| */ |
| public ActiveThreadsFilter(List<Range<Long>> cpuRanges, boolean cpuRangesBasedFiltering, @Nullable ITmfTrace trace) { |
| super(); |
| if (cpuRanges != null) { |
| fCpuRanges = ImmutableList.copyOf(cpuRanges); |
| } else { |
| fCpuRanges = Collections.emptyList(); |
| } |
| fCpuRangesBasedFiltering = cpuRangesBasedFiltering; |
| |
| fTrace = trace; |
| } |
| |
| /** |
| * @return If the filter is enabled |
| */ |
| public boolean isEnabled() { |
| return fEnabled; |
| } |
| |
| /** |
| * @return If the filter is based on CPU ranges filtering |
| */ |
| public boolean isCpuRangesBased() { |
| return fCpuRangesBasedFiltering; |
| } |
| |
| /** |
| * Set the enabled state of the filter |
| * |
| * @param enabled |
| * The state of the filter |
| */ |
| public void setEnabled(boolean enabled) { |
| fEnabled = enabled; |
| } |
| |
| /** |
| * Get the CPU ranges of the filter |
| * |
| * @return The CPU ranges of the filter |
| */ |
| public @NonNull List<Range<Long>> getCpuRanges() { |
| return fCpuRanges; |
| } |
| |
| @Override |
| public boolean select(Viewer viewer, Object parentElement, Object element) { |
| |
| if (!fEnabled || !(element instanceof TimeGraphEntry)) { |
| return true; |
| } |
| TimeGraphEntry entry = (TimeGraphEntry) element; |
| ThreadEntryModel entryModel = ControlFlowView.getThreadEntryModel(entry); |
| if (entryModel == null) { |
| return true; |
| } |
| |
| ITmfTrace trace = BaseDataProviderTimeGraphView.getTrace(entry); |
| |
| Set<Long> onCpusThreadForTimeRange = fCachedOnCpusThreadForTimeRange.get(trace); |
| Set<Long> activeThreadForTimeRange = fCachedActiveThreadForTimeRange.get(trace); |
| |
| /* Check if on CPU */ |
| if (fCpuRangesBasedFiltering && (onCpusThreadForTimeRange != null) && onCpusThreadForTimeRange.contains(entryModel.getId())) { |
| return true; |
| } else if ((activeThreadForTimeRange != null) && activeThreadForTimeRange.contains(entryModel.getId())) { |
| return true; |
| } |
| |
| /* Not active per see. Check children if any is active */ |
| for (TimeGraphEntry child : entry.getChildren()) { |
| if (select(viewer, entry, child)) { |
| return true; |
| } |
| } |
| |
| /* No children are active */ |
| return false; |
| } |
| |
| private static @NonNull Set<Long> getOnCpuThreads(@NonNull List<Range<Long>> cpuRanges, TmfTimeRange winRange, @NonNull ITmfTrace trace) { |
| |
| ThreadStatusDataProvider threadStatusProvider = DataProviderManager.getInstance().getDataProvider(trace, |
| ThreadStatusDataProvider.ID, ThreadStatusDataProvider.class); |
| |
| if (threadStatusProvider == null) { |
| return Collections.emptySet(); |
| } |
| |
| long beginTS = winRange.getStartTime().getValue(); |
| long endTS = winRange.getEndTime().getValue(); |
| |
| @NonNull Set<@NonNull Long> cpus = new HashSet<>(); |
| for (Range<Long> range : cpuRanges) { |
| for (long cpu = range.lowerEndpoint(); cpu <= range.upperEndpoint(); cpu ++) { |
| cpus.add(cpu); |
| } |
| } |
| SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(beginTS, endTS, 2, cpus); |
| Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter); |
| parameters.put(ThreadStatusDataProvider.ACTIVE_THREAD_FILTER_KEY, true); |
| TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = threadStatusProvider.fetchTree(parameters, null); |
| TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel(); |
| |
| if (model == null) { |
| return Collections.emptySet(); |
| } |
| HashSet<Long> onCpuThreads = Sets.newHashSet(Iterables.transform(model.getEntries(), TimeGraphEntryModel::getId)); |
| return onCpuThreads == null ? Collections.emptySet() : onCpuThreads; |
| } |
| |
| private static @NonNull Set<Long> getActiveThreads(TmfTimeRange winRange, @NonNull ITmfTrace trace) { |
| |
| ThreadStatusDataProvider threadStatusProvider = DataProviderManager.getInstance().getDataProvider(trace, |
| ThreadStatusDataProvider.ID, ThreadStatusDataProvider.class); |
| |
| if (threadStatusProvider == null) { |
| return Collections.emptySet(); |
| } |
| |
| long beginTS = winRange.getStartTime().getValue(); |
| long endTS = winRange.getEndTime().getValue(); |
| |
| TimeQueryFilter filter = new TimeQueryFilter(beginTS, endTS, 2); |
| Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.timeQueryToMap(filter); |
| parameters.put(ThreadStatusDataProvider.ACTIVE_THREAD_FILTER_KEY, true); |
| TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = threadStatusProvider.fetchTree(parameters, null); |
| TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel(); |
| |
| if (model == null) { |
| return Collections.emptySet(); |
| } |
| HashSet<Long> activeThreads = Sets.newHashSet(Iterables.transform(model.getEntries(), TimeGraphEntryModel::getId)); |
| return activeThreads == null ? Collections.emptySet() : activeThreads; |
| } |
| |
| /** |
| * Compute the filter internal data |
| * |
| * @param beginTS |
| * start timestamp to update the filter for |
| * @param endTS |
| * end timestamp to update the filter for |
| * @return map of filter data per trace, or null |
| */ |
| public @Nullable Map<ITmfTrace, Set<Long>> computeData(long beginTS, long endTS) { |
| TmfTimeRange timeRange = new TmfTimeRange(TmfTimestamp.fromNanos(beginTS), TmfTimestamp.fromNanos(endTS)); |
| |
| ITmfTrace parentTrace = fTrace; |
| if (parentTrace == null || !fEnabled || (fCachedTimeRange != null && fCachedTimeRange.equals(timeRange))) { |
| return null; |
| } |
| Map<ITmfTrace, Set<Long>> data = new HashMap<>(); |
| for (ITmfTrace trace : TmfTraceManager.getTraceSet(parentTrace)) { |
| if (fCpuRangesBasedFiltering) { |
| Set<Long> onCpusThreadForTimeRange = getOnCpuThreads(fCpuRanges, timeRange, trace); |
| data.put(trace, onCpusThreadForTimeRange); |
| } else { |
| Set<Long> activeThreadForTimeRange = getActiveThreads(timeRange, trace); |
| data.put(trace, activeThreadForTimeRange); |
| } |
| } |
| return data; |
| } |
| |
| /** |
| * Update the filter internal data |
| * |
| * @param beginTS |
| * start timestamp to update the filter for |
| * @param endTS |
| * end timestamp to update the filter for |
| * @param data |
| * map of filter data per trace |
| */ |
| public void updateData(long beginTS, long endTS, @NonNull Map<ITmfTrace, Set<Long>> data) { |
| /* Caching result for subsequent select() call for other entry */ |
| fCachedTimeRange = new TmfTimeRange(TmfTimestamp.fromNanos(beginTS), TmfTimestamp.fromNanos(endTS)); |
| if (fCpuRangesBasedFiltering) { |
| fCachedOnCpusThreadForTimeRange = data; |
| } else { |
| fCachedActiveThreadForTimeRange = data; |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(fCpuRanges, fCpuRangesBasedFiltering, fEnabled); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| ActiveThreadsFilter other = (ActiveThreadsFilter) obj; |
| return Objects.equals(fCpuRanges, other.fCpuRanges) && |
| fCpuRangesBasedFiltering == other.fCpuRangesBasedFiltering && |
| fEnabled == other.fEnabled; |
| } |
| } |