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