1 /* Jdwp.java -- Virtual machine to JDWP back-end programming interface 2 Copyright (C) 2005 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.id.ThreadId; 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.HashMap; 55 56 /** 57 * Main interface from the virtual machine to the JDWP back-end. 58 * 59 * @author Keith Seitz (keiths@redhat.com) 60 */ 61 public class Jdwp 62 extends Thread 63 { 64 // The single instance of the back-end 65 private static Jdwp _instance = null; 66 67 /** 68 * Are we debugging? 69 */ 70 public static boolean isDebugging = false; 71 72 // Packet processor 73 private PacketProcessor _packetProcessor; 74 private Thread _ppThread; 75 76 // JDWP configuration properties 77 private HashMap _properties; 78 79 // The suspend property of the configure string 80 // (-Xrunjdwp:..suspend=<boolean>) 81 private static final String _PROPERTY_SUSPEND = "suspend"; 82 83 // User's main application thread 84 private Thread _mainThread; 85 86 // Connection to debugger 87 private JdwpConnection _connection; 88 89 // Are we shutting down the current session? 90 private boolean _shutdown; 91 92 // A thread group for the JDWP threads 93 private ThreadGroup _group; 94 95 /** 96 * constructor 97 */ Jdwp()98 public Jdwp () 99 { 100 _shutdown = false; 101 isDebugging = true; 102 _instance = this; 103 } 104 105 /** 106 * Returns the JDWP back-end, creating an instance of it 107 * if one does not already exist. 108 */ getDefault()109 public static Jdwp getDefault () 110 { 111 return _instance; 112 } 113 114 /** 115 * Should the virtual machine suspend on startup? 116 */ suspendOnStartup()117 public static boolean suspendOnStartup () 118 { 119 Jdwp jdwp = getDefault (); 120 if (jdwp != null) 121 { 122 String suspend = (String) jdwp._properties.get (_PROPERTY_SUSPEND); 123 if (suspend != null && suspend.equals ("y")) 124 return true; 125 } 126 127 return false; 128 } 129 130 /** 131 * Configures the back-end 132 * 133 * @param configArgs a string of configury options 134 * @param mainThread the main application thread 135 */ configure(String configArgs, Thread mainThread)136 public void configure (String configArgs, Thread mainThread) 137 { 138 _mainThread = mainThread; 139 _processConfigury (configArgs); 140 } 141 142 // A helper function to initialize the transport layer _doInitialization()143 private void _doInitialization () 144 throws TransportException 145 { 146 _group = new ThreadGroup ("JDWP threads"); 147 // initialize transport 148 ITransport transport = TransportFactory.newInstance (_properties); 149 _connection = new JdwpConnection (_group, transport); 150 _connection.initialize (); 151 _connection.start (); 152 153 // Create processor 154 _packetProcessor = new PacketProcessor (_connection); 155 _ppThread = new Thread (_group, new Runnable () 156 { 157 public void run () 158 { 159 AccessController.doPrivileged (_packetProcessor); 160 } 161 }); 162 _ppThread.start (); 163 } 164 165 /** 166 * Shutdown the JDWP back-end 167 * 168 * NOTE: This does not quite work properly. See notes in 169 * run() on this subject (catch of InterruptedException). 170 */ shutdown()171 public void shutdown () 172 { 173 if (!_shutdown) 174 { 175 _packetProcessor.shutdown (); 176 _ppThread.interrupt (); 177 _connection.shutdown (); 178 _shutdown = true; 179 isDebugging = false; 180 181 /* FIXME: probably need to check state of user's 182 program -- if it is suspended, we need to either 183 resume or kill them. */ 184 185 interrupt (); 186 } 187 } 188 189 /** 190 * Notify the debugger of an event. This method should not 191 * be called if debugging is not active (but it would not 192 * cause any harm). Places where event notifications occur 193 * should check isDebugging before doing anything. 194 * 195 * The event is filtered through the event manager before being 196 * sent. 197 * 198 * FIXME: Probably need logic to send multiple events 199 * @param event the event to report 200 */ notify(Event event)201 public static void notify (Event event) 202 { 203 Jdwp jdwp = getDefault (); 204 if (jdwp != null) 205 { 206 EventManager em = EventManager.getDefault (); 207 EventRequest request = em.getEventRequest (event); 208 if (request != null) 209 sendEvent (request, event); 210 } 211 } 212 213 /** 214 * Sends the event to the debugger. 215 * 216 * This method bypasses the event manager's filtering. 217 * 218 * @param request the debugger request for the event 219 * @param event the event to send 220 */ sendEvent(EventRequest request, Event event)221 public static void sendEvent (EventRequest request, Event event) 222 { 223 Jdwp jdwp = getDefault (); 224 if (jdwp != null) 225 { 226 try 227 { 228 // !! May need to implement send queue? 229 synchronized (jdwp._connection) 230 { 231 jdwp._connection.sendEvent (request, event); 232 } 233 234 // Follow suspend policy 235 jdwp._enforceSuspendPolicy (request.getSuspendPolicy ()); 236 } 237 catch (IOException ie) 238 { 239 System.out.println ("Jdwp.notify: caught exception: " + ie); 240 } 241 } 242 } 243 244 // Helper function to enforce suspend policies on event notification _enforceSuspendPolicy(byte suspendPolicy)245 private void _enforceSuspendPolicy (byte suspendPolicy) 246 { 247 switch (suspendPolicy) 248 { 249 case EventRequest.SUSPEND_NONE: 250 // do nothing 251 break; 252 253 case EventRequest.SUSPEND_THREAD: 254 VMVirtualMachine.suspendThread (this); 255 break; 256 257 case EventRequest.SUSPEND_ALL: 258 VMVirtualMachine.suspendAllThreads (); 259 break; 260 } 261 } 262 run()263 public void run () 264 { 265 try 266 { 267 _doInitialization (); 268 269 _mainThread.start (); 270 271 _mainThread.join (); 272 } 273 catch (InterruptedException ie) 274 { 275 /* Shutting down. If we're in server mode, we should 276 prepare for a new connection. Otherwise, we should 277 simply exit. */ 278 // FIXME 279 } 280 catch (Throwable t) 281 { 282 System.out.println ("Exception in JDWP back-end: " + t); 283 System.exit (1); 284 } 285 } 286 287 // A helper function to process the configure string "-Xrunjdwp:..." _processConfigury(String configString)288 private void _processConfigury (String configString) 289 { 290 // Loop through configuration arguments looking for a 291 // transport name 292 _properties = new HashMap (); 293 String[] options = configString.split (","); 294 for (int i = 0; i < options.length; ++i) 295 { 296 String[] property = options[i].split ("="); 297 if (property.length == 2) 298 _properties.put (property[0], property[1]); 299 // ignore malformed options 300 } 301 } 302 } 303