blob: c95583f7aab02f7df7ae453d2ef15b8efd9a0df1 [file] [log] [blame]
/*******************************************************************************
* 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.internal.callstack.core.xml.callstack;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
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.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.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;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdResolver;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries.IThreadIdResolver;
import org.eclipse.tracecompass.incubator.internal.callstack.core.Activator;
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.incubator.internal.callstack.core.xml.callstack.CallstackXmlModuleHelper.ISubModuleHelper;
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.tmf.analysis.xml.core.module.TmfXmlStrings;
import org.eclipse.tracecompass.tmf.analysis.xml.core.module.TmfXmlUtils;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.w3c.dom.Element;
import com.google.common.collect.ImmutableList;
/**
*
*
* @author Geneviève Bastien
*/
public class CallstackXmlAnalysis extends TmfAbstractAnalysisModule implements IFlameChartProvider, ITmfAnalysisModuleWithStateSystems, ICallGraphProvider {
private final Path fSourceFile;
private final ISubModuleHelper fHelper;
private @Nullable IAnalysisModule fModule = null;
private @Nullable CallStackSeries fCallStacks = null;
private final CallGraphAnalysis fCallGraph;
private boolean fHasTid = false;
private final ListenerList fListeners = new ListenerList(ListenerList.IDENTITY);
/**
* Constructor
*
* @param sourceFile
* The source file containing this callstack analysis
* @param helper
* The helper for the dependent module
*/
public CallstackXmlAnalysis(Path sourceFile, ISubModuleHelper helper) {
super();
fSourceFile = sourceFile;
fHelper = helper;
fCallGraph = new CallGraphAnalysis(this);
}
@Override
public @Nullable CallStackSeries getCallStackSeries() {
CallStackSeries series = fCallStacks;
if (series == null) {
IAnalysisModule module = getAnalysisModule();
if (!(module instanceof ITmfAnalysisModuleWithStateSystems)) {
return null;
}
Iterator<@NonNull ITmfStateSystem> stateSystems = ((ITmfAnalysisModuleWithStateSystems) module).getStateSystems().iterator();
if (!stateSystems.hasNext()) {
return null;
}
ITmfStateSystem ss = stateSystems.next();
Path xmlFile = fSourceFile;
final String pathString = xmlFile.toString();
Element doc = TmfXmlUtils.getElementInFile(pathString, CallstackXmlStrings.CALLSTACK, getId());
if (doc == null) {
fCallStacks = null;
return null;
}
/* parser for defined Fields */
List<Element> callStackElements = TmfXmlUtils.getChildElements(doc, CallstackXmlStrings.CALLSTACK_GROUP);
if (callStackElements.size() > 1) {
Activator.getInstance().logWarning("More than one callstack series defined. Only the first one will be displayed"); //$NON-NLS-1$
} else if (callStackElements.isEmpty()) {
fCallStacks = null;
return null;
}
Element callStackElement = callStackElements.get(0);
List<String[]> patterns = new ArrayList<>();
for (Element child : TmfXmlUtils.getChildElements(callStackElement, CallstackXmlStrings.CALLSTACK_LEVEL)) {
String attribute = child.getAttribute(CallstackXmlStrings.CALLSTACK_PATH);
patterns.add(attribute.split("/")); //$NON-NLS-1$
}
// Build the thread resolver
List<Element> childElements = TmfXmlUtils.getChildElements(callStackElement, CallstackXmlStrings.CALLSTACK_THREAD);
IThreadIdResolver resolver = null;
if (childElements.size() > 0) {
Element threadElement = childElements.get(0);
String attribute = threadElement.getAttribute(CallstackXmlStrings.CALLSTACK_THREADCPU);
if (!attribute.isEmpty()) {
resolver = new CallStackSeries.CpuResolver(attribute.split("/")); //$NON-NLS-1$
} else {
attribute = threadElement.getAttribute(CallstackXmlStrings.CALLSTACK_THREADLEVEL);
if (!attribute.isEmpty()) {
String type = threadElement.getAttribute(CallstackXmlStrings.CALLSTACK_THREADLEVEL_TYPE);
if (type.equals(CallstackXmlStrings.CALLSTACK_THREADLEVEL_VALUE)) {
resolver = new CallStackSeries.AttributeValueThreadResolver(Integer.valueOf(attribute));
} else {
resolver = new CallStackSeries.AttributeNameThreadResolver(Integer.valueOf(attribute));
}
}
}
}
fHasTid = (resolver != null);
// Build the host resolver
childElements = TmfXmlUtils.getChildElements(callStackElement, CallstackXmlStrings.CALLSTACK_HOST);
IHostIdResolver hostResolver = null;
if (childElements.size() > 0) {
Element hostElement = childElements.get(0);
String attribute = hostElement.getAttribute(CallstackXmlStrings.CALLSTACK_THREADLEVEL);
if (!attribute.isEmpty()) {
String type = hostElement.getAttribute(CallstackXmlStrings.CALLSTACK_THREADLEVEL_TYPE);
if (type.equals(CallstackXmlStrings.CALLSTACK_THREADLEVEL_VALUE)) {
hostResolver = new CallStackHostUtils.AttributeValueHostResolver(Integer.valueOf(attribute));
} else {
hostResolver = new CallStackHostUtils.AttributeNameHostResolver(Integer.valueOf(attribute));
}
}
}
hostResolver = hostResolver == null ? new CallStackHostUtils.TraceHostIdResolver(Objects.requireNonNull(getTrace())) : hostResolver;
fHasTid = resolver != null;
series = new CallStackSeries(ss, patterns, 0, callStackElement.getAttribute(TmfXmlStrings.NAME), hostResolver, resolver);
fCallStacks = series;
}
return series;
}
@Override
public String getHostId() {
ITmfTrace trace = getTrace();
if (trace == null) {
return ""; //$NON-NLS-1$
}
return trace.getHostId();
}
@Override
protected boolean executeAnalysis(IProgressMonitor monitor) throws TmfAnalysisException {
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule == null) {
return false;
}
boolean ret = analysisModule.waitForCompletion(monitor);
if (!ret) {
return ret;
}
ISegmentStore<ISegment> segmentStore = getSegmentStore();
if (segmentStore != null) {
sendUpdate(segmentStore);
}
fCallGraph.schedule();
return true;
}
@Override
protected void canceling() {
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule != null) {
analysisModule.cancel();
}
fCallGraph.cancel();
}
@Override
public void dispose() {
/*
* The sub-analyses are not registered to the trace directly, so we need
* to tell them when the trace is disposed.
*/
super.dispose();
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule != null) {
analysisModule.dispose();
}
fCallGraph.dispose();
}
@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
protected Iterable<IAnalysisModule> getDependentAnalyses() {
ITmfTrace trace = getTrace();
if (trace == null) {
throw new NullPointerException("Trace should not be null at this point"); //$NON-NLS-1$
}
IAnalysisModule module = getAnalysisModule();
if (module == null) {
return Collections.emptyList();
}
return Collections.singleton(module);
}
private synchronized @Nullable IAnalysisModule getAnalysisModule() {
IAnalysisModule module = fModule;
if (module == null) {
ITmfTrace trace = getTrace();
if (trace == null) {
return null;
}
module = fHelper.getAnalysis(trace);
if (module != null) {
fModule = module;
}
}
return module;
}
@Override
public @Nullable ITmfStateSystem getStateSystem(String id) {
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule instanceof ITmfAnalysisModuleWithStateSystems) {
return ((ITmfAnalysisModuleWithStateSystems) analysisModule).getStateSystem(id);
}
return null;
}
@Override
public Iterable<ITmfStateSystem> getStateSystems() {
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule instanceof ITmfAnalysisModuleWithStateSystems) {
return ((ITmfAnalysisModuleWithStateSystems) analysisModule).getStateSystems();
}
return Collections.emptyList();
}
@Override
public boolean waitForInitialization() {
IAnalysisModule analysisModule = getAnalysisModule();
if (analysisModule instanceof ITmfAnalysisModuleWithStateSystems) {
return ((ITmfAnalysisModuleWithStateSystems) analysisModule).waitForInitialization();
}
return false;
}
@Override
public Collection<IWeightedTreeGroupDescriptor> getGroupDescriptors() {
fCallGraph.schedule();
fCallGraph.waitForCompletion();
return fCallGraph.getGroupDescriptors();
}
@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 AggregatedCallSite createCallSite(Object symbol) {
return fCallGraph.createCallSite(symbol);
}
@Override
public String getTitle() {
return fCallGraph.getTitle();
}
@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 (fHasTid) {
return ImmutableList.of(FunctionTidAspect.TID_ASPECT, SymbolAspect.SYMBOL_ASPECT);
}
return Collections.singletonList(SymbolAspect.SYMBOL_ASPECT);
}
@Override
public @Nullable ISegmentStore<ISegment> getSegmentStore() {
CallStackSeries series = getCallStackSeries();
if (series == null) {
return null;
}
return series;
}
/**
* 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);
}
}
@Override
public boolean isComplete() {
// Initialization error, but the analysis is completed
if (!waitForInitialization()) {
return true;
}
Iterator<ITmfStateSystem> iterator = getStateSystems().iterator();
if (!iterator.hasNext()) {
throw new IllegalStateException("The initialization is complete, so the state system must not be null"); //$NON-NLS-1$
}
return iterator.next().waitUntilBuilt(0);
}
@Override
public long getEnd() {
// Initialization error, but the analysis is completed
if (!waitForInitialization()) {
return Integer.MIN_VALUE;
}
Iterator<ITmfStateSystem> iterator = getStateSystems().iterator();
if (!iterator.hasNext()) {
throw new IllegalStateException("The initialization is complete, so the state system must not be null"); //$NON-NLS-1$
}
return iterator.next().getCurrentEndTime();
}
@Override
public List<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() {
fCallGraph.schedule();
fCallGraph.waitForCompletion();
return fCallGraph.getPalette();
}
}