Przeglądaj źródła

Hot Model Replacement: Debugging session is not terminated when there are only notational changes in the model

Andreas Mülder 14 lat temu
rodzic
commit
3da49eaf3c

+ 165 - 19
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/debugmodel/SCTDebugTarget.java

@@ -10,8 +10,14 @@
  */
 package org.yakindu.sct.simulation.core.debugmodel;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
 import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.ILaunch;
@@ -20,40 +26,90 @@ import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.IMemoryBlock;
 import org.eclipse.debug.core.model.IProcess;
 import org.eclipse.debug.core.model.IThread;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.yakindu.sct.model.sgraph.Region;
+import org.yakindu.sct.model.sgraph.State;
+import org.yakindu.sct.model.sgraph.Statechart;
+import org.yakindu.sct.model.sgraph.Transition;
+import org.yakindu.sct.model.sgraph.Vertex;
+import org.yakindu.sct.simulation.core.ISGraphExecutionBuilder;
 import org.yakindu.sct.simulation.core.ISGraphExecutionFacade;
+import org.yakindu.sct.simulation.core.ISGraphExecutionListener;
 import org.yakindu.sct.simulation.core.SGraphSimulationSession;
+import org.yakindu.sct.simulation.core.extensions.Extensions;
+import org.yakindu.sct.simulation.core.extensions.IExtensionPoints;
 
 /**
  * 
  * @author andreas muelder - Initial contribution and API
  * 
  */
