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