callstack: The flame graph view uses a data provider
The flame graph view is updated to use a data provider instead of the
content provider, which was ui only. Symbol resolution is done by the
data provider, as well as the grouping.
GoTo Min/Max are taking a break
[changed] The flame graph view uses a data provider
Change-Id: Id5e37179bd65ae29f224bfcb03d2c0048cdb14b0
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/149298
Tested-by: CI Bot
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
diff --git a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
index fbd99f1..bf1d932 100644
--- a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
+++ b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/analysis/core/concepts/AggregatedCallSite.java
@@ -11,7 +11,6 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -93,14 +92,4 @@
return "CallSite: " + getObject(); //$NON-NLS-1$
}
- /**
- * Get extra children sites that come with this callsite. For instance, an
- * instrumented callsite could return the kernel processes
- *
- * @return The extra children sites
- */
- public Collection<AggregatedCallSite> getExtraChildrenSites() {
- return Collections.emptyList();
- }
-
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
index d63d620..287df3d 100644
--- a/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
+++ b/callstack/org.eclipse.tracecompass.incubator.analysis.core/src/org/eclipse/tracecompass/incubator/internal/analysis/core/weighted/tree/Messages.java
@@ -30,4 +30,4 @@
private Messages() {
}
-}
\ No newline at end of file
+}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
index e8ad703..3eea029 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/build.properties
@@ -15,4 +15,6 @@
.,\
about.html,\
plugin.properties,\
- plugin.xml
+ plugin.xml,\
+ test_xml_files/,\
+ testfiles/
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
index c0f6e05..390e8de 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core.tests/src/org/eclipse/tracecompass/incubator/callstack/core/tests/callgraph/instrumented/AggregationTreeTest.java
@@ -12,16 +12,21 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
+import org.eclipse.tracecompass.incubator.callstack.core.tests.Activator;
import org.eclipse.tracecompass.incubator.callstack.core.tests.flamechart.CallStackTestBase;
import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallGraphAnalysisStub;
import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedCalledFunction;
@@ -30,7 +35,15 @@
import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
+import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub;
+import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStubNs;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
@@ -44,6 +57,10 @@
*/
public class AggregationTreeTest {
+ private static final String CALLSTACK_FILE = "testfiles/traces/callstack.xml";
+
+ private ITmfTrace fTrace;
+
private static final String QUARK_0 = "0";
private static final String QUARK_1 = "1";
private static final String QUARK_2 = "2";
@@ -79,6 +96,28 @@
}
/**
+ * Open a flamegraph
+ */
+ @Before
+ public void before() {
+ TmfXmlTraceStub trace = new TmfXmlTraceStubNs();
+ IPath filePath = Activator.getAbsoluteFilePath(CALLSTACK_FILE);
+ IStatus status = trace.validate(null, filePath.toOSString());
+ if (!status.isOK()) {
+ fail(status.getException().getMessage());
+ }
+ try {
+ trace.initTrace(null, filePath.toOSString(), TmfEvent.class);
+ } catch (TmfTraceException e) {
+ fail(e.getMessage());
+ }
+ fTrace = trace;
+ TmfTraceOpenedSignal signal = new TmfTraceOpenedSignal(this, trace, null);
+ trace.traceOpened(signal);
+ TmfTraceManager.getInstance().traceOpened(signal);
+ }
+
+ /**
* Dispose the callgraph analysis that has been set
*/
@After
@@ -87,6 +126,10 @@
if (cga != null) {
cga.dispose();
}
+ ITmfTrace trace = fTrace;
+ if (trace != null) {
+ trace.dispose();
+ }
}
/**
@@ -631,4 +674,13 @@
private void setCga(CallGraphAnalysisStub cga) {
fCga = cga;
}
+
+ /**
+ * Get a trace for when a trace is needed
+ *
+ * @return A trace, that has no relation with the callgraphs of these tests
+ */
+ protected @NonNull ITmfTrace getTrace() {
+ return Objects.requireNonNull(fTrace);
+ }
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
index 2cc39a4..86e4448 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/META-INF/MANIFEST.MF
@@ -34,7 +34,7 @@
org.eclipse.tracecompass.incubator.callstack.core.symbol,
org.eclipse.tracecompass.incubator.internal.callstack.core;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
org.eclipse.tracecompass.incubator.internal.callstack.core.base;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
- org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
+ org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui,org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests",
org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph;x-friends:="org.eclipse.tracecompass.incubator.callstack.core.tests,org.eclipse.tracecompass.incubator.callstack.ui",
org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider,
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java
deleted file mode 100644
index 5fe7950..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/callgraph/AllGroupDescriptor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 É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 v1.0 which
- * accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
-
-package org.eclipse.tracecompass.incubator.callstack.core.callgraph;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackGroupDescriptor;
-
-/**
- * A group descriptor represents all the groups together
- *
- * @author Geneviève Bastien
- */
-public final class AllGroupDescriptor implements ICallStackGroupDescriptor {
-
- private static final String ALL_NAME = "all"; //$NON-NLS-1$
-
- private static final ICallStackGroupDescriptor INSTANCE = new AllGroupDescriptor();
-
- /**
- * Get the instance of the all group descriptor
- *
- * @return The instance of this group.
- */
- public static ICallStackGroupDescriptor getInstance() {
- return INSTANCE;
- }
-
- private AllGroupDescriptor() {
-
- }
-
- @Override
- public @Nullable ICallStackGroupDescriptor getNextGroup() {
- return null;
- }
-
- @Override
- public boolean isSymbolKeyGroup() {
- return false;
- }
-
- @Override
- public String getName() {
- return ALL_NAME;
- }
-
-// @Override
-// public List<ICallStackElement> getElements(@Nullable ICallStackElement parent, @Nullable ICallStackElement symbolKeyElement) {
-// return Collections.emptyList();
-// }
-
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
index 4120167..401da11 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/internal/callstack/core/flamegraph/FlameGraphDataProviderFactory.java
@@ -10,7 +10,9 @@
package org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph;
import java.util.Collection;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
import java.util.Objects;
import org.eclipse.jdt.annotation.Nullable;
@@ -24,15 +26,18 @@
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
/**
- * Factory for the flame chart data provider
+ * Factory for the flame graph data provider
*
* @author Geneviève Bastien
*/
public class FlameGraphDataProviderFactory implements IDataProviderFactory {
+ private static Map<String, FlameGraphDataProvider<?, ?, ?>> INSTANCES = new HashMap<>();
+
@Override
public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
// Need the analysis
@@ -59,6 +64,10 @@
}
private static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace, String secondaryId) {
+ FlameGraphDataProvider<?, ?, ?> dataProvider = INSTANCES.get(secondaryId);
+ if (dataProvider != null) {
+ return dataProvider;
+ }
// The trace can be an experiment, so we need to know if there are
// multiple analysis modules with the same ID
Iterable<IWeightedTreeProvider> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, IWeightedTreeProvider.class);
@@ -78,4 +87,19 @@
return null;
}
+ /**
+ * Adds a reference to a data provider identified by the id, but not
+ * associated with a trace. Useful for data provider unit testing where
+ * fixtures of data are used without a trace
+ *
+ * @param id
+ * ID of the data provider
+ * @param dataProvider
+ * The data provider
+ */
+ @VisibleForTesting
+ public static void registerDataProviderWithId(String id, FlameGraphDataProvider<?, ?, ?> dataProvider) {
+ INSTANCES.put(id, dataProvider);
+ }
+
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
index 50d5dee..ef91a54 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui.swtbot.tests/src/org/eclipse/tracecompass/incubator/callstack/core/ui/swtbot/tests/flamegraph/FlameGraphTest.java
@@ -14,8 +14,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
@@ -28,12 +28,20 @@
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.eclipse.swtbot.swt.finder.results.Result;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.tracecompass.incubator.callstack.core.tests.callgraph.instrumented.AggregationTreeTest;
+import org.eclipse.tracecompass.incubator.callstack.core.tests.stubs.CallGraphAnalysisStub;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProvider;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProviderFactory;
import org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph.FlameGraphPresentationProvider;
import org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph.FlameGraphView;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers.SWTBotTestCondition;
+import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotTimeGraph;
import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils;
import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
@@ -42,24 +50,29 @@
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
import org.eclipse.ui.IViewPart;
+import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
+import org.junit.runner.RunWith;
/**
* Unit tests for the flame graph view
*
* @author Matthew Khouzam
*/
+@RunWith(SWTBotJunit4ClassRunner.class)
public class FlameGraphTest extends AggregationTreeTest {
+ private static final @NonNull String SECONDARY_ID = "org.eclipse.tracecompass.incubator.callstack.ui.swtbot.test";
private static final String FLAMEGRAPH_ID = FlameGraphView.ID;
private SWTWorkbenchBot fBot;
- private SWTBotView fView;
+ private SWTBotView fViewBot;
private FlameGraphView fFg;
/** The Log4j logger instance. */
private static final Logger fLogger = Logger.getRootLogger();
private TimeGraphViewer fTimeGraphViewer;
+ private SWTBotTimeGraph fTimeGraph;
/**
* Initialization
@@ -86,14 +99,14 @@
* Open a flamegraph
*/
@Before
- public void before() {
+ public void openView() {
fBot = new SWTWorkbenchBot();
- SWTBotUtils.openView(FLAMEGRAPH_ID);
+ SWTBotUtils.openView(FLAMEGRAPH_ID, SECONDARY_ID);
SWTBotView view = fBot.viewById(FLAMEGRAPH_ID);
assertNotNull(view);
- fView = view;
+ fViewBot = view;
FlameGraphView flamegraph = UIThreadRunnable.syncExec((Result<FlameGraphView>) () -> {
- IViewPart viewRef = fView.getViewReference().getView(true);
+ IViewPart viewRef = fViewBot.getViewReference().getView(true);
return (viewRef instanceof FlameGraphView) ? (FlameGraphView) viewRef : null;
});
assertNotNull(flamegraph);
@@ -101,14 +114,54 @@
assertNotNull(fTimeGraphViewer);
SWTBotUtils.maximize(flamegraph);
fFg = flamegraph;
+ fTimeGraph = new SWTBotTimeGraph(view.bot());
}
- private void loadFlameGraph() {
- UIThreadRunnable.syncExec(() -> fFg.buildFlameGraph(Collections.singleton(getCga()), null, null));
+ /**
+ * Clean up after a test, reset the views and reset the states of the
+ * timegraph by pressing reset on all the resets of the legend
+ */
+ @After
+ public void after() {
+ // Calling maximize again will minimize
+ FlameGraphView fg = fFg;
+ if (fg != null) {
+ SWTBotUtils.maximize(fg);
+ }
+ // Setting the input to null so the view can be emptied, to avoid race conditions with subsequent tests
+ TimeGraphViewer tg = fTimeGraphViewer;
+ if (tg != null) {
+ UIThreadRunnable.syncExec(() -> {
+ tg.setInput(null);
+ tg.refresh();
+ });
+ }
fBot.waitUntil(new SWTBotTestCondition() {
@Override
public boolean test() throws Exception {
- return !fFg.isDirty();
+ return fTimeGraph.getEntries().length == 0;
+ }
+
+ @Override
+ public String getFailureMessage() {
+ return "Flame graph not emptied";
+ }
+ });
+ fViewBot.close();
+ fBot.waitUntil(ConditionHelpers.viewIsClosed(fViewBot));
+ }
+
+ private void loadFlameGraph() {
+ CallGraphAnalysisStub cga = Objects.requireNonNull(getCga());
+ ITmfTrace trace = getTrace();
+ fFg.traceSelected(new TmfTraceSelectedSignal(this, trace));
+ FlameGraphDataProvider<?, ?, ?> dp = new FlameGraphDataProvider<>(trace, cga, FlameGraphDataProvider.ID + ':' + SECONDARY_ID);
+ FlameGraphDataProviderFactory.registerDataProviderWithId(SECONDARY_ID, dp);
+ UIThreadRunnable.syncExec(() -> fFg.buildFlameGraph(trace, null, null));
+ fBot.waitUntil(new SWTBotTestCondition() {
+ @Override
+ public boolean test() throws Exception {
+ return fTimeGraph.getEntries().length > 0;
}
@Override
@@ -120,10 +173,10 @@
}
private ITimeGraphEntry selectRoot() {
- // FIXME: there should be only 2 selectNextItem, not 3
UIThreadRunnable.syncExec(() -> {
fTimeGraphViewer.selectNextItem();
fTimeGraphViewer.selectNextItem();
+ fTimeGraphViewer.selectNextItem();
});
ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
assertNotNull(entry);
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java
deleted file mode 100644
index e53ea14..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphContentProvider.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Ericsson
- *
- * 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.incubator.internal.callstack.ui.flamegraph;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite;
-import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
-import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.AggregatedThreadStatus;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
-
-/**
- * Content provider for the flame graph view
- *
- * @author Sonia Farrah
- *
- */
-public class FlameGraphContentProvider implements ITimeGraphContentProvider {
-
- private final List<TimeGraphEntry> fFlameGraphEntries = new ArrayList<>();
-
- private SortOption fSortOption = SortOption.BY_NAME;
- private Comparator<TimeGraphEntry> fThreadComparator = new ThreadNameComparator();
-
- private final Map<ICallStackElement, TimeGraphEntry> fLevelEntries = new HashMap<>();
-
- /**
- * Parse the aggregated tree created by the callGraphAnalysis and creates
- * the event list (functions) for each entry (depth)
- *
- * @param firstNode
- * The first node of the aggregation tree
- * @param childrenEntries
- * The list of entries for one thread
- * @param timestampStack
- * A stack used to save the functions timeStamps
- */
- private void setData(AggregatedCallSite firstNode, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack) {
- Long lastEnd = timestampStack.peek();
- if (lastEnd == null) {
- return;
- }
- for (int i = 0; i <= firstNode.getMaxDepth() - 1; i++) {
- if (i >= childrenEntries.size()) {
- FlamegraphDepthEntry entry = new FlamegraphDepthEntry(String.valueOf(i), 0, firstNode.getWeight(), i, String.valueOf(i));
- childrenEntries.add(entry);
- }
- childrenEntries.get(i).updateEndTime(lastEnd + firstNode.getWeight());
- }
- FlamegraphDepthEntry firstEntry = checkNotNull(childrenEntries.get(0));
- firstEntry.addEvent(new FlamegraphEvent(firstEntry, lastEnd, firstNode));
- // Build the event list for next entries (next depth)
- addEvent(firstNode, childrenEntries, timestampStack, 1);
- timestampStack.pop();
- }
-
- private static void setExtraData(AggregatedCallSite firstNode, List<TimeGraphEntry> extraEntries, Deque<Long> timestampStack) {
- Long lastEnd = timestampStack.peek();
- if (lastEnd == null) {
- return;
- }
- Iterator<AggregatedCallSite> extraChildrenSites = firstNode.getExtraChildrenSites().iterator();
-
- if (!extraChildrenSites.hasNext()) {
- return;
- }
- // Get or add the entry
- if (extraEntries.isEmpty()) {
- TimeGraphEntry entry = new TimeGraphEntry(Messages.FlameGraphContentProvider_EntryName, 0, 0);
- extraEntries.add(entry);
- }
- TimeGraphEntry entry = extraEntries.get(0);
-
- // TODO: loop in the extra data and add the events
- while (extraChildrenSites.hasNext()) {
- AggregatedCallSite next = extraChildrenSites.next();
- if (next instanceof AggregatedThreadStatus) {
- entry.addEvent(new TimeEvent(entry, lastEnd, next.getWeight(), ((AggregatedThreadStatus) next).getProcessStatus().getStateValue().unboxInt()));
- } else {
- entry.addEvent(new FlamegraphEvent(entry, lastEnd, next));
- }
- lastEnd += next.getWeight();
- entry.updateEndTime(lastEnd);
- }
- }
-
- /**
- * Build the events list for an entry (depth), then creates recursively the
- * events for the next entries. This parses the aggregation tree starting
- * from the bottom. This uses a stack to save the timestamp for each
- * function. Once we save a function's timestamp we'll use it to create the
- * callees events.
- *
- * @param node
- * The node of the aggregation tree
- * @param childrenEntries
- * The list of entries for one thread
- * @param timestampStack
- * A stack used to save the functions timeStamps
- */
- private void addEvent(AggregatedCallSite node, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack, int depth) {
- node.getCallees().stream()
- .sorted(Comparator.comparingLong(AggregatedCallSite::getWeight))
- .forEach(child -> {
- addEvent(child, childrenEntries, timestampStack, depth + 1);
- });
- node.getCallees().stream().forEach(child -> {
- timestampStack.pop();
- });
-
- Long lastEnd = timestampStack.peek();
- if (lastEnd == null) {
- return;
- }
- FlamegraphDepthEntry entry = checkNotNull(childrenEntries.get(depth - 1));
- // Create the event corresponding to the function using the caller's
- // timestamp
- entry.addEvent(new FlamegraphEvent(entry, lastEnd, node));
- timestampStack.push(lastEnd + node.getWeight());
- }
-
- @Override
- public boolean hasChildren(@Nullable Object element) {
- return !fFlameGraphEntries.isEmpty();
- }
-
- @Override
- public ITimeGraphEntry[] getElements(@Nullable Object inputElement) {
- fFlameGraphEntries.clear();
- fLevelEntries.clear();
- // Get the root of each thread
- if (inputElement instanceof Collection<?>) {
- Collection<?> callgraphProviders = (Collection<?>) inputElement;
- for (Object object : callgraphProviders) {
- if (object instanceof CallGraph) {
- buildCallGraph((CallGraph) object);
- }
- }
- } else {
- return new ITimeGraphEntry[0];
- }
-
- // Sort the threads
- fFlameGraphEntries.sort(fThreadComparator);
- return fFlameGraphEntries.toArray(new ITimeGraphEntry[fFlameGraphEntries.size()]);
- }
-
- private void buildCallGraph(CallGraph object) {
- Collection<ICallStackElement> elements = object.getElements();
- for (ICallStackElement element : elements) {
- buildChildrenEntries(element, object, null);
- }
- }
-
- /**
- * Build the entry list for one thread
- *
- * @param element
- * The node of the aggregation tree
- */
- private void buildChildrenEntries(ICallStackElement element, CallGraph object, @Nullable TimeGraphEntry parent) {
- // Add the entry
- TimeGraphEntry currentEntry = new TimeGraphEntry(element.getName(), 0L, 0L);
- if (parent != null) {
- parent.addChild(currentEntry);
- } else {
- fFlameGraphEntries.add(currentEntry);
- }
-
- // Create the children entries
- for (ICallStackElement child : element.getChildrenElements()) {
- buildChildrenEntries(child, object, currentEntry);
- }
-
- // Create the callsites entries
- if (!(element.isLeaf())) {
- return;
- }
-
- List<FlamegraphDepthEntry> childrenEntries = new ArrayList<>();
- List<TimeGraphEntry> extraEntries = new ArrayList<>();
- Deque<Long> timestampStack = new ArrayDeque<>();
- timestampStack.push(0L);
-
- // Sort children by duration
- object.getCallingContextTree(element).stream()
- .sorted(Comparator.comparingLong(AggregatedCallSite::getWeight))
- .forEach(rootFunction -> {
- setData(rootFunction, childrenEntries, timestampStack);
- setExtraData(rootFunction, extraEntries, timestampStack);
- long currentThreadDuration = timestampStack.pop() + rootFunction.getWeight();
- timestampStack.push(currentThreadDuration);
- });
- childrenEntries.forEach(child -> {
- currentEntry.addChild(child);
- });
- extraEntries.forEach(child -> {
- currentEntry.addChild(child);
- });
- currentEntry.updateEndTime(timestampStack.pop());
- return;
-
- }
-
- @Override
- public ITimeGraphEntry[] getChildren(@Nullable Object parentElement) {
- return fFlameGraphEntries.toArray(new TimeGraphEntry[fFlameGraphEntries.size()]);
- }
-
- @Override
- public @Nullable ITimeGraphEntry getParent(@Nullable Object element) {
- // Do nothing
- return null;
- }
-
- @Override
- public void dispose() {
- // Do nothing
- }
-
- @Override
- public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) {
- // Do nothing
- }
-
- /**
- * Get the sort option
- *
- * @return the sort option.
- */
- public SortOption getSortOption() {
- return fSortOption;
- }
-
- /**
- * Set the sort option for sorting the thread entries
- *
- * @param sortOption
- * the sort option to set
- *
- */
- public void setSortOption(SortOption sortOption) {
- fSortOption = sortOption;
- switch (sortOption) {
- case BY_NAME:
- fThreadComparator = new ThreadNameComparator();
- break;
- case BY_NAME_REV:
- fThreadComparator = checkNotNull(new ThreadNameComparator().reversed());
- break;
- case BY_ID:
- fThreadComparator = new ThreadIdComparator();
- break;
- case BY_ID_REV:
- fThreadComparator = checkNotNull(new ThreadIdComparator().reversed());
- break;
- default:
- break;
- }
- }
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
index 97ac26e..b8fa89c 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphPresentationProvider.java
@@ -8,31 +8,29 @@
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.callstack.ui.flamegraph;
-import java.text.Format;
-import java.text.NumberFormat;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
-import org.eclipse.tracecompass.incubator.analysis.core.concepts.ICallStackSymbol;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
import org.eclipse.tracecompass.incubator.internal.callstack.ui.FlameViewPalette;
-import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
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.NullTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
-
-import com.google.common.collect.ImmutableMap;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
/**
* Presentation provider for the flame graph view, based on the generic TMF
@@ -44,12 +42,8 @@
/** Number of colors used for flameGraph events */
public static final int NUM_COLORS = 360;
- private static final Format FORMATTER = new SubSecondTimeWithUnitFormat();
-
private @Nullable FlameGraphView fView;
- private @Nullable Integer fAverageCharWidth;
-
private FlameViewPalette fFlameViewPalette;
/**
@@ -77,70 +71,56 @@
@NonNullByDefault({})
@Override
public Map<String, String> getEventHoverToolTipInfo(ITimeEvent event, long hoverTime) {
- if (!(event instanceof FlamegraphEvent)) {
- return Collections.emptyMap();
+ Map<String, String> retMap = super.getEventHoverToolTipInfo(event, hoverTime);
+ if (retMap == null) {
+ retMap = new LinkedHashMap<>(1);
}
- ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
- ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
- String funcSymbol = null;
- FlamegraphEvent fgEvent = (FlamegraphEvent) event;
- Object symbol = fgEvent.getSymbol();
- if (activeTrace != null && (symbol instanceof ICallStackSymbol)) {
- funcSymbol = ((ICallStackSymbol) symbol).resolve(SymbolProviderManager.getInstance().getSymbolProviders(activeTrace));
+ if (!(event instanceof TimeEvent) || !((TimeEvent) event).hasValue() ||
+ !(event.getEntry() instanceof TimeGraphEntry)) {
+ return retMap;
}
- builder.put(Messages.FlameGraph_Symbol, funcSymbol == null ? String.valueOf(fgEvent.getSymbol()) : funcSymbol + " (" + fgEvent.getSymbol() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
- long nb = (fgEvent.getNumberOfCalls());
- builder.put(Messages.FlameGraph_NbCalls, NumberFormat.getIntegerInstance().format(nb)); // $NON-NLS-1$
- Map<String, String> tooltip = ((FlamegraphEvent) event).getTooltip(FORMATTER);
- builder.putAll(tooltip);
- return builder.build();
+
+ TimeGraphEntry entry = (TimeGraphEntry) event.getEntry();
+ ITimeGraphDataProvider<? extends TimeGraphEntryModel> dataProvider = FlameGraphView.getProvider(entry);
+ TmfModelResponse<@NonNull Map<@NonNull String, @NonNull String>> response = dataProvider.fetchTooltip(
+ FetchParametersUtils.selectionTimeQueryToMap(new SelectionTimeQueryFilter(hoverTime, hoverTime, 1, Collections.singletonList(entry.getEntryModel().getId()))), null);
+ Map<@NonNull String, @NonNull String> tooltipModel = response.getModel();
+ if (tooltipModel != null) {
+ retMap.putAll(tooltipModel);
+ }
+
+ return retMap;
}
@Override
public int getStateTableIndex(@Nullable ITimeEvent event) {
- if (event instanceof FlamegraphEvent) {
- FlamegraphEvent flameGraphEvent = (FlamegraphEvent) event;
- return FlameViewPalette.getIndexForValue(flameGraphEvent.getValue());
- } else if (event instanceof NullTimeEvent) {
+ if (event == null || event instanceof NullTimeEvent) {
return INVISIBLE;
- } else if (event instanceof TimeEvent) {
- int cfIndex = fFlameViewPalette.getControlFlowIndex(event);
- if (cfIndex >= 0) {
- return cfIndex;
+ }
+ ITimeGraphEntry entry = event.getEntry();
+ if (event instanceof TimeEvent && entry instanceof TimeGraphEntry) {
+ TimeGraphEntry tgEntry = (TimeGraphEntry) entry;
+ ITmfTreeDataModel entryModel = tgEntry.getEntryModel();
+ if (entryModel instanceof FlameChartEntryModel) {
+ switch(((FlameChartEntryModel) entryModel).getEntryType()) {
+ case FUNCTION:
+ return FlameViewPalette.getIndexForValue(((TimeEvent) event).getValue());
+ case KERNEL:
+ int cfIndex = fFlameViewPalette.getControlFlowIndex(event);
+ if (cfIndex >= 0) {
+ return cfIndex;
+ }
+ break;
+ case LEVEL: // Fallthrough
+ case TRACE: // Fallthrough
+ default:
+ break;
+ }
}
}
- return FlameViewPalette.MULTIPLE_STATE_INDEX;
- }
- @Override
- public void postDrawEvent(@Nullable ITimeEvent event, @Nullable Rectangle bounds, @Nullable GC gc) {
- if (bounds == null || gc == null) {
- return;
- }
- Integer averageCharWidth = fAverageCharWidth;
- if (averageCharWidth == null) {
- averageCharWidth = gc.getFontMetrics().getAverageCharWidth();
- fAverageCharWidth = averageCharWidth;
- }
- if (bounds.width <= averageCharWidth) {
- return;
- }
- if (!(event instanceof FlamegraphEvent)) {
- return;
- }
- String funcSymbol = ""; //$NON-NLS-1$
- ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
- if (activeTrace != null) {
- FlamegraphEvent fgEvent = (FlamegraphEvent) event;
- Object symbol = fgEvent.getSymbol();
- if (symbol instanceof ICallStackSymbol) {
- funcSymbol = ((ICallStackSymbol) symbol).resolve(SymbolProviderManager.getInstance().getSymbolProviders(activeTrace));
- } else {
- funcSymbol = String.valueOf(symbol);
- }
- }
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
- Utils.drawText(gc, funcSymbol, bounds.x, bounds.y, bounds.width, bounds.height, true, true);
+
+ return FlameViewPalette.MULTIPLE_STATE_INDEX;
}
/**
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
index 2d7dfc0..d1f51d9 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphSelView.java
@@ -16,6 +16,7 @@
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
/**
* View to display the flame graph. This uses the flameGraphNode tree generated
@@ -48,13 +49,17 @@
*/
@TmfSignalHandler
public void handleSelectionChange(TmfSelectionRangeUpdatedSignal signal) {
+ ITmfTrace trace = getTrace();
+ if (trace == null) {
+ return;
+ }
ITmfTimestamp beginTime = signal.getBeginTime();
ITmfTimestamp endTime = signal.getEndTime();
if (beginTime != endTime) {
- buildFlameGraph(getCallgraphModules(), beginTime, endTime);
+ buildFlameGraph(trace, beginTime, endTime);
} else {
- buildFlameGraph(getCallgraphModules(), null, null);
+ buildFlameGraph(trace, null, null);
}
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
index f235c2d..a0e230a 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlameGraphView.java
@@ -14,10 +14,15 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
@@ -29,6 +34,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -53,6 +59,8 @@
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
@@ -60,15 +68,29 @@
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
+import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.AllGroupDescriptor;
import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.AllGroupDescriptor;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraph;
-import org.eclipse.tracecompass.incubator.callstack.core.callgraph.CallGraphGroupBy;
import org.eclipse.tracecompass.incubator.callstack.core.callgraph.ICallGraphProvider;
-import org.eclipse.tracecompass.incubator.callstack.core.instrumented.ICalledFunction;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.flamegraph.FlameGraphDataProvider;
import org.eclipse.tracecompass.incubator.internal.callstack.ui.Activator;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TmfFilterAppliedSignal;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TraceCompassFilter;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
@@ -76,10 +98,10 @@
import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
-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.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ui.TmfUiRefreshHandler;
import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
import org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProviderPreferencePage;
import org.eclipse.tracecompass.tmf.ui.symbols.SymbolProviderConfigDialog;
@@ -88,6 +110,13 @@
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NamedTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry.Sampling;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
import org.eclipse.ui.IActionBars;
@@ -97,7 +126,10 @@
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
/**
@@ -114,7 +146,7 @@
*/
public static final String ID = FlameGraphView.class.getPackage().getName() + ".flamegraphView"; //$NON-NLS-1$
- private static final String SYMBOL_MAPPING_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
+ private static final @NonNull String SYMBOL_MAPPING_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
private static final @NonNull String GROUP_BY_ICON_PATH = "icons/etool16/group_by.gif"; //$NON-NLS-1$
private static final String SORT_OPTION_KEY = "sort.option"; //$NON-NLS-1$
@@ -124,9 +156,11 @@
private static final ImageDescriptor SORT_BY_ID_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
private static final ImageDescriptor AGGREGATE_BY_ICON = Activator.getDefault().getImageDescripterFromPath(GROUP_BY_ICON_PATH);
+ private static final int DEFAULT_BUFFER_SIZE = 3;
+
private TimeGraphViewer fTimeGraphViewer;
- private FlameGraphContentProvider fTimeGraphContentProvider;
+ private SortOption fSortOption = SortOption.BY_NAME;
private TimeGraphPresentationProvider fPresentationProvider;
@@ -140,7 +174,6 @@
// The action to import a binary file mapping */
private Action fConfigureSymbolsAction;
- private final Multimap<ITmfTrace, ISymbolProvider> fSymbolProviders = LinkedHashMultimap.create();
private @Nullable IWeightedTreeGroupDescriptor fGroupBy = null;
/**
* A plain old semaphore is used since different threads will be competing
@@ -151,7 +184,24 @@
// Variable used to specify when the graph is dirty, ie waiting for data refresh
private final AtomicInteger fDirty = new AtomicInteger();
- private Job fJob;
+ /** The trace to build thread hash map */
+ private final Map<ITmfTrace, Job> fBuildJobMap = new HashMap<>();
+ private final Map<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>, Map<Long, @NonNull TimeGraphEntry>> fEntries = new HashMap<>();
+
+ /**
+ * Set of visible entries to zoom on.
+ */
+ private @NonNull Set<@NonNull TimeGraphEntry> fVisibleEntries = Collections.emptySet();
+
+ private long fEndTime = Long.MIN_VALUE;
+
+ /** The trace to entry list hash map */
+ private final Map<ITmfTrace, List<@NonNull TimeGraphEntry>> fEntryListMap = new HashMap<>();
+
+ private int fDisplayWidth;
+ private @Nullable ZoomThread fZoomThread;
+ private final Object fZoomThreadResultLock = new Object();
+
/**
* Constructor
@@ -173,10 +223,9 @@
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
+ fDisplayWidth = Display.getDefault().getBounds().width;
fTimeGraphViewer = new TimeGraphViewer(parent, SWT.NONE);
- fTimeGraphContentProvider = new FlameGraphContentProvider();
fPresentationProvider = new FlameGraphPresentationProvider();
- fTimeGraphViewer.setTimeGraphContentProvider(fTimeGraphContentProvider);
fTimeGraphViewer.setTimeGraphProvider(fPresentationProvider);
fTimeGraphViewer.setTimeFormat(TimeFormat.NUMBER);
IEditorPart editor = getSite().getPage().getActiveEditor();
@@ -198,8 +247,8 @@
ISelection selection = timeGraphControl.getSelection();
if (selection instanceof IStructuredSelection) {
for (Object object : ((IStructuredSelection) selection).toList()) {
- if (object instanceof FlamegraphEvent) {
- FlamegraphEvent event = (FlamegraphEvent) object;
+ if (object instanceof TimeEvent) {
+ TimeEvent event = (TimeEvent) object;
long startTime = event.getTime();
long endTime = startTime + event.getDuration();
getTimeGraphViewer().setStartFinishTime(startTime, endTime);
@@ -209,6 +258,41 @@
}
}
});
+ fTimeGraphViewer.addRangeListener(event -> {
+ startZoomThread(event.getStartTime(), event.getEndTime(), false);
+ });
+ TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
+ timeGraphControl.addPaintListener(new PaintListener() {
+
+ /**
+ * This paint control allows the virtual time graph refresh to occur on paint
+ * events instead of just scrolling the time axis or zooming. To avoid
+ * refreshing the model on every paint event, we use a TmfUiRefreshHandler to
+ * coalesce requests and only execute the last one, we also check if the entries
+ * have changed to avoid useless model refresh.
+ *
+ * @param e
+ * paint event on the visible area
+ */
+ @Override
+ public void paintControl(PaintEvent e) {
+ TmfUiRefreshHandler.getInstance().queueUpdate(this, () -> {
+ if (timeGraphControl.isDisposed()) {
+ return;
+ }
+ Set<@NonNull TimeGraphEntry> newSet = getVisibleItems(DEFAULT_BUFFER_SIZE);
+ if (!fVisibleEntries.equals(newSet)) {
+ /*
+ * Start a zoom thread if the set of visible entries has changed. We do not use
+ * lists as the order is not important. We cannot use the start index / size of
+ * the visible entries as we can collapse / reorder events.
+ */
+ fVisibleEntries = newSet;
+ startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), false);
+ }
+ });
+ }
+ });
}
/**
@@ -229,8 +313,29 @@
*/
@TmfSignalHandler
public void traceSelected(final TmfTraceSelectedSignal signal) {
- fTrace = signal.getTrace();
- Display.getDefault().asyncExec(() -> buildFlameGraph(getCallgraphModules(), null, null));
+ ITmfTrace trace = signal.getTrace();
+
+ fTrace = trace;
+ if (trace == null) {
+ return;
+ }
+ // If entries for this trace are already available, just zoom on them,
+ // otherwise, rebuild
+ List<@NonNull TimeGraphEntry> list = fEntryListMap.get(trace);
+ if (list == null) {
+ refresh();
+ Display.getDefault().asyncExec(() -> buildFlameGraph(trace, null, null));
+ } else {
+ // Reset end time
+ long endTime = Long.MIN_VALUE;
+ for (TimeGraphEntry entry : list) {
+ endTime = Math.max(endTime, entry.getEndTime());
+ }
+ setEndTime(endTime);
+ refresh();
+ startZoomThread(0, endTime, false);
+ }
+
}
/**
@@ -255,11 +360,698 @@
.collect(Collectors.toSet());
}
+ private String getProviderId() {
+ String secondaryId = this.getViewSite().getSecondaryId();
+ return (secondaryId == null) ? FlameGraphDataProvider.ID : FlameGraphDataProvider.ID + ':' + secondaryId;
+ }
+
+ private class BuildRunnable {
+ private final @NonNull ITmfTrace fBuildTrace;
+ private final @NonNull ITmfTrace fParentTrace;
+ private final @NonNull FlowScopeLog fScope;
+ private final @NonNull Map<String, Object> fParameters;
+
+ public BuildRunnable(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd, final @NonNull FlowScopeLog log) {
+ fBuildTrace = trace;
+ fParentTrace = parentTrace;
+ fScope = log;
+ if (selStart != null && selEnd != null) {
+ fParameters = ImmutableMap.of(FlameGraphDataProvider.SELECTION_RANGE_KEY, ImmutableList.of(selStart.toNanos(), selEnd.toNanos()));
+ } else {
+ fParameters = Collections.emptyMap();
+ }
+ }
+
+ public void run(IProgressMonitor monitor) {
+ try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:BuildThread", "trace", fBuildTrace.getName()).setParentScope(fScope).build()) { //$NON-NLS-1$ //$NON-NLS-2$
+ buildEntryList(fBuildTrace, fParentTrace, fParameters, NonNullUtils.checkNotNull(monitor));
+ synchronized (fBuildJobMap) {
+ fBuildJobMap.remove(fBuildTrace);
+ }
+ }
+ }
+ }
+
+ private void buildEntryList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull Map<String, Object> additionalParams, @NonNull IProgressMonitor monitor) {
+ ITimeGraphDataProvider<@NonNull TimeGraphEntryModel> dataProvider = DataProviderManager
+ .getInstance().getDataProvider(trace, getProviderId(), ITimeGraphDataProvider.class);
+ if (dataProvider == null) {
+ return;
+ }
+ boolean complete = false;
+ while (!complete && !monitor.isCanceled()) {
+ Map<String, Object> parameters = new HashMap<>(additionalParams);
+ parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, ImmutableList.of(0, Long.MAX_VALUE));
+ IWeightedTreeGroupDescriptor groupBy = fGroupBy;
+ if (groupBy != null) {
+ parameters.put(FlameGraphDataProvider.GROUP_BY_KEY, groupBy.getName());
+ }
+ TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = dataProvider.fetchTree(parameters, monitor);
+ if (response.getStatus() == ITmfResponse.Status.FAILED) {
+ Activator.getDefault().logError(getClass().getSimpleName() + " Data Provider failed: " + response.getStatusMessage()); //$NON-NLS-1$
+ return;
+ } else if (response.getStatus() == ITmfResponse.Status.CANCELLED) {
+ return;
+ }
+ complete = response.getStatus() == ITmfResponse.Status.COMPLETED;
+
+
+ TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel();
+ long endTime = Long.MIN_VALUE;
+ if (model != null) {
+ Map<Long, TimeGraphEntry> entries;
+ synchronized (fEntries) {
+ entries = fEntries.computeIfAbsent(dataProvider, dp -> new HashMap<>());
+ /*
+ * The provider may send entries unordered and parents may
+ * not exist when child is constructor, we'll re-unite
+ * families at the end
+ */
+ List<TimeGraphEntry> orphaned = new ArrayList<>();
+ for (TimeGraphEntryModel entry : model.getEntries()) {
+ TimeGraphEntry uiEntry = entries.get(entry.getId());
+ if (entry.getParentId() != -1) {
+ if (uiEntry == null) {
+ uiEntry = new TimeGraphEntry(entry);
+ TimeGraphEntry parent = entries.get(entry.getParentId());
+ if (parent != null) {
+ parent.addChild(uiEntry);
+ } else {
+ orphaned.add(uiEntry);
+ }
+ entries.put(entry.getId(), uiEntry);
+ } else {
+ uiEntry.updateModel(entry);
+ }
+ } else {
+ endTime = Long.max(endTime, entry.getEndTime() + 1);
+
+ if (uiEntry != null) {
+ uiEntry.updateModel(entry);
+ } else {
+ // Do not assume that parentless entries are
+ // trace entries
+ uiEntry = new ParentEntry(entry, dataProvider);
+ entries.put(entry.getId(), uiEntry);
+ addToEntryList(parentTrace, Collections.singletonList(uiEntry));
+ }
+ }
+ }
+ setEndTime(endTime);
+ // Find missing parents
+ for (TimeGraphEntry orphanedEntry : orphaned) {
+ TimeGraphEntry parent = entries.get(orphanedEntry.getEntryModel().getParentId());
+ if (parent != null) {
+ parent.addChild(orphanedEntry);
+ }
+ }
+ }
+
+ long start = 0;
+ long end = getEndTime();
+ final long resolution = Long.max(1, (end - start) / getDisplayWidth());
+ zoomEntries(ImmutableList.copyOf(entries.values()), start, end, resolution, monitor);
+ }
+
+ if (monitor.isCanceled()) {
+ return;
+ }
+
+ if (parentTrace.equals(getTrace())) {
+ refresh();
+ }
+ monitor.worked(1);
+
+ if (!complete && !monitor.isCanceled()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Activator.getDefault().logError("Failed to wait for data provider", e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /**
+ * Zoom thread
+ *
+ * @since 1.1
+ */
+ protected class ZoomThread extends Thread {
+ private final long fZoomStartTime;
+ private final long fZoomEndTime;
+ private final long fResolution;
+ private int fScopeId = -1;
+ private final @NonNull IProgressMonitor fMonitor;
+ private @NonNull Collection<@NonNull TimeGraphEntry> fCurrentEntries;
+ private boolean fForce;
+
+
+ /**
+ * Constructor
+ *
+ * @param entries
+ * The entries to zoom on
+ * @param startTime
+ * the start time
+ * @param endTime
+ * the end time
+ * @param resolution
+ * the resolution
+ * @param force
+ * Whether to force the zoom of all entries or only those
+ * that have not the same sampling
+ */
+ public ZoomThread(@NonNull Collection<@NonNull TimeGraphEntry> entries, long startTime, long endTime, long resolution, boolean force) {
+ super(FlameGraphView.this.getName() + " zoom"); //$NON-NLS-1$
+ fZoomStartTime = startTime;
+ fZoomEndTime = endTime;
+ fResolution = resolution;
+ fCurrentEntries = entries;
+ fMonitor = new NullProgressMonitor();
+ fForce = force;
+ }
+
+ /**
+ * @return the zoom start time
+ */
+ public long getZoomStartTime() {
+ return fZoomStartTime;
+ }
+
+ /**
+ * @return the zoom end time
+ */
+ public long getZoomEndTime() {
+ return fZoomEndTime;
+ }
+
+ /**
+ * @return the resolution
+ */
+ public long getResolution() {
+ return fResolution;
+ }
+
+ /**
+ * @return the monitor
+ */
+ public @NonNull IProgressMonitor getMonitor() {
+ return fMonitor;
+ }
+
+ /**
+ * Cancel the zoom thread
+ */
+ public void cancel() {
+ fMonitor.setCanceled(true);
+ }
+
+ @Override
+ public final void run() {
+ try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:ZoomThread", "start", fZoomStartTime, "end", fZoomEndTime).setCategoryAndId(getViewId(), fScopeId).build()) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (fCurrentEntries.isEmpty()) {
+ // No rows to zoom on
+ return;
+ }
+ Sampling sampling = new Sampling(getZoomStartTime(), getZoomEndTime(), getResolution());
+ Iterable<@NonNull TimeGraphEntry> incorrectSample = fForce ? fCurrentEntries : Iterables.filter(fCurrentEntries, entry -> !sampling.equals(entry.getSampling()));
+ zoomEntries(incorrectSample, getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
+ } finally {
+
+ if (fDirty.decrementAndGet() < 0) {
+ Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Set the ID of the calling flow scope. This data will allow to determine the
+ * causality between the zoom thread and its caller if tracing is enabled.
+ *
+ * @param scopeId
+ * The ID of the calling flow scope
+ * @since 3.0
+ */
+ public void setScopeId(int scopeId) {
+ fScopeId = scopeId;
+ }
+ }
+
+ /**
+ * Start or restart the zoom thread.
+ *
+ * @param startTime
+ * the zoom start time
+ * @param endTime
+ * the zoom end time
+ * @param force
+ * Whether to force the fetch of all rows, or only those that
+ * don't have the same range
+ */
+ protected final void startZoomThread(long startTime, long endTime, boolean force) {
+ ITmfTrace trace = getTrace();
+ if (trace == null) {
+ return;
+ }
+
+ fDirty.incrementAndGet();
+ try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:ZoomThreadCreated").setCategory(getViewId()).build()) { //$NON-NLS-1$
+ long clampedStartTime = Math.max(0, Math.min(startTime, getEndTime()));
+ long clampedEndTime = Math.min(getEndTime(), Math.max(endTime, 0));
+ // Ignore if end time < start time, data has not been set correctly [yet]
+ if (clampedEndTime < clampedStartTime) {
+ return;
+ }
+ ZoomThread zoomThread = fZoomThread;
+ if (zoomThread != null) {
+ zoomThread.cancel();
+ }
+ int timeSpace = getTimeGraphViewer().getTimeSpace();
+ if (timeSpace > 0) {
+ long resolution = Long.max(1, (clampedEndTime - clampedStartTime) / timeSpace);
+ zoomThread = new ZoomThread(getVisibleItems(DEFAULT_BUFFER_SIZE), clampedStartTime, clampedEndTime, resolution, force);
+ } else {
+ zoomThread = null;
+ }
+ fZoomThread = zoomThread;
+ if (zoomThread != null) {
+ zoomThread.setScopeId(log.getId());
+ /*
+ * Don't start a new thread right away if results are being applied from an old
+ * ZoomThread. Otherwise, the old results might overwrite the new results if it
+ * finishes after.
+ */
+ synchronized (fZoomThreadResultLock) {
+ zoomThread.start();
+ // zoomThread decrements, so we increment here
+ fDirty.incrementAndGet();
+ }
+ }
+ } finally {
+ if (fDirty.decrementAndGet() < 0) {
+ Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private @NonNull Set<@NonNull TimeGraphEntry> getVisibleItems(int buffer) {
+
+ TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
+ if (timeGraphControl.isDisposed()) {
+ return Collections.emptySet();
+ }
+
+ int start = Integer.max(0, fTimeGraphViewer.getTopIndex() - buffer);
+ int end = Integer.min(fTimeGraphViewer.getExpandedElementCount() - 1,
+ fTimeGraphViewer.getTopIndex() + timeGraphControl.countPerPage() + buffer);
+
+ Set<@NonNull TimeGraphEntry> visible = new HashSet<>(end - start + 1);
+ for (int i = start; i <= end; i++) {
+ /*
+ * Use the getExpandedElement by index to avoid creating a copy of
+ * all the the elements.
+ */
+ TimeGraphEntry element = (TimeGraphEntry) timeGraphControl.getExpandedElement(i);
+ if (element != null) {
+ visible.add(element);
+ }
+ }
+ return visible;
+
+ }
+
+ private void zoomEntries(@NonNull Iterable<@NonNull TimeGraphEntry> entries, long zoomStartTime, long zoomEndTime, long resolution, @NonNull IProgressMonitor monitor) {
+ if (resolution < 0) {
+ // StateSystemUtils.getTimes would throw an illegal argument exception.
+ return;
+ }
+
+ long start = Long.min(zoomStartTime, zoomEndTime);
+ long end = Long.max(zoomStartTime, zoomEndTime);
+ List<@NonNull Long> times = StateSystemUtils.getTimes(start, end, resolution);
+ Sampling sampling = new Sampling(start, end, resolution);
+ Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = filterGroupEntries(entries, zoomStartTime, zoomEndTime);
+ SubMonitor subMonitor = SubMonitor.convert(monitor, getClass().getSimpleName() + "#zoomEntries", providersToModelIds.size()); //$NON-NLS-1$
+
+ for (Entry<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Collection<Long>> entry : providersToModelIds.asMap().entrySet()) {
+ ITimeGraphDataProvider<? extends TimeGraphEntryModel> dataProvider = entry.getKey();
+ SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(times, entry.getValue());
+ Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+ Multimap<@NonNull Integer, @NonNull String> regexesMap = getRegexes();
+ if (!regexesMap.isEmpty()) {
+ parameters.put(DataProviderParameterUtils.REGEX_MAP_FILTERS_KEY, regexesMap.asMap());
+ }
+ TmfModelResponse<TimeGraphModel> response = dataProvider.fetchRowModel(parameters, monitor);
+
+ TimeGraphModel model = response.getModel();
+ if (model != null) {
+ zoomEntries(fEntries.get(dataProvider), model.getRows(), response.getStatus() == ITmfResponse.Status.COMPLETED, sampling);
+ }
+ subMonitor.worked(1);
+ }
+ redraw();
+ }
+
+ /**
+ * This method build the multimap of regexes by property that will be used to
+ * filter the timegraph states
+ *
+ * Override this method to add other regexes with their properties. The data
+ * provider should handle everything after.
+ *
+ * @return The multimap of regexes by property
+ */
+ private @NonNull Multimap<@NonNull Integer, @NonNull String> getRegexes() {
+ Multimap<@NonNull Integer, @NonNull String> regexes = HashMultimap.create();
+
+ ITmfTrace trace = getTrace();
+ if (trace == null) {
+ return regexes;
+ }
+ TraceCompassFilter globalFilter = TraceCompassFilter.getFilterForTrace(trace);
+ if (globalFilter == null) {
+ return regexes;
+ }
+ regexes.putAll(IFilterProperty.DIMMED, globalFilter.getRegexes());
+
+ return regexes;
+ }
+
+ private void zoomEntries(Map<Long, TimeGraphEntry> map, List<ITimeGraphRowModel> model, boolean completed, Sampling sampling) {
+ boolean isZoomThread = false; // Thread.currentThread() instanceof ZoomThread;
+ for (ITimeGraphRowModel rowModel : model) {
+ TimeGraphEntry entry = map.get(rowModel.getEntryID());
+
+ if (entry != null) {
+ List<ITimeEvent> events = createTimeEvents(entry, rowModel.getStates());
+ if (isZoomThread) {
+ synchronized (fZoomThreadResultLock) {
+ Display.getDefault().asyncExec(() -> {
+ entry.setZoomedEventList(events);
+ if (completed) {
+ entry.setSampling(sampling);
+ }
+ });
+ }
+ } else {
+ entry.setEventList(events);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create {@link ITimeEvent}s for an entry from the list of
+ * {@link ITimeGraphState}s, filling in the gaps.
+ *
+ * @param entry
+ * the {@link TimeGraphEntry} on which we are working
+ * @param values
+ * the list of {@link ITimeGraphState}s from the
+ * {@link ITimeGraphDataProvider}.
+ * @return a contiguous List of {@link ITimeEvent}s
+ */
+ private List<ITimeEvent> createTimeEvents(TimeGraphEntry entry, List<ITimeGraphState> values) {
+ List<ITimeEvent> events = new ArrayList<>(values.size());
+ ITimeEvent prev = null;
+ for (ITimeGraphState state : values) {
+ ITimeEvent event = createTimeEvent(entry, state);
+ if (prev != null) {
+ long prevEnd = prev.getTime() + prev.getDuration();
+ if (prevEnd < event.getTime()) {
+ // fill in the gap.
+ TimeEvent timeEvent = new TimeEvent(entry, prevEnd, event.getTime() - prevEnd);
+ events.add(timeEvent);
+ }
+ }
+ prev = event;
+ events.add(event);
+ }
+ return events;
+ }
+
+ /**
+ * Create a {@link TimeEvent} for a {@link TimeGraphEntry} and a
+ * {@link TimeGraphState}
+ *
+ * @param entry
+ * {@link TimeGraphEntry} for which we create a state
+ * @param state
+ * {@link ITimeGraphState} from the data provider
+ * @return a new {@link TimeEvent} for these arguments
+ *
+ * @since 3.3
+ */
+ protected TimeEvent createTimeEvent(TimeGraphEntry entry, ITimeGraphState state) {
+ String label = state.getLabel();
+ if (state.getValue() == Integer.MIN_VALUE && label == null) {
+ return new NullTimeEvent(entry, state.getStartTime(), state.getDuration());
+ }
+ if (label != null) {
+ return new NamedTimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), label, state.getActiveProperties());
+ }
+ return new TimeEvent(entry, state.getStartTime(), state.getDuration(), state.getValue(), state.getActiveProperties());
+ }
+
+ /**
+ * Filter the entries to return only the Non Null {@link TimeGraphEntry} which
+ * intersect the time range.
+ *
+ * @param visible
+ * the input list of visible entries
+ * @param zoomStartTime
+ * the leftmost time bound of the view
+ * @param zoomEndTime
+ * the rightmost time bound of the view
+ * @return A Multimap of data providers to their visible entries' model IDs.
+ */
+ private static Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> filterGroupEntries(Iterable<TimeGraphEntry> visible,
+ long zoomStartTime, long zoomEndTime) {
+ Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = HashMultimap.create();
+ for (TimeGraphEntry entry : visible) {
+ if (zoomStartTime <= entry.getEndTime() && zoomEndTime >= entry.getStartTime() && entry.hasTimeEvents()) {
+ ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = getProvider(entry);
+ if (provider != null) {
+ providersToModelIds.put(provider, entry.getEntryModel().getId());
+ }
+ }
+ }
+ return providersToModelIds;
+ }
+
+ /**
+ * Get the {@link ITimeGraphDataProvider} from a {@link TimeGraphEntry}'s
+ * parent.
+ *
+ * @param entry
+ * queried {@link TimeGraphEntry}.
+ * @return the {@link ITimeGraphDataProvider}
+ * @since 3.3
+ */
+ public static ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider(ITimeGraphEntry entry) {
+ ITimeGraphEntry parent = entry;
+ while (parent != null) {
+ if (parent instanceof ParentEntry) {
+ return ((ParentEntry) parent).getProvider();
+ }
+ parent = parent.getParent();
+ }
+ throw new IllegalStateException(entry + " should have a TraceEntry parent"); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the trace associated with this view
+ *
+ * @return The trace
+ */
+ protected ITmfTrace getTrace() {
+ return fTrace;
+ }
+
+ private void refresh() {
+ try (FlowScopeLog parentLogger = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:RefreshRequested").setCategory(getViewId()).build()) { //$NON-NLS-1$
+ final boolean isZoomThread = Thread.currentThread() instanceof ZoomThread;
+ TmfUiRefreshHandler.getInstance().queueUpdate(this, () -> {
+ try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Refresh").setParentScope(parentLogger).build()) { //$NON-NLS-1$
+ fDirty.incrementAndGet();
+ if (fTimeGraphViewer.getControl().isDisposed()) {
+ return;
+ }
+ List<TimeGraphEntry> entries;
+ synchronized (fEntryListMap) {
+ entries = fEntryListMap.get(getTrace());
+ Comparator<ITimeGraphEntry> entryComparator = getEntryComparator();
+ if (entries == null) {
+ entries = new CopyOnWriteArrayList<>();
+ } else if (entryComparator != null) {
+ List<TimeGraphEntry> list = new ArrayList<>(entries);
+ Collections.sort(list, entryComparator);
+ for (ITimeGraphEntry entry : list) {
+ sortChildren(entry, entryComparator);
+ }
+ entries.clear();
+ entries.addAll(list);
+ }
+ }
+
+ boolean inputChanged = entries != fTimeGraphViewer.getInput();
+ if (inputChanged) {
+ fTimeGraphViewer.setInput(entries);
+ } else {
+ fTimeGraphViewer.refresh();
+ }
+
+ long startBound = 0;
+ long endBound = getEndTime();
+ endBound = (endBound == Long.MIN_VALUE ? SWT.DEFAULT : endBound);
+ fTimeGraphViewer.setTimeBounds(startBound, endBound);
+
+ if (inputChanged && !isZoomThread) {
+ fTimeGraphViewer.resetStartFinishTime();
+ }
+
+ } finally {
+ if (fDirty.decrementAndGet() < 0) {
+ Activator.getDefault().logError("Dirty underflow", new Throwable()); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+ }
+
+ private Comparator<ITimeGraphEntry> getEntryComparator() {
+ switch (fSortOption) {
+ case BY_ID:
+ return ThreadIdComparator.getInstance();
+ case BY_ID_REV:
+ return ThreadIdComparator.getInstance().reversed();
+ case BY_NAME:
+ return ThreadNameComparator.getInstance();
+ case BY_NAME_REV:
+ return ThreadNameComparator.getInstance().reversed();
+ default:
+ break;
+ }
+ return null;
+ }
+
+ private void redraw() {
+
+ try (FlowScopeLog flowParent = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:RedrawRequested").setCategory(getViewId()).build()) { //$NON-NLS-1$
+ Display.getDefault().asyncExec(() -> {
+ try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Redraw").setParentScope(flowParent).build()) { //$NON-NLS-1$
+ if (fTimeGraphViewer.getControl().isDisposed()) {
+ return;
+ }
+ fTimeGraphViewer.getControl().redraw();
+ fTimeGraphViewer.getControl().update();
+ }
+ });
+ }
+ }
+
+ private void sortChildren(ITimeGraphEntry entry, Comparator<ITimeGraphEntry> comparator) {
+ if (entry instanceof TimeGraphEntry) {
+ ((TimeGraphEntry) entry).sortChildren(comparator);
+ }
+ for (ITimeGraphEntry child : entry.getChildren()) {
+ sortChildren(child, comparator);
+ }
+ }
+
+ private int getDisplayWidth() {
+ int displayWidth = fDisplayWidth;
+ return displayWidth <= 0 ? 1 : displayWidth;
+ }
+
+ /**
+ * A class for parent entries that contain a link to the data provider
+ *
+ * @author Geneviève Bastien
+ */
+ private static class ParentEntry extends TimeGraphEntry {
+ private final @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> fProvider;
+
+ /**
+ * Constructor
+ *
+ * @param model
+ * trace level model
+ * @param provider
+ * reference to the provider for this trace and view
+ */
+ public ParentEntry(@NonNull TimeGraphEntryModel model,
+ @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider) {
+ super(model);
+ fProvider = provider;
+ }
+
+ /**
+ * Getter for the data provider for this {@link ParentEntry}
+ *
+ * @return this entry's {@link ITimeGraphDataProvider}
+ */
+ public @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider() {
+ return fProvider;
+ }
+ }
+
+ private synchronized void setEndTime(long endTime) {
+ fEndTime = endTime;
+ }
+
+ private long getEndTime() {
+ return fEndTime;
+ }
+
+ /**
+ * Adds a list of entries to a trace's entry list
+ *
+ * @param trace
+ * the trace
+ * @param list
+ * the list of time graph entries to add
+ */
+ private void addToEntryList(ITmfTrace trace, List<@NonNull TimeGraphEntry> list) {
+ synchronized (fEntryListMap) {
+ List<TimeGraphEntry> entryList = fEntryListMap.get(trace);
+ if (entryList == null) {
+ fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list));
+ } else {
+ for (TimeGraphEntry entry : list) {
+ if (!entryList.contains(entry)) {
+ entryList.add(entry);
+ }
+ }
+ }
+ }
+ }
+
+ private void resetEntries(ITmfTrace trace) {
+ synchronized (fEntries) {
+ synchronized (fEntryListMap) {
+ // Remove the entries from the entry list map and from the
+ // fEntries cache
+ List<@NonNull TimeGraphEntry> entries = fEntryListMap.remove(trace);
+ if (entries == null) {
+ return;
+ }
+ for (TimeGraphEntry entry : entries) {
+ if (entry instanceof ParentEntry) {
+ fEntries.remove(((ParentEntry) entry).getProvider());
+ }
+ }
+ refresh();
+ }
+ }
+ }
+
/**
* Get the necessary data for the flame graph and display it
*
- * @param callGraphProviders
- * the callGraphAnalysis
+ * @param viewTrace
+ * the trace
* @param selStart
* The selection start timestamp or <code>null</code> to show all
* data
@@ -268,7 +1060,7 @@
* data
*/
@VisibleForTesting
- public void buildFlameGraph(Iterable<ICallGraphProvider> callGraphProviders, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd) {
+ public void buildFlameGraph(@NonNull ITmfTrace viewTrace, @Nullable ITmfTimestamp selStart, @Nullable ITmfTimestamp selEnd) {
/*
* Note for synchronization:
*
@@ -280,10 +1072,6 @@
*
* 3- when the job starts running and can thus be canceled
*/
- Job job = fJob;
- if (job != null) {
- job.cancel();
- }
try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:Building").setCategory(getViewId()).build()) { //$NON-NLS-1$
try {
fLock.acquire();
@@ -291,97 +1079,40 @@
Activator.getDefault().logError(e.getMessage(), e);
fLock.release();
}
- /*
- * Load the symbol provider for the current trace, even if it does not provide a
- * call stack analysis module. See
- * https://bugs.eclipse.org/bugs/show_bug.cgi?id=494212
- */
- ITmfTrace trace = fTrace;
- if (trace != null) {
- /*
- * Load the symbol provider for the current trace, even if it does not provide a
- * call stack analysis module. See
- * https://bugs.eclipse.org/bugs/show_bug.cgi?id=494212
- */
- Collection<ISymbolProvider> symbolProviders = fSymbolProviders.get(trace);
- if (symbolProviders.isEmpty()) {
- symbolProviders = SymbolProviderManager.getInstance().getSymbolProviders(trace);
- symbolProviders.forEach(provider -> provider.loadConfiguration(new NullProgressMonitor()));
- fSymbolProviders.putAll(trace, symbolProviders);
- }
- }
- if (!callGraphProviders.iterator().hasNext()) {
- fTimeGraphViewer.setInput(null);
- fLock.release();
- return;
- }
- for (ICallGraphProvider provider : callGraphProviders) {
- if (provider instanceof IAnalysisModule) {
- ((IAnalysisModule) provider).schedule();
- }
- }
- job = new Job(Messages.FlameGraphView_RetrievingData) {
-
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- try (FlowScopeLog runLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:GettingFlameGraphs").setParentScope(log).build()) { //$NON-NLS-1$
- if (monitor.isCanceled()) {
- return Status.CANCEL_STATUS;
- }
- // Set the view as dirty before releasing the lock
- fDirty.incrementAndGet();
- fLock.release();
- Set<CallGraph> callgraphs = new HashSet<>();
- IWeightedTreeGroupDescriptor group = fGroupBy;
- for (ICallGraphProvider provider : callGraphProviders) {
- if (provider instanceof IAnalysisModule) {
- ((IAnalysisModule) provider).waitForCompletion(monitor);
- }
- // FIXME: This waits for completion, there is no way of cancelling this call, so
- // make the views responsive to updates in the model, so that we can return a
- // partial callgraph
- CallGraph callGraph;
- if (selStart == null || selEnd == null) {
- callGraph = provider.getCallGraph();
- } else {
- callGraph = provider.getCallGraph(selStart, selEnd);
- }
- if (group == null) {
- callgraphs.add(callGraph);
- } else {
- callgraphs.add(CallGraphGroupBy.groupCallGraphBy(group, callGraph));
- }
- }
- if (monitor.isCanceled()) {
- // Decrease dirtiness, job canceled
- fDirty.decrementAndGet();
- return Status.CANCEL_STATUS;
- }
- Display.getDefault().asyncExec(() -> {
- try (FlowScopeLog asyncLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "FlameGraphView:SettingInput").setParentScope(runLog).build()) { //$NON-NLS-1$
- fTimeGraphViewer.setInput(callgraphs);
- fTimeGraphViewer.resetStartFinishTime();
- } finally {
- // Finished updating, decrease dirtiness
- fDirty.decrementAndGet();
- }
- });
- return Status.OK_STATUS;
- }
- }
- };
+ // Run the build jobs through the site progress service if available
IWorkbenchSiteProgressService service = null;
IWorkbenchPartSite site = getSite();
if (site != null) {
service = site.getService(IWorkbenchSiteProgressService.class);
}
- fJob = job;
- if (service != null) {
- service.schedule(job);
- } else {
- job.schedule();
+
+ for (ITmfTrace trace : TmfTraceManager.getTraceSet(viewTrace)) {
+
+ // Cancel previous build job for this trace
+ Job buildJob = fBuildJobMap.remove(trace);
+ if (buildJob != null) {
+ buildJob.cancel();
+ }
+ resetEntries(viewTrace);
+ // Build job will decrement
+
+ buildJob = new Job(getTitle() + Messages.FlameGraphView_RetrievingData) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ new BuildRunnable(trace, viewTrace, selStart, selEnd, log).run(monitor);
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+ };
+ fBuildJobMap.put(trace, buildJob);
+ if (service != null) {
+ service.schedule(buildJob);
+ } else {
+ buildJob.schedule();
+ }
}
+ fLock.release();
}
}
@@ -448,8 +1179,10 @@
@Override
public void menuDetected(MenuDetectEvent event) {
Menu menu = timeEventMenu;
- if (event.data instanceof FlamegraphEvent) {
- timeGraphControl.setMenu(menu);
+ if (event.data instanceof TimeEvent) {
+ if (((TimeEvent) event.data).hasValue()) {
+ timeGraphControl.setMenu(menu);
+ }
return;
}
timeGraphControl.setMenu(null);
@@ -474,35 +1207,36 @@
* a menuManager to fill
*/
protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager) {
- ISelection selection = getSite().getSelectionProvider().getSelection();
- if (selection instanceof IStructuredSelection) {
- for (Object object : ((IStructuredSelection) selection).toList()) {
- if (object instanceof FlamegraphEvent) {
- final FlamegraphEvent flamegraphEvent = (FlamegraphEvent) object;
- final ICalledFunction maxSeg = flamegraphEvent.getMaxObject();
- if (maxSeg != null) {
- menuManager.add(new Action(Messages.FlameGraphView_GotoMaxDuration) {
- @Override
- public void run() {
- TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()), fTrace);
- broadcast(sig);
- }
- });
- }
-
- final ICalledFunction minSeg = flamegraphEvent.getMinObject();
- if (minSeg != null) {
- menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
- @Override
- public void run() {
- TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()), fTrace);
- broadcast(sig);
- }
- });
- }
- }
- }
- }
+ // TODO Bring this functionality back
+// ISelection selection = getSite().getSelectionProvider().getSelection();
+// if (selection instanceof IStructuredSelection) {
+// for (Object object : ((IStructuredSelection) selection).toList()) {
+// if (object instanceof FlamegraphEvent) {
+// final FlamegraphEvent flamegraphEvent = (FlamegraphEvent) object;
+// final ICalledFunction maxSeg = flamegraphEvent.getMaxObject();
+// if (maxSeg != null) {
+// menuManager.add(new Action(Messages.FlameGraphView_GotoMaxDuration) {
+// @Override
+// public void run() {
+// TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()), fTrace);
+// broadcast(sig);
+// }
+// });
+// }
+//
+// final ICalledFunction minSeg = flamegraphEvent.getMinObject();
+// if (minSeg != null) {
+// menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
+// @Override
+// public void run() {
+// TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()), fTrace);
+// broadcast(sig);
+// }
+// });
+// }
+// }
+// }
+// }
}
private void contributeToActionBars() {
@@ -523,7 +1257,7 @@
fAggregateByAction = new Action(Messages.FlameGraphView_GroupByName, IAction.AS_DROP_DOWN_MENU) {
@Override
public void run() {
- SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+ SortOption sortOption = fSortOption;
if (sortOption == SortOption.BY_NAME) {
setSortOption(SortOption.BY_NAME_REV);
} else {
@@ -533,8 +1267,9 @@
};
fAggregateByAction.setToolTipText(Messages.FlameGraphView_GroupByTooltip);
fAggregateByAction.setImageDescriptor(AGGREGATE_BY_ICON);
- fAggregateByAction.setMenuCreator(new IMenuCreator () {
+ fAggregateByAction.setMenuCreator(new IMenuCreator() {
Menu menu = null;
+
@Override
public void dispose() {
if (menu != null) {
@@ -555,14 +1290,14 @@
return menu;
}
ICallGraphProvider provider = iterator.next();
- // Add the all group element
- Action allGroupAction = createActionForGroup(provider, AllGroupDescriptor.getInstance());
+ // Add the all group element
+ Action allGroupAction = createActionForGroup(AllGroupDescriptor.getInstance());
new ActionContributionItem(allGroupAction).fill(menu, -1);
- Collection<IWeightedTreeGroupDescriptor> series = provider.getGroupDescriptors();
+ Collection<@NonNull IWeightedTreeGroupDescriptor> series = provider.getGroupDescriptors();
series.forEach(group -> {
IWeightedTreeGroupDescriptor subGroup = group;
do {
- Action groupAction = createActionForGroup(provider, subGroup);
+ Action groupAction = createActionForGroup(subGroup);
new ActionContributionItem(groupAction).fill(menu, -1);
subGroup = subGroup.getNextGroup();
} while (subGroup != null);
@@ -579,22 +1314,30 @@
return fAggregateByAction;
}
- private Action createActionForGroup(ICallGraphProvider provider, IWeightedTreeGroupDescriptor descriptor) {
+ private Action createActionForGroup(IWeightedTreeGroupDescriptor descriptor) {
return new Action(descriptor.getName(), IAction.AS_RADIO_BUTTON) {
@Override
public void run() {
+ ITmfTrace trace = getTrace();
+ if (trace == null) {
+ return;
+ }
fGroupBy = descriptor;
- buildFlameGraph(Collections.singleton(provider), null, null);
+ buildFlameGraph(trace, null, null);
}
};
}
+ // --------------------------------
+ // Sorting related methods
+ // --------------------------------
+
private Action getSortByNameAction() {
if (fSortByNameAction == null) {
fSortByNameAction = new Action(Messages.FlameGraph_SortByThreadName, IAction.AS_CHECK_BOX) {
@Override
public void run() {
- SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+ SortOption sortOption = fSortOption;
if (sortOption == SortOption.BY_NAME) {
setSortOption(SortOption.BY_NAME_REV);
} else {
@@ -613,7 +1356,7 @@
fSortByIdAction = new Action(Messages.FlameGraph_SortByThreadId, IAction.AS_CHECK_BOX) {
@Override
public void run() {
- SortOption sortOption = fTimeGraphContentProvider.getSortOption();
+ SortOption sortOption = fSortOption;
if (sortOption == SortOption.BY_ID) {
setSortOption(SortOption.BY_ID_REV);
} else {
@@ -627,6 +1370,59 @@
return fSortByIdAction;
}
+ private void setSortOption(SortOption sortOption) {
+ // reset defaults
+ getSortByNameAction().setChecked(false);
+ getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
+ getSortByIdAction().setChecked(false);
+ getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
+
+ if (sortOption.equals(SortOption.BY_NAME)) {
+ fSortOption = SortOption.BY_NAME;
+ getSortByNameAction().setChecked(true);
+ } else if (sortOption.equals(SortOption.BY_NAME_REV)) {
+ fSortOption = SortOption.BY_NAME_REV;
+ getSortByNameAction().setChecked(true);
+ getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
+ } else if (sortOption.equals(SortOption.BY_ID)) {
+ fSortOption = SortOption.BY_ID;
+ getSortByIdAction().setChecked(true);
+ } else if (sortOption.equals(SortOption.BY_ID_REV)) {
+ fSortOption = SortOption.BY_ID_REV;
+ getSortByIdAction().setChecked(true);
+ getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
+ }
+ saveSortOption();
+ refresh();
+ }
+
+ private void saveSortOption() {
+ SortOption sortOption = fSortOption;
+ IDialogSettings settings = Activator.getDefault().getDialogSettings();
+ IDialogSettings section = settings.getSection(getClass().getName());
+ if (section == null) {
+ section = settings.addNewSection(getClass().getName());
+ }
+ section.put(SORT_OPTION_KEY, sortOption.name());
+ }
+
+ private void loadSortOption() {
+ IDialogSettings settings = Activator.getDefault().getDialogSettings();
+ IDialogSettings section = settings.getSection(getClass().getName());
+ if (section == null) {
+ return;
+ }
+ String sortOption = section.get(SORT_OPTION_KEY);
+ if (sortOption == null) {
+ return;
+ }
+ setSortOption(SortOption.fromName(sortOption));
+ }
+
+ //--------------------------------
+ // Symbol related methods
+ //--------------------------------
+
private Action getConfigureSymbolsAction() {
if (fConfigureSymbolsAction != null) {
return fConfigureSymbolsAction;
@@ -637,8 +1433,7 @@
public void run() {
SymbolProviderConfigDialog dialog = new SymbolProviderConfigDialog(getSite().getShell(), getProviderPages());
if (dialog.open() == IDialogConstants.OK_ID) {
-// fPresentationProvider.resetFunctionNames();
-// refresh();
+ startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
}
}
};
@@ -679,55 +1474,6 @@
return pages.toArray(new ISymbolProviderPreferencePage[pages.size()]);
}
- private void setSortOption(SortOption sortOption) {
- // reset defaults
- getSortByNameAction().setChecked(false);
- getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
- getSortByIdAction().setChecked(false);
- getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
-
- if (sortOption.equals(SortOption.BY_NAME)) {
- fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME);
- getSortByNameAction().setChecked(true);
- } else if (sortOption.equals(SortOption.BY_NAME_REV)) {
- fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME_REV);
- getSortByNameAction().setChecked(true);
- getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
- } else if (sortOption.equals(SortOption.BY_ID)) {
- fTimeGraphContentProvider.setSortOption(SortOption.BY_ID);
- getSortByIdAction().setChecked(true);
- } else if (sortOption.equals(SortOption.BY_ID_REV)) {
- fTimeGraphContentProvider.setSortOption(SortOption.BY_ID_REV);
- getSortByIdAction().setChecked(true);
- getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
- }
- saveSortOption();
- fTimeGraphViewer.refresh();
- }
-
- private void saveSortOption() {
- SortOption sortOption = fTimeGraphContentProvider.getSortOption();
- IDialogSettings settings = Activator.getDefault().getDialogSettings();
- IDialogSettings section = settings.getSection(getClass().getName());
- if (section == null) {
- section = settings.addNewSection(getClass().getName());
- }
- section.put(SORT_OPTION_KEY, sortOption.name());
- }
-
- private void loadSortOption() {
- IDialogSettings settings = Activator.getDefault().getDialogSettings();
- IDialogSettings section = settings.getSection(getClass().getName());
- if (section == null) {
- return;
- }
- String sortOption = section.get(SORT_OPTION_KEY);
- if (sortOption == null) {
- return;
- }
- setSortOption(SortOption.fromName(sortOption));
- }
-
/**
* Symbol map provider updated
*
@@ -736,9 +1482,9 @@
*/
@TmfSignalHandler
public void symbolMapUpdated(TmfSymbolProviderUpdatedSignal signal) {
-// if (signal.getSource() != this) {
- fTimeGraphViewer.refresh();
-// }
+ if (signal.getSource() != this) {
+ startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
+ }
}
@Override
@@ -746,4 +1492,29 @@
return SaveImageUtil.createSaveAction(getName(), this::getTimeGraphViewer);
}
+ /**
+ * Cancel and restart the zoom thread.
+ */
+ public void restartZoomThread() {
+ ZoomThread zoomThread = fZoomThread;
+ if (zoomThread != null) {
+ // Make sure that the zoom thread is not a restart (resume of the previous)
+ zoomThread.cancel();
+ fZoomThread = null;
+ }
+ startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1(), true);
+ }
+
+ /**
+ * Set or remove the global regex filter value
+ *
+ * @param signal
+ * the signal carrying the regex value
+ */
+ @TmfSignalHandler
+ public void regexFilterApplied(TmfFilterAppliedSignal signal) {
+ // Restart the zoom thread to apply the new filter
+ Display.getDefault().asyncExec(() -> restartZoomThread());
+ }
+
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java
deleted file mode 100644
index a6f7619..0000000
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/FlamegraphDepthEntry.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Ericsson
- *
- * 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.incubator.internal.callstack.ui.flamegraph;
-
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
-
-/**
- * An entry, or row, in the flame Graph view
- *
- * @author Sonia Farrah
- */
-public class FlamegraphDepthEntry extends TimeGraphEntry {
-
- private final int fDepth;
- private final String fId;
-
- /**
- * Constructor
- *
- * @param name
- * name of an entry
- * @param startTime
- * Start time of an entry
- * @param endTime
- * The end time of an entry
- * @param depth
- * The Depth of an entry
- * @param id
- * The id of an entry
- */
- public FlamegraphDepthEntry(String name, long startTime, long endTime, int depth, String id) {
- super(name, startTime, endTime);
- fDepth = depth;
- fId = id;
- }
-
- /**
- * The depth of a flame graph entry
- *
- * @return The depth of a flame graph entry
- */
- public int getDepth() {
- return fDepth;
- }
-
- /**
- * The id of an entry
- *
- * @return The id
- */
- public String getId() {
- return fId;
- }
-}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
index f71b270..8fc739e 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadIdComparator.java
@@ -10,6 +10,10 @@
import java.util.Comparator;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
/**
@@ -18,12 +22,33 @@
* @author Bernd Hufmann
*
*/
-class ThreadIdComparator implements Comparator<TimeGraphEntry> {
+class ThreadIdComparator implements Comparator<ITimeGraphEntry> {
+
+ private static final Comparator<ITimeGraphEntry> INSTANCE = new ThreadIdComparator();
+
+ private ThreadIdComparator() {
+ // Nothing to do
+ }
+
+ public static Comparator<ITimeGraphEntry> getInstance() {
+ return INSTANCE;
+ }
+
@Override
- public int compare(TimeGraphEntry o1, TimeGraphEntry o2) {
- if (o1 instanceof FlamegraphDepthEntry && o2 instanceof FlamegraphDepthEntry) {
- return ((FlamegraphDepthEntry) o1).getId().compareTo(((FlamegraphDepthEntry) o2).getId());
+ public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
+ if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
+ ITmfTreeDataModel entryModel1 = ((TimeGraphEntry) o1).getEntryModel();
+ ITmfTreeDataModel entryModel2 = ((TimeGraphEntry) o2).getEntryModel();
+ if (entryModel1 instanceof FlameChartEntryModel && entryModel2 instanceof FlameChartEntryModel) {
+ FlameChartEntryModel fcEntry1 = (FlameChartEntryModel) entryModel1;
+ FlameChartEntryModel fcEntry2 = (FlameChartEntryModel) entryModel2;
+ // If any of the entry is a function, compare their depth
+ if (fcEntry1.getEntryType().equals(EntryType.FUNCTION) && fcEntry2.getEntryType().equals(EntryType.FUNCTION)) {
+ return Integer.compare(fcEntry1.getDepth(), fcEntry2.getDepth());
+ }
+ }
}
- return o1.getName().compareTo(o2.getName());
+ // Fallback where all entries are equal
+ return 0;
}
}
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
index 33ab1ed..2dc62b4 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.ui/src/org/eclipse/tracecompass/incubator/internal/callstack/ui/flamegraph/ThreadNameComparator.java
@@ -10,17 +10,45 @@
import java.util.Comparator;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel;
+import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.provider.FlameChartEntryModel.EntryType;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
/**
* Comparator to compare by thread name.
*
* @author Bernd Hufmann
- *
*/
-class ThreadNameComparator implements Comparator<TimeGraphEntry> {
+class ThreadNameComparator implements Comparator<ITimeGraphEntry> {
+
+ private static final Comparator<ITimeGraphEntry> INSTANCE = new ThreadNameComparator();
+
+ private ThreadNameComparator() {
+ // Nothing to do
+ }
+
+ public static Comparator<ITimeGraphEntry> getInstance() {
+ return INSTANCE;
+ }
+
@Override
- public int compare(TimeGraphEntry o1, TimeGraphEntry o2) {
+ public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
+ if (o1 instanceof TimeGraphEntry && o2 instanceof TimeGraphEntry) {
+ ITmfTreeDataModel entryModel1 = ((TimeGraphEntry) o1).getEntryModel();
+ ITmfTreeDataModel entryModel2 = ((TimeGraphEntry) o2).getEntryModel();
+ if (entryModel1 instanceof FlameChartEntryModel && entryModel2 instanceof FlameChartEntryModel) {
+ FlameChartEntryModel fcEntry1 = (FlameChartEntryModel) entryModel1;
+ FlameChartEntryModel fcEntry2 = (FlameChartEntryModel) entryModel2;
+ // If any of the entry is a function of kernel, don't compare
+ if (fcEntry1.getEntryType().equals(EntryType.FUNCTION) || fcEntry2.getEntryType().equals(EntryType.FUNCTION) ||
+ fcEntry1.getEntryType().equals(EntryType.KERNEL) || fcEntry2.getEntryType().equals(EntryType.KERNEL)) {
+ return 0;
+ }
+ }
+ }
+ // Fallback to entry name comparator
return o1.getName().compareTo(o2.getName());
}
}