os.linux & tmf.ui: introduce dynamic filter for cfv: Active threads and Threads on... dynamic-active-cpu-filters
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Fri, 18 Nov 2016 00:16:40 +0000 (19:16 -0500)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Fri, 25 Nov 2016 23:33:02 +0000 (18:33 -0500)
A dynamic filter is defined as a filter that is calculated on each
update of the visual time range.

Users can now apply dynamic filters on the Control Flow view. A new
sub menu of the view menu allows users to enable and configure those
dynamic filters. For now, one dynamic filter is available: "Active
Threads Only". This filter has two configurable options:

    - Show all threads considered active.
    - Show threads running on a selection of CPUs.

A thread is considered inactive if its state is one of the following:

    - non-existing
    - unknown
    - wait (blocked)
    - wait (unknown)

Otherwise the thread is considered active.

The "Show threads running on a selection of CPUs" filter shows
threads that were currently running or waiting on the run queue for the
ranges of CPUs supplied to the filter during the visible time range of
the Control Flow view.

Change-Id: Ib58fe09aabd070e4db9270955f86737c36f309ca
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
15 files changed:
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/Messages.java
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/ControlFlowView.java
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/PackageMessages.java
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/ActiveThreadsFilter.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/DynamicFilterDialog.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/messages.properties
doc/org.eclipse.tracecompass.doc.user/doc/User-Guide.mediawiki
doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFilter_ShowActiveThreadsConfigure.png [new file with mode: 0644]
doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersConfigure.png [new file with mode: 0644]
doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersToggle.png [new file with mode: 0644]
lttng/org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests/src/org/eclipse/tracecompass/lttng2/kernel/ui/swtbot/tests/ControlFlowViewTest.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java
tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/TimeGraphCombo.java

index 018848ca101760c7ed1d3b2ac583d55d76149723..31d80cce292b8380a7b10b17e6d60c3f9c8138e0 100644 (file)
@@ -41,6 +41,7 @@ public class Messages extends NLS {
     public static String ControlFlowView_uncheckInactiveToolTip;
     public static String ControlFlowView_attributeSyscallName;
     public static String ControlFlowView_attributeCpuName;
+
     public static String ControlFlowView_flatViewLabel;
     public static String ControlFlowView_flatViewToolTip;
     public static String ControlFlowView_hierarchicalViewLabel;
index c135bef5fc3aca19d0355f855cb784c1e86e5884..fa71123d540655decb6b5c4c018145aeec127366 100644 (file)
 
 package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow;
 
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -43,8 +46,12 @@ import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.window.Window;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
@@ -54,6 +61,8 @@ import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attribute
 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Activator;
 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Messages;
 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.FollowThreadAction;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters.ActiveThreadsFilter;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters.DynamicFilterDialog;
 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
 import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
 import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
@@ -85,7 +94,9 @@ import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphContro
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
+import org.eclipse.ui.PlatformUI;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 
 /**
@@ -167,6 +178,46 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
 
     private IAction fHierarchicalAction;
 
+    private @NonNull ActiveThreadsFilter fActiveThreadsFilter = new ActiveThreadsFilter(null, false);
+
+    private final ActiveThreadsFilterAction fActiveThreadsRapidToggle = new ActiveThreadsFilterAction();
+
+    class ActiveThreadsFilterAction extends Action {
+        public ActiveThreadsFilterAction() {
+            super(PackageMessages.ControlFlowView_DynamicFiltersActiveThreadToggleLabel, IAction.AS_CHECK_BOX);
+            setToolTipText(PackageMessages.ControlFlowView_DynamicFiltersActiveThreadToggleToolTip);
+            addPropertyChangeListener(new IPropertyChangeListener() {
+                @Override
+                public void propertyChange(PropertyChangeEvent event) {
+                    if (!(event.getNewValue() instanceof Boolean)) {
+                        return;
+                    }
+
+                    Boolean enabled = (Boolean) event.getNewValue();
+
+                    /* Always remove the previous Active Threads filter */
+                    getTimeGraphCombo().removeFilter(fActiveThreadsFilter);
+
+                    if (enabled) {
+                        fActiveThreadsFilter.setEnabled(true);
+                        getTimeGraphCombo().addFilter(fActiveThreadsFilter);
+
+                        /* Use flat representation */
+                        if (fFlatAction != null) {
+                            applyFlatPresentation();
+                            fFlatAction.setChecked(true);
+                            fHierarchicalAction.setChecked(false);
+                        }
+                    } else {
+                        fActiveThreadsFilter.setEnabled(false);
+                    }
+
+                    refresh();
+                }
+            });
+        }
+    }
+
     // ------------------------------------------------------------------------
     // Constructors
     // ------------------------------------------------------------------------
@@ -264,7 +315,8 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
     @Override
     protected void fillLocalMenu(IMenuManager manager) {
         super.fillLocalMenu(manager);
-        final MenuManager item = new MenuManager(Messages.ControlFlowView_threadPresentation);
+
+        MenuManager item = new MenuManager(Messages.ControlFlowView_threadPresentation);
         fFlatAction = createFlatAction();
         item.add(fFlatAction);
 
@@ -272,8 +324,18 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
         item.add(fHierarchicalAction);
         manager.add(item);
 
+        item = new MenuManager(PackageMessages.ControlFlowView_DynamicFiltersMenuLabel);
+        item.add(fActiveThreadsRapidToggle);
+        item.add(new Separator());
+
+        IAction dynamicFiltersConfigureAction = createDynamicFilterConfigureAction();
+        item.add(dynamicFiltersConfigureAction);
+
+        manager.add(item);
     }
 
+
+
     /**
      * Base Action for the "Go to Next/Previous Event for thread" actions
      */
@@ -352,6 +414,44 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
         }
     }
 
