1 /* VirtualMachineCommandSet.java -- class to implement the VirtualMachine
2    Command Set
3    Copyright (C) 2005 Free Software Foundation
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
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.processor;
41 
42 import gnu.classpath.jdwp.JdwpConstants;
43 import gnu.classpath.jdwp.VMVirtualMachine;
44 import gnu.classpath.jdwp.exception.JdwpException;
45 import gnu.classpath.jdwp.exception.JdwpInternalErrorException;
46 import gnu.classpath.jdwp.exception.NotImplementedException;
47 import gnu.classpath.jdwp.id.ObjectId;
48 import gnu.classpath.jdwp.id.ReferenceTypeId;
49 import gnu.classpath.jdwp.util.JdwpString;
50 import gnu.classpath.jdwp.util.Signature;
51 
52 import java.io.DataOutputStream;
53 import java.io.IOException;
54 import java.nio.ByteBuffer;
55 import java.util.ArrayList;
56 import java.util.Iterator;
57 import java.util.Properties;
58 
59 /**
60  * A class representing the VirtualMachine Command Set.
61  *
62  * @author Aaron Luchko <aluchko@redhat.com>
63  */
64 public class VirtualMachineCommandSet
65   extends CommandSet
66 {
runCommand(ByteBuffer bb, DataOutputStream os, byte command)67   public boolean runCommand(ByteBuffer bb, DataOutputStream os, byte command)
68     throws JdwpException
69   {
70     boolean shutdown = false;
71     try
72       {
73         switch (command)
74           {
75           case JdwpConstants.CommandSet.VirtualMachine.VERSION:
76             executeVersion(bb, os);
77             break;
78           case JdwpConstants.CommandSet.VirtualMachine.CLASSES_BY_SIGNATURE:
79             executeClassesBySignature(bb, os);
80             break;
81           case JdwpConstants.CommandSet.VirtualMachine.ALL_CLASSES:
82             executeAllClasses(bb, os);
83             break;
84           case JdwpConstants.CommandSet.VirtualMachine.ALL_THREADS:
85             executeAllThreads(bb, os);
86             break;
87           case JdwpConstants.CommandSet.VirtualMachine.TOP_LEVEL_THREAD_GROUPS:
88             executeTopLevelThreadGroups(bb, os);
89             break;
90           case JdwpConstants.CommandSet.VirtualMachine.IDSIZES:
91             executeIDsizes(bb, os);
92             break;
93           case JdwpConstants.CommandSet.VirtualMachine.DISPOSE:
94             shutdown = true;
95             executeDispose(bb, os);
96             break;
97           case JdwpConstants.CommandSet.VirtualMachine.SUSPEND:
98             executeSuspend(bb, os);
99             break;
100           case JdwpConstants.CommandSet.VirtualMachine.RESUME:
101             executeResume(bb, os);
102             break;
103           case JdwpConstants.CommandSet.VirtualMachine.EXIT:
104 	    shutdown = true;
105             executeExit(bb, os);
106             break;
107           case JdwpConstants.CommandSet.VirtualMachine.CREATE_STRING:
108             executeCreateString(bb, os);
109             break;
110           case JdwpConstants.CommandSet.VirtualMachine.CAPABILITIES:
111             executeCapabilities(bb, os);
112             break;
113           case JdwpConstants.CommandSet.VirtualMachine.CLASS_PATHS:
114             executeClassPaths(bb, os);
115             break;
116           case JdwpConstants.CommandSet.VirtualMachine.DISPOSE_OBJECTS:
117             executeDisposeObjects(bb, os);
118             break;
119           case JdwpConstants.CommandSet.VirtualMachine.HOLD_EVENTS:
120             executeHoldEvents(bb, os);
121             break;
122           case JdwpConstants.CommandSet.VirtualMachine.RELEASE_EVENTS:
123             executeReleaseEvents(bb, os);
124             break;
125           case JdwpConstants.CommandSet.VirtualMachine.CAPABILITIES_NEW:
126             executeCapabilitiesNew(bb, os);
127             break;
128           case JdwpConstants.CommandSet.VirtualMachine.REDEFINE_CLASSES:
129             executeRedefineClasses(bb, os);
130             break;
131           case JdwpConstants.CommandSet.VirtualMachine.SET_DEFAULT_STRATUM:
132             executeSetDefaultStratum(bb, os);
133             break;
134           case JdwpConstants.CommandSet.VirtualMachine.ALL_CLASSES_WITH_GENERIC:
135             executeAllClassesWithGeneric(bb, os);
136             break;
137           default:
138             throw new NotImplementedException("Command " + command +
139             " not found in VirtualMachine Command Set.");
140           }
141       }
142     catch (IOException ex)
143       {
144         // The DataOutputStream we're using isn't talking to a socket at all
145         // So if we throw an IOException we're in serious trouble
146         throw new JdwpInternalErrorException(ex);
147       }
148 
149     return shutdown;
150   }
151 
executeVersion(ByteBuffer bb, DataOutputStream os)152   private void executeVersion(ByteBuffer bb, DataOutputStream os)
153     throws JdwpException, IOException
154   {
155 
156     Properties props = System.getProperties();
157 
158     int jdwpMajor = JdwpConstants.Version.MAJOR;
159     int jdwpMinor = JdwpConstants.Version.MINOR;
160     // The description field is pretty loosely defined
161     String description = "JDWP version " + jdwpMajor + "." + jdwpMinor
162                          + ", JVM version " + props.getProperty("java.vm.name")
163                          + " " + props.getProperty("java.vm.version") + " "
164                          + props.getProperty("java.version");
165     String vmVersion = props.getProperty("java.version");
166     String vmName = props.getProperty("java.vm.name");
167     JdwpString.writeString(os, description);
168     os.writeInt(jdwpMajor);
169     os.writeInt(jdwpMinor);
170     JdwpString.writeString(os, vmName);
171     JdwpString.writeString(os, vmVersion);
172   }
173 
executeClassesBySignature(ByteBuffer bb, DataOutputStream os)174   private void executeClassesBySignature(ByteBuffer bb, DataOutputStream os)
175     throws JdwpException, IOException
176   {
177     String sig = JdwpString.readString(bb);
178     ArrayList allMatchingClasses = new ArrayList();
179 
180     // This will be an Iterator over all loaded Classes
181     Iterator iter = VMVirtualMachine.getAllLoadedClasses();
182 
183     while (iter.hasNext())
184       {
185         Class clazz = (Class) iter.next();
186         String clazzSig = Signature.computeClassSignature(clazz);
187         if (clazzSig.equals(sig))
188           allMatchingClasses.add(clazz);
189       }
190 
191     os.writeInt(allMatchingClasses.size());
192     for (int i = 0; i < allMatchingClasses.size(); i++)
193       {
194         Class clazz = (Class) allMatchingClasses.get(i);
195         ReferenceTypeId id = idMan.getReferenceTypeId(clazz);
196         id.writeTagged(os);
197         int status = VMVirtualMachine.getClassStatus(clazz);
198         os.writeInt(status);
199       }
200   }
201 
executeAllClasses(ByteBuffer bb, DataOutputStream os)202   private void executeAllClasses(ByteBuffer bb, DataOutputStream os)
203     throws JdwpException, IOException
204   {
205     // Disable garbage collection while we're collecting the info on loaded
206     // classes so we some classes don't get collected between the time we get
207     // the count and the time we get the list
208     //VMVirtualMachine.disableGarbageCollection();
209 
210     int classCount = VMVirtualMachine.getAllLoadedClassesCount();
211     os.writeInt(classCount);
212 
213     // This will be an Iterator over all loaded Classes
214     Iterator iter = VMVirtualMachine.getAllLoadedClasses();
215     //VMVirtualMachine.enableGarbageCollection();
216     int count = 0;
217 
218     // Note it's possible classes were created since out classCount so make
219     // sure we don't write more classes than we told the debugger
220     while (iter.hasNext() && count++ < classCount)
221       {
222         Class clazz = (Class) iter.next();
223         ReferenceTypeId id = idMan.getReferenceTypeId(clazz);
224         id.writeTagged(os);
225         String sig = Signature.computeClassSignature(clazz);
226         JdwpString.writeString(os, sig);
227         int status = VMVirtualMachine.getClassStatus(clazz);
228         os.writeInt(status);
229       }
230   }
231 
executeAllThreads(ByteBuffer bb, DataOutputStream os)232   private void executeAllThreads(ByteBuffer bb, DataOutputStream os)
233     throws JdwpException, IOException
234   {
235     ThreadGroup jdwpGroup = Thread.currentThread().getThreadGroup();
236     ThreadGroup root = getRootThreadGroup(jdwpGroup);
237 
238     int numThreads = root.activeCount();
239     Thread allThreads[] = new Thread[numThreads];
240     root.enumerate(allThreads);
241 
242     // We need to loop through for the true count since some threads may have
243     // been destroyed since we got
244     // activeCount so those spots in the array will be null. As well we must
245     // ignore any threads that belong to jdwp
246     numThreads = 0;
247     for (int i = 0; i < allThreads.length; i++)
248       {
249         Thread thread = allThreads[i];
250         if (thread == null)
251           break; // No threads after this point
252         if (!thread.getThreadGroup().equals(jdwpGroup))
253           numThreads++;
254       }
255 
256     os.writeInt(numThreads);
257 
258     for (int i = 0; i < allThreads.length; i++)
259       {
260         Thread thread = allThreads[i];
261         if (thread == null)
262           break; // No threads after this point
263         if (!thread.getThreadGroup().equals(jdwpGroup))
264           idMan.getObjectId(thread).write(os);
265       }
266   }
267 
executeTopLevelThreadGroups(ByteBuffer bb, DataOutputStream os)268   private void executeTopLevelThreadGroups(ByteBuffer bb, DataOutputStream os)
269     throws JdwpException, IOException
270   {
271     ThreadGroup jdwpGroup = Thread.currentThread().getThreadGroup ();
272     ThreadGroup root = getRootThreadGroup(jdwpGroup);
273 
274     os.writeInt(1); // Just one top level group allowed?
275     idMan.getObjectId(root);
276   }
277 
executeDispose(ByteBuffer bb, DataOutputStream os)278   private void executeDispose(ByteBuffer bb, DataOutputStream os)
279     throws JdwpException
280   {
281     // resumeAllThreads isn't sufficient as a thread may have been
282     // suspended multiple times, we likely need a way to keep track of how many
283     // times a thread has been suspended or else a stronger resume method for
284     // this purpose
285     // VMVirtualMachine.resumeAllThreads ();
286 
287     // Simply shutting down the jdwp layer will take care of the rest of the
288     // shutdown other than disabling debugging in the VM
289     // VMVirtualMachine.disableDebugging();
290 
291     // Don't implement this until we're sure how to remove all the debugging
292     // effects from the VM.
293     throw new NotImplementedException(
294       "Command VirtualMachine.Dispose not implemented");
295 
296   }
297 
executeIDsizes(ByteBuffer bb, DataOutputStream os)298   private void executeIDsizes(ByteBuffer bb, DataOutputStream os)
299     throws JdwpException, IOException
300   {
301     ObjectId oid = new ObjectId();
302     os.writeInt(oid.size()); // fieldId
303     os.writeInt(oid.size()); // methodId
304     os.writeInt(oid.size()); // objectId
305     os.writeInt(new ReferenceTypeId((byte) 0x00).size()); // referenceTypeId
306     os.writeInt(oid.size()); // frameId
307   }
308 
executeSuspend(ByteBuffer bb, DataOutputStream os)309   private void executeSuspend(ByteBuffer bb, DataOutputStream os)
310     throws JdwpException
311   {
312     VMVirtualMachine.suspendAllThreads ();
313   }
314 
executeResume(ByteBuffer bb, DataOutputStream os)315   private void executeResume(ByteBuffer bb, DataOutputStream os)
316     throws JdwpException
317   {
318     VMVirtualMachine.resumeAllThreads ();
319   }
320 
executeExit(ByteBuffer bb, DataOutputStream os)321   private void executeExit(ByteBuffer bb, DataOutputStream os)
322     throws JdwpException, IOException
323   {
324     int exitCode = bb.getInt();
325     System.exit (exitCode);
326   }
327 
executeCreateString(ByteBuffer bb, DataOutputStream os)328   private void executeCreateString(ByteBuffer bb, DataOutputStream os)
329     throws JdwpException, IOException
330   {
331     String string = JdwpString.readString(bb);
332     ObjectId stringId = idMan.getObjectId(string);
333 
334     // Since this string isn't referenced anywhere we'll disable garbage
335     // collection on it so it's still around when the debugger gets back to it.
336     stringId.disableCollection();
337     stringId.write(os);
338   }
339 
executeCapabilities(ByteBuffer bb, DataOutputStream os)340   private void executeCapabilities(ByteBuffer bb, DataOutputStream os)
341     throws JdwpException, IOException
342   {
343     // Store these somewhere?
344     os.writeBoolean(false); // canWatchFieldModification
345     os.writeBoolean(false); // canWatchFieldAccess
346     os.writeBoolean(false); // canGetBytecodes
347     os.writeBoolean(false); // canGetSyntheticAttribute
348     os.writeBoolean(false); // canGetOwnedMonitorInfo
349     os.writeBoolean(false); // canGetCurrentContendedMonitor
350     os.writeBoolean(false); // canGetMonitorInfo
351   }
352 
executeClassPaths(ByteBuffer bb, DataOutputStream os)353   private void executeClassPaths(ByteBuffer bb, DataOutputStream os)
354     throws JdwpException, IOException
355   {
356     String baseDir = System.getProperty("user.dir");
357     JdwpString.writeString(os, baseDir);
358 
359     // Find and write the classpath
360     String classPath = System.getProperty("java.class.path");
361     String[] paths = classPath.split(":");
362 
363     os.writeInt(paths.length);
364     for (int i = 0; i < paths.length; i++)
365       JdwpString.writeString(os, paths[i]);
366 
367     // Now the bootpath
368     String bootPath = System.getProperty("sun.boot.class.path");
369     paths = bootPath.split(":");
370     os.writeInt(paths.length);
371     for (int i = 0; i < paths.length; i++)
372       JdwpString.writeString(os, paths[i]);
373   }
374 
executeDisposeObjects(ByteBuffer bb, DataOutputStream os)375   private void executeDisposeObjects(ByteBuffer bb, DataOutputStream os)
376     throws JdwpException
377   {
378     // Instead of going through the list of objects they give us it's probably
379     // better just to find the garbage collected objects ourselves
380     //idMan.update();
381   }
382 
executeHoldEvents(ByteBuffer bb, DataOutputStream os)383   private void executeHoldEvents(ByteBuffer bb, DataOutputStream os)
384     throws JdwpException
385   {
386     // Going to have to implement a send queue somewhere and do this without
387     // triggering events
388     // Until then just don't implement
389     throw new NotImplementedException(
390       "Command VirtualMachine.HoldEvents not implemented");
391   }
392 
393   // Opposite of executeHoldEvents
executeReleaseEvents(ByteBuffer bb, DataOutputStream os)394   private void executeReleaseEvents(ByteBuffer bb, DataOutputStream os)
395     throws JdwpException
396   {
397     throw new NotImplementedException(
398       "Command VirtualMachine.ReleaseEvents not implemented");
399   }
400 
executeCapabilitiesNew(ByteBuffer bb, DataOutputStream os)401   private void executeCapabilitiesNew(ByteBuffer bb, DataOutputStream os)
402     throws JdwpException, IOException
403   {
404     // Store these somewhere?
405     final int CAPABILITIES_NEW_SIZE = 32;
406     os.writeBoolean(false); // canWatchFieldModification
407     os.writeBoolean(false); // canWatchFieldAccess
408     os.writeBoolean(false); // canGetBytecodes
409     os.writeBoolean(false); // canGetSyntheticAttribute
410     os.writeBoolean(false); // canGetOwnedMonitorInfo
411     os.writeBoolean(false); // canGetCurrentContendedMonitor
412     os.writeBoolean(false); // canGetMonitorInfo
413     os.writeBoolean(false); // canRedefineClasses
414     os.writeBoolean(false); // canAddMethod
415     os.writeBoolean(false); // canUnrestrictedlyRedefineClasses
416     os.writeBoolean(false); // canPopFrames
417     os.writeBoolean(false); // canUseInstanceFilters
418     os.writeBoolean(false); // canGetSourceDebugExtension
419     os.writeBoolean(false); // canRequestVMDeathEvent
420     os.writeBoolean(false); // canSetDefaultStratum
421     for (int i = 15; i < CAPABILITIES_NEW_SIZE; i++)
422       // Future capabilities
423       // currently unused
424       os.writeBoolean(false); // Set to false
425   }
426 
executeRedefineClasses(ByteBuffer bb, DataOutputStream os)427   private void executeRedefineClasses(ByteBuffer bb, DataOutputStream os)
428     throws JdwpException
429   {
430     // Optional command, don't implement
431     throw new NotImplementedException(
432       "Command VirtualMachine.RedefineClasses not implemented");
433   }
434 
executeSetDefaultStratum(ByteBuffer bb, DataOutputStream os)435   private void executeSetDefaultStratum(ByteBuffer bb, DataOutputStream os)
436     throws JdwpException
437   {
438     // Optional command, don't implement
439     throw new NotImplementedException(
440       "Command VirtualMachine.SetDefaultStratum not implemented");
441   }
442 
executeAllClassesWithGeneric(ByteBuffer bb, DataOutputStream os)443   private void executeAllClassesWithGeneric(ByteBuffer bb, DataOutputStream os)
444     throws JdwpException
445   {
446     // We don't handle generics
447     throw new NotImplementedException(
448       "Command VirtualMachine.AllClassesWithGeneric not implemented");
449   }
450 
451   /**
452    * Find the root ThreadGroup of this ThreadGroup
453    */
getRootThreadGroup(ThreadGroup group)454   private ThreadGroup getRootThreadGroup(ThreadGroup group)
455   {
456     ThreadGroup parent = group.getParent();
457 
458     while (parent != null)
459       {
460         group = parent;
461         parent = group.getParent();
462       }
463     return group; // This group was the root
464   }
465 }
466