| /******************************************************************************* |
| * Copyright (c) 2009, 2018 R.Dvorak and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Radek Dvorak - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.m2m.qvt.oml.debug.core.vm; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.ArrayBlockingQueue; |
| import java.util.concurrent.BlockingQueue; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.debug.core.model.IValue; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEvaluationEnv; |
| import org.eclipse.m2m.qvt.oml.debug.core.DebugOptions; |
| import org.eclipse.m2m.qvt.oml.debug.core.QVTODebugCore; |
| import org.eclipse.m2m.qvt.oml.debug.core.QVTODebugTarget; |
| import org.eclipse.m2m.qvt.oml.debug.core.QVTOLocalValue; |
| import org.eclipse.m2m.qvt.oml.debug.core.QVTOLocalValue.LocalValue; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.BreakpointData; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.NewBreakpointData; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMBreakpointRequest; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMBreakpointRequest.ActionKind; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMBreakpointResponse; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMDetailRequest; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMDetailResponse; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMEvent; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMRequest; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMResponse; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMStackFrameRequest; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMStackFrameResponse; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMStartEvent; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMStartRequest; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMTerminateEvent; |
| import org.eclipse.m2m.qvt.oml.debug.core.vm.protocol.VMVariableRequest; |
| import org.eclipse.ocl.utilities.ASTNode; |
| |
| public class QVTOVirtualMachine implements IQVTOVirtualMachineShell { |
| |
| private final List<VMRequest> fRequests = new ArrayList<VMRequest>(); |
| private final BlockingQueue<VMEvent> fEvents = new ArrayBlockingQueue<VMEvent>(50); |
| |
| |
| private IQVTODebuggerShell fDebuggerShell; |
| |
| private final VMBreakpointManager fBreakpointManager; |
| private QVTODebugEvaluator fInterpreter; |
| private DebuggableExecutorAdapter fExecutor; |
| |
| private boolean fRunning; |
| private boolean fTerminated; |
| |
| private Object fStateMonitor = new Object(); |
| private final Object fLock = new Object(); |
| |
| |
| public QVTOVirtualMachine(DebuggableExecutorAdapter executorAdapter) { |
| if(executorAdapter == null) { |
| throw new IllegalArgumentException(); |
| } |
| |
| fExecutor = executorAdapter; |
| fDebuggerShell = new DebuggerShell(); |
| fBreakpointManager = new VMBreakpointManager(executorAdapter.getUnit()); |
| fTerminated = false; |
| } |
| |
| public boolean isTerminated() { |
| return fTerminated; |
| } |
| |
| public VMEvent readVMEvent() throws IOException { |
| try { |
| return fEvents.take(); |
| } catch(InterruptedException e) { |
| throw new IOException("Waiting for event interrupted"); |
| } |
| } |
| |
| public VMResponse sendRequest(VMRequest request) throws IOException { |
| try { |
| if(request instanceof VMStartRequest) { |
| return start(); |
| } else if(request instanceof VMBreakpointRequest) { |
| return handleBreakPointRequest((VMBreakpointRequest) request); |
| } else if(request instanceof VMStackFrameRequest) { |
| return handleStackFrameRequest((VMStackFrameRequest) request); |
| } else if(request instanceof VMVariableRequest) { |
| return handleVariableRequest((VMVariableRequest) request); |
| } else if(request instanceof VMDetailRequest) { |
| return handleValueDetailRequest((VMDetailRequest) request); |
| } |
| } catch (RuntimeException e) { |
| QVTODebugCore.log(e); |
| return VMResponse.createERROR(); |
| } |
| |
| synchronized (fLock) { |
| fRequests.add(request); |
| fLock.notifyAll(); |
| } |
| |
| return VMResponse.createOK(); |
| } |
| |
| public IValue evaluate(String expressionText, QVTODebugTarget debugTarget, long frameID) throws CoreException { |
| if (fInterpreter == null) { |
| return null; |
| } |
| |
| ASTNode astNode = fInterpreter.getCurrentLocation().getElement(); |
| if (astNode == null) { |
| return null; |
| } |
| |
| ConditionChecker localChecker = new ConditionChecker(expressionText, astNode); |
| LocalValue lv = new LocalValue(); |
| lv.valueObject = localChecker.evaluate(fInterpreter); |
| lv.valueType = localChecker.getConditionType(); |
| |
| return new QVTOLocalValue(debugTarget, frameID, new String[] {expressionText}, lv, |
| new UnitLocationExecutionContext( |
| fInterpreter.getEnvironment(), fInterpreter.getOperationalEvaluationEnv())); |
| } |
| |
| /** |
| * @since 1.1 |
| */ |
| public QvtOperationalEvaluationEnv getEvaluationEnv() { |
| if (fInterpreter == null) { |
| return null; |
| } |
| return fInterpreter.getOperationalEvaluationEnv(); |
| } |
| |
| private VMResponse start() { |
| Thread executorThread = new Thread(createVMRunnable()); |
| |
| synchronized (fStateMonitor) { |
| if(fRunning) { |
| return VMResponse.createERROR(); |
| } |
| |
| executorThread.start(); |
| |
| while(!(fRunning || fTerminated)) { |
| try { |
| fStateMonitor.wait(); |
| } catch (InterruptedException e) { |
| QVTODebugCore.log(QVTODebugCore.createStatus(IStatus.ERROR, |
| "VM startup process interrupted")); |
| } |
| } |
| } |
| |
| return VMResponse.createOK(); |
| } |
| |
| private VMResponse handleStackFrameRequest(VMStackFrameRequest request) { |
| if(fInterpreter != null) { |
| List<UnitLocation> locationStack = fInterpreter.getLocationStack(); |
| VMStackFrame frame = VMStackFrame.createFrame(request.frameID, locationStack); |
| VMStackFrameResponse response = new VMStackFrameResponse(frame); |
| if(!locationStack.isEmpty()) { |
| UnitLocation topLocation = locationStack.get(0); |
| response.isDeferredExecution = topLocation.isDeferredExecution(); |
| } |
| |
| return response; |
| } |
| |
| // FIXME |
| return VMResponse.createERROR(); |
| } |
| |
| private VMBreakpointResponse handleBreakPointRequest(VMBreakpointRequest request) { |
| ActionKind actionKind = request.getActionKind(); |
| |
| if(actionKind == VMBreakpointRequest.ActionKind.ADD) { |
| List<BreakpointData> allBpData = request.getBreakpointData(); |
| if(allBpData != null) { |
| List<Long> addedBpIDs = new ArrayList<Long>(); |
| for (BreakpointData bpData : allBpData) { |
| if(bpData instanceof NewBreakpointData == false) { |
| continue; |
| } |
| |
| NewBreakpointData newBreakpoint = (NewBreakpointData) bpData; |
| VMBreakpoint breakpoint = fBreakpointManager.createBreakpoint(newBreakpoint); |
| |
| if(breakpoint != null) { |
| addedBpIDs.add(new Long(newBreakpoint.ID)); |
| |
| QVTODebugCore.TRACE.trace(DebugOptions.VM, |
| "Installing breakpoing: " + " line:" //$NON-NLS-1$ //$NON-NLS-2$ |
| + newBreakpoint.line + " " //$NON-NLS-1$ |
| + newBreakpoint.targetURI); |
| } else { |
| QVTODebugCore.TRACE.trace(DebugOptions.VM, |
| "Failed to create breakpoing: " + " line:" //$NON-NLS-1$ //$NON-NLS-2$ |
| + newBreakpoint.line + " " //$NON-NLS-1$ |
| + newBreakpoint.targetURI); |
| } |
| } |
| |
| return new VMBreakpointResponse(addedBpIDs); |
| } |
| } else if(actionKind == VMBreakpointRequest.ActionKind.REMOVE) { |
| fBreakpointManager.removeBreakpoint(request.getBreakpointID()); |
| } else if(actionKind == VMBreakpointRequest.ActionKind.CHANGE) { |
| fBreakpointManager.changeBreakpoint(request.getBreakpointID(), request.getFirstBreakpointData()); |
| } |
| |
| // TODO |
| return new VMBreakpointResponse(); |
| } |
| |
| private VMResponse handleValueDetailRequest(VMDetailRequest request) { |
| // FIXME - ensure VM is in SUSPEND state, otherwise report fError |
| UnitLocationExecutionContext context = new UnitLocationExecutionContext( |
| fInterpreter.getEnvironment(), fInterpreter.getCurrentLocation().getEvalEnv()); |
| String detail = VariableFinder.computeDetail(request.getVariableURI(), context); |
| return new VMDetailResponse(detail != null ? detail : ""); //$NON-NLS-1$ |
| } |
| |
| private VMResponse handleVariableRequest(VMVariableRequest request) { |
| // FIXME - ensure VM is in SUSPEND state, otherwise report fError |
| UnitLocationExecutionContext context = new UnitLocationExecutionContext( |
| fInterpreter.getEnvironment(), fInterpreter.getCurrentLocation().getEvalEnv()); |
| return VariableFinder.process(request, fInterpreter.getLocationStack(), |
| context); |
| } |
| |
| private Runnable createVMRunnable() { |
| return new Runnable() { |
| public void run() { |
| int exitCode = -1; |
| try { |
| fExecutor.connect(fDebuggerShell); |
| exitCode = execute(fExecutor); |
| } catch(Throwable e) { |
| QVTODebugCore.log(e); |
| } finally { |
| fDebuggerShell.handleVMEvent(new VMTerminateEvent(exitCode)); |
| } |
| } |
| }; |
| } |
| |
| private static int execute(DebuggableExecutorAdapter executorAdapter) { |
| int exitCode = 0; |
| try { |
| Diagnostic diagnostic = executorAdapter.execute(); |
| int severity = diagnostic.getSeverity(); |
| if(severity == Diagnostic.ERROR || severity == Diagnostic.CANCEL) { |
| System.err.println(diagnostic.toString()); |
| exitCode = -1; |
| } |
| } catch (Throwable e) { |
| exitCode = -2; |
| // FIXME Auto-generated catch block |
| e.printStackTrace(); |
| } |
| |
| return exitCode; |
| } |
| |
| |
| private class DebuggerShell implements IQVTODebuggerShellExtension { |
| |
| public VMBreakpointManager getBreakPointManager() { |
| return QVTOVirtualMachine.this.fBreakpointManager; |
| } |
| |
| public void sessionStarted(QVTODebugEvaluator evaluator) { |
| fInterpreter = evaluator; |
| } |
| |
| public boolean isSessionStarted() { |
| return fInterpreter != null; |
| } |
| |
| public void handleVMEvent(VMEvent event) { |
| if(event instanceof VMStartEvent) { |
| // first start event |
| synchronized (fStateMonitor) { |
| fRunning = true; |
| fStateMonitor.notify(); |
| } |
| } else if(event instanceof VMTerminateEvent) { |
| synchronized (fStateMonitor) { |
| fRunning = false; |
| fTerminated = true; |
| fStateMonitor.notify(); |
| } |
| } |
| |
| try { |
| fEvents.add(event); |
| } catch(IllegalStateException e) { |
| // FIXME |
| System.err.println("Event queue full!!!!"); |
| } |
| } |
| |
| public VMRequest popRequest() { |
| synchronized (fLock) { |
| return fRequests.isEmpty() ? null : fRequests.remove(0); |
| } |
| } |
| |
| public VMRequest waitAndPopRequest(VMEvent suspend) throws InterruptedException { |
| // FIXME - should be locked to ensure none can really send a request until |
| // we deliver the event |
| handleVMEvent(suspend); |
| |
| synchronized (fLock) { |
| while(fRequests.isEmpty()) { |
| fLock.wait(); |
| } |
| return fRequests.remove(0); |
| } |
| } |
| |
| public VMRequest peekRequest() { |
| synchronized (fLock) { |
| return fRequests.isEmpty() ? null : fRequests.get(0); |
| } |
| } |
| } |
| |
| } |