blob: 92a5a995597de22c6e38bf734367846f0d6a5df6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 IBM Corporation and others.
* 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Bjorn Freeman-Benson - initial API and implementation
* Pawel Piech (Wind River) - ported PDA Virtual Machine to Java (Bug 261400)
*******************************************************************************/
package org.eclipse.debug.examples.core.pda.model;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
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.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.IBreakpointManagerListener;
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.debug.examples.core.pda.DebugCorePlugin;
import org.eclipse.debug.examples.core.pda.breakpoints.PDALineBreakpoint;
import org.eclipse.debug.examples.core.pda.breakpoints.PDARunToLineBreakpoint;
import org.eclipse.debug.examples.core.pda.protocol.PDACommand;
import org.eclipse.debug.examples.core.pda.protocol.PDACommandResult;
import org.eclipse.debug.examples.core.pda.protocol.PDAEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDAEventStopCommand;
import org.eclipse.debug.examples.core.pda.protocol.PDAExitedEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDARestartCommand;
import org.eclipse.debug.examples.core.pda.protocol.PDAStartedEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDATerminateCommand;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMResumeCommand;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMResumedEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMStartedEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMSuspendCommand;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMSuspendedEvent;
import org.eclipse.debug.examples.core.pda.protocol.PDAVMTerminatedEvent;
/**
* PDA Debug Target
*/
public class PDADebugTarget extends PDADebugElement implements IDebugTarget, IBreakpointManagerListener, IPDAEventListener {
// associated system process (VM)
private IProcess fProcess;
// containing launch object
private ILaunch fLaunch;
// sockets to communicate with VM
private Socket fRequestSocket;
private PrintWriter fRequestWriter;
private BufferedReader fRequestReader;
private Socket fEventSocket;
private BufferedReader fEventReader;
// suspended state
private boolean fVMSuspended = false;
// terminated state
private boolean fTerminated = false;
// threads
private Map fThreads = Collections.synchronizedMap(new LinkedHashMap());
// event dispatch job
private EventDispatchJob fEventDispatch;
// event listeners
private List fEventListeners = Collections.synchronizedList(new ArrayList());
/**
* Listens to events from the PDA VM and fires corresponding
* debug events.
*/
class EventDispatchJob extends Job {
public EventDispatchJob() {
super("PDA Event Dispatch");
setSystem(true);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus run(IProgressMonitor monitor) {
String message = "";
while (!isTerminated() && message != null) {
try {
message = fEventReader.readLine();
if (message != null) {
PDAEvent event = null;
try {
event = PDAEvent.parseEvent(message);
}
catch (IllegalArgumentException e) {
DebugCorePlugin.getDefault().getLog().log(
new Status (IStatus.ERROR, "org.eclipse.debug.examples.core", "Error parsing PDA event", e));
continue;
}
Object[] listeners = fEventListeners.toArray();
for (int i = 0; i < listeners.length; i++) {
((IPDAEventListener)listeners[i]).handleEvent(event);
}
}
} catch (IOException e) {
vmTerminated();
}
}
return Status.OK_STATUS;
}
}
/**
* Registers the given event listener. The listener will be notified of
* events in the program being interpretted. Has no effect if the listener
* is already registered.
*
* @param listener event listener
*/
public void addEventListener(IPDAEventListener listener) {
synchronized(fEventListeners) {
if (!fEventListeners.contains(listener)) {
fEventListeners.add(listener);
}
}
}
/**
* Deregisters the given event listener. Has no effect if the listener is
* not currently registered.
*
* @param listener event listener
*/
public void removeEventListener(IPDAEventListener listener) {
fEventListeners.remove(listener);
}
/**
* Constructs a new debug target in the given launch for the
* associated PDA VM process.
*
* @param launch containing launch
* @param process PDA VM
* @param requestPort port to send requests to the VM
* @param eventPort port to read events from
* @exception CoreException if unable to connect to host
*/
public PDADebugTarget(ILaunch launch, IProcess process, int requestPort, int eventPort) throws CoreException {
super(null);
fLaunch = launch;
fProcess = process;
addEventListener(this);
try {
// give interpreter a chance to start
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
fRequestSocket = new Socket("localhost", requestPort);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// give interpreter a chance to open next socket
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
fEventSocket = new Socket("localhost", eventPort);
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
} catch (UnknownHostException e) {
requestFailed("Unable to connect to PDA VM", e);
} catch (IOException e) {
requestFailed("Unable to connect to PDA VM", e);
}
fEventDispatch = new EventDispatchJob();
fEventDispatch.schedule();
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.addBreakpointListener(this);
breakpointManager.addBreakpointManagerListener(this);
// initialize error hanlding to suspend on 'unimplemented instructions'
// and 'no such label' errors
sendCommand(new PDAEventStopCommand(PDAEventStopCommand.UNIMPINSTR, true));
sendCommand(new PDAEventStopCommand(PDAEventStopCommand.NOSUCHLABEL, true));
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
*/
public IProcess getProcess() {
return fProcess;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
*/
public IThread[] getThreads() throws DebugException {
synchronized (fThreads) {
return (IThread[])fThreads.values().toArray(new IThread[fThreads.size()]);
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
*/
public boolean hasThreads() throws DebugException {
return fThreads.size() > 0;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#getName()
*/
public String getName() throws DebugException {
return "PDA";
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
*/
public boolean supportsBreakpoint(IBreakpoint breakpoint) {
if (!isTerminated() && breakpoint.getModelIdentifier().equals(getModelIdentifier())) {
try {
String program = getLaunch().getLaunchConfiguration().getAttribute(DebugCorePlugin.ATTR_PDA_PROGRAM, (String)null);
if (program != null) {
IResource resource = null;
if (breakpoint instanceof PDARunToLineBreakpoint) {
PDARunToLineBreakpoint rtl = (PDARunToLineBreakpoint) breakpoint;
resource = rtl.getSourceFile();
} else {
IMarker marker = breakpoint.getMarker();
if (marker != null) {
resource = marker.getResource();
}
}
if (resource != null) {
IPath p = new Path(program);
return resource.getFullPath().equals(p);
}
}
} catch (CoreException e) {
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget()
*/
public IDebugTarget getDebugTarget() {
return this;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
*/
public ILaunch getLaunch() {
return fLaunch;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
public boolean canTerminate() {
return getProcess().canTerminate();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
public synchronized boolean isTerminated() {
return fTerminated || getProcess().isTerminated();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
//#ifdef ex2
//# // TODO: Exercise 2 - send termination request to interpreter
//#else
sendCommand(new PDATerminateCommand());
//#endif
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canResume()
*/
public boolean canResume() {
return !isTerminated() && isSuspended();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
*/
public boolean canSuspend() {
return !isTerminated() && !isSuspended();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
*/
public synchronized boolean isSuspended() {
return !isTerminated() && fVMSuspended;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#resume()
*/
public void resume() throws DebugException {
sendCommand(new PDAVMResumeCommand());
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#suspend()
*/
public void suspend() throws DebugException {
sendCommand(new PDAVMSuspendCommand());
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
*/
public void breakpointAdded(IBreakpoint breakpoint) {
if (supportsBreakpoint(breakpoint)) {
try {
if ((breakpoint.isEnabled() && getBreakpointManager().isEnabled()) || !breakpoint.isRegistered()) {
PDALineBreakpoint pdaBreakpoint = (PDALineBreakpoint)breakpoint;
pdaBreakpoint.install(this);
}
} catch (CoreException e) {
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
*/
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
if (supportsBreakpoint(breakpoint)) {
try {
PDALineBreakpoint pdaBreakpoint = (PDALineBreakpoint)breakpoint;
pdaBreakpoint.remove(this);
} catch (CoreException e) {
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
*/
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
if (supportsBreakpoint(breakpoint)) {
try {
if (breakpoint.isEnabled() && getBreakpointManager().isEnabled()) {
breakpointAdded(breakpoint);
} else {
breakpointRemoved(breakpoint, null);
}
} catch (CoreException e) {
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
*/
public boolean canDisconnect() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDisconnect#disconnect()
*/
public void disconnect() throws DebugException {
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
*/
public boolean isDisconnected() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
*/
public boolean supportsStorageRetrieval() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
*/
public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
return null;
}
/**
* Notification we have connected to the VM and it has started.
* Resume the VM.
*/
private void vmStarted(PDAVMStartedEvent event) {
fireCreationEvent();
installDeferredBreakpoints();
try {
resume();
} catch (DebugException e) {
}
}
/**
* Install breakpoints that are already registered with the breakpoint
* manager.
*/
private void installDeferredBreakpoints() {
IBreakpoint[] breakpoints = getBreakpointManager().getBreakpoints(getModelIdentifier());
for (int i = 0; i < breakpoints.length; i++) {
breakpointAdded(breakpoints[i]);
}
}
/**
* Called when this debug target terminates.
*/
private void vmTerminated() {
setTerminated(true);
fThreads.clear();
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.removeBreakpointListener(this);
breakpointManager.removeBreakpointManagerListener(this);
fireTerminateEvent();
removeEventListener(this);
}
private void vmResumed(PDAVMResumedEvent event) {
setVMSuspended(false);
fireResumeEvent(calcDetail(event.fReason));
}
private void vmSuspended(PDAVMSuspendedEvent event) {
setVMSuspended(true);
fireSuspendEvent(calcDetail(event.fReason));
}
private int calcDetail(String reason) {
if (reason.equals("breakpoint") || reason.equals("watch")) {
return DebugEvent.BREAKPOINT;
} else if (reason.equals("step")) {
return DebugEvent.STEP_OVER;
} else if (reason.equals("drop")) {
return DebugEvent.STEP_RETURN;
} else if (reason.equals("client")) {
return DebugEvent.CLIENT_REQUEST;
} else if (reason.equals("event")) {
return DebugEvent.BREAKPOINT;
} else {
return DebugEvent.UNSPECIFIED;
}
}
private void started(PDAStartedEvent event) {
PDAThread newThread = new PDAThread(this, event.fThreadId);
fThreads.put(new Integer(event.fThreadId), newThread);
newThread.start();
}
private void exited(PDAExitedEvent event) {
PDAThread thread = (PDAThread)fThreads.remove(new Integer(event.fThreadId));
if (thread != null) {
thread.exit();
}
}
private synchronized void setVMSuspended(boolean suspended) {
fVMSuspended = suspended;
}
private synchronized void setTerminated(boolean terminated) {
fTerminated = terminated;
}
/* (non-Javadoc)
* @see org.eclipse.debug.examples.core.pda.model.PDADebugElement#sendRequest(java.lang.String)
*/
private String sendRequest(String request) throws DebugException {
synchronized (fRequestSocket) {
fRequestWriter.println(request);
fRequestWriter.flush();
try {
// wait for reply
String retVal = fRequestReader.readLine();
if (retVal == null) {
requestFailed("Request failed: " + request + ". Debugger connection closed.", null);
}
return retVal;
} catch (IOException e) {
requestFailed("Request failed: " + request, e);
}
}
// Should never reach this satement.
return null;
}
public PDACommandResult sendCommand(PDACommand command) throws DebugException {
String response = sendRequest(command.getRequest());
return command.createResult(response);
}
/**
* When the breakpoint manager disables, remove all registered breakpoints
* requests from the VM. When it enables, reinstall them.
*/
public void breakpointManagerEnablementChanged(boolean enabled) {
IBreakpoint[] breakpoints = getBreakpointManager().getBreakpoints(getModelIdentifier());
for (int i = 0; i < breakpoints.length; i++) {
if (enabled) {
breakpointAdded(breakpoints[i]);
} else {
breakpointRemoved(breakpoints[i], null);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.examples.core.pda.model.IPDAEventListener#handleEvent(java.lang.String)
*/
public void handleEvent(PDAEvent event) {
if (event instanceof PDAStartedEvent) {
started((PDAStartedEvent)event);
} else if (event instanceof PDAExitedEvent) {
exited((PDAExitedEvent)event);
} else if (event instanceof PDAVMStartedEvent) {
vmStarted((PDAVMStartedEvent)event);
} else if (event instanceof PDAVMTerminatedEvent) {
vmTerminated();
} else if (event instanceof PDAVMSuspendedEvent) {
vmSuspended((PDAVMSuspendedEvent)event);
} else if (event instanceof PDAVMResumedEvent) {
vmResumed((PDAVMResumedEvent)event);
}
}
/**
* Returns this debug target's single thread, or <code>null</code>
* if terminated.
*
* @param threadId ID of the thread to return, or <code>0</code>
* to return the first available thread
* @return this debug target's single thread, or <code>null</code>
* if terminated
*/
public PDAThread getThread(int threadId) {
if (threadId > 0) {
return (PDAThread)fThreads.get(new Integer(threadId));
} else {
synchronized(fThreads) {
if (fThreads.size() > 0) {
return (PDAThread)fThreads.values().iterator().next();
}
}
}
return null;
}
/**
* Restarts the current debug session
*
* @throws DebugException
*/
public void restart() throws DebugException {
sendCommand(new PDARestartCommand());
}
}