blob: 6df9fad766fcd44843908d9fe611982f02152688 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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
*******************************************************************************/
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.Vector;
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.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.core.model.IValue;
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;
/**
* 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;
// terminated state
private boolean fTerminated = false;
// threads
private IThread[] fThreads;
private PDAThread fThread;
// event dispatch job
private EventDispatchJob fEventDispatch;
// event listeners
private Vector fEventListeners = new Vector();
/**
* 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 event = "";
while (!isTerminated() && event != null) {
try {
event = fEventReader.readLine();
if (event != null) {
Object[] listeners = fEventListeners.toArray();
for (int i = 0; i < listeners.length; i++) {
((IPDAEventListener)listeners[i]).handleEvent(event);
}
}
} catch (IOException e) {
terminated();
}
}
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) {
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);
}
fThread = new PDAThread(this);
fThreads = new IThread[] {fThread};
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
sendRequest("eventstop unimpinstr 1");
sendRequest("eventstop nosuchlabel 1");
}
/* (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 {
return fThreads;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
*/
public boolean hasThreads() throws DebugException {
return true;
}
/* (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 boolean isTerminated() {
return fTerminated || getProcess().isTerminated();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
getThread().terminate();
}
/* (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 boolean isSuspended() {
return !isTerminated() && getThread().isSuspended();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#resume()
*/
public void resume() throws DebugException {
getThread().resume();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#suspend()
*/
public void suspend() throws DebugException {
getThread().suspend();
}
/* (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 started() {
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 synchronized void terminated() {
fTerminated = true;
fThread = null;
fThreads = new IThread[0];
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.removeBreakpointListener(this);
breakpointManager.removeBreakpointManagerListener(this);
fireTerminateEvent();
removeEventListener(this);
}
/**
* Returns the values on the data stack (top down)
*
* @return the values on the data stack (top down)
*/
public IValue[] getDataStack() throws DebugException {
String dataStack = sendRequest("data");
if (dataStack != null && dataStack.length() > 0) {
String[] values = dataStack.split("\\|");
IValue[] theValues = new IValue[values.length];
for (int i = 0; i < values.length; i++) {
String value = values[values.length - i - 1];
theValues[i] = new PDAStackValue(this, value, i);
}
return theValues;
}
return new IValue[0];
}
/* (non-Javadoc)
* @see org.eclipse.debug.examples.core.pda.model.PDADebugElement#sendRequest(java.lang.String)
*/
public String sendRequest(String request) throws DebugException {
synchronized (fRequestSocket) {
fRequestWriter.println(request);
fRequestWriter.flush();
try {
// wait for reply
return fRequestReader.readLine();
} catch (IOException e) {
requestFailed("Request failed: " + request, e);
}
}
return null;
}
/**
* 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);
}
}
}
/**
* Returns whether popping the data stack is currently permitted
*
* @return whether popping the data stack is currently permitted
*/
public boolean canPop() {
try {
return !isTerminated() && isSuspended() && getDataStack().length > 0;
} catch (DebugException e) {
}
return false;
}
/**
* Pops and returns the top of the data stack
*
* @return the top value on the stack
* @throws DebugException if the stack is empty or the request fails
*/
public IValue pop() throws DebugException {
IValue[] dataStack = getDataStack();
if (dataStack.length > 0) {
sendRequest("popdata");
return dataStack[0];
}
requestFailed("Empty stack", null);
return null;
}
/**
* Returns whether pushing a value is currently supported.
*
* @return whether pushing a value is currently supported
*/
public boolean canPush() {
return !isTerminated() && isSuspended();
}
/**
* Pushes a value onto the stack.
*
* @param value value to push
* @throws DebugException on failure
*/
public void push(String value) throws DebugException {
sendRequest("pushdata " + value);
}
/* (non-Javadoc)
* @see org.eclipse.debug.examples.core.pda.model.IPDAEventListener#handleEvent(java.lang.String)
*/
public void handleEvent(String event) {
if (event.equals("started")) {
started();
} else if (event.equals("terminated")) {
terminated();
}
}
/**
* Returns this debug target's single thread, or <code>null</code>
* if terminated.
*
* @return this debug target's single thread, or <code>null</code>
* if terminated
*/
public synchronized PDAThread getThread() {
return fThread;
}
}