| /******************************************************************************* |
| * Copyright (c) 2013, 2015 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.callstack.core.instrumented.statesystem; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackStateProvider; |
| import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener; |
| import org.eclipse.tracecompass.incubator.analysis.core.concepts.AggregatedCallSite; |
| import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IDataPalette; |
| import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor; |
| import org.eclipse.tracecompass.incubator.callstack.core.base.EdgeStateValue; |
| 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.callgraph.SymbolAspect; |
| import org.eclipse.tracecompass.incubator.callstack.core.instrumented.IFlameChartProvider; |
| import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.TraceHostIdResolver; |
| import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries.IThreadIdResolver; |
| import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.FunctionTidAspect; |
| import org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented.callgraph.CallGraphAnalysis; |
| import org.eclipse.tracecompass.segmentstore.core.ISegment; |
| import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
| import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; |
| import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException; |
| import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect; |
| import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; |
| import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| /** |
| * The base classes for analyses who want to populate the CallStack state |
| * system. |
| * |
| * If the elements in the callstack are grouped by process ID / thread ID, the |
| * default state provider {@link CallStackStateProvider} can be extended, and |
| * implement how to retrieve function entry and exit and process/thread IDs. |
| * |
| * @author Matthew Khouzam |
| * @author Geneviève Bastien |
| */ |
| public abstract class InstrumentedCallStackAnalysis extends TmfStateSystemAnalysisModule implements IFlameChartProvider, ICallGraphProvider { |
| |
| /** CallStack stack-attribute */ |
| public static final String CALL_STACK = "CallStack"; //$NON-NLS-1$ |
| |
| private static final String[] DEFAULT_PROCESSES_PATTERN = new String[] { CallStackStateProvider.PROCESSES, "*" }; //$NON-NLS-1$ |
| private static final String[] DEFAULT_THREADS_PATTERN = new String[] { "*" }; //$NON-NLS-1$ |
| |
| private static final List<String[]> PATTERNS = ImmutableList.of(DEFAULT_PROCESSES_PATTERN, DEFAULT_THREADS_PATTERN); |
| |
| private @Nullable CallStackSeries fCallStacks; |
| |
| private final CallGraphAnalysis fCallGraph; |
| |
| /** |
| * Listeners |
| */ |
| private final ListenerList fListeners = new ListenerList(ListenerList.IDENTITY); |
| /* |
| * Whether the callgraph execution will be triggered automatically after build |
| */ |
| private boolean fAutomaticCallgraph = true; |
| |
| /** |
| * Abstract constructor (should only be called via the sub-classes' |
| * constructors. |
| */ |
| protected InstrumentedCallStackAnalysis() { |
| super(); |
| fCallGraph = new CallGraphAnalysis(this); |
| } |
| |
| @Override |
| public boolean setTrace(@NonNull ITmfTrace trace) throws TmfAnalysisException { |
| if (!super.setTrace(trace)) { |
| return false; |
| } |
| return fCallGraph.setTrace(trace); |
| } |
| |
| @Override |
| public void setName(String name) { |
| super.setName(name); |
| fCallGraph.setName(name); |
| } |
| |
| @Override |
| public synchronized @Nullable CallStackSeries getCallStackSeries() { |
| CallStackSeries callstacks = fCallStacks; |
| if (callstacks == null) { |
| ITmfStateSystem ss = getStateSystem(); |
| ITmfTrace trace = getTrace(); |
| if (ss == null || trace == null) { |
| return null; |
| } |
| callstacks = new CallStackSeries(ss, getPatterns(), 0, "", getCallStackHostResolver(trace), getCallStackTidResolver()); //$NON-NLS-1$ |
| fCallStacks = callstacks; |
| } |
| return callstacks; |
| } |
| |
| /** |
| * Get the callstack host ID resolver for this instrumented series. The default |
| * is to use the host name of the trace. |
| * |
| * @param trace |
| * The trace this analysis is run on |
| * @return The host ID resolver |
| */ |
| protected TraceHostIdResolver getCallStackHostResolver(ITmfTrace trace) { |
| return new CallStackHostUtils.TraceHostIdResolver(trace); |
| } |
| |
| /** |
| * Get the callstack TID resolver for this instrumented series. The default is |
| * to use the name of the second attribute as the thread ID. |
| * |
| * @return The thread ID resolver |
| */ |
| protected @Nullable IThreadIdResolver getCallStackTidResolver() { |
| return new CallStackSeries.AttributeValueThreadResolver(1); |
| } |
| |
| @Override |
| protected boolean executeAnalysis(@Nullable IProgressMonitor monitor) { |
| fCallGraph.setId(getId()); |
| boolean ret = super.executeAnalysis(monitor); |
| if (!ret) { |
| return ret; |
| } |
| ISegmentStore<ISegment> segmentStore = getSegmentStore(); |
| if (segmentStore != null) { |
| sendUpdate(segmentStore); |
| } |
| if (fAutomaticCallgraph) { |
| fCallGraph.schedule(); |
| } |
| return true; |
| } |
| |
| /** |
| * Get the patterns for the process, threads and callstack levels in the state |
| * system |
| * |
| * @return The patterns for the different levels in the state system |
| */ |
| protected List<String[]> getPatterns() { |
| return PATTERNS; |
| } |
| |
| @Override |
| public @NonNull String getHostId() { |
| ITmfTrace trace = getTrace(); |
| if (trace == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| return trace.getHostId(); |
| } |
| |
| /** |
| * Get the edges (links) of the callstack |
| * |
| * @param start |
| * start time of the arrows to sample |
| * @param end |
| * end time of the arrows to sample |
| * @param monitor |
| * monitor to cancel the job |
| * |
| * @return a list of the edges, as {@link ITmfStateInterval}s with {@link EdgeStateValue}s. |
| */ |
| public List<ITmfStateInterval> getLinks(long start, long end, IProgressMonitor monitor) { |
| ITmfStateSystem ss = getStateSystem(); |
| if (ss == null) { |
| return Collections.emptyList(); |
| } |
| |
| Collection<Integer> quarks = getEdgeQuarks(); |
| if (quarks.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| // collect all the sampled edge intervals |
| List<ITmfStateInterval> list = new ArrayList<>(); |
| try { |
| for (ITmfStateInterval interval : ss.query2D(quarks, start, end)) { |
| Object value = interval.getValue(); |
| if (monitor.isCanceled()) { |
| return Collections.emptyList(); |
| } else if (value instanceof EdgeStateValue) { |
| list.add(interval); |
| } |
| } |
| } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) { |
| return Collections.emptyList(); |
| } |
| return list; |
| } |
| |
| @Override |
| public CallGraph getCallGraph(ITmfTimestamp start, ITmfTimestamp end) { |
| fCallGraph.schedule(); |
| fCallGraph.waitForCompletion(); |
| return fCallGraph.getCallGraph(start, end); |
| } |
| |
| @Override |
| public CallGraph getCallGraph() { |
| fCallGraph.schedule(); |
| fCallGraph.waitForCompletion(); |
| return fCallGraph.getCallGraph(); |
| } |
| |
| @Override |
| public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() { |
| fCallGraph.schedule(); |
| fCallGraph.waitForCompletion(); |
| return fCallGraph.getGroupDescriptors(); |
| } |
| |
| @Override |
| public String getTitle() { |
| return fCallGraph.getTitle(); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| fCallGraph.dispose(); |
| } |
| |
| @Override |
| public AggregatedCallSite createCallSite(Object symbol) { |
| return fCallGraph.createCallSite(symbol); |
| } |
| |
| /** |
| * Get the edges (links) of the callstack |
| * |
| * @return a list of the edges |
| */ |
| @Override |
| public @Nullable ISegmentStore<ISegment> getSegmentStore() { |
| CallStackSeries series = getCallStackSeries(); |
| if (series == null) { |
| return null; |
| } |
| return series; |
| } |
| |
| @Override |
| public void addListener(@NonNull IAnalysisProgressListener listener) { |
| fListeners.add(listener); |
| } |
| |
| @Override |
| public void removeListener(@NonNull IAnalysisProgressListener listener) { |
| fListeners.remove(listener); |
| } |
| |
| @Override |
| public Iterable<ISegmentAspect> getSegmentAspects() { |
| if (getCallStackTidResolver() != null) { |
| return ImmutableList.of(FunctionTidAspect.TID_ASPECT, SymbolAspect.SYMBOL_ASPECT); |
| } |
| return Collections.singletonList(SymbolAspect.SYMBOL_ASPECT); |
| } |
| |
| /** |
| * Returns all the listeners |
| * |
| * @return latency listeners |
| */ |
| protected Iterable<IAnalysisProgressListener> getListeners() { |
| List<IAnalysisProgressListener> listeners = new ArrayList<>(); |
| for (Object listener : fListeners.getListeners()) { |
| if (listener != null) { |
| listeners.add((IAnalysisProgressListener) listener); |
| } |
| } |
| return listeners; |
| } |
| |
| /** |
| * Send the segment store to all its listener |
| * |
| * @param store |
| * The segment store to broadcast |
| */ |
| protected void sendUpdate(final ISegmentStore<ISegment> store) { |
| for (IAnalysisProgressListener listener : getListeners()) { |
| listener.onComplete(this, store); |
| } |
| } |
| |
| /** |
| * Set whether the callgraph execution should be triggered automatically after |
| * building the callstack or if it should wait to be requested |
| * |
| * @param trigger |
| * {@code true} means the callgraph analysis will be executed after |
| * the callstack, {@code false} means it will be executed on demand |
| * only. |
| */ |
| public void triggerAutomatically(boolean trigger) { |
| fAutomaticCallgraph = trigger; |
| } |
| |
| /** |
| * Get the quarks to query to get the Edges in the call stack |
| * |
| * @return list of quarks who's intervals should have {@link EdgeStateValue}s |
| */ |
| protected Collection<Integer> getEdgeQuarks() { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public boolean isComplete() { |
| // Initialization error, but the analysis is completed |
| if (!waitForInitialization()) { |
| return true; |
| } |
| ITmfStateSystem stateSystem = getStateSystem(); |
| if (stateSystem == null) { |
| throw new IllegalStateException("The initialiation is complete, so the state system must not be null"); //$NON-NLS-1$ |
| } |
| return stateSystem.waitUntilBuilt(0); |
| } |
| |
| @Override |
| public @NonNull List<@NonNull String> getExtraDataSets() { |
| return fCallGraph.getExtraDataSets(); |
| } |
| |
| @Override |
| public MetricType getWeightType() { |
| return fCallGraph.getWeightType(); |
| } |
| |
| @Override |
| public List<MetricType> getAdditionalMetrics() { |
| return fCallGraph.getAdditionalMetrics(); |
| } |
| |
| @Override |
| public String toDisplayString(AggregatedCallSite object) { |
| return fCallGraph.toDisplayString(object); |
| } |
| |
| @Override |
| public Object getAdditionalMetric(AggregatedCallSite object, int metricIndex) { |
| return fCallGraph.getAdditionalMetric(object, metricIndex); |
| } |
| |
| @Override |
| public IDataPalette getPalette() { |
| // Schedule the analysis (it will likely be needed) but don't wait for |
| // completion as this should be a fast return. |
| fCallGraph.schedule(); |
| return fCallGraph.getPalette(); |
| } |
| |
| } |