From cc1b8519d6b4ccaa37ff6ac776a43db9ce8fbbfc Mon Sep 17 00:00:00 2001 From: Alexandre Montplaisir Date: Wed, 13 Apr 2016 02:17:08 -0400 Subject: [PATCH] tmf: Introduce base class and extension point for on-demand analyses The on-demand analyses are analyses that are run manually when the user requests it. As such, they are much more simple and much less featureful than regular analyses. The upcoming integration with the LTTng-Analyses scripts will make use of this. Change-Id: I012bcc3fe3ddbd586a8d23887a481d4bf927aadc Signed-off-by: Alexandre Montplaisir Reviewed-on: https://git.eclipse.org/r/70921 Reviewed-by: Hudson CI Reviewed-by: Patrick Tasse Tested-by: Patrick Tasse Reviewed-by: Genevieve Bastien --- .../plugin.xml | 7 + .../ondemand/OnDemandAnalysisTest.java | 122 ++++++++++++++++ .../ondemand/OnDemandAnalysisStub.java | 53 +++++++ .../META-INF/MANIFEST.MF | 2 + .../plugin.properties | 1 + .../plugin.xml | 1 + ...acecompass.tmf.core.analysis.ondemand.exsd | 137 ++++++++++++++++++ .../analysis/ondemand/IOnDemandAnalysis.java | 88 +++++++++++ .../ondemand/OnDemandAnalysisManager.java | 123 ++++++++++++++++ .../core/analysis/ondemand/package-info.java | 11 ++ 10 files changed, 545 insertions(+) create mode 100644 tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/analysis/ondemand/OnDemandAnalysisTest.java create mode 100644 tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/ondemand/OnDemandAnalysisStub.java create mode 100644 tmf/org.eclipse.tracecompass.tmf.core/schema/org.eclipse.tracecompass.tmf.core.analysis.ondemand.exsd create mode 100644 tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/IOnDemandAnalysis.java create mode 100644 tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/OnDemandAnalysisManager.java create mode 100644 tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/package-info.java diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml b/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml index 7953e4acc8..771c08f1be 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml @@ -142,5 +142,12 @@ trace_type="org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub"> + + + + diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/analysis/ondemand/OnDemandAnalysisTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/analysis/ondemand/OnDemandAnalysisTest.java new file mode 100644 index 0000000000..535b21c22e --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/analysis/ondemand/OnDemandAnalysisTest.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir + * + * 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.tmf.core.tests.analysis.ondemand; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Set; + +import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysis; +import org.eclipse.tracecompass.tmf.core.analysis.ondemand.OnDemandAnalysisManager; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.tests.stubs.analysis.ondemand.OnDemandAnalysisStub; +import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub2; +import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub3; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Iterables; + +/** + * Basic tests for {@link IOnDemandAnalysis}, {@link OnDemandAnalysisManager} + * and the related extension point. + * + * @author Alexandre Montplaisir + */ +public class OnDemandAnalysisTest { + + private IOnDemandAnalysis analysis = new OnDemandAnalysisStub(); + + private ITmfTrace fTraceThatApplies; + private ITmfTrace fTraceThatDoesntApply; + + /** + * Test setup + */ + @Before + public void setup() { + fTraceThatApplies = new TmfTraceStub2(); + fTraceThatDoesntApply = new TmfTraceStub3(); + } + + /** + * Test cleanup + */ + @After + public void teardown() { + if (fTraceThatApplies != null) { + fTraceThatApplies.dispose(); + fTraceThatApplies = null; + } + if (fTraceThatDoesntApply != null) { + fTraceThatDoesntApply.dispose(); + fTraceThatDoesntApply = null; + } + } + + /** + * Test our stub analysis with a trace type on which it applies. + */ + @Test + public void testApplies() { + ITmfTrace trace = fTraceThatApplies; + assertNotNull(trace); + + assertTrue(analysis.appliesTo(trace)); + } + + /** + * Test our stub analysis with a trace type on which it does not apply. + */ + @Test + public void testDoesNotApply() { + ITmfTrace trace = fTraceThatDoesntApply; + assertNotNull(trace); + + assertFalse(analysis.appliesTo(trace)); + } + + /** + * Test getting an analysis via the manager. The expected way of doing + * things. + */ + @Test + public void testGetAnalysisFromTrace() { + ITmfTrace trace = fTraceThatApplies; + assertNotNull(trace); + + Set set1 = OnDemandAnalysisManager.getInstance().getOndemandAnalyses(trace); + + assertEquals(1, set1.size()); + assertTrue(Iterables.getOnlyElement(set1) instanceof OnDemandAnalysisStub); + + /* Make sure it is still true on a subsequent call */ + Set set2 = OnDemandAnalysisManager.getInstance().getOndemandAnalyses(trace); + + assertTrue(set1.equals(set2)); + } + + /** + * Test querying the manager for analyses, but for a trace that does not + * support any. + */ + @Test + public void testGetNoAnalysisFromTrace() { + ITmfTrace trace = fTraceThatDoesntApply; + assertNotNull(trace); + + Set set = OnDemandAnalysisManager.getInstance().getOndemandAnalyses(trace); + assertTrue(set.isEmpty()); + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/ondemand/OnDemandAnalysisStub.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/ondemand/OnDemandAnalysisStub.java new file mode 100644 index 0000000000..52f20ecbfb --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/ondemand/OnDemandAnalysisStub.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir + * + * 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.tmf.tests.stubs.analysis.ondemand; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysis; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub2; + +/** + * Stub for on-demand analysis tests. + * + * It applies to trace type {@link TmfTraceStub2} only. + * + * @author Alexandre Montplaisir + */ +@NonNullByDefault +public class OnDemandAnalysisStub implements IOnDemandAnalysis { + + @Override + public String getName() { + return "Test On-Demand Analysis"; + } + + @Override + public boolean appliesTo(ITmfTrace trace) { + if (trace instanceof TmfTraceStub2) { + return true; + } + return false; + } + + @Override + public boolean canExecute(ITmfTrace trace) { + return appliesTo(trace); + } + + @Override + public Object execute(ITmfTrace trace, @Nullable TmfTimeRange range, String extraParams, @Nullable IProgressMonitor monitor) { + /* Do nothing */ + return new Object(); + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF b/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF index 6a6ac60669..95adb56cf5 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF +++ b/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF @@ -31,6 +31,7 @@ Export-Package: org.eclipse.tracecompass.internal.tmf.core;x-friends:="org.eclip org.eclipse.tracecompass.internal.tmf.core.trace.indexer;x-friends:="org.eclipse.tracecompass.tmf.core.tests", org.eclipse.tracecompass.tmf.core, org.eclipse.tracecompass.tmf.core.analysis, + org.eclipse.tracecompass.tmf.core.analysis.ondemand, org.eclipse.tracecompass.tmf.core.analysis.requirements, org.eclipse.tracecompass.tmf.core.callstack, org.eclipse.tracecompass.tmf.core.component, @@ -63,6 +64,7 @@ Export-Package: org.eclipse.tracecompass.internal.tmf.core;x-friends:="org.eclip org.eclipse.tracecompass.tmf.core.uml2sd, org.eclipse.tracecompass.tmf.core.util Import-Package: com.google.common.base, + com.google.common.cache, com.google.common.collect, com.google.common.hash;version="15.0.0", org.apache.commons.io diff --git a/tmf/org.eclipse.tracecompass.tmf.core/plugin.properties b/tmf/org.eclipse.tracecompass.tmf.core/plugin.properties index 8aa4cdf94d..a459c9db82 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/plugin.properties +++ b/tmf/org.eclipse.tracecompass.tmf.core/plugin.properties @@ -16,6 +16,7 @@ Bundle-Name = Trace Compass TMF Core Plug-in # Extension points extensionpoint.trace_type.name = Tmf Trace Type extensionpoint.analysis_module.name = Trace Analysis Module +extensionpoint.ondemand_analysis.name = On-demand Analysis # Experiment type experimenttype.type.generic = Generic Experiment diff --git a/tmf/org.eclipse.tracecompass.tmf.core/plugin.xml b/tmf/org.eclipse.tracecompass.tmf.core/plugin.xml index f6c8d16b42..b620162525 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/plugin.xml +++ b/tmf/org.eclipse.tracecompass.tmf.core/plugin.xml @@ -3,6 +3,7 @@ + diff --git a/tmf/org.eclipse.tracecompass.tmf.core/schema/org.eclipse.tracecompass.tmf.core.analysis.ondemand.exsd b/tmf/org.eclipse.tracecompass.tmf.core/schema/org.eclipse.tracecompass.tmf.core.analysis.ondemand.exsd new file mode 100644 index 0000000000..f5791ef5bc --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core/schema/org.eclipse.tracecompass.tmf.core.analysis.ondemand.exsd @@ -0,0 +1,137 @@ + + + + + + + + + This extension point is used to provide on-demand analyses. Unlike regular analyses, the on-demand ones are only executed when the user specifies it. + + + + + + + + + + + + + + + + + A fully qualified identifier of the target extension point + + + + + + + An optional identifier of the extension instance + + + + + + + An optional name of the extension instance + + + + + + + + + + + + + An on-demand analysis which can run on the traces it specifies + + + + + + + The unique ID that identifies this on-demand analysis + + + + + + + The class implementing the on-demand analysis + + + + + + + + + + + + + + + 2.0 + + + + + + + + + <p> +For an example implementation of an on-demand analysis see: +<pre> +plug-in: org.eclipse.linuxtools.tmf.core.tests +package: org.eclipse.linuxtools.tmf.core.tests.stubs.analysis.ondemand +class: OnDemandAnalysisStub +</pre> +</p> + +<p> +The following is an example of the extension point usage: +<pre> + <extension + point="org.eclipse.tracecompass.tmf.core.analysis.ondemand"> + <analysis + class="org.eclipse.tracecompass.tmf.tests.stubs.analysis.ondemand.OnDemandAnalysisStub" + id="org.eclipse.tracecompass.tmf.core.tests.analysis1"> + </analysis> + </extension> +</pre> +</p> + + + + + + + + + Since 2.0 + + + + + + + + + + Copyright (c) 2016 EfficiOS Inc. and others + +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + + + + diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/IOnDemandAnalysis.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/IOnDemandAnalysis.java new file mode 100644 index 0000000000..ca0daaf251 --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/IOnDemandAnalysis.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir + * + * 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.tmf.core.analysis.ondemand; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +/** + * An on-demand analysis is an analysis run on a trace and started from an + * explicit action from the user. + * + * The on-demand analysis object should be stateless, meaning every call of the + * {@link #execute} method should be independant and not affect each other. + * + * @author Alexandre Montplaisir + * @since 2.0 + */ +public interface IOnDemandAnalysis { + + /** + * Get the displayed name of this analysis. It should usually be + * externalized. + * + * @return The name of this analysis + */ + String getName(); + + /** + * Determine if the current analysis can run on the given trace. + * + * If it does not apply, then it should not be suggested for the given trace + * at all. + * + * This method should be a quick filter, and should not for instance call + * external processes. + * + * @param trace + * The trace to check for + * @return If this analysis applies to the trace + */ + boolean appliesTo(ITmfTrace trace); + + /** + * Second level of checking if an analysis can run on a trace. + * + * A analysis that {@link #appliesTo} a trace but whose {@link #canExecute} + * returns false should be suggested to the user, albeit unavailable. For + * example, striked-out in the UI. + * + * This will indicate to the user that in normal cases this analysis should + * work, but something (trace contents, environment, etc.) is preventing it. + * + * @param trace + * The trace to check for + * @return If this analysis can be run on this trace + */ + boolean canExecute(ITmfTrace trace); + + /** + * Execute the analysis on the given trace. + * + * It should have been ensured that the analysis can run first on this + * trace, by calling both {@link #appliesTo} and {@link #canExecute}. + * + * @param trace + * The trace on which to execute the analysis + * @param range + * The time range on which to execute the analysis + * @param extraParams + * Extra user-specified parameters to pass to the analysis + * @param monitor + * The progress monitor, can be null for a default monitor + * @return The results of this analysis. Exact object type is + * analysis-dependent, a more specific return type is encouraged. + */ + Object execute(ITmfTrace trace, @Nullable TmfTimeRange range, + String extraParams, @Nullable IProgressMonitor monitor); + +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/OnDemandAnalysisManager.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/OnDemandAnalysisManager.java new file mode 100644 index 0000000000..0bdf08a9dc --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/OnDemandAnalysisManager.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir + * + * 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.tmf.core.analysis.ondemand; + +import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.internal.tmf.core.Activator; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; + +/** + * Manager for {@link IOnDemandAnalysis}. + * + * Takes care of reading the extension point information, and providing analyses + * for traces via the {@link #getOndemandAnalyses(ITmfTrace)} method. + * + * @author Alexandre Montplaisir + * @since 2.0 + */ +public final class OnDemandAnalysisManager { + + /** The singleton instance */ + private static @Nullable OnDemandAnalysisManager INSTANCE; + + private static final String EXTENSION_POINT_ID = "org.eclipse.tracecompass.tmf.core.analysis.ondemand"; //$NON-NLS-1$ + private static final String ELEM_NAME_ANALYSIS = "analysis"; //$NON-NLS-1$ + + private static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + + private final LoadingCache> analysisCache = checkNotNull(CacheBuilder.newBuilder() + .weakKeys() + .softValues() + .build(new CacheLoader>() { + @Override + public Set load(ITmfTrace trace) { + return fAnalysisWrappers.stream() + .map(wrapper -> wrapper.analysis) + .filter(analysis -> analysis.appliesTo(trace)) + .collect(Collectors.collectingAndThen(Collectors.toSet(), ImmutableSet::copyOf)); + } + })); + + private final List fAnalysisWrappers; + + /** + * Internal class used to store extension point information + */ + private static class OndemandAnalysisWrapper { + + public final IOnDemandAnalysis analysis; + + public OndemandAnalysisWrapper(IOnDemandAnalysis analysis) { + this.analysis = analysis; + } + } + + /** + * Get the instance of this manager. + * + * @return The singleton instance of this class + */ + public static synchronized OnDemandAnalysisManager getInstance() { + OnDemandAnalysisManager inst = INSTANCE; + if (inst == null) { + inst = new OnDemandAnalysisManager(); + INSTANCE = inst; + } + return inst; + } + + /** + * Private constructor, should only be called via {@link #getInstance()}. + */ + private OnDemandAnalysisManager() { + fAnalysisWrappers = new ArrayList<>(); + IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_POINT_ID); + + for (IConfigurationElement element : configElements) { + if (ELEM_NAME_ANALYSIS.equals(element.getName())) { + try { + Object extension = element.createExecutableExtension(ATTR_CLASS); + if (extension != null) { + fAnalysisWrappers.add(new OndemandAnalysisWrapper((IOnDemandAnalysis) extension)); + } + } catch (CoreException | ClassCastException e) { + Activator.logError("Exception while loading extension point", e); //$NON-NLS-1$ + } + } + } + } + + /** + * Get all the registered on-demand analyses that apply to the given trace. + * + * @param trace + * The trace to get the analyses for + * @return The set of on-demand analyses that apply to this trace. It can be + * empty, but not null + */ + public Set getOndemandAnalyses(ITmfTrace trace) { + return checkNotNull(analysisCache.getUnchecked(trace)); + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/package-info.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/package-info.java new file mode 100644 index 0000000000..928fb1ecbf --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/analysis/ondemand/package-info.java @@ -0,0 +1,11 @@ +/******************************************************************************* + * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir + * + * 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 + *******************************************************************************/ + +@org.eclipse.jdt.annotation.NonNullByDefault +package org.eclipse.tracecompass.tmf.core.analysis.ondemand; \ No newline at end of file -- 2.34.1