Source for gnu.classpath.jdwp.processor.VirtualMachineCommandSet

   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: {
  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: 
 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: 
 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: 
 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: 
 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: 
 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: 
 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: 
 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: 
 300:   private void executeSuspend(ByteBuffer bb, DataOutputStream os)
 301:     throws JdwpException
 302:   {
 303:     VMVirtualMachine.suspendAllThreads ();
 304:   }
 305: 
 306:   private void executeResume(ByteBuffer bb, DataOutputStream os)
 307:     throws JdwpException
 308:   {
 309:     VMVirtualMachine.resumeAllThreads ();
 310:   }
 311: 
 312:   private void executeExit(ByteBuffer bb, DataOutputStream os)
 313:     throws JdwpException, IOException
 314:   {
 315:     int exitCode = bb.getInt();
 316:     System.exit (exitCode);
 317:   }
 318: 
 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: 
 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: 
 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: 
 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: 
 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
 384:   private void executeReleaseEvents(ByteBuffer bb, DataOutputStream os)
 385:     throws JdwpException
 386:   {
 387:     throw new NotImplementedException(
 388:       "Command VirtualMachine.ReleaseEvents not implemented");
 389:   }
 390: 
 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: 
 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: 
 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: 
 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:    */
 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: }