Frames | No Frames |
1: /* Jdwp.java -- Virtual machine to JDWP back-end programming interface 2: Copyright (C) 2005, 2006, 2007 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: terms of your choice, provided that you also meet, for each linked 32: independent module, the terms and conditions of the license of that 33: module. An independent module is a module which is not derived from 34: or based on this library. If you modify this library, you may extend 35: this exception to your version of the library, but you are not 36: obligated to do so. If you do not wish to do so, delete this 37: exception statement from your version. */ 38: 39: 40: package gnu.classpath.jdwp; 41: 42: import gnu.classpath.jdwp.event.Event; 43: import gnu.classpath.jdwp.event.EventManager; 44: import gnu.classpath.jdwp.event.EventRequest; 45: import gnu.classpath.jdwp.exception.JdwpException; 46: import gnu.classpath.jdwp.processor.PacketProcessor; 47: import gnu.classpath.jdwp.transport.ITransport; 48: import gnu.classpath.jdwp.transport.JdwpConnection; 49: import gnu.classpath.jdwp.transport.TransportException; 50: import gnu.classpath.jdwp.transport.TransportFactory; 51: 52: import java.io.IOException; 53: import java.security.AccessController; 54: import java.util.ArrayList; 55: import java.util.HashMap; 56: 57: /** 58: * Main interface from the virtual machine to the JDWP back-end. 59: * 60: * The thread created by this class is only used for initialization. 61: * Once it exits, the JDWP backend is fully initialized. 62: * 63: * @author Keith Seitz (keiths@redhat.com) 64: */ 65: public class Jdwp 66: extends Thread 67: { 68: // The single instance of the back-end 69: private static Jdwp _instance = null; 70: 71: /** 72: * Are we debugging? Only true if debugging 73: * *and* initialized. 74: */ 75: public static boolean isDebugging = false; 76: 77: // Packet processor 78: private PacketProcessor _packetProcessor; 79: private Thread _ppThread; 80: 81: // JDWP configuration properties 82: private HashMap _properties; 83: 84: // The suspend property of the configure string 85: // (-Xrunjdwp:..suspend=<boolean>) 86: private static final String _PROPERTY_SUSPEND = "suspend"; 87: 88: // Connection to debugger 89: private JdwpConnection _connection; 90: 91: // Are we shutting down the current session? 92: private boolean _shutdown; 93: 94: // A thread group for the JDWP threads 95: private ThreadGroup _group; 96: 97: // Initialization synchronization 98: private Object _initLock = new Object (); 99: private int _initCount = 0; 100: 101: /** 102: * constructor 103: */ 104: public Jdwp () 105: { 106: _shutdown = false; 107: _instance = this; 108: } 109: 110: /** 111: * Returns the JDWP back-end, creating an instance of it 112: * if one does not already exist. 113: */ 114: public static Jdwp getDefault () 115: { 116: return _instance; 117: } 118: 119: /** 120: * Get the thread group used by JDWP threads 121: * 122: * @return the thread group 123: */ 124: public ThreadGroup getJdwpThreadGroup() 125: { 126: return _group; 127: } 128: 129: /** 130: * Should the virtual machine suspend on startup? 131: */ 132: public static boolean suspendOnStartup () 133: { 134: Jdwp jdwp = getDefault (); 135: if (jdwp != null) 136: { 137: String suspend = (String) jdwp._properties.get (_PROPERTY_SUSPEND); 138: if (suspend != null && suspend.equals ("y")) 139: return true; 140: } 141: 142: return false; 143: } 144: 145: /** 146: * Configures the back-end 147: * 148: * @param configArgs a string of configury options 149: */ 150: public void configure (String configArgs) 151: { 152: _processConfigury (configArgs); 153: } 154: 155: // A helper function to initialize the transport layer 156: private void _doInitialization () 157: throws TransportException 158: { 159: _group = new ThreadGroup ("JDWP threads"); 160: // initialize transport 161: ITransport transport = TransportFactory.newInstance (_properties); 162: _connection = new JdwpConnection (_group, transport); 163: _connection.initialize (); 164: _connection.start (); 165: 166: // Create processor 167: _packetProcessor = new PacketProcessor (_connection); 168: _ppThread = new Thread (_group, new Runnable () 169: { 170: public void run () 171: { 172: AccessController.doPrivileged (_packetProcessor); 173: } 174: }, "packet processor"); 175: _ppThread.start (); 176: } 177: 178: /** 179: * Shutdown the JDWP back-end 180: * 181: * NOTE: This does not quite work properly. See notes in 182: * run() on this subject (catch of InterruptedException). 183: */ 184: public void shutdown () 185: { 186: if (!_shutdown) 187: { 188: _packetProcessor.shutdown (); 189: _ppThread.interrupt (); 190: _connection.shutdown (); 191: _shutdown = true; 192: isDebugging = false; 193: 194: /* FIXME: probably need to check state of user's 195: program -- if it is suspended, we need to either 196: resume or kill them. */ 197: 198: interrupt (); 199: } 200: } 201: 202: /** 203: * Notify the debugger of an event. This method should not 204: * be called if debugging is not active (but it would not 205: * cause any harm). Places where event notifications occur 206: * should check isDebugging before doing anything. 207: * 208: * The event is filtered through the event manager before being 209: * sent. 210: * 211: * @param event the event to report 212: */ 213: public static void notify(Event event) 214: { 215: Jdwp jdwp = getDefault(); 216: if (jdwp != null) 217: { 218: EventManager em = EventManager.getDefault(); 219: EventRequest[] requests = em.getEventRequests(event); 220: for (int i = 0; i < requests.length; ++i) 221: { 222: try 223: { 224: sendEvent(requests[i], event); 225: jdwp._enforceSuspendPolicy(requests[i].getSuspendPolicy()); 226: } 227: catch (Exception e) 228: { 229: /* Really not much we can do. For now, just print out 230: a warning to the user. */ 231: System.out.println ("Jdwp.notify: caught exception: " + e); 232: } 233: } 234: } 235: } 236: 237: /** 238: * Notify the debugger of "co-located" events. This method should 239: * not be called if debugging is not active (but it would not 240: * cause any harm). Places where event notifications occur 241: * should check isDebugging before doing anything. 242: * 243: * The events are filtered through the event manager before being 244: * sent. 245: * 246: * @param events the events to report 247: */ 248: public static void notify(Event[] events) 249: { 250: Jdwp jdwp = getDefault(); 251: 252: if (jdwp != null) 253: { 254: byte suspendPolicy = JdwpConstants.SuspendPolicy.NONE; 255: EventManager em = EventManager.getDefault(); 256: ArrayList allEvents = new ArrayList (); 257: ArrayList allRequests = new ArrayList (); 258: for (int i = 0; i < events.length; ++i) 259: { 260: EventRequest[] r = em.getEventRequests(events[i]); 261: for (int j = 0; j < r.length; ++j) 262: { 263: /* This is hacky, but it's not clear whether this 264: can really happen, and if it does, what should 265: occur. */ 266: allEvents.add (events[i]); 267: allRequests.add (r[j]); 268: 269: // Perhaps this is overkill? 270: if (r[j].getSuspendPolicy() > suspendPolicy) 271: suspendPolicy = r[j].getSuspendPolicy(); 272: } 273: } 274: 275: try 276: { 277: Event[] e = new Event[allEvents.size()]; 278: allEvents.toArray(e); 279: EventRequest[] r = new EventRequest[allRequests.size()]; 280: allRequests.toArray(r); 281: sendEvents(r, e, suspendPolicy); 282: jdwp._enforceSuspendPolicy(suspendPolicy); 283: } 284: catch (Exception e) 285: { 286: /* Really not much we can do. For now, just print out 287: a warning to the user. */ 288: System.out.println ("Jdwp.notify: caught exception: " + e); 289: } 290: } 291: } 292: 293: /** 294: * Sends the event to the debugger. 295: * 296: * This method bypasses the event manager's filtering. 297: * 298: * @param request the debugger request for the event 299: * @param event the event to send 300: * @throws IOException if a communications failure occurs 301: */ 302: public static void sendEvent (EventRequest request, Event event) 303: throws IOException 304: { 305: sendEvents (new EventRequest[] { request }, new Event[] { event }, 306: request.getSuspendPolicy()); 307: } 308: 309: /** 310: * Sends the events to the debugger. 311: * 312: * This method bypasses the event manager's filtering. 313: * 314: * @param requests list of debugger requests for the events 315: * @param events the events to send 316: * @param suspendPolicy the suspendPolicy enforced by the VM 317: * @throws IOException if a communications failure occurs 318: */ 319: public static void sendEvents (EventRequest[] requests, Event[] events, 320: byte suspendPolicy) 321: throws IOException 322: { 323: Jdwp jdwp = getDefault(); 324: if (jdwp != null) 325: { 326: synchronized (jdwp._connection) 327: { 328: jdwp._connection.sendEvents (requests, events, suspendPolicy); 329: } 330: } 331: } 332: 333: // Helper function to enforce suspend policies on event notification 334: private void _enforceSuspendPolicy (byte suspendPolicy) 335: throws JdwpException 336: { 337: switch (suspendPolicy) 338: { 339: case EventRequest.SUSPEND_NONE: 340: // do nothing 341: break; 342: 343: case EventRequest.SUSPEND_THREAD: 344: VMVirtualMachine.suspendThread (Thread.currentThread ()); 345: break; 346: 347: case EventRequest.SUSPEND_ALL: 348: VMVirtualMachine.suspendAllThreads (); 349: break; 350: } 351: } 352: 353: /** 354: * Allows subcomponents to specify that they are 355: * initialized. 356: * 357: * Subcomponents include JdwpConnection and PacketProcessor. 358: */ 359: public void subcomponentInitialized () 360: { 361: synchronized (_initLock) 362: { 363: ++_initCount; 364: _initLock.notify (); 365: } 366: } 367: 368: public void run () 369: { 370: try 371: { 372: _doInitialization (); 373: 374: /* We need a little internal synchronization here, so that 375: when this thread dies, the back-end will be fully initialized, 376: ready to start servicing the VM and debugger. */ 377: synchronized (_initLock) 378: { 379: while (_initCount != 2) 380: _initLock.wait (); 381: } 382: _initLock = null; 383: } 384: catch (Throwable t) 385: { 386: System.out.println ("Exception in JDWP back-end: " + t); 387: System.exit (1); 388: } 389: 390: /* Force creation of the EventManager. If the event manager 391: has not been created when isDebugging is set, it is possible 392: that the VM will call Jdwp.notify (which uses EventManager) 393: while the EventManager is being created (or at least this is 394: a problem with gcj/gij). */ 395: EventManager.getDefault(); 396: 397: // Now we are finally ready and initialized 398: isDebugging = true; 399: } 400: 401: // A helper function to process the configure string "-Xrunjdwp:..." 402: private void _processConfigury (String configString) 403: { 404: // Loop through configuration arguments looking for a 405: // transport name 406: _properties = new HashMap (); 407: String[] options = configString.split (","); 408: for (int i = 0; i < options.length; ++i) 409: { 410: String[] property = options[i].split ("="); 411: if (property.length == 2) 412: _properties.put (property[0], property[1]); 413: // ignore malformed options 414: } 415: } 416: }