+    private IAction createDynamicFilterConfigureAction() {
+        return new Action(PackageMessages.ControlFlowView_DynamicFiltersConfigureLabel, IAction.AS_PUSH_BUTTON) {
+            @Override
+            public void run() {
+                DynamicFilterDialog dialog = new DynamicFilterDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), fActiveThreadsFilter);
+                if (dialog.open() == Window.OK) {
+                    /* Remove the previous Active Threads filter */
+                    checkNotNull(getTimeGraphCombo()).removeFilter(fActiveThreadsFilter);
+
+                    ActiveThreadsFilter newFilter = dialog.getActiveThreadsResult();
+                    ActiveThreadsFilter previousFilter = fActiveThreadsFilter;
+
+                    /* Set the filter to the view */
+                    fActiveThreadsFilter = newFilter;
+
+                    boolean enabled = fActiveThreadsFilter.isEnabled();
+                    if (enabled) {
+                        checkNotNull(getTimeGraphCombo()).addFilter(newFilter);
+                    }
+
+                    /*
+                     * Prevent double refresh from change state of setChecked
+                     * and ensure that a refresh is done if the mode of the
+                     * filter is changed or options are changed
+                     */
+                    if (previousFilter.isEnabled() && newFilter.isEnabled()) {
+                        boolean changed = !Objects.equal(previousFilter.getCpuRanges(), newFilter.getCpuRanges()) || previousFilter.isCpuRangesBased() != newFilter.isCpuRangesBased();
+                        if (changed) {
+                            refresh();
+                        }
+                    } else {
+                        fActiveThreadsRapidToggle.setChecked(enabled);
+                    }
+                }
+            }
+        };
+    }
+
     private IAction createHierarchicalAction() {
         IAction action = new Action(Messages.ControlFlowView_hierarchicalViewLabel, IAction.AS_RADIO_BUTTON) {
             @Override
@@ -382,26 +482,31 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
         IAction action = new Action(Messages.ControlFlowView_flatViewLabel, IAction.AS_RADIO_BUTTON) {
             @Override
             public void run() {
-                ITmfTrace parentTrace = getTrace();
-                synchronized (fFlatTraces) {
-                    fFlatTraces.add(parentTrace);
-                    for (ITmfTrace trace : TmfTraceManager.getTraceSet(parentTrace)) {
-                        final ITmfStateSystem ss = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
-                        List<@NonNull TimeGraphEntry> entryList = getEntryList(ss);
-                        if (entryList != null) {
-                            for (TimeGraphEntry traceEntry : entryList) {
-                                hierarchicalToFlatTree(traceEntry);
-                            }
-                        }
-                    }
-                }
+                applyFlatPresentation();
                 refresh();
             }
         };
+        action.setChecked(true);
         action.setToolTipText(Messages.ControlFlowView_flatViewToolTip);
         return action;
     }
 
+    private void applyFlatPresentation() {
+        ITmfTrace parentTrace = getTrace();
+        synchronized (fFlatTraces) {
+            fFlatTraces.add(parentTrace);
+            for (ITmfTrace trace : TmfTraceManager.getTraceSet(parentTrace)) {
+                final ITmfStateSystem ss = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
+                List<@NonNull TimeGraphEntry> entryList = getEntryList(ss);
+                if (entryList != null) {
+                    for (TimeGraphEntry traceEntry : entryList) {
+                        hierarchicalToFlatTree(traceEntry);
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     protected String getNextText() {
         return Messages.ControlFlowView_nextProcessActionNameText;
@@ -580,6 +685,8 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
     @Override
     public void traceSelected(TmfTraceSelectedSignal signal) {
         super.traceSelected(signal);
+
+        /* Update the Flat and Hierarchical actions */
         synchronized (fFlatTraces) {
             if (fFlatTraces.contains(signal.getTrace())) {
                 fHierarchicalAction.setChecked(false);
@@ -589,6 +696,21 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
                 fHierarchicalAction.setChecked(true);
             }
         }
+
+        /* Update the Dynamic Filters related actions */
+        ViewerFilter activeThreadFilter = null;
+        ViewerFilter[] traceFilters = getFiltersMap().get(signal.getTrace());
+        if (traceFilters != null) {
+            activeThreadFilter = getActiveThreadsFilter(traceFilters);
+        }
+
+        if (activeThreadFilter == null) {
+            fActiveThreadsFilter = new ActiveThreadsFilter(null, false);
+        } else {
+            fActiveThreadsFilter = (@NonNull ActiveThreadsFilter) checkNotNull(activeThreadFilter);
+        }
+
+        fActiveThreadsRapidToggle.setChecked(fActiveThreadsFilter.isEnabled());
     }
 
     // ------------------------------------------------------------------------
@@ -1053,4 +1175,15 @@ public class ControlFlowView extends AbstractStateSystemTimeGraphView {
         }
         return null;
     }
+
+    private static ActiveThreadsFilter getActiveThreadsFilter(ViewerFilter[] filters) {
+        return (ActiveThreadsFilter) Arrays.stream(filters).filter(filter -> filter instanceof ActiveThreadsFilter).findFirst().orElse(null);
+    }
+
+    @Override
+    protected void updateFilters() {
+        super.updateFilters();
+        fActiveThreadsFilter.updateData();
+    }
+
 }
index 42b2b4ec26db1b71a882329cb2e5db91a835f6b4..139c542676daf37d452e8f4db632074ebbfe757f 100644 (file)
@@ -29,6 +29,10 @@ public class PackageMessages extends NLS {
     public static String ControlFlowView_PreviousEventActionTooltip;
     public static String ControlFlowView_PreviousEventJobName;
 
+    public static String ControlFlowView_DynamicFiltersActiveThreadToggleLabel;
+    public static String ControlFlowView_DynamicFiltersActiveThreadToggleToolTip;
+    public static String ControlFlowView_DynamicFiltersConfigureLabel;
+    public static String ControlFlowView_DynamicFiltersMenuLabel;
 
     static {
         NLS.initializeMessages(BUNDLE_NAME, PackageMessages.class);
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/ActiveThreadsFilter.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/ActiveThreadsFilter.java
new file mode 100644 (file)
index 0000000..f97c504
--- /dev/null
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider;
+import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.ControlFlowEntry;
+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.TmfTraceContext;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Range;
+
+/**
+ * Provide active threads filtering for the control flow view. <br>
+ * <br>
+ * The Active Thread filter can be used in two mode: <br>
+ * Show threads running on a range of CPUs <br>
+ * Show all threads considered active <br>
+ *
+ * @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 Set<Integer> fCachedOnCpusThreadForTimeRange = new HashSet<>();
+    /** The local cache for Active Threads filtering */
+    private @NonNull Set<Integer> fCachedActiveThreadForTimeRange = new HashSet<>();
+
+    /** 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;
+
+    /**
+     * 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.
+     */
+    public ActiveThreadsFilter(List<Range<Long>> cpuRanges, boolean cpuRangesBasedFiltering) {
+        super();
+        if (cpuRanges != null) {
+            fCpuRanges = ImmutableList.copyOf(cpuRanges);
+        } else {
+            fCpuRanges = new ArrayList<>();
+        }
+
+        fCpuRangesBasedFiltering = cpuRangesBasedFiltering;
+    }
+
+    /**
+     * @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 ControlFlowEntry)) {
+            return true;
+        }
+        ControlFlowEntry cfe = (ControlFlowEntry) element;
+
+        /* Check if on CPU */
+        if (fCpuRangesBasedFiltering && fCachedOnCpusThreadForTimeRange.contains(cfe.getThreadId())) {
+            return true;
+        } else if (fCachedActiveThreadForTimeRange.contains(cfe.getThreadId())) {
+            return true;
+        }
+
+        /* Not active per see. Check children if any is active */
+        for (@NonNull
+        TimeGraphEntry child : cfe.getChildren()) {
+            if (select(viewer, cfe, child)) {
+                return true;
+            }
+        }
+
+        /* No children are active */
+        return false;
+    }
+
+    private static @NonNull Set<Integer> getOnCpuThreads(List<Range<Long>> cpuRanges) {
+        if (cpuRanges == null) {
+            return new HashSet<>();
+        }
+
+        TmfTraceManager traceManager = TmfTraceManager.getInstance();
+        TmfTraceContext traceContext = traceManager.getCurrentTraceContext();
+        TmfTimeRange winRange = traceContext.getWindowRange();
+
+        ITmfTrace trace = traceManager.getActiveTrace();
+
+        if (trace == null) {
+            return new HashSet<>();
+        }
+
+        KernelAnalysisModule kernelAnalysisModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelAnalysisModule.class, KernelAnalysisModule.ID);
+
+        long beginTS = winRange.getStartTime().getValue();
+        long endTS = winRange.getEndTime().getValue();
+
+        if (kernelAnalysisModule == null) {
+            /* TODO: what to do here ? */
+            return new HashSet<>();
+        }
+
+        /* Convert range to sets */
+        @NonNull
+        Set<@NonNull Long> cpus = new HashSet<>();
+        for (Range<Long> range : cpuRanges) {
+            Long minimum = range.lowerEndpoint();
+            Long maximum = range.upperEndpoint();
+            for (Long i = minimum; i <= maximum; i++) {
+                cpus.add(i);
+            }
+        }
+
+        Set<Integer> set = KernelThreadInformationProvider.getThreadsOfCpus(kernelAnalysisModule, cpus, beginTS, endTS);
+
+        if (set == null) {
+            set = new HashSet<>();
+        }
+
+        return set;
+    }
+
+    private static @NonNull Set<Integer> getActiveThreads() {
+        TmfTraceManager traceManager = TmfTraceManager.getInstance();
+        TmfTraceContext traceContext = traceManager.getCurrentTraceContext();
+        TmfTimeRange winRange = traceContext.getWindowRange();
+
+        ITmfTrace trace = traceManager.getActiveTrace();
+
+        if (trace == null) {
+            return new HashSet<>();
+        }
+
+        KernelAnalysisModule kernelModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelAnalysisModule.class, KernelAnalysisModule.ID);
+
+        long beginTS = winRange.getStartTime().getValue();
+        long endTS = winRange.getEndTime().getValue();
+
+        if (kernelModule == null) {
+            /* TODO: what to do here ? */
+            return new HashSet<>();
+        }
+
+        Set<Integer> set = KernelThreadInformationProvider.getActiveThreadsForRange(kernelModule, beginTS, endTS);
+
+        if (set == null) {
+            set = new HashSet<>();
+        }
+
+        return set;
+    }
+
+    /**
+     * Update the filter internal data
+     */
+    public void updateData() {
+        TmfTraceManager traceManager = TmfTraceManager.getInstance();
+        TmfTraceContext traceContext = traceManager.getCurrentTraceContext();
+        TmfTimeRange winRange = traceContext.getWindowRange();
+        long beginTS = winRange.getStartTime().getValue();
+        long endTS = winRange.getEndTime().getValue();
+
+        TmfTimeRange timeRange = new TmfTimeRange(TmfTimestamp.fromNanos(beginTS), TmfTimestamp.fromNanos(endTS));
+
+        /* Caching result for subsequent select() call for other entry */
+        if (fEnabled && (fCachedTimeRange == null || !fCachedTimeRange.equals(timeRange))) {
+            fCachedTimeRange = timeRange;
+            if (fCpuRangesBasedFiltering) {
+                fCachedOnCpusThreadForTimeRange = getOnCpuThreads(fCpuRanges);
+            } else {
+                fCachedActiveThreadForTimeRange = getActiveThreads();
+            }
+        }
+    }
+
+    @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;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/DynamicFilterDialog.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/DynamicFilterDialog.java
new file mode 100644 (file)
index 0000000..3376fd2
--- /dev/null
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.google.common.collect.Range;
+
+/**
+ * The Dynamic Filters configuration dialog.
+ *
+ * @author Jonathan Rajotte Julien
+ */
+public class DynamicFilterDialog extends TitleAreaDialog {
+
+    private static final @NonNull String INTERNAL_RANGE_SEPARATOR = "-"; //$NON-NLS-1$
+    private static final @NonNull String RANGES_DELIMITER = ","; //$NON-NLS-1$
+
+    /** The internal ActiveThreadsFilter result */
+    private @NonNull ActiveThreadsFilter fInternalActiveThreadsFilter;
+
+    private Button fActiveThreadEnabledButton;
+    private Button fAllActiveThreadsRadionButton;
+    private Button fCpuRangesRadioButton;
+    private Text fCpuRangesField;
+
+    /**
+     * Create a DynamicFilterDialog f
+     *
+     * @param parentShell
+     *            The parent shell
+     * @param filter
+     *            An
+     *            {@link org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters.ActiveThreadsFilter
+     *            ActiveThreadFilter} instance.
+     */
+    public DynamicFilterDialog(Shell parentShell, @NonNull ActiveThreadsFilter filter) {
+        super(parentShell);
+        fInternalActiveThreadsFilter = filter;
+    }
+
+    @Override
+    public void create() {
+        super.create();
+        setTitle(Messages.DynamicFilterDialog_Title);
+
+        /* TODO: This is used for swtbot testing to discover the dialog */
+        this.getShell().setText(Messages.DynamicFilterDialog_Title);
+    }
+
+    private static boolean validateCpuRange(final String newString) {
+        /* Pattern for CPUS ranges e.g.: 1,1-200,2,3 */
+        return Pattern.matches("^((\\d+(\\-\\d+)?, ?)*(\\d+(\\-\\d+)?))+$", newString); //$NON-NLS-1$
+    }
+
+    private void createActiveThreadSection(Composite parent) {
+        boolean usesCpuRanges = false;
+        boolean filterActive = false;
+
+        ActiveThreadsFilter filter = fInternalActiveThreadsFilter;
+
+        filterActive = fInternalActiveThreadsFilter.isEnabled();
+        usesCpuRanges = filter.isCpuRangesBased();
+
+        GridData gd;
+        GridLayout gl;
+        Group activeThreadGroup = new Group(parent, SWT.SHADOW_NONE | SWT.BORDER);
+        activeThreadGroup.setText(Messages.DynamicFilterDialog_ActiveThreadsFilterName);
+
+        activeThreadGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        gl = new GridLayout(1, true);
+        gl.marginLeft = gl.marginRight = 0;
+        activeThreadGroup.setLayout(gl);
+
+        fActiveThreadEnabledButton = new Button(activeThreadGroup, SWT.CHECK);
+        fActiveThreadEnabledButton.setText(Messages.DynamicFilterDialog_ActiveThreadsFilterName);
+        fActiveThreadEnabledButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        /* Cpu selection */
+        Group cpuSelectionGroup = new Group(activeThreadGroup, SWT.SHADOW_NONE);
+        cpuSelectionGroup.setText(Messages.DynamicFilterDialog_OptionsGroupLabel);
+        cpuSelectionGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        cpuSelectionGroup.setLayout(new GridLayout(2, false));
+
+        fAllActiveThreadsRadionButton = new Button(cpuSelectionGroup, SWT.RADIO);
+        fAllActiveThreadsRadionButton.setText(Messages.DynamicFilterDialog_RadioButtonAllActiveThreads);
+        fAllActiveThreadsRadionButton.setToolTipText(Messages.DynamicFilterDialog_RadioButtonAllActiveThreadsToolTip);
+
+        fAllActiveThreadsRadionButton.setSelection(!usesCpuRanges);
+        gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+        gd.horizontalSpan = 2;
+        fAllActiveThreadsRadionButton.setLayoutData(gd);
+
+        fCpuRangesRadioButton = new Button(cpuSelectionGroup, SWT.RADIO);
+        fCpuRangesRadioButton.setText(Messages.DynamicFilterDialog_CpuRangesLabel);
+        fCpuRangesRadioButton.setToolTipText(Messages.DynamicFilterDialog_CpuRangesTooltip);
+
+        fCpuRangesRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
+
+        fCpuRangesField = new Text(cpuSelectionGroup, SWT.SINGLE | SWT.BORDER);
+        fCpuRangesField.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, true));
+        fCpuRangesField.setMessage(Messages.DynamicFilterDialog_CpuRangesExamples);
+
+        fCpuRangesRadioButton.setToolTipText(Messages.DynamicFilterDialog_CpuRangesTooltip);
+
+        /* Attach an automatic validation to the field */
+        fCpuRangesField.addVerifyListener(new VerifyListener() {
+            @Override
+            public void verifyText(VerifyEvent e) {
+                /* Reconstruct the string */
+                final String oldString = fCpuRangesField.getText();
+                final String newString = oldString.substring(0, e.start) + e.text + oldString.substring(e.end);
+
+                /* Validate the string */
+                boolean valid = validateCpuRange(newString);
+
+                Button okButton = getButton(IDialogConstants.OK_ID);
+                if (okButton != null) {
+                    getButton(IDialogConstants.OK_ID).setEnabled(valid);
+                }
+                if (valid) {
+                    setErrorMessage(null);
+                } else {
+                    setErrorMessage(Messages.DynamicFilterDialog_InvalidRangesErrorMsg);
+                }
+            }
+        });
+
+        fAllActiveThreadsRadionButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                super.widgetSelected(e);
+                boolean selected = ((Button) e.widget).getSelection();
+                if (!selected) {
+                    return;
+                }
+                setErrorMessage(null);
+                getButton(IDialogConstants.OK_ID).setEnabled(true);
+            }
+        });
+
+        fCpuRangesRadioButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                super.widgetSelected(e);
+                boolean selected = ((Button) e.widget).getSelection();
+                fCpuRangesField.setEnabled(selected);
+                validateCpuRange(fCpuRangesField.getText());
+            }
+        });
+
+        fActiveThreadEnabledButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                super.widgetSelected(e);
+                boolean selected = ((Button) e.widget).getSelection();
+                cpuSelectionGroup.setEnabled(selected);
+                fAllActiveThreadsRadionButton.setEnabled(selected);
+                fCpuRangesRadioButton.setEnabled(selected);
+                fCpuRangesField.setEnabled(selected && fCpuRangesRadioButton.getSelection());
+            }
+        });
+
+        /* Set the base state for the ui control */
+        fActiveThreadEnabledButton.setSelection(filterActive);
+        cpuSelectionGroup.setEnabled(filterActive);
+        fCpuRangesRadioButton.setEnabled(filterActive);
+        fAllActiveThreadsRadionButton.setEnabled(filterActive);
+
+        fAllActiveThreadsRadionButton.setSelection(!usesCpuRanges);
+        fCpuRangesRadioButton.setSelection(usesCpuRanges);
+
+        fCpuRangesField.setEnabled(filterActive && usesCpuRanges);
+
+        /* Populate the CPU ranges fields */
+        if (!filter.getCpuRanges().isEmpty()) {
+            StringJoiner joiner = new StringJoiner(RANGES_DELIMITER);
+            for (Range<Long> range : filter.getCpuRanges()) {
+                String rangeString = range.lowerEndpoint().toString();
+                if (range.lowerEndpoint() != range.upperEndpoint()) {
+                    rangeString = rangeString.concat(INTERNAL_RANGE_SEPARATOR + range.upperEndpoint());
+                }
+                joiner.add(rangeString);
+            }
+            fCpuRangesField.setText(joiner.toString());
+        }
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite area = (Composite) super.createDialogArea(parent);
+        Composite container = new Composite(area, SWT.NONE);
+        container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        GridLayout layout = new GridLayout(1, true);
+        container.setLayout(layout);
+
+        createActiveThreadSection(container);
+        return area;
+    }
+
+    @Override
+    protected boolean isResizable() {
+        return true;
+    }
+
+    private void saveInput() {
+        if (!(fAllActiveThreadsRadionButton.getSelection() || fCpuRangesRadioButton.getSelection())
+                || (fAllActiveThreadsRadionButton.getSelection() && fCpuRangesRadioButton.getSelection())) {
+            throw new IllegalStateException(Messages.DynamicFilterDialog_InvalidRadioButtonState);
+        }
+
+        List<Range<Long>> ranges = null;
+        ranges = parseCpuRangesText(fCpuRangesField.getText());
+
+        fInternalActiveThreadsFilter = new ActiveThreadsFilter(ranges, fCpuRangesRadioButton.getSelection());
+        fInternalActiveThreadsFilter.setEnabled(fActiveThreadEnabledButton.getSelection());
+    }
+
+    private static List<Range<Long>> parseCpuRangesText(final String string) {
+        if (!validateCpuRange(string)) {
+            return null;
+        }
+
+        string.split(RANGES_DELIMITER);
+        List<Range<Long>> results = new ArrayList<>();
+        for (String range : Arrays.asList(string.split(RANGES_DELIMITER))) {
+            if (range.contains(INTERNAL_RANGE_SEPARATOR)) {
+                /* Parse as a range */
+                String[] split = range.split(INTERNAL_RANGE_SEPARATOR);
+                if (split.length != 2) {
+                    /* Invalid range */
+                    continue;
+                }
+
+                long[] sorted = Arrays.asList(split).stream().map(Long::parseLong).sorted().mapToLong(Long::longValue).toArray();
+                results.add(Range.closed(sorted[0], sorted[1]));
+            } else {
+                /* Parse as an individual number */
+                Long value = Long.parseLong(range);
+                results.add(Range.closed(value, value));
+            }
+        }
+        return results;
+    }
+
+    @Override
+    protected void okPressed() {
+        saveInput();
+        super.okPressed();
+    }
+
+    @Override
+    public boolean isHelpAvailable() {
+        return false;
+    }
+
+    /**
+     * Get the resulting ActiveThreadsFilter
+     *
+     * @return The configured
+     *         {@link org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters.ActiveThreadsFilter
+     *         ActiveThreadFilter} instance.
+     */
+    public @NonNull ActiveThreadsFilter getActiveThreadsResult() {
+        return fInternalActiveThreadsFilter;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/Messages.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/Messages.java
new file mode 100644 (file)
index 0000000..fa36304
--- /dev/null
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The messages for
+ * {@link org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters}
+ *
+ * @author Jonathan Rajotte Julien
+ */
+public class Messages extends NLS {
+    private static final String BUNDLE_NAME = "org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow.filters.messages"; //$NON-NLS-1$
+    /** The CPUs ranges example */
+    public static String DynamicFilterDialog_CpuRangesExamples;
+    /** The CPUs radio button label*/
+    public static String DynamicFilterDialog_CpuRangesLabel;
+    /** The CPUs radio button tooltip  */
+    public static String DynamicFilterDialog_CpuRangesTooltip;
+    /** The active thread filter name */
+    public static String DynamicFilterDialog_ActiveThreadsFilterName;
+    /** Thrown error if the radios button are in a invalid state */
+    public static String DynamicFilterDialog_InvalidRadioButtonState;
+    /** The error message displayed when input ranges are invalid */
+    public static String DynamicFilterDialog_InvalidRangesErrorMsg;
+    /** The Options sub groups label */
+    public static String DynamicFilterDialog_OptionsGroupLabel;
+    /** The All CPUs option label */
+    public static String DynamicFilterDialog_RadioButtonAllActiveThreads;
+    /** The All CPUs option tooltip */
+    public static String DynamicFilterDialog_RadioButtonAllActiveThreadsToolTip;
+    /** The dialog title */
+    public static String DynamicFilterDialog_Title;
+
+    static {
+        // initialize resource bundle
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/messages.properties b/analysis/org.eclipse.tracecompass.analysis.os.linux.ui/src/org/eclipse/tracecompass/internal/analysis/os/linux/ui/views/controlflow/filters/messages.properties
new file mode 100644 (file)
index 0000000..700c705
--- /dev/null
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2016 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+DynamicFilterDialog_CpuRangesExamples=e.g. 0-3,5,7-8
+DynamicFilterDialog_CpuRangesLabel=Active Threads on CPUs:
+DynamicFilterDialog_ActiveThreadsFilterName=Show Active Threads Only
+DynamicFilterDialog_CpuRangesTooltip=Show active threads for specified CPU
+DynamicFilterDialog_RadioButtonAllActiveThreadsToolTip=Show all active threads
+DynamicFilterDialog_InvalidRadioButtonState=Invalid radio button state
+DynamicFilterDialog_InvalidRangesErrorMsg=Invalid CPUs Ranges
+DynamicFilterDialog_OptionsGroupLabel=Options
+DynamicFilterDialog_RadioButtonAllActiveThreads=All Active Threads
+DynamicFilterDialog_Title=Dynamic Filters Configuration
index 9b6cddbf7cf504563824987b3656aa9d5dda8d9e..896e84452973e90669c0686fa8de75cb8573c0c2 100644 (file)
@@ -14,3 +14,8 @@ ControlFlowView_NextEventJobName = Searching for next matching event
 ControlFlowView_PreviousEventActionName = Go to previous event
 ControlFlowView_PreviousEventActionTooltip = Go to previous event of the selected thread
 ControlFlowView_PreviousEventJobName = Searching for previous matching event
+
+ControlFlowView_DynamicFiltersActiveThreadToggleLabel=&Active Threads only
+ControlFlowView_DynamicFiltersActiveThreadToggleToolTip=Toggle active threads filtering
+ControlFlowView_DynamicFiltersConfigureLabel=&Configure...
+ControlFlowView_DynamicFiltersMenuLabel=&Dynamic Filters
index f9f51ed37e7398005d07747a345621c0bdeccee7..e7f51ff04d71a757379c9d3404cbf5b7f4cf6fd5 100644 (file)
@@ -1968,6 +1968,36 @@ The tooltip indicates:
 * the pointed state date and start/stop times
 * the pointed state duration (seconds)
 
+==== Dynamics Filters ====
+
+Dynamic filters are filters that are processed and applied each time the control flow view visible time range changes.
+
+The dynamics filters can be rapidly toggled in their view sub menu.
+
+[[Image:images/DynamicFilters/DynamicFiltersToggle.png]]
+
+
+The dynamics filters  can be fine tuned in the configuration dialog.
+
+[[Image:images/DynamicFilters/DynamicFiltersConfigure.png]]
+
+Note: Dynamic filters can induce performance degradation.
+
+===== Show Active Threads Only =====
+
+The Show Active Threads Only filter allow a user to increase the signal to noise ratio by filtering out all <u>inactive</u> threads.
+
+A thread is considered inactive when it is in the following state:
+
+* non-existing
+* unknown
+* wait and blocked
+* wait and unknown
+
+A user can fine tune this filter by providing ranges of CPUs allowing the filter to only show active thread running on the specified CPUs.
+
+[[Image:images/DynamicFilters/DynamicFilter_ShowActiveThreadsConfigure.png]]
+
 === Toolbar ===
 
 <!-- Keep in sync with ref:resource-view-toolbar -->
@@ -2062,6 +2092,10 @@ View Menu
 |
 | Thread Presentation
 | Select the threads layout. Two layouts are available. '''Flat''' layout lists the threads in a flat list per trace. '''Hierarchical''' layout shows the threads in a parent-child tree per trace.
+|-
+|
+| Dynamic Filters
+| Select and configure the Dynamic Filters to be applied.
 |}
 
 === Marker Axis ===
diff --git a/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFilter_ShowActiveThreadsConfigure.png b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFilter_ShowActiveThreadsConfigure.png
new file mode 100644 (file)
index 0000000..c3d8b59
Binary files /dev/null and b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFilter_ShowActiveThreadsConfigure.png differ
diff --git a/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersConfigure.png b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersConfigure.png
new file mode 100644 (file)
index 0000000..730ebaf
Binary files /dev/null and b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersConfigure.png differ
diff --git a/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersToggle.png b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersToggle.png
new file mode 100644 (file)
index 0000000..ca64357
Binary files /dev/null and b/doc/org.eclipse.tracecompass.doc.user/doc/images/DynamicFilters/DynamicFiltersToggle.png differ
index 14602cbfbac9a0fedadc3f352cbbf765ad8e8e76..85df65bc271b14f1ec34edf1e07f543323e09730 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Ericsson
+ * Copyright (c) 2015, 2016 Ericsson, EfficiOS Inc.
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -13,6 +13,7 @@
 package org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -27,6 +28,11 @@ import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
 import org.eclipse.swtbot.swt.finder.keyboard.Keyboard;
 import org.eclipse.swtbot.swt.finder.keyboard.KeyboardFactory;
 import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes;
+import org.eclipse.swtbot.swt.finder.waits.Conditions;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotRadio;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotText;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
@@ -57,6 +63,20 @@ import org.junit.runner.RunWith;
 @RunWith(SWTBotJunit4ClassRunner.class)
 public class ControlFlowViewTest extends KernelTimeGraphViewTestBase {
 
+    private static final String DIALOG_OK = "OK";
+    private static final String DIALOG_CANCEL = "Cancel";
+
+    private static final String THREAD_PRESENTATION_HIERARCHICAL = "Hierarchical";
+    private static final String THREAD_PRESENTATION_FLAT = "Flat";
+
+    private static final String DYNAMIC_FILTER_ACTIVE_THREADS_ONLY_TOGGLE = "Active Threads only";
+    private static final String DYNAMIC_FILTER_ON_CPU_FIELD_MESSAGE = "e.g. 0-3,5,7-8";
+    private static final String DYNAMIC_FILTERS_ALL_ACTIVE_RADIO = "All Active Threads";
+    private static final String DYNAMIC_FILTERS_ON_CPU_RADIO = "Active Threads on CPUs:";
+    private static final String DYNAMIC_FILTERS_SHOW_ACTIVE_THREADS_ONLY_CHECKBOX = "Show Active Threads Only";
+    private static final String DYNAMIC_FILTERS_SHELL_TEXT = "Dynamic Filters Configuration";
+    private static final String DYNAMIC_FILTER_CONFIGURE_LABEL = "Configure...";
+
     private static final String CHECK_SELECTED = "Check selected";
     private static final String CHECK_ALL = "Check all";
     private static final String CHECK_SUBTREE = "Check subtree";
@@ -310,7 +330,7 @@ public class ControlFlowViewTest extends KernelTimeGraphViewTestBase {
         bot.button(CHECK_ALL).click();
         checked = UIThreadRunnable.syncExec(treeCheckCounter);
         assertEquals("Filtered", 26, checked.intValue());
-        bot.button("OK").click();
+        bot.button(DIALOG_OK).click();
         SWTBotTimeGraph timeGraph = new SWTBotTimeGraph(getViewBot().bot());
         SWTBotTimeGraphEntry traceEntry = timeGraph.getEntry(LttngTraceGenerator.getName());
         for (SWTBotTimeGraphEntry entry : traceEntry.getEntries()) {
@@ -318,6 +338,176 @@ public class ControlFlowViewTest extends KernelTimeGraphViewTestBase {
         }
     }
 
+    /**
+     * Test dynamic filters dialog
+     */
+    @Test
+    public void testDynamicFiltersDialog() {
+
+        String valid_cpu_ranges = "0,1,2-100";
+        String invalid_cpu_ranges = "-1,1";
+
+        /* Change window range to 10 ms */
+        TmfTimeRange range = new TmfTimeRange(START_TIME, START_TIME.normalize(10000000L, ITmfTimestamp.NANOSECOND_SCALE));
+        TmfSignalManager.dispatchSignal(new TmfWindowRangeUpdatedSignal(this, range));
+        TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, range.getStartTime(), range.getEndTime()));
+        timeGraphIsReadyCondition(range);
+
+        getViewBot().viewMenu(DYNAMIC_FILTER_CONFIGURE_LABEL).click();
+        fBot.waitUntil(Conditions.shellIsActive(DYNAMIC_FILTERS_SHELL_TEXT));
+        SWTBotShell shell = fBot.shell(DYNAMIC_FILTERS_SHELL_TEXT);
+        shell.activate();
+
+        /* Make sure nothing is checked and radio buttons are disabled */
+        SWTBotCheckBox activeThreadsCheckbox = shell.bot().checkBox(DYNAMIC_FILTERS_SHOW_ACTIVE_THREADS_ONLY_CHECKBOX);
+        SWTBotRadio onCpuRadio = shell.bot().radio(DYNAMIC_FILTERS_ON_CPU_RADIO);
+        SWTBotRadio allActiveRadio = shell.bot().radio(DYNAMIC_FILTERS_ALL_ACTIVE_RADIO);
+        SWTBotText onCpuField = shell.bot().textWithMessage(DYNAMIC_FILTER_ON_CPU_FIELD_MESSAGE);
+
+        assertFalse(activeThreadsCheckbox.isChecked());
+        assertFalse(onCpuRadio.isEnabled());
+        assertFalse(allActiveRadio.isEnabled());
+        assertFalse(onCpuField.isEnabled());
+
+        /*
+         * Test Active Filter buttons toggle
+         */
+        activeThreadsCheckbox.click();
+
+        /* All objects should be enabled except for the CPU ranges field */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertFalse(onCpuField.isEnabled());
+
+        /*
+         * The All Active Threads option should be the default for a new filter
+         */
+        assertTrue(allActiveRadio.isSelected());
+
+        /*
+         * Select All Threads on CPUs option
+         */
+        onCpuRadio.click();
+
+        /* All objects should be enabled */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertTrue(onCpuField.isEnabled());
+
+        assertFalse(allActiveRadio.isSelected());
+        assertTrue(onCpuRadio.isSelected());
+
+        /*
+         * Select All Active Threads then Active Threads on CPUs to validate
+         * toggle of options
+         */
+        allActiveRadio.click();
+
+        /* All objects should be enabled except for the CPU ranges field */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertFalse(onCpuField.isEnabled());
+
+        assertTrue(allActiveRadio.isSelected());
+        assertFalse(onCpuRadio.isSelected());
+
+        /* Select Active Threads on CPUs */
+        onCpuRadio.click();
+
+        /* All objects should be enabled */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertTrue(onCpuField.isEnabled());
+
+        assertFalse(allActiveRadio.isSelected());
+        assertTrue(onCpuRadio.isSelected());
+
+        /* Put an invalid value in the CPU ranges field */
+        onCpuField.setText(invalid_cpu_ranges);
+
+        /* Make sure the OK button is not enabled when in an invalid state */
+        assertFalse(shell.bot().button(DIALOG_OK).isEnabled());
+
+        /* Put a valid value in the CPU ranges field */
+        onCpuField.setText(valid_cpu_ranges);
+
+        /* Make sure the OK button is enabled when in a valid state */
+        assertTrue(shell.bot().button(DIALOG_OK).isEnabled());
+
+        shell.bot().button(DIALOG_OK).click();
+        timeGraphIsReadyCondition(range);
+
+        /* Make sure that the quick Active Thread Filter toggle is checked */
+        assertTrue(getViewBot().viewMenu(DYNAMIC_FILTER_ACTIVE_THREADS_ONLY_TOGGLE).isChecked());
+
+        /* Make sure that the Flat presentation is checked */
+        assertTrue(getViewBot().viewMenu(THREAD_PRESENTATION_FLAT).isChecked());
+        assertFalse(getViewBot().viewMenu(THREAD_PRESENTATION_HIERARCHICAL).isChecked());
+
+        /* Reopen the dialog */
+        getViewBot().viewMenu(DYNAMIC_FILTER_CONFIGURE_LABEL).click();
+        fBot.waitUntil(Conditions.shellIsActive(DYNAMIC_FILTERS_SHELL_TEXT));
+        shell = fBot.shell(DYNAMIC_FILTERS_SHELL_TEXT);
+        shell.activate();
+
+        /* Make sure nothing is checked and radio buttons are disabled */
+        activeThreadsCheckbox = shell.bot().checkBox(DYNAMIC_FILTERS_SHOW_ACTIVE_THREADS_ONLY_CHECKBOX);
+        onCpuRadio = shell.bot().radio(DYNAMIC_FILTERS_ON_CPU_RADIO);
+        allActiveRadio = shell.bot().radio(DYNAMIC_FILTERS_ALL_ACTIVE_RADIO);
+        onCpuField = shell.bot().textWithMessage(DYNAMIC_FILTER_ON_CPU_FIELD_MESSAGE);
+
+        /* Make sure the previous settings are set correctly */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertTrue(onCpuField.isEnabled());
+        assertFalse(allActiveRadio.isSelected());
+        assertTrue(onCpuRadio.isSelected());
+        assertTrue(onCpuField.isEnabled());
+        assertEquals("CPU ranges not equal", onCpuField.getText(), valid_cpu_ranges);
+
+        /*
+         * Change to All Active Threads option click OK then reopen. The
+         * previous CPU range should still be there.
+         */
+
+        allActiveRadio.click();
+        /* Make sure that the ranges are still visible but disabled */
+        assertFalse(onCpuField.isEnabled());
+        assertEquals("Cpu ranges not equal", onCpuField.getText(), valid_cpu_ranges);
+
+        /* Close the dialog */
+        shell.bot().button(DIALOG_OK).click();
+        timeGraphIsReadyCondition(range);
+
+        /* Open the dialog */
+        getViewBot().viewMenu(DYNAMIC_FILTER_CONFIGURE_LABEL).click();
+        fBot.waitUntil(Conditions.shellIsActive(DYNAMIC_FILTERS_SHELL_TEXT));
+        shell = fBot.shell(DYNAMIC_FILTERS_SHELL_TEXT);
+        shell.activate();
+
+        activeThreadsCheckbox = shell.bot().checkBox(DYNAMIC_FILTERS_SHOW_ACTIVE_THREADS_ONLY_CHECKBOX);
+        onCpuRadio = shell.bot().radio(DYNAMIC_FILTERS_ON_CPU_RADIO);
+        allActiveRadio = shell.bot().radio(DYNAMIC_FILTERS_ALL_ACTIVE_RADIO);
+        onCpuField = shell.bot().textWithMessage(DYNAMIC_FILTER_ON_CPU_FIELD_MESSAGE);
+
+        /* Range field should have a value in it */
+        assertTrue(activeThreadsCheckbox.isChecked());
+        assertTrue(allActiveRadio.isEnabled());
+        assertTrue(onCpuRadio.isEnabled());
+        assertFalse(onCpuField.isEnabled());
+        assertTrue(allActiveRadio.isSelected());
+        assertFalse(onCpuRadio.isSelected());
+        assertFalse(onCpuField.isEnabled());
+        assertEquals("CPU ranges not equal", onCpuField.getText(), valid_cpu_ranges);
+
+        shell.bot().button(DIALOG_CANCEL).click();
+    }
+
     /**
      * Test tool bar buttons "Follow CPU Forward" and "Follow CPU Backward"
      */
index 1a2cc5df578d2640678f41efece55179964221d8..73d4863fec55d04a31a45749f03c1e55ede4c011 100644 (file)
@@ -1866,6 +1866,16 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         return markerEventSources;
     }
 
+    /**
+     * Gets the trace to viewer filters map.
+     *
+     * @return The trace to viewer filters map
+     * @since 2.2
+     */
+    protected @NonNull Map<ITmfTrace, ViewerFilter[]> getFiltersMap() {
+        return checkNotNull(fFiltersMap);
+    }
+
     /**
      * Refresh the display
      */
@@ -1881,6 +1891,9 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
                 }
                 fDirty.incrementAndGet();
 
