Source for gnu.classpath.jdwp.Jdwp

   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: }