Source for gnu.classpath.jdwp.processor.VirtualMachineCommandSet

   1: /* VirtualMachineCommandSet.java -- class to implement the VirtualMachine
   2:    Command Set
   3:    Copyright (C) 2005, 2006 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.Iterator;
  58: import java.util.Properties;
  59: 
  60: /**
  61:  * A class representing the VirtualMachine Command Set.
  62:  * 
  63:  * @author Aaron Luchko <aluchko@redhat.com>
  64:  */
  65: public class VirtualMachineCommandSet
  66:   extends CommandSet
  67: {
  68:   public boolean runCommand(ByteBuffer bb, DataOutputStream os, byte command)
  69:     throws JdwpException
  70:   {
  71:     boolean shutdown = false;
  72:     try
  73:       {
  74:         switch (command)
  75:           {
  76:           case JdwpConstants.CommandSet.VirtualMachine.VERSION:
  77:             executeVersion(bb, os);
  78:             break;
  79:           case JdwpConstants.CommandSet.VirtualMachine.CLASSES_BY_SIGNATURE:
  80:             executeClassesBySignature(bb, os);
  81:             break;
  82:           case JdwpConstants.CommandSet.VirtualMachine.ALL_CLASSES:
  83:             executeAllClasses(bb, os);
  84:             break;
  85:           case JdwpConstants.CommandSet.VirtualMachine.ALL_THREADS:
  86:             executeAllThreads(bb, os);
  87:             break;
  88:           case JdwpConstants.CommandSet.VirtualMachine.TOP_LEVEL_THREAD_GROUPS:
  89:             executeTopLevelThreadGroups(bb, os);
  90:             break;
  91:           case JdwpConstants.CommandSet.VirtualMachine.IDSIZES:
  92:             executeIDsizes(bb, os);
  93:             break;
  94:           case JdwpConstants.CommandSet.VirtualMachine.DISPOSE:
  95:             shutdown = true;
  96:             executeDispose(bb, os);
  97:             break;
  98:           case JdwpConstants.CommandSet.VirtualMachine.SUSPEND:
  99:             executeSuspend(bb, os);
 100:             break;
 101:           case JdwpConstants.CommandSet.VirtualMachine.RESUME:
 102:             executeResume(bb, os);
 103:             break;
 104:           case JdwpConstants.CommandSet.VirtualMachine.EXIT:
 105:         shutdown = true;
 106:             executeExit(bb, os);
 107:             break;
 108:           case JdwpConstants.CommandSet.VirtualMachine.CREATE_STRING:
 109:             executeCreateString(bb, os);
 110:             break;
 111:           case JdwpConstants.CommandSet.VirtualMachine.CAPABILITIES:
 112:             executeCapabilities(bb, os);
 113:             break;
 114:           case JdwpConstants.CommandSet.VirtualMachine.CLASS_PATHS:
 115:             executeClassPaths(bb, os);
 116:             break;
 117:           case JdwpConstants.CommandSet.VirtualMachine.DISPOSE_OBJECTS:
 118:             executeDisposeObjects(bb, os);
 119:             break;
 120:           case JdwpConstants.CommandSet.VirtualMachine.HOLD_EVENTS:
 121:             executeHoldEvents(bb, os);
 122:             break;
 123:           case JdwpConstants.CommandSet.VirtualMachine.RELEASE_EVENTS:
 124:             executeReleaseEvents(bb, os);
 125:             break;
 126:           case JdwpConstants.CommandSet.VirtualMachine.CAPABILITIES_NEW:
 127:             executeCapabilitiesNew(bb, os);
 128:             break;
 129:           case JdwpConstants.CommandSet.VirtualMachine.REDEFINE_CLASSES:
 130:             executeRedefineClasses(bb, os);
 131:             break;
 132:           case JdwpConstants.CommandSet.VirtualMachine.SET_DEFAULT_STRATUM:
 133:             executeSetDefaultStratum(bb, os);
 134:             break;
 135:           case JdwpConstants.CommandSet.VirtualMachine.ALL_CLASSES_WITH_GENERIC:
 136:             executeAllClassesWithGeneric(bb, os);
 137:             break;
 138:           default:
 139:             throw new NotImplementedException("Command " + command +
 140:             " not found in VirtualMachine Command Set.");
 141:           }
 142:       }
 143:     catch (IOException ex)
 144:       {
 145:         // The DataOutputStream we're using isn't talking to a socket at all
 146:         // So if we throw an IOException we're in serious trouble
 147:         throw new JdwpInternalErrorException(ex);
 148:       }
 149: 
 150:     return shutdown;
 151:   }
 152: 
 153:   private void executeVersion(ByteBuffer bb, DataOutputStream os)
 154:     throws JdwpException, IOException
 155:   {
 156: 
 157:     Properties props = System.getProperties();
 158: 
 159:     int jdwpMajor = JdwpConstants.Version.MAJOR;
 160:     int jdwpMinor = JdwpConstants.Version.MINOR;
 161:     // The description field is pretty loosely defined
 162:     String description = "JDWP version " + jdwpMajor + "." + jdwpMinor
 163:                          + ", JVM version " + props.getProperty("java.vm.name")
 164:                          + " " + props.getProperty("java.vm.version") + " "
 165:                          + props.getProperty("java.version");
 166:     String vmVersion = props.getProperty("java.version");
 167:     String vmName = props.getProperty("java.vm.name");
 168:     JdwpString.writeString(os, description);
 169:     os.writeInt(jdwpMajor);
 170:     os.writeInt(jdwpMinor);
 171:     JdwpString.writeString(os, vmName);
 172:     JdwpString.writeString(os, vmVersion);
 173:   }
 174: 
 175:   private void executeClassesBySignature(ByteBuffer bb, DataOutputStream os)
 176:     throws JdwpException, IOException
 177:   {
 178:     String sig = JdwpString.readString(bb);
 179:     ArrayList allMatchingClasses = new ArrayList();
 180: 
 181:     // This will be an Iterator over all loaded Classes
 182:     Iterator iter = VMVirtualMachine.getAllLoadedClasses();
 183: 
 184:     while (iter.hasNext())
 185:       {
 186:         Class clazz = (Class) iter.next();
 187:         String clazzSig = Signature.computeClassSignature(clazz);
 188:         if (clazzSig.equals(sig))
 189:           allMatchingClasses.add(clazz);
 190:       }
 191: 
 192:     os.writeInt(allMatchingClasses.size());
 193:     for (int i = 0; i < allMatchingClasses.size(); i++)
 194:       {
 195:         Class clazz = (Class) allMatchingClasses.get(i);
 196:         ReferenceTypeId id = idMan.getReferenceTypeId(clazz);
 197:         id.writeTagged(os);
 198:         int status = VMVirtualMachine.getClassStatus(clazz);
 199:         os.writeInt(status);
 200:       }
 201:   }
 202: 
 203:   private void executeAllClasses(ByteBuffer bb, DataOutputStream os)
 204:     throws JdwpException, IOException
 205:   {
 206:     // Disable garbage collection while we're collecting the info on loaded
 207:     // classes so we some classes don't get collected between the time we get
 208:     // the count and the time we get the list
 209:     //VMVirtualMachine.disableGarbageCollection();
 210: 
 211:     int classCount = VMVirtualMachine.getAllLoadedClassesCount();
 212:     os.writeInt(classCount);
 213: 
 214:     // This will be an Iterator over all loaded Classes
 215:     Iterator iter = VMVirtualMachine.getAllLoadedClasses();
 216:     //VMVirtualMachine.enableGarbageCollection();
 217:     int count = 0;
 218: 
 219:     // Note it's possible classes were created since out classCount so make
 220:     // sure we don't write more classes than we told the debugger
 221:     while (iter.hasNext() && count++ < classCount)
 222:       {
 223:         Class clazz = (Class) iter.next();
 224:         ReferenceTypeId id = idMan.getReferenceTypeId(clazz);
 225:         id.writeTagged(os);
 226:         String sig = Signature.computeClassSignature(clazz);
 227:         JdwpString.writeString(os, sig);
 228:         int status = VMVirtualMachine.getClassStatus(clazz);
 229:         os.writeInt(status);
 230:       }
 231:   }
 232: 
 233:   private void executeAllThreads(ByteBuffer bb, DataOutputStream os)
 234:     throws JdwpException, IOException
 235:   {
 236:     ThreadGroup jdwpGroup = Thread.currentThread().getThreadGroup();
 237:     ThreadGroup root = getRootThreadGroup(jdwpGroup);
 238: 
 239:     int numThreads = root.activeCount();
 240:     Thread allThreads[] = new Thread[numThreads];
 241:     root.enumerate(allThreads);
 242: 
 243:     // We need to loop through for the true count since some threads may have
 244:     // been destroyed since we got
 245:     // activeCount so those spots in the array will be null. As well we must
 246:     // ignore any threads that belong to jdwp
 247:     numThreads = 0;
 248:     for (int i = 0; i < allThreads.length; i++)
 249:       {
 250:         Thread thread = allThreads[i];
 251:         if (thread == null)
 252:           break; // No threads after this point
 253:         if (!thread.getThreadGroup().equals(jdwpGroup))
 254:           numThreads++;
 255:       }
 256: 
 257:     os.writeInt(numThreads);
 258: 
 259:     for (int i = 0; i < allThreads.length; i++)
 260:       {
 261:         Thread thread = allThreads[i];
 262:         if (thread == null)
 263:           break; // No threads after this point
 264:         if (!thread.getThreadGroup().equals(jdwpGroup))
 265:           idMan.getObjectId(thread).write(os);
 266:       }
 267:   }
 268: 
 269:   private void executeTopLevelThreadGroups(ByteBuffer bb, DataOutputStream os)
 270:     throws JdwpException, IOException
 271:   {
 272:     ThreadGroup jdwpGroup = Thread.currentThread().getThreadGroup ();
 273:     ThreadGroup root = getRootThreadGroup(jdwpGroup);
 274: 
 275:     os.writeInt(1); // Just one top level group allowed?
 276:     idMan.getObjectId(root);
 277:   }
 278: 
 279:   private void executeDispose(ByteBuffer bb, DataOutputStream os)
 280:     throws JdwpException
 281:   {
 282:     // resumeAllThreads isn't sufficient as a thread may have been
 283:     // suspended multiple times, we likely need a way to keep track of how many
 284:     // times a thread has been suspended or else a stronger resume method for
 285:     // this purpose
 286:     // VMVirtualMachine.resumeAllThreads ();
 287: 
 288:     // Simply shutting down the jdwp layer will take care of the rest of the
 289:     // shutdown other than disabling debugging in the VM
 290:     // VMVirtualMachine.disableDebugging();
 291: 
 292:     // Don't implement this until we're sure how to remove all the debugging
 293:     // effects from the VM.
 294:     throw new NotImplementedException(
 295:       "Command VirtualMachine.Dispose not implemented");
 296: 
 297:   }
 298: 
 299:   private void executeIDsizes(ByteBuffer bb, DataOutputStream os)
 300:     throws JdwpException, IOException
 301:   {
 302:     os.writeInt(ObjectId.SIZE); // fieldId FIXME
 303:     os.writeInt(ObjectId.SIZE); // methodId FIXME
 304:     os.writeInt(ObjectId.SIZE); // objectId
 305:     os.writeInt(ReferenceTypeId.SIZE); // referenceTypeId
 306:     os.writeInt(VMFrame.SIZE); // frameId
 307:   }
 308: 
 309:   private void executeSuspend(ByteBuffer bb, DataOutputStream os)
 310:     throws JdwpException
 311:   {
 312:     VMVirtualMachine.suspendAllThreads ();
 313:   }
 314: 
 315:   private void executeResume(ByteBuffer bb, DataOutputStream os)
 316:     throws JdwpException
 317:   {
 318:     VMVirtualMachine.resumeAllThreads ();
 319:   }
 320: 
 321:   private void executeExit(ByteBuffer bb, DataOutputStream os)
 322:     throws JdwpException, IOException
 323:   {
 324:     int exitCode = bb.getInt();
 325:     System.exit (exitCode);
 326:   }
 327: 
 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: 
 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: 
 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: 
 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: 
 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
 394:   private void executeReleaseEvents(ByteBuffer bb, DataOutputStream os)
 395:     throws JdwpException
 396:   {
 397:     throw new NotImplementedException(
 398:       "Command VirtualMachine.ReleaseEvents not implemented");
 399:   }
 400: 
 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: 
 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: 
 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: 
 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:    */
 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: }