blob: f5c7e62e2ca75555bf5d1099236ec6f883f78f9f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 É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 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.nebula.widgets.opal.notifier.Notifier;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.tracecompass.analysis.os.linux.core.execution.graph.OsExecutionGraph;
import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace;
import org.eclipse.tracecompass.analysis.timing.core.segmentstore.AbstractSegmentStoreAnalysisModule;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.backend.StateMachineBackendAnalysis;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.builder.BuilderInstanceGroup;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.Activator;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.handlers.XaFParameterProvider;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineUtils.TimestampInterval;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent;
import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISaveablePart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.xml.sax.SAXException;
import com.google.common.collect.ImmutableList;
/**
* @author Raphaël Beamonte
*
*/
public class StateMachineAnalysis extends AbstractSegmentStoreAnalysisModule {
/**
* The ID of this analysis
*/
public static final String ANALYSIS_ID = "org.eclipse.tracecompass.xaf.core.statemachine"; //$NON-NLS-1$
private static Image XAF = Activator.getImage("icons/xaf@4x.png"); //$NON-NLS-1$
private static final @NonNull Collection<ISegmentAspect> BASE_ASPECTS =
ImmutableList.of(
StateMachineSegment.TidAspect.INSTANCE,
StateMachineSegment.StatusAspect.INSTANCE,
StateMachineSegment.InvalidConstraintsAspect.INSTANCE);
@Override
public Iterable<ISegmentAspect> getSegmentAspects() {
return BASE_ASPECTS;
}
private boolean finishedEditing = false;
private boolean contentEdited = false;
private Object finishedEditingLock = new Object();
@SuppressWarnings("nls")
@Override
protected boolean buildAnalysisSegments(@NonNull ISegmentStore<@NonNull ISegment> segmentStore, @NonNull IProgressMonitor monitor) throws TmfAnalysisException {
ITmfTrace trace = getTrace();
if (trace == null) {
return false;
}
// We will create two more experiments: one with only the
// kernel traces, and one with only the other traces
List<ITmfTrace> kernelTraces = new ArrayList<>();
List<ITmfTrace> otherTraces = new ArrayList<>();
for (ITmfTrace t : getAllTraces(trace)) {
if (t instanceof IKernelTrace) {
kernelTraces.add(t);
} else {
otherTraces.add(t);
}
}
TmfExperiment expKernel = new TmfExperiment(CtfTmfEvent.class, trace.getName()+" (Kernel only)", kernelTraces.toArray(new CtfTmfTrace[kernelTraces.size()]), TmfExperiment.DEFAULT_INDEX_PAGE_SIZE, null);
TmfExperiment expOther = new TmfExperiment(CtfTmfEvent.class, trace.getName()+" (No kernel)", otherTraces.toArray(new CtfTmfTrace[otherTraces.size()]), TmfExperiment.DEFAULT_INDEX_PAGE_SIZE, null);
// Get the environment variables
Map<String, String> env = System.getenv();
String envv;
// Get the analysis properties
Properties xafproperties = null;
boolean ENV_use_env = Boolean.parseBoolean(env.get("PARAMETER_USE_ENV"));
if (ENV_use_env) {
xafproperties = new Properties();
xafproperties.setProperty(XaFParameterProvider.PROPERTY_MODEL_PROVIDED,
Boolean.toString(Boolean.parseBoolean(env.get("PARAMETER_MODEL_PROVIDED"))));
xafproperties.setProperty(XaFParameterProvider.PROPERTY_MODEL_LOCATION,
env.getOrDefault("MODEL", StringUtils.EMPTY));
xafproperties.setProperty(XaFParameterProvider.PROPERTY_SELECTED_VARIABLES,
env.getOrDefault("PARAMETER_SELECTED_VARIABLES", StringUtils.EMPTY));
xafproperties.setProperty(XaFParameterProvider.PROPERTY_SELECTED_TIMERANGES,
env.getOrDefault("PARAMETER_SELECTED_TIMERANGES", StringUtils.EMPTY));
xafproperties.setProperty(XaFParameterProvider.PROPERTY_MODEL_LOCATION_HISTORY,
StringUtils.EMPTY);
xafproperties.setProperty(XaFParameterProvider.PROPERTY_CHECK_MODEL,
Boolean.toString(Boolean.parseBoolean(env.get("PARAMETER_CHECK_MODEL"))));
xafproperties.setProperty(XaFParameterProvider.PROPERTY_ALL_INSTANCES_VALID,
Boolean.toString(Boolean.parseBoolean(env.get("PARAMETER_ALL_INSTANCES_VALID"))));
} else {
xafproperties = (Properties) getParameter("parameters");
}
if (xafproperties == null) {
// No available properties, or the user cancelled
return false;
}
for (ITmfTrace kernelTrace : expKernel.getTraces()) {
KernelAnalysisModule kernelAnalysisModule = TmfTraceUtils.getAnalysisModuleOfClass(kernelTrace, KernelAnalysisModule.class, KernelAnalysisModule.ID);
if (kernelAnalysisModule != null) {
IStatus status = kernelAnalysisModule.schedule();
if (!status.isOK()) {
System.out.println("kernelAnalysisModule status is not ok");
}
} else {
System.out.println("kernelAnalysisModule is null");
}
}
StateMachineBenchmark benchmarkObject = new StateMachineBenchmark("State system build");
List<StateMachineBackendAnalysis> stateMachineBackendAnalysisList = new ArrayList<>();
for (ITmfTrace kernelTrace : expKernel.getTraces()) {
StateMachineBackendAnalysis stateMachineBackendAnalysis = TmfTraceUtils.getAnalysisModuleOfClass(kernelTrace, StateMachineBackendAnalysis.class, StateMachineBackendAnalysis.ID);
stateMachineBackendAnalysisList.add(stateMachineBackendAnalysis);
if (stateMachineBackendAnalysis != null) {
IStatus status = stateMachineBackendAnalysis.schedule();
if (status.isOK()) {
stateMachineBackendAnalysis.waitForCompletion();
} else {
System.out.println("stateMachineAnalysisModule status is not ok");
}
} else {
System.out.println("stateMachineAnalysisModule is null");
}
}
benchmarkObject.stop();
benchmarkObject = new StateMachineBenchmark("Critical path build");
List<OsExecutionGraph> criticalPathModulesList = new ArrayList<>();
for (ITmfTrace kernelTrace : expKernel.getTraces()) {
OsExecutionGraph criticalPathAnalysisModule = TmfTraceUtils.getAnalysisModuleOfClass(kernelTrace, OsExecutionGraph.class, OsExecutionGraph.ANALYSIS_ID);
criticalPathModulesList.add(criticalPathAnalysisModule);
if (criticalPathAnalysisModule != null) {
IStatus status = criticalPathAnalysisModule.schedule();
if (!status.isOK()) {
System.out.println("fModuleCriticalPath status is not ok");
}
} else {
System.out.println("fModuleCriticalPath is null");
}
}
benchmarkObject.stop();
// Either load the initial transitions from the file or generate them from the trace
List<StateMachineTransition> initialTransitions = null;
BuilderInstanceGroup builderInstanceGroup = null;
boolean modelProvided = Boolean.parseBoolean(xafproperties.getProperty(XaFParameterProvider.PROPERTY_MODEL_PROVIDED, Boolean.TRUE.toString()));
boolean allInstancesAsValid = Boolean.parseBoolean(xafproperties.getProperty(XaFParameterProvider.PROPERTY_ALL_INSTANCES_VALID, Boolean.FALSE.toString()));
String model = xafproperties.getProperty(XaFParameterProvider.PROPERTY_MODEL_LOCATION);
if (!modelProvided) {
benchmarkObject = new StateMachineBenchmark("Building model");
Set<String> variablesTypes = new HashSet<>(Arrays.asList(
xafproperties.getProperty(XaFParameterProvider.PROPERTY_SELECTED_VARIABLES)
.split(XaFParameterProvider.PROPERTY_SEPARATOR)));
Set<TimestampInterval> timestampIntervals = null;
String timestampIntervalsStr = xafproperties.getProperty(XaFParameterProvider.PROPERTY_SELECTED_TIMERANGES);
if (timestampIntervalsStr != null && !timestampIntervalsStr.isEmpty()) {
timestampIntervals = new TreeSet<>();
for (String intervalStr : timestampIntervalsStr.split(XaFParameterProvider.PROPERTY_SEPARATOR)) {
String[] intervalStrVal = intervalStr.split(XaFParameterProvider.PROPERTY_SELECTED_TIMERANGES_SEPARATOR);
long startTime = Long.parseLong(intervalStrVal[0]);
long endTime = Long.parseLong(intervalStrVal[1]);
timestampIntervals.add(new TimestampInterval(startTime, endTime));
}
}
builderInstanceGroup = new BuilderInstanceGroup(stateMachineBackendAnalysisList, criticalPathModulesList, variablesTypes, timestampIntervals);
builderInstanceGroup.buildOn(expOther);
initialTransitions = builderInstanceGroup.getBasicInitialTransitions();
benchmarkObject.stop();
} else {
try {
initialTransitions = StateMachineUtils.getModelFromXML(model);
if (initialTransitions == null) {
throw new RuntimeException("No initial transition found");
}
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
System.exit(1);
}
}
benchmarkObject = new StateMachineBenchmark("Instances construction and constraint verification");
StateMachineInstanceGroup smig = new StateMachineInstanceGroup(initialTransitions, stateMachineBackendAnalysisList, criticalPathModulesList, allInstancesAsValid);
smig.buildOn(expOther);
/*ITmfContext ctx = expOther.seekEvent(0);
ITmfEvent event = null;
event = expOther.getNext(ctx);
while (event != null) {
smig.receivedEvent(event);
event = expOther.getNext(ctx);
}*/
benchmarkObject.stop();
if (builderInstanceGroup != null) {
benchmarkObject = new StateMachineBenchmark("Clean up the model built");
builderInstanceGroup.cleanUnusedVariablesAndConstraints(initialTransitions);
benchmarkObject.stop();
try (PrintWriter writer = new PrintWriter("/tmp/sm.dot", "UTF-8")) { // FIXME: DEBUG PRINT SM
Display.getDefault().asyncExec(()->
Notifier.notify(XAF, "Saving the completed state machine...", ""));
writer.write(StateMachineUtils.StateMachineToDot.drawStateMachine(initialTransitions));
} catch (FileNotFoundException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (model != null) {
// Save the newly created state machine
try (PrintWriter writer = new PrintWriter(model, "UTF-8")) {
writer.write(StateMachineUtils.getXMLFromModel(initialTransitions));
} catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
boolean checkModel = Boolean.parseBoolean(xafproperties.getProperty(XaFParameterProvider.PROPERTY_CHECK_MODEL, Boolean.TRUE.toString()));
if (!checkModel) {
smig.cleanUpAdaptive();
} else {
// Open the editor so the user can change stuff in the generated state machine
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
contentEdited = false;
if (page != null) {
IWorkspace ws = ResourcesPlugin.getWorkspace();
IProject uniqueProject;
do {
uniqueProject = ws.getRoot().getProject(
String.format("XaFModels_%s", RandomStringUtils.randomAlphanumeric(8)));
} while (uniqueProject.exists());
final IProject modelProject = uniqueProject;
try {
modelProject.create(null);
modelProject.open(null);
IPath location = new Path(model);
IFile file = modelProject.getFile(location.lastSegment());
file.createLink(location, IResource.NONE, null);
IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName());
IEditorPart editorPart = IDE.openEditor(page, file, desc.getId(), true);
editorPart.addPropertyListener(new IPropertyListener() {
@Override
public void propertyChanged(@Nullable Object source, int propId) {
if (ISaveablePart.PROP_DIRTY == propId) {
if (!editorPart.isDirty()) {
try {
List<StateMachineTransition> trans = StateMachineUtils.getModelFromXML(model);
if (trans == null) {
MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), SWT.ICON_ERROR);
messageBox.setMessage("No initial transition found");
messageBox.open();
}
} catch (SAXException | IOException | ParserConfigurationException e) {
MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), SWT.ICON_ERROR);
messageBox.setMessage(e.getMessage());
messageBox.open();
}
contentEdited = true;
}
}
}
});
page.addPartListener(new IPartListener2() {
@Override
public void partVisible(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partOpened(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partInputChanged(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partHidden(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partDeactivated(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partClosed(@Nullable IWorkbenchPartReference partRef) {
if (partRef instanceof IEditorReference) {
IEditorPart ieditorPart = ((IEditorReference)partRef).getEditor(true);
if (ieditorPart != null && ieditorPart.equals(editorPart)) {
// Signal that we finished editing the XML file
synchronized(finishedEditingLock) {
finishedEditing = true;
finishedEditingLock.notifyAll();
}
}
// Delete the temporary project we created to edit this file
try {
modelProject.delete(true, true, null);
} catch (CoreException e) {
e.printStackTrace();
}
}
}
@Override
public void partBroughtToTop(@Nullable IWorkbenchPartReference partRef) {
}
@Override
public void partActivated(@Nullable IWorkbenchPartReference partRef) {
}
});
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
// Wait that the user finishes editing the model
synchronized(finishedEditingLock) {
while(!finishedEditing) {
try {
finishedEditingLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
// If the model has been changed, load it back
if (contentEdited) {
try {
initialTransitions = StateMachineUtils.getModelFromXML(model);
if (initialTransitions == null) {
MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), SWT.ICON_ERROR);
messageBox.setMessage("No initial transition found");
messageBox.open();
return false;
}
// We need to rebuild the state machine instance group as everything could have changed...
smig = new StateMachineInstanceGroup(initialTransitions, stateMachineBackendAnalysisList, criticalPathModulesList, allInstancesAsValid);
smig.buildOn(expOther);
} catch (SAXException | IOException | ParserConfigurationException e) {
MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), SWT.ICON_ERROR);
messageBox.setMessage(e.getMessage());
messageBox.open();
return false;
}
} else {
smig.cleanUpAdaptive();
}
}
}
}
benchmarkObject = new StateMachineBenchmark("Printing checked instances");
boolean print = true;
envv = env.get("PRINTCHECK");
if (envv != null) {
if (Boolean.parseBoolean(envv)) {
print = true;
} else {
print = false;
}
}
if (print) {
System.out.println(smig.toString());
}
benchmarkObject.stop();
// All instances must be considered valid, there's no analysis to be ran
if (!modelProvided && allInstancesAsValid) {
return true;
}
boolean analyze = true;
envv = env.get("ANALYZE");
if (envv != null) {
if (Boolean.parseBoolean(envv)) {
analyze = true;
} else {
analyze = false;
}
}
if (analyze) {
StateMachineReport.debug("\nVérification des instances:");
StateMachineReport.debug("===");
benchmarkObject = new StateMachineBenchmark("Analyze");
smig.analyze(expKernel);
benchmarkObject.stop();
}
StateMachineBenchmark.printBenchmarks();
// TODO: Takes a lot of time !!!
segmentStore.addAll(smig.getSegmentStore());
return true;
}
private static Set<ITmfTrace> getAllTraces(ITmfTrace trace) {
Set<ITmfTrace> traces = new HashSet<>();
if (trace instanceof TmfExperiment) {
TmfExperiment exp = (TmfExperiment)trace;
traces.addAll(exp.getTraces());
} else {
traces.add(trace);
}
return traces;
}
@Override
public boolean canExecute(ITmfTrace trace) {
final String vtidContext = "context._vtid"; //$NON-NLS-1$
for (ITmfTrace t : getAllTraces(trace)) {
// Check if vtid is available
ITmfEvent event = t.getNext(t.seekEvent(0));
if (event != null && event.getContent() != null && !event.getContent().getFieldNames().contains(vtidContext)) {
return false;
}
}
return true;
}
@Override
protected void canceling() {
// TODO Auto-generated method stub
}
}