-public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget {
+public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget,
+		ISGraphExecutionListener {
 
 	private IProcess process;
 
 	private ILaunch launch;
 
-	private SCTDebugThread thread;
+	private ISGraphExecutionFacade facade;
+
+	private SGraphSimulationSession session;
+
+	private boolean stepping = false;
+	private boolean terminated = false;
+	private boolean suspended = false;
 
-	private final ISGraphExecutionFacade facade;
+	private List<Region> activeRegions = new ArrayList<Region>();
+	private List<Vertex> activeStates = new ArrayList<Vertex>();
 
-	public SCTDebugTarget(ILaunch launch, ISGraphExecutionFacade facade,
-			String resourceString) throws CoreException {
-		super(null, resourceString);
+	private final Statechart statechart;
+
+	public SCTDebugTarget(ILaunch launch, Statechart statechart)
+			throws CoreException {
+		super(null, statechart.eResource().getURI().toPlatformString(true));
 		this.launch = launch;
-		this.facade = facade;
-		thread = new SCTDebugThread(this, facade, resourceString);
+		this.statechart = statechart;
+
 		DebugPlugin.getDefault().getBreakpointManager()
 				.addBreakpointListener(this);
+
+		createExecutionModel(statechart);
+
+	}
+
+	private void createExecutionModel(Statechart statechart) {
+		ISGraphExecutionBuilder builder = getBuilder(statechart);
+		facade = builder.build(statechart);
+		facade.addExecutionListener(this);
+		session = new SGraphSimulationSession(facade);
+		new Thread(session).start();
+		session.start();
+	}
+
+	protected ISGraphExecutionBuilder getBuilder(EObject context) {
+		Extensions<ISGraphExecutionBuilder> extensions = new Extensions<ISGraphExecutionBuilder>(
+				IExtensionPoints.EXECUTION_BUILDER);
+		return extensions.getRegisteredProvider(context);
 	}
 
 	public IProcess getProcess() {
 		return process;
 	}
 
+	public void stepOver() {
+		fireEvent(new DebugEvent(getDebugTarget(), DebugEvent.STEP_OVER));
+		session.singleStep();
+	}
+
 	public IThread[] getThreads() throws DebugException {
-		return new IThread[] { thread };
+		List<SCTDebugThread> threads = new ArrayList<SCTDebugThread>();
+		for (int i = activeRegions.size() - 1; i >= 0; i--) {
+			threads.add(new SCTDebugThread(this, facade, getResourceString(),
+					activeRegions.get(i)));
+		}
+
+		return threads.toArray(new IThread[] {});
 	}
 
 	public boolean hasThreads() throws DebugException {
@@ -69,36 +125,45 @@ public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget {
 	}
 
 	public boolean canTerminate() {
-		return thread.canTerminate();
+		return !terminated;
 	}
 
 	public boolean isTerminated() {
-		return thread.isTerminated();
+		return terminated;
 	}
 
 	public void terminate() throws DebugException {
-
-		thread.terminate();
+		facade.removeExecutionListener(this);
+		// activeStates = Collections.emptyList();
+		fireEvent(new DebugEvent(getDebugTarget(), DebugEvent.TERMINATE));
+		terminated = true;
+		session.terminate();
 	}
 
 	public boolean canResume() {
-		return thread.canResume();
+		return suspended && !terminated;
 	}
 
 	public boolean canSuspend() {
-		return thread.canSuspend();
+		return !suspended && !terminated;
 	}
 
 	public boolean isSuspended() {
-		return thread.isSuspended();
+		return suspended;
 	}
 
 	public void resume() throws DebugException {
-		thread.resume();
+		fireEvent(new DebugEvent(this, DebugEvent.RESUME));
+		fireChangeEvent(DebugEvent.CONTENT);
+		session.resume();
+		suspended = false;
 	}
 
 	public void suspend() throws DebugException {
-		thread.suspend();
+		fireEvent(new DebugEvent(this, DebugEvent.SUSPEND));
+		fireChangeEvent(DebugEvent.CONTENT);
+		session.suspend();
+		suspended = true;
 	}
 
 	public void breakpointAdded(IBreakpoint breakpoint) {
@@ -141,10 +206,91 @@ public class SCTDebugTarget extends SCTDebugElement implements IDebugTarget {
 
 	public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
 		if (adapter == SGraphSimulationSession.class)
-			return thread.getAdapter(SGraphSimulationSession.class);
+			return session;
 		if (adapter == ISGraphExecutionFacade.class)
 			return facade;
 		return super.getAdapter(adapter);
 	}
 
+	public boolean canStepOver() {
+		return isSuspended() && !isTerminated();
+	}
+
+	@Override
+	public void stateEntered(Vertex vertex) {
+		if (vertex instanceof State) {
+			if (((State) vertex).isLeaf()) {
+				activeStates.add(vertex);
+				activeRegions.add(vertex.getParentRegion());
+			}
+		}
+		fireChangeEvent(DebugEvent.CONTENT);
+	}
+
+	@Override
+	public void stateLeft(Vertex vertex) {
+		if (vertex instanceof State) {
+			activeStates.remove(vertex);
+			if (((State) vertex).isLeaf()) {
+				if (activeRegions.contains(vertex.getParentRegion()))
+					activeRegions.remove(vertex.getParentRegion());
+			}
+		}
+		fireChangeEvent(DebugEvent.CONTENT);
+	}
+
+	@Override
+	public void transitionFired(Transition transition) {
+	}
+
+	@Override
+	public void variableValueChanged(String variableName, Object value) {
+	}
+
+	@Override
+	public void eventRaised(String eventName) {
+	}
+
+	public boolean isStepping() {
+		return stepping;
+	}
+
+	public List<Region> getActiveRegions() {
+		return activeRegions;
+	}
+
+	public List<Vertex> getActiveStates() {
+		return activeStates;
+	}
+
+	public List<Vertex> getActiveStatesForRegion(Region region) {
+		List<Vertex> result = new ArrayList<Vertex>();
+		for (Vertex vertex : activeStates) {
+			if (EcoreUtil.isAncestor(region, vertex)) {
+				result.addAll(getActiveHierachy(vertex));
+			}
+
+			Collections.reverse(result);
+		}
+		return result;
+
+	}
+
+	private Collection<? extends Vertex> getActiveHierachy(Vertex vertex) {
+		List<Vertex> result = new ArrayList<Vertex>();
+		result.add(vertex);
+		EObject container = vertex.eContainer();
+		while (container != null) {
+			if (container instanceof State) {
+				result.add((State) container);
+			}
+			container = container.eContainer();
+		}
+		return result;
+	}
+
+	public Statechart getStatechart() {
+		return statechart;
+	}
+
 }

+ 26 - 67
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/debugmodel/SCTDebugThread.java

@@ -11,18 +11,15 @@
 package org.yakindu.sct.simulation.core.debugmodel;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
-import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.model.IBreakpoint;
 import org.eclipse.debug.core.model.IStackFrame;
 import org.eclipse.debug.core.model.IThread;
-import org.yakindu.sct.model.sgraph.Transition;
+import org.yakindu.sct.model.sgraph.Region;
 import org.yakindu.sct.model.sgraph.Vertex;
 import org.yakindu.sct.simulation.core.ISGraphExecutionFacade;
-import org.yakindu.sct.simulation.core.ISGraphExecutionListener;
 import org.yakindu.sct.simulation.core.SGraphSimulationSession;
 
 /**
@@ -30,26 +27,14 @@ import org.yakindu.sct.simulation.core.SGraphSimulationSession;
  * @author andreas muelder - Initial contribution and API
  * 
  */
-public class SCTDebugThread extends SCTDebugElement implements IThread,
-		ISGraphExecutionListener {
+public class SCTDebugThread extends SCTDebugElement implements IThread {
 
-	private boolean stepping = false;
-	private boolean terminated = false;
-	private boolean suspended = false;
-	private Thread thread;
-	private SGraphSimulationSession session;
-	private final ISGraphExecutionFacade facade;
-	private List<Vertex> activeStates = new ArrayList<Vertex>();
+	private final Region region;
 
 	public SCTDebugThread(SCTDebugTarget target, ISGraphExecutionFacade facade,
-			String resourceString) {
+			String resourceString, Region region) {
 		super(target, resourceString);
-		this.facade = facade;
-		session = new SGraphSimulationSession(facade);
-		facade.addExecutionListener(this);
-		thread = new Thread(session);
-		thread.start();
-		session.start();
+		this.region = region;
 	}
 
 	public int getPriority() throws DebugException {
@@ -57,6 +42,9 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 	}
 
 	public IStackFrame[] getStackFrames() throws DebugException {
+		List<Vertex> activeStates = getDebugTarget().getActiveStatesForRegion(
+				region);
+		System.out.println(getName() + " active States " + activeStates);
 		List<IStackFrame> stackFrames = new ArrayList<IStackFrame>();
 		for (int i = activeStates.size() - 1; i >= 0; i--) {
 			stackFrames.add(new SCTStackFrame(this, activeStates.get(i),
@@ -69,31 +57,8 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 		return true;
 	}
 
-	public void stateEntered(Vertex vertex) {
-		activeStates.add(vertex);
-		fireChangeEvent(DebugEvent.CONTENT);
-	}
-
-	public void stateLeft(Vertex vertex) {
-		activeStates.remove(vertex);
-		fireChangeEvent(DebugEvent.CONTENT);
-
-	}
-
-	public void transitionFired(Transition transition) {
-		// Nothing to do
-	}
-
-	public void variableValueChanged(String variableName, Object value) {
-		// Nothing to do
-	}
-
-	public void eventRaised(String eventName) {
-		// Nothing to do
-	}
-
 	public String getName() throws DebugException {
-		return "TODO";
+		return region.getName();
 	}
 
 	public IBreakpoint[] getBreakpoints() {
@@ -101,29 +66,23 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 	}
 
 	public boolean canResume() {
-		return suspended && !terminated;
+		return getDebugTarget().canResume();
 	}
 
 	public boolean canSuspend() {
-		return !suspended && !terminated;
+		return getDebugTarget().canSuspend();
 	}
 
 	public boolean isSuspended() {
-		return suspended;
+		return getDebugTarget().isSuspended();
 	}
 
 	public void resume() throws DebugException {
-		fireEvent(new DebugEvent(this, DebugEvent.RESUME));
-		fireChangeEvent(DebugEvent.CONTENT);
-		session.resume();
-		suspended = false;
+		getDebugTarget().resume();
 	}
 
 	public void suspend() throws DebugException {
-		fireEvent(new DebugEvent(this, DebugEvent.SUSPEND));
-		fireChangeEvent(DebugEvent.CONTENT);
-		session.suspend();
-		suspended = true;
+		getDebugTarget().suspend();
 	}
 
 	public boolean canStepInto() {
@@ -131,7 +90,7 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 	}
 
 	public boolean canStepOver() {
-		return isSuspended() && !isTerminated();
+		return getDebugTarget().canStepOver();
 	}
 
 	public boolean canStepReturn() {
@@ -139,34 +98,29 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 	}
 
 	public boolean isStepping() {
-		return stepping;
+		return getDebugTarget().isStepping();
 	}
 
 	public void stepInto() throws DebugException {
 	}
 
 	public void stepOver() throws DebugException {
-		fireEvent(new DebugEvent(getDebugTarget(), DebugEvent.STEP_OVER));
-		session.singleStep();
+		getDebugTarget().stepOver();
 	}
 
 	public void stepReturn() throws DebugException {
 	}
 
 	public boolean canTerminate() {
-		return !terminated;
+		return getDebugTarget().canTerminate();
 	}
 
 	public boolean isTerminated() {
-		return terminated;
+		return getDebugTarget().isTerminated();
 	}
 
 	public void terminate() throws DebugException {
-		facade.removeExecutionListener(this);
-		activeStates = Collections.emptyList();
-		fireEvent(new DebugEvent(getDebugTarget(), DebugEvent.TERMINATE));
-		terminated = true;
-		session.terminate();
+		getDebugTarget().terminate();
 	}
 
 	public IStackFrame getTopStackFrame() throws DebugException {
@@ -175,10 +129,15 @@ public class SCTDebugThread extends SCTDebugElement implements IThread,
 
 	public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
 		if (adapter == SGraphSimulationSession.class)
-			return session;
+			return getDebugTarget().getAdapter(SGraphSimulationSession.class);
 		if (adapter == ISGraphExecutionFacade.class)
 			return getDebugTarget().getAdapter(ISGraphExecutionFacade.class);
 		return super.getAdapter(adapter);
 	}
 
+	@Override
+	public SCTDebugTarget getDebugTarget() {
+		return (SCTDebugTarget) super.getDebugTarget();
+	}
+
 }

+ 28 - 4
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/hmr/SCTHotModelReplacementManager.java

@@ -28,7 +28,10 @@ import org.eclipse.debug.core.IDebugEventSetListener;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchListener;
 import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.yakindu.sct.model.sgraph.Statechart;
 import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
+import org.yakindu.sct.simulation.core.util.ResourceUtil;
 
 /**
  * 
@@ -139,10 +142,7 @@ public class SCTHotModelReplacementManager implements IResourceChangeListener,
 			changedFiles.clear();
 			delta.accept(this);
 			if (changedFiles.size() > 0) {
-				List<SCTDebugTarget> targets = getAffectedTargets();
-				if (targets.size() > 0) {
-					notifyHotModelReplacementFailed(targets);
-				}
+				handleHotModelReplacement();
 			}
 
 		} catch (CoreException e) {
@@ -150,6 +150,30 @@ public class SCTHotModelReplacementManager implements IResourceChangeListener,
 		}
 	}
 
+	private void handleHotModelReplacement() {
+		// first implementation: If the underlying model does not change
+		// semantically, no notification is required...
+
+		List<SCTDebugTarget> targets = getAffectedTargets();
+		List<SCTDebugTarget> modelReplacementFailedTargets = new ArrayList<SCTDebugTarget>();
+		for (SCTDebugTarget sctDebugTarget : targets) {
+			// Reload the Statechart form the changes resource
+			Statechart newStatechart = ResourceUtil
+					.loadStatechart(sctDebugTarget.getResourceString());
+			if (!EcoreUtil
+					.equals(newStatechart, sctDebugTarget.getStatechart())) {
+				// The model semantically changed, we have to create a
+				// notificiation for that....
+				modelReplacementFailedTargets.add(sctDebugTarget);
+			}
+		}
+
+		if (modelReplacementFailedTargets.size() > 0) {
+			notifyHotModelReplacementFailed(targets);
+		}
+
+	}
+
 	protected void notifyHotModelReplacementFailed(
 			List<SCTDebugTarget> affectedTargets) {
 		synchronized (listeners) {

+ 4 - 52
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/launch/StatechartLaunchConfigurationDelegate.java

@@ -10,9 +10,6 @@
  */
 package org.yakindu.sct.simulation.core.launch;
 
-import java.io.IOException;
-import java.util.Collections;
-
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
@@ -23,21 +20,8 @@ import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
 import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.Resource.Factory;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceFactoryRegistryImpl;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.yakindu.sct.model.sgraph.SGraphPackage;
-import org.yakindu.sct.model.sgraph.Statechart;
-import org.yakindu.sct.simulation.core.ISGraphExecutionBuilder;
-import org.yakindu.sct.simulation.core.ISGraphExecutionFacade;
 import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget;
-import org.yakindu.sct.simulation.core.extensions.Extensions;
-import org.yakindu.sct.simulation.core.extensions.IExtensionPoints;
+import org.yakindu.sct.simulation.core.util.ResourceUtil;
 
 /**
  * 
@@ -45,30 +29,18 @@ import org.yakindu.sct.simulation.core.extensions.IExtensionPoints;
  * 
  */
 public class StatechartLaunchConfigurationDelegate extends
-		LaunchConfigurationDelegate implements ILaunchConfigurationDelegate,
-		IExtensionPoints {
+		LaunchConfigurationDelegate implements ILaunchConfigurationDelegate {
 
 	public void launch(ILaunchConfiguration configuration, String mode,
 			ILaunch launch, IProgressMonitor monitor) throws CoreException {
 		String filename = configuration.getAttribute(
 				IStatechartLaunchParameters.FILE_NAME, "");
 
-		Resource resource = loadResource(filename);
-
-		Statechart statechart = (Statechart) EcoreUtil.getObjectByType(
-				resource.getContents(), SGraphPackage.Literals.STATECHART);
-
-		String platformResource = statechart.eResource().getURI()
-				.toPlatformString(true);
-
-		ISGraphExecutionBuilder builder = getBuilder(statechart);
-		ISGraphExecutionFacade executionFacade = builder.build(statechart);
-		IDebugTarget target = new SCTDebugTarget(launch, executionFacade,
-				platformResource);
+		IDebugTarget target = new SCTDebugTarget(launch,
+				ResourceUtil.loadStatechart(filename));
 		launch.addDebugTarget(target);
 
 	}
-	
 
 	@Override
 	protected IProject[] getProjectsForProblemSearch(
@@ -82,24 +54,4 @@ public class StatechartLaunchConfigurationDelegate extends
 
 	}
 
-	protected ISGraphExecutionBuilder getBuilder(EObject context) {
-		Extensions<ISGraphExecutionBuilder> extensions = new Extensions<ISGraphExecutionBuilder>(
-				EXECUTION_BUILDER);
-		return extensions.getRegisteredProvider(context);
-	}
-
-	protected Resource loadResource(String filename) {
-		URI uri = URI.createPlatformResourceURI(filename, true);
-		Factory factory = ResourceFactoryRegistryImpl.INSTANCE.getFactory(uri);
-		Resource resource = factory.createResource(uri);
-		ResourceSet resourceSet = new ResourceSetImpl();
-		resourceSet.getResources().add(resource);
-		try {
-			resource.load(Collections.EMPTY_MAP);
-			return resource;
-		} catch (IOException e) {
-			throw new IllegalStateException("Error loading resource", e);
-		}
-	}
-
 }

+ 44 - 0
plugins/org.yakindu.sct.simulation.core/src/org/yakindu/sct/simulation/core/util/ResourceUtil.java

@@ -0,0 +1,44 @@
+package org.yakindu.sct.simulation.core.util;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.Resource.Factory;
+import org.eclipse.emf.ecore.resource.impl.ResourceFactoryRegistryImpl;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.yakindu.sct.model.sgraph.SGraphPackage;
+import org.yakindu.sct.model.sgraph.Statechart;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class ResourceUtil {
+
+	public static Resource loadResource(String filename) {
+		URI uri = URI.createPlatformResourceURI(filename, true);
+		Factory factory = ResourceFactoryRegistryImpl.INSTANCE.getFactory(uri);
+		Resource resource = factory.createResource(uri);
+		ResourceSet resourceSet = new ResourceSetImpl();
+		resourceSet.getResources().add(resource);
+		try {
+			resource.load(Collections.EMPTY_MAP);
+			return resource;
+		} catch (IOException e) {
+			throw new IllegalStateException("Error loading resource", e);
+		}
+	}
+
+	public static Statechart loadStatechart(String filename) {
+		Resource resource = loadResource(filename);
+		Statechart statechart = (Statechart) EcoreUtil.getObjectByType(
+				resource.getContents(), SGraphPackage.Literals.STATECHART);
+		return statechart;
+	}
+
+}