Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 336   Methods: 21
NCLOC: 175   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
EventHandlerThread.java 0% 0% 0% 0%
coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.model.debug.jpda;
 38   
 39    import edu.rice.cs.util.Log;
 40   
 41    import com.sun.jdi.*;
 42    import com.sun.jdi.event.*;
 43    import com.sun.jdi.request.*;
 44   
 45    import java.io.*;
 46    import javax.swing.SwingUtilities; // used in instead of java.awt.EventQueue because of class name clash
 47   
 48    import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
 49    import edu.rice.cs.drjava.model.debug.DebugException;
 50    import edu.rice.cs.plt.tuple.Pair;
 51    import edu.rice.cs.util.UnexpectedException;
 52   
 53    /** A thread that listens and responds to events from JPDA when the debugger has attached to another JVM.
 54    * @version $Id: EventHandlerThread.java 5442 2011-08-16 09:11:12Z rcartwright $
 55    */
 56    public class EventHandlerThread extends Thread {
 57   
 58    /** Debugger to which this class reports events. */
 59    private final JPDADebugger _debugger;
 60   
 61    /** JPDA reference to the VirtualMachine generating the events. */
 62    private final VirtualMachine _vm;
 63   
 64    /** Whether this event handler is currently connected to the JPDA VirtualMachine. */
 65    private volatile boolean _connected;
 66   
 67    /** A log for recording messages in a file. */
 68    private static final Log _log = new Log("GlobalModel.txt", false);
 69   
 70    /** Creates a new EventHandlerThread to listen to events from the given debugger and virtual machine. Calling
 71    * this Thread's start() method causes it to begin listenting.
 72    * @param debugger Debugger to which to report events
 73    * @param vm JPDA reference to the VirtualMachine generating the events
 74    */
 75  0 EventHandlerThread(JPDADebugger debugger, VirtualMachine vm) {
 76  0 super("DrJava Debug Event Handler");
 77  0 _debugger = debugger;
 78  0 _vm = vm;
 79  0 _connected = true;
 80    }
 81   
 82    /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
 83    * @param message message to print to the log
 84    */
 85  0 private void _log(String message) { _log.log(message); }
 86   
 87    /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
 88    * @param message message to print to the log
 89    * @param t Exception or Error being logged
 90    */
 91  0 private void _log(String message, Throwable t) { _log.log(message, t); }
 92   
 93    /** Continually consumes events from the VM's event queue until it is disconnected.*/
 94  0 public void run() {
 95  0 _log.log("Debugger starting");
 96  0 _debugger.notifyDebuggerStarted();
 97   
 98  0 EventQueue queue = _vm.eventQueue();
 99  0 while (_connected) {
 100  0 try {
 101  0 try {
 102    // Remove and consume a set of events from the queue (blocks for an event)
 103  0 EventSet eventSet = queue.remove();
 104  0 EventIterator it = eventSet.eventIterator();
 105   
 106  0 while (it.hasNext()) handleEvent(it.nextEvent());
 107    }
 108    catch (InterruptedException ie) {
 109    // Don't need to do anything. If the VM was disconnected,
 110    // the loop will terminate.
 111  0 _log("InterruptedException in main loop: " + ie);
 112    }
 113    catch (VMDisconnectedException de) {
 114    // We expect this to happen if the other JVM is reset
 115  0 handleDisconnectedException();
 116  0 break;
 117    }
 118    }
 119    catch (Exception e) {
 120    // Log and report to the debugger
 121  0 _log("Exception in main event handler loop.", e);
 122  0 _debugger.eventHandlerError(e);
 123  0 _debugger.printMessage("An exception occurred in the event handler:\n" + e);
 124  0 _debugger.printMessage("The debugger may have become unstable as a result.");
 125  0 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 126  0 e.printStackTrace(new PrintWriter(baos, true));
 127  0 _debugger.printMessage("Stack trace: " + baos.toString());
 128    }
 129    }
 130   
 131  0 _debugger.notifyDebuggerShutdown();
 132    }
 133   
 134    /** Processes a given event from JPDA. A visitor approach would be much better for this, but Sun's Event class
 135    * doesn't have an appropriate visit() method.
 136    */
 137  0 private void handleEvent(Event e) throws DebugException {
 138    // Utilities.showDebug("EventHandler.handleEvent(" + e + ") called");
 139  0 _log("handling event: " + e);
 140   
 141  0 if (e instanceof BreakpointEvent) _handleBreakpointEvent((BreakpointEvent) e);
 142  0 else if (e instanceof StepEvent) _handleStepEvent((StepEvent) e);
 143    //else if (e instanceof ModificationWatchpointEvent) {
 144    // _handleModificationWatchpointEvent((ModificationWatchpointEvent) e);
 145    //}
 146  0 else if (e instanceof ClassPrepareEvent) _handleClassPrepareEvent((ClassPrepareEvent) e);
 147  0 else if (e instanceof ThreadStartEvent) _handleThreadStartEvent((ThreadStartEvent) e);
 148  0 else if (e instanceof ThreadDeathEvent) _handleThreadDeathEvent((ThreadDeathEvent) e);
 149  0 else if (e instanceof VMDeathEvent) _handleVMDeathEvent((VMDeathEvent) e);
 150  0 else if (e instanceof VMDisconnectEvent) _handleVMDisconnectEvent((VMDisconnectEvent) e);
 151    else
 152  0 throw new DebugException("Unexpected event type: " + e);
 153    }
 154   
 155    /** Returns whether the given thread is both suspended and has stack frames. */
 156  0 private boolean _isSuspendedWithFrames(ThreadReference thread) throws DebugException {
 157   
 158  0 try { return thread.isSuspended() && thread.frameCount() > 0; }
 159    catch (IncompatibleThreadStateException itse) {
 160  0 throw new DebugException("Could not count frames on a suspended thread: " + itse);
 161    }
 162    }
 163   
 164    /** Responds to a breakpoint event.
 165    * @param e breakpoint event from JPDA
 166    */
 167  0 private void _handleBreakpointEvent(final BreakpointEvent e) /* throws DebugException */ {
 168    // To ensure non-interference, run in Event Thread
 169  0 SwingUtilities.invokeLater(new Runnable() {
 170  0 public void run() {
 171    // System.err.println("handleBreakpointEvent(" + e + ") called");
 172  0 try {
 173  0 if (_isSuspendedWithFrames(e.thread()) && _debugger.setCurrentThread(e.thread())) {
 174    // Utilities.showDebug("EventHandlerThread._handleBreakpointEvent(" + e + ") called");
 175  0 _debugger.currThreadSuspended((BreakpointRequest) e.request());
 176  0 _debugger.reachedBreakpoint((BreakpointRequest) e.request());
 177    }
 178    }
 179  0 catch(DebugException e) { throw new UnexpectedException(e); }
 180    }
 181    });
 182    }
 183   
 184    /** Responds to a step event.
 185    * @param e step event from JPDA
 186    */
 187  0 private void _handleStepEvent(final StepEvent e) /* throws DebugException */ {
 188    /* Note: all synchronized methods have been removed from JPDADebugger except for locks on instances of the
 189    * private class RandomAccessStack. The non-interference policy is confinement to the Event thread. */
 190   
 191    // To ensure non-interference, run in Event thread.
 192  0 SwingUtilities.invokeLater(new Runnable() {
 193  0 public void run() {
 194  0 try {
 195  0 Pair<Location,OpenDefinitionsDocument> locAndDoc = _debugger.preloadDocument(e.location());
 196  0 Location lll = locAndDoc.first();
 197  0 if (_isSuspendedWithFrames(e.thread()) && _debugger.setCurrentThread(e.thread())) {
 198  0 _debugger.printMessage("Stepped to " + lll.declaringType().name() + "." + lll.method().name()
 199    + "(...) [line " + lll.lineNumber() + "]");
 200  0 _debugger.currThreadSuspended();
 201    }
 202    // Delete the step request so it doesn't happen again
 203  0 _debugger.getEventRequestManager().deleteEventRequest(e.request());
 204    }
 205  0 catch(DebugException e) { throw new UnexpectedException(e); }
 206    }
 207    });
 208    }
 209   
 210    // /** Responds to an event for a modified watchpoint.
 211    // * This event is not currently expected in DrJava.
 212    // * @param e modification watchpoint event from JPDA
 213    // */
 214    // private void _handleModificationWatchpointEvent(ModificationWatchpointEvent e) {
 215    // _debugger.printMessage("ModificationWatchpointEvent occured ");
 216    // _debugger.printMessage("Field: " + e.field() + " Value: " +
 217    // e.valueToBe() + "]");
 218    // }
 219   
 220    /** Responds when a class of interest has been prepared. Allows the debugger to set a pending breakpoint before any
 221    * code in the class is executed.
 222    * @param e class prepare event from JPDA
 223    */
 224  0 private void _handleClassPrepareEvent(final ClassPrepareEvent e) /* throws DebugException */ {
 225    // To ensure non-interference, run in Event thread
 226  0 SwingUtilities.invokeLater(new Runnable() {
 227  0 public void run() {
 228  0 try {
 229  0 _debugger.getPendingRequestManager().classPrepared(e);
 230    // resume this thread which was suspended because its suspend policy was SUSPEND_EVENT_THREAD
 231  0 e.thread().resume();
 232    }
 233  0 catch(DebugException e) { throw new UnexpectedException(e); }
 234    }
 235    });
 236    }
 237   
 238    /** Responds to a thread start event. Not run in event thread because threadStarted forces event thread execution.
 239    * @param e thread start event from JPDA
 240    */
 241  0 private void _handleThreadStartEvent(ThreadStartEvent e) {
 242    // To ensure non-interference, run in Event thread
 243  0 SwingUtilities.invokeLater(new Runnable() {
 244  0 public void run() { _debugger.threadStarted(); }
 245    });
 246    }
 247   
 248    /** Reponds to a thread death event.
 249    * @param e thread death event from JPDA
 250    */
 251  0 private void _handleThreadDeathEvent(final ThreadDeathEvent e) /* throws DebugException */ {
 252    // No need to check if there are suspended threads on the stack because all that logic should be in the debugger.
 253    // To ensure non-interference, run in Event thread
 254  0 SwingUtilities.invokeLater(new Runnable() {
 255  0 public void run() {
 256  0 try {
 257  0 ThreadReference running = _debugger.getCurrentRunningThread();
 258  0 if (e.thread().equals(running)) {
 259    // Delete any step requests pending on this thread
 260  0 EventRequestManager erm = _vm.eventRequestManager();
 261  0 for (StepRequest step : erm.stepRequests()) {
 262  0 if (step.thread().equals(e.thread())) {
 263  0 erm.deleteEventRequest(step);
 264   
 265    // There can only be one step request per thread,
 266    // so we can stop looking
 267  0 break;
 268    }
 269    }
 270  0 _debugger.currThreadDied();
 271    }
 272  0 else _debugger.nonCurrThreadDied();
 273   
 274    // Thread is suspended on death, so resume it now.
 275  0 e.thread().resume();
 276    }
 277  0 catch(DebugException e) { throw new UnexpectedException(e); }
 278    }
 279    });
 280    }
 281   
 282   
 283    /** Responds if the virtual machine being debugged dies.
 284    * @param e virtual machine death event from JPDA
 285    */
 286  0 private void _handleVMDeathEvent(VMDeathEvent e) throws DebugException { _cleanUp(e); }
 287   
 288    /** Responds if the virtual machine being debugged disconnects.
 289    * @param e virtual machine disconnect event from JPDA
 290    */
 291  0 private void _handleVMDisconnectEvent(VMDisconnectEvent e) throws DebugException { _cleanUp(e); }
 292   
 293    /** Cleans up the state after the virtual machine being debugged dies or disconnects.
 294    * @param e JPDA event indicating the debugging session has ended
 295    */
 296  0 private void _cleanUp(Event e) throws DebugException {
 297    // To ensure non-interference, run in Event thread.
 298  0 SwingUtilities.invokeLater(new Runnable() {
 299  0 public void run() {
 300  0 _connected = false;
 301  0 if (_debugger.isReady()) {
 302    // caused crash if "Run Document's Main Method" was invoked while debugging
 303    // if (_debugger.hasSuspendedThreads()) _debugger.currThreadDied();
 304  0 _debugger.shutdown();
 305    }
 306    }
 307    });
 308    }
 309   
 310    /** Responds when a VMDisconnectedException occurs while dealing with another event. We need to flush the event
 311    * queue, dealing only with exit events (VMDeath, VMDisconnect) so that we terminate correctly. */
 312  0 private void handleDisconnectedException() throws DebugException {
 313  0 EventQueue queue = _vm.eventQueue();
 314  0 while (_connected) {
 315  0 try {
 316  0 EventSet eventSet = queue.remove();
 317  0 EventIterator iter = eventSet.eventIterator();
 318  0 while (iter.hasNext()) {
 319  0 Event event = iter.nextEvent();
 320  0 if (event instanceof VMDeathEvent) _handleVMDeathEvent((VMDeathEvent)event);
 321  0 else if (event instanceof VMDisconnectEvent) _handleVMDisconnectEvent((VMDisconnectEvent)event);
 322    // else ignore the event
 323    }
 324  0 eventSet.resume(); // Resume the VM
 325    }
 326    catch (InterruptedException ie) {
 327    // ignore
 328  0 _log("InterruptedException after a disconnected exception.", ie);
 329    }
 330    catch (VMDisconnectedException de) {
 331    // try to continue flushing the event queue anyway
 332  0 _log("A second VMDisconnectedException.", de);
 333    }
 334    }
 335    }
 336    }