| /******************************************************************************* |
| * 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; |
| } |
| } |