+                /* Update view filters */
+                updateFilters();
+
                 boolean hasEntries = false;
                 synchronized (fEntryListMap) {
                     fEntryList = fEntryListMap.get(fTrace);
@@ -1965,6 +1978,18 @@ public abstract class AbstractTimeGraphView extends TmfView implements ITmfTimeA
         });
     }
 
+    /**
+     * Update view specific filters. A view that possess filter should override
+     * this method to gain access to asynchronous update on refresh
+     *
+     * @since 2.2
+     */
+    protected void updateFilters() {
+        /*
+         * Do nothing. Sub classes are responsible for their view specific filters.
+         */
+    }
+
     /**
      * Redraw the canvas
      */
index 0c27d2515b296c2fc8907e5f5217a580e6fa2457..f2b8e3a33a9d1d6d67222abca1ae63d0dc8df0b6 100644 (file)
@@ -1081,7 +1081,9 @@ public class TimeGraphCombo extends Composite {
     public void removeFilter(@NonNull ViewerFilter filter) {
         fInhibitTreeSelection = true;
         ViewerFilter wrapper = fViewerFilterMap.get(filter);
-        fTreeViewer.removeFilter(wrapper);
+        if (wrapper != null) {
+            fTreeViewer.removeFilter(wrapper);
+        }
         fTimeGraphViewer.removeFilter(filter);
         fViewerFilterMap.remove(filter);
         alignTreeItems(true);
This page took 0.039555 seconds and 5 git commands to generate.