blob: 652d3c47265ca8960ea4358587f4fc20aedfb8d5 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013 CEA LIST.
*
*
* 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
*
* Contributors:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.moka.debug.engine;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
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.papyrus.moka.fuml.Semantics.Classes.Kernel.IObject_;
import org.eclipse.papyrus.moka.fuml.Semantics.Loci.LociL1.ISemanticVisitor;
public class MokaDebugTarget extends MokaDebugElement implements IMokaDebugTarget {
// The launcher from which this debug target was created
protected ILaunch launcher;
// The process from which execution events come from
protected MokaProcess executionEngineProcess;
// Set of logical threads currently executing the model
protected Set<IMokaThread> executionThreads;
// Ensure mutual exclusive access to this object for execution
// engine threads and debug target thread
protected ReentrantLock targetLock;
// The current status of the debug target
protected MokaDebugTargetState status;
public MokaDebugTarget(ILaunch launcher) {
super(null);
this.launcher = launcher;
this.executionEngineProcess = null;
this.executionThreads = new HashSet<IMokaThread>();
this.targetLock = new ReentrantLock(true);
this.status = MokaDebugTargetState.RUNNING;
this.fireCreationEvent();
}
public void setProcess(MokaProcess process) {
this.executionEngineProcess = process;
}
@Override
public ILaunch getLaunch() {
return this.launcher;
}
@Override
public IDebugTarget getDebugTarget() {
return this;
}
@Override
public boolean canTerminate() {
return !this.isTerminated();
}
@Override
public boolean isTerminated() {
return this.status.equals(MokaDebugTargetState.TERMINATED);
}
@Override
public void terminate() throws DebugException {
for (IMokaThread thread : this.executionThreads) {
if (thread.isSuspended()) {
thread.resume();
}
thread.terminate();
}
this.executionThreads.clear();
this.executionEngineProcess.terminate();
this.status = MokaDebugTargetState.TERMINATED;
}
@Override
public boolean canResume() {
// The debug target can only be resumed if it is suspended
return this.isSuspended();
}
@Override
public boolean canSuspend() {
// The debug target can only be suspended if it is currently running
return this.status.equals(MokaDebugTargetState.RUNNING);
}
@Override
public boolean isSuspended() {
// The debug target is considered as being suspended only if its status is suspended
return this.status.equals(MokaDebugTargetState.SUSPENDED);
}
@Override
public void resume() throws DebugException {
// Resume all thread that are currently suspended
this.targetLock.lock();
try {
for (IMokaThread logicalThread : this.executionThreads) {
if (logicalThread.isSuspended()) {
logicalThread.resume();
}
}
this.status = MokaDebugTargetState.RUNNING;
this.fireResumeEvent(DebugEvent.RESUME);
} finally {
this.targetLock.unlock();
}
}
@Override
public void suspend() throws DebugException {
// Request suspension for all thread that are known by this debug target
this.targetLock.lock();
try {
for (IMokaThread logicalThread : this.executionThreads) {
if (!logicalThread.isSuspended()) {
logicalThread.suspend();
}
}
this.status = MokaDebugTargetState.SUSPENDED;
this.fireChangeEvent(DebugEvent.SUSPEND);
} finally {
this.targetLock.unlock();
}
}
@Override
public void registerThread(IObject_ object) {
IMokaThread thread = new MokaThread(this, object);
this.executionThreads.add(thread);
thread.registered();
}
public boolean isNewThread(IObject_ object) {
// Determine if the active class instance is already associated with a logical thread.
// If this is the case the it returns true, false otherwise
IMokaThread existingThread = null;
Iterator<IMokaThread> threadIterator = this.executionThreads.iterator();
while (existingThread == null && threadIterator.hasNext()) {
existingThread = threadIterator.next();
if (existingThread.getLogicalThread() != object) {
existingThread = null;
}
}
return existingThread == null;
}
@Override
public void unregisterThread(IObject_ object) {
// Remove the thread corresponding this active class instance
IMokaThread targetThread = null;
Iterator<IMokaThread> threadIterator = this.executionThreads.iterator();
while (targetThread == null && threadIterator.hasNext()) {
targetThread = threadIterator.next();
if (targetThread.getLogicalThread() == object) {
threadIterator.remove();
targetThread.unregistered();
}else {
targetThread = null;
}
}
}
protected IMokaThread getLogicalThread(IObject_ object) {
IMokaThread targetThread = null;
Iterator<IMokaThread> threadIterator = this.executionThreads.iterator();
while (targetThread == null && threadIterator.hasNext()) {
IMokaThread currentThread = threadIterator.next();
if (currentThread.getLogicalThread() == object) {
targetThread = currentThread;
}
}
return targetThread;
}
@Override
public boolean isSuspensionRequired(IObject_ object, ISemanticVisitor nodeVisitor) {
// There are three cases in which a suspension might be required.
// 1 - The suspension is requested by the user
// 2 - The suspension is implied by the presence of a breakpoint on the node that is visited
// 3 - The logical thread is currently in stepping mode
IMokaThread thread = this.getLogicalThread(object);
if (thread != null) {
if (thread.getSuspensionFlag()) {
return true;
} else if (DebugServiceHelper.INSTANCE.hasBreakpoint(nodeVisitor)) {
try {
thread.suspend(); // Request a suspension for that thread
} catch (DebugException e) {
e.printStackTrace();
}
return true;
} else if (thread.isStepping()) {
try {
thread.suspend(); // Request a suspension for that thread
} catch (DebugException e) {
e.printStackTrace();
}
return true;
}
}
return false;
}
@Override
public void suspend(IObject_ object, ISemanticVisitor nodeVisitor) {
IMokaThread logicalThread = this.getLogicalThread(object);
if (logicalThread != null) {
logicalThread.suspend(nodeVisitor);
}
}
@Override
public void breakpointAdded(IBreakpoint breakpoint) {
}
@Override
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
}
@Override
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
}
@Override
public boolean canDisconnect() {
// In this implementation the debug target is allowed
return !this.isDisconnected() && !this.isTerminated();
}
@Override
public void disconnect() throws DebugException {
// When the debug target gets disconnected from the target program
// the following set of actions take place:
// 1 - Threads are removed
// 2 - The process is not watched anymore
this.targetLock.lock();
try {
for (IMokaThread thread : this.executionThreads) {
if (thread.isSuspended()) {
thread.resume();
}
}
this.executionThreads.clear();
this.status = MokaDebugTargetState.DISCONNECTED;
this.fireTerminateEvent();
} finally {
this.targetLock.unlock();
}
}
@Override
public boolean isDisconnected() {
return this.status.equals(MokaDebugTargetState.DISCONNECTED)|| this.isTerminated();
}
@Override
public boolean supportsStorageRetrieval() {
return false;
}
@Override
public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
return null;
}
@Override
public IProcess getProcess() {
return this.executionEngineProcess;
}
@Override
public IThread[] getThreads() throws DebugException {
return this.executionThreads.toArray(new IThread[0]);
}
@Override
public boolean hasThreads() throws DebugException {
return !this.executionThreads.isEmpty();
}
@Override
public String getName() throws DebugException {
return "Moka Execution";
}
@Override
public boolean supportsBreakpoint(IBreakpoint breakpoint) {
return true;
}
@Override
public void update(IObject_ object, ISemanticVisitor visitor) {
IMokaThread thread = this.getLogicalThread(object);
if (thread != null) {
thread.setSuspensionPoint(visitor);
}
}
@Override
public boolean hasSuspendedThread() {
boolean hasSuspendedThread = false;
this.targetLock.lock();
try {
Iterator<IMokaThread> threadIterator = this.executionThreads.iterator();
while (!hasSuspendedThread && threadIterator.hasNext()) {
hasSuspendedThread = threadIterator.next().isSuspended();
}
} finally {
this.targetLock.unlock();
}
return hasSuspendedThread;
}
}