1 /* 2 * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdi; 27 28 import com.sun.jdi.*; 29 import com.sun.jdi.connect.spi.Connection; 30 import com.sun.jdi.request.EventRequestManager; 31 import com.sun.jdi.request.EventRequest; 32 import com.sun.jdi.request.BreakpointRequest; 33 import com.sun.jdi.event.EventQueue; 34 35 import java.util.*; 36 import java.text.MessageFormat; 37 import java.lang.ref.ReferenceQueue; 38 import java.lang.ref.Reference; 39 import java.lang.ref.SoftReference; 40 import java.lang.ref.WeakReference; 41 42 class VirtualMachineImpl extends MirrorImpl 43 implements PathSearchingVirtualMachine, ThreadListener { 44 // VM Level exported variables, these 45 // are unique to a given vm 46 public final int sizeofFieldRef; 47 public final int sizeofMethodRef; 48 public final int sizeofObjectRef; 49 public final int sizeofClassRef; 50 public final int sizeofFrameRef; 51 52 final int sequenceNumber; 53 54 private final TargetVM target; 55 private final EventQueueImpl eventQueue; 56 private final EventRequestManagerImpl internalEventRequestManager; 57 private final EventRequestManagerImpl eventRequestManager; 58 final VirtualMachineManagerImpl vmManager; 59 private final ThreadGroup threadGroupForJDI; 60 61 // Allow direct access to this field so that that tracing code slows down 62 // JDI as little as possible when not enabled. 63 int traceFlags = TRACE_NONE; 64 65 static int TRACE_RAW_SENDS = 0x01000000; 66 static int TRACE_RAW_RECEIVES = 0x02000000; 67 68 boolean traceReceives = false; // pre-compute because of frequency 69 70 // ReferenceType access - updated with class prepare and unload events 71 // Protected by "synchronized(this)". "retrievedAllTypes" may be 72 // tested unsynchronized (since once true, it stays true), but must 73 // be set synchronously 74 private Map<Long, ReferenceType> typesByID; 75 private TreeSet<ReferenceType> typesBySignature; 76 private boolean retrievedAllTypes = false; 77 78 // For other languages support 79 private String defaultStratum = null; 80 81 // ObjectReference cache 82 // "objectsByID" protected by "synchronized(this)". 83 private final Map<Long, SoftObjectReference> objectsByID = new HashMap<Long, SoftObjectReference>(); 84 private final ReferenceQueue<ObjectReferenceImpl> referenceQueue = new ReferenceQueue<ObjectReferenceImpl>(); 85 static private final int DISPOSE_THRESHOLD = 50; 86 private final List<SoftObjectReference> batchedDisposeRequests = 87 Collections.synchronizedList(new ArrayList<SoftObjectReference>(DISPOSE_THRESHOLD + 10)); 88 89 // These are cached once for the life of the VM 90 private JDWP.VirtualMachine.Version versionInfo; 91 private JDWP.VirtualMachine.ClassPaths pathInfo; 92 private JDWP.VirtualMachine.Capabilities capabilities = null; 93 private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew = null; 94 95 // Per-vm singletons for primitive types and for void. 96 // singleton-ness protected by "synchronized(this)". 97 private BooleanType theBooleanType; 98 private ByteType theByteType; 99 private CharType theCharType; 100 private ShortType theShortType; 101 private IntegerType theIntegerType; 102 private LongType theLongType; 103 private FloatType theFloatType; 104 private DoubleType theDoubleType; 105 106 private VoidType theVoidType; 107 108 private VoidValue voidVal; 109 110 // Launched debuggee process 111 private Process process; 112 113 // coordinates state changes and corresponding listener notifications 114 private VMState state = new VMState(this); 115 116 private Object initMonitor = new Object(); 117 private boolean initComplete = false; 118 private boolean shutdown = false; 119 120 private void notifyInitCompletion() { 121 synchronized(initMonitor) { 122 initComplete = true; 123 initMonitor.notifyAll(); 124 } 125 } 126 127 void waitInitCompletion() { 128 synchronized(initMonitor) { 129 while (!initComplete) { 130 try { 131 initMonitor.wait(); 132 } catch (InterruptedException e) { 133 // ignore 134 } 135 } 136 } 137 } 138 139 VMState state() { 140 return state; 141 } 142 143 /* 144 * ThreadListener implementation 145 */ 146 public boolean threadResumable(ThreadAction action) { 147 /* 148 * If any thread is resumed, the VM is considered not suspended. 149 * Just one thread is being resumed so pass it to thaw. 150 */ 151 state.thaw(action.thread()); 152 return true; 153 } 154 155 VirtualMachineImpl(VirtualMachineManager manager, 156 Connection connection, Process process, 157 int sequenceNumber) { 158 super(null); // Can't use super(this) 159 vm = this; 160 161 this.vmManager = (VirtualMachineManagerImpl)manager; 162 this.process = process; 163 this.sequenceNumber = sequenceNumber; 164 165 /* Create ThreadGroup to be used by all threads servicing 166 * this VM. 167 */ 168 threadGroupForJDI = new ThreadGroup(vmManager.mainGroupForJDI(), 169 "JDI [" + 170 this.hashCode() + "]"); 171 172 /* 173 * Set up a thread to communicate with the target VM over 174 * the specified transport. 175 */ 176 target = new TargetVM(this, connection); 177 178 /* 179 * Set up a thread to handle events processed internally 180 * the JDI implementation. 181 */ 182 EventQueueImpl internalEventQueue = new EventQueueImpl(this, target); 183 new InternalEventHandler(this, internalEventQueue); 184 /* 185 * Initialize client access to event setting and handling 186 */ 187 eventQueue = new EventQueueImpl(this, target); 188 eventRequestManager = new EventRequestManagerImpl(this); 189 190 target.start(); 191 192 /* 193 * Many ids are variably sized, depending on target VM. 194 * Find out the sizes right away. 195 */ 196 JDWP.VirtualMachine.IDSizes idSizes; 197 try { 198 idSizes = JDWP.VirtualMachine.IDSizes.process(vm); 199 } catch (JDWPException exc) { 200 throw exc.toJDIException(); 201 } 202 sizeofFieldRef = idSizes.fieldIDSize; 203 sizeofMethodRef = idSizes.methodIDSize; 204 sizeofObjectRef = idSizes.objectIDSize; 205 sizeofClassRef = idSizes.referenceTypeIDSize; 206 sizeofFrameRef = idSizes.frameIDSize; 207 208 /** 209 * Set up requests needed by internal event handler. 210 * Make sure they are distinguished by creating them with 211 * an internal event request manager. 212 * 213 * Warning: create events only with SUSPEND_NONE policy. 214 * In the current implementation other policies will not 215 * be handled correctly when the event comes in. (notfiySuspend() 216 * will not be properly called, and if the event is combined 217 * with external events in the same set, suspend policy is not 218 * correctly determined for the internal vs. external event sets) 219 */ 220 internalEventRequestManager = new EventRequestManagerImpl(this); 221 EventRequest er = internalEventRequestManager.createClassPrepareRequest(); 222 er.setSuspendPolicy(EventRequest.SUSPEND_NONE); 223 er.enable(); 224 er = internalEventRequestManager.createClassUnloadRequest(); 225 er.setSuspendPolicy(EventRequest.SUSPEND_NONE); 226 er.enable(); 227 228 /* 229 * Tell other threads, notably TargetVM, that initialization 230 * is complete. 231 */ 232 notifyInitCompletion(); 233 } 234 235 EventRequestManagerImpl getInternalEventRequestManager() { 236 return internalEventRequestManager; 237 } 238 239 void validateVM() { 240 /* 241 * We no longer need to do this. The spec now says 242 * that a VMDisconnected _may_ be thrown in these 243 * cases, not that it _will_ be thrown. 244 * So, to simplify things we will just let the 245 * caller's of this method proceed with their business. 246 * If the debuggee is disconnected, either because it 247 * crashed or finished or something, or because the 248 * debugger called exit() or dispose(), then if 249 * we end up trying to communicate with the debuggee, 250 * code in TargetVM will throw a VMDisconnectedException. 251 * This means that if we can satisfy a request without 252 * talking to the debuggee, (eg, with cached data) then 253 * VMDisconnectedException will _not_ be thrown. 254 * if (shutdown) { 255 * throw new VMDisconnectedException(); 256 * } 257 */ 258 } 259 260 public boolean equals(Object obj) { 261 return this == obj; 262 } 263 264 public int hashCode() { 265 return System.identityHashCode(this); 266 } 267 268 public List<ReferenceType> classesByName(String className) { 269 validateVM(); 270 String signature = JNITypeParser.typeNameToSignature(className); 271 List<ReferenceType> list; 272 if (retrievedAllTypes) { 273 list = findReferenceTypes(signature); 274 } else { 275 list = retrieveClassesBySignature(signature); 276 } 277 return Collections.unmodifiableList(list); 278 } 279 280 public List<ReferenceType> allClasses() { 281 validateVM(); 282 283 if (!retrievedAllTypes) { 284 retrieveAllClasses(); 285 } 286 ArrayList<ReferenceType> a; 287 synchronized (this) { 288 a = new ArrayList<ReferenceType>(typesBySignature); 289 } 290 return Collections.unmodifiableList(a); 291 } 292 293 public void 294 redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes) 295 { 296 int cnt = classToBytes.size(); 297 JDWP.VirtualMachine.RedefineClasses.ClassDef[] defs = 298 new JDWP.VirtualMachine.RedefineClasses.ClassDef[cnt]; 299 validateVM(); 300 if (!canRedefineClasses()) { 301 throw new UnsupportedOperationException(); 302 } 303 Iterator<?> it = classToBytes.entrySet().iterator(); 304 for (int i = 0; it.hasNext(); i++) { 305 Map.Entry<?,?> entry = (Map.Entry)it.next(); 306 ReferenceTypeImpl refType = (ReferenceTypeImpl)entry.getKey(); 307 validateMirror(refType); 308 defs[i] = new JDWP.VirtualMachine.RedefineClasses 309 .ClassDef(refType, (byte[])entry.getValue()); 310 } 311 312 // flush caches and disable caching until the next suspend 313 vm.state().thaw(); 314 315 try { 316 JDWP.VirtualMachine.RedefineClasses. 317 process(vm, defs); 318 } catch (JDWPException exc) { 319 switch (exc.errorCode()) { 320 case JDWP.Error.INVALID_CLASS_FORMAT : 321 throw new ClassFormatError( 322 "class not in class file format"); 323 case JDWP.Error.CIRCULAR_CLASS_DEFINITION : 324 throw new ClassCircularityError( 325 "circularity has been detected while initializing a class"); 326 case JDWP.Error.FAILS_VERIFICATION : 327 throw new VerifyError( 328 "verifier detected internal inconsistency or security problem"); 329 case JDWP.Error.UNSUPPORTED_VERSION : 330 throw new UnsupportedClassVersionError( 331 "version numbers of class are not supported"); 332 case JDWP.Error.ADD_METHOD_NOT_IMPLEMENTED: 333 throw new UnsupportedOperationException( 334 "add method not implemented"); 335 case JDWP.Error.SCHEMA_CHANGE_NOT_IMPLEMENTED : 336 throw new UnsupportedOperationException( 337 "schema change not implemented"); 338 case JDWP.Error.HIERARCHY_CHANGE_NOT_IMPLEMENTED: 339 throw new UnsupportedOperationException( 340 "hierarchy change not implemented"); 341 case JDWP.Error.DELETE_METHOD_NOT_IMPLEMENTED : 342 throw new UnsupportedOperationException( 343 "delete method not implemented"); 344 case JDWP.Error.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED: 345 throw new UnsupportedOperationException( 346 "changes to class modifiers not implemented"); 347 case JDWP.Error.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED : 348 throw new UnsupportedOperationException( 349 "changes to method modifiers not implemented"); 350 case JDWP.Error.NAMES_DONT_MATCH : 351 throw new NoClassDefFoundError( 352 "class names do not match"); 353 default: 354 throw exc.toJDIException(); 355 } 356 } 357 358 // Delete any record of the breakpoints 359 List<BreakpointRequest> toDelete = new ArrayList<BreakpointRequest>(); 360 EventRequestManager erm = eventRequestManager(); 361 it = erm.breakpointRequests().iterator(); 362 while (it.hasNext()) { 363 BreakpointRequest req = (BreakpointRequest)it.next(); 364 if (classToBytes.containsKey(req.location().declaringType())) { 365 toDelete.add(req); 366 } 367 } 368 erm.deleteEventRequests(toDelete); 369 370 // Invalidate any information cached for the classes just redefined. 371 it = classToBytes.keySet().iterator(); 372 while (it.hasNext()) { 373 ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next(); 374 rti.noticeRedefineClass(); 375 } 376 } 377 378 public List<ThreadReference> allThreads() { 379 validateVM(); 380 return state.allThreads(); 381 } 382 383 public List<ThreadGroupReference> topLevelThreadGroups() { 384 validateVM(); 385 return state.topLevelThreadGroups(); 386 } 387 388 /* 389 * Sends a command to the back end which is defined to do an 390 * implicit vm-wide resume. The VM can no longer be considered 391 * suspended, so certain cached data must be invalidated. 392 */ 393 PacketStream sendResumingCommand(CommandSender sender) { 394 return state.thawCommand(sender); 395 } 396 397 /* 398 * The VM has been suspended. Additional caching can be done 399 * as long as there are no pending resumes. 400 */ 401 void notifySuspend() { 402 state.freeze(); 403 } 404 405 public void suspend() { 406 validateVM(); 407 try { 408 JDWP.VirtualMachine.Suspend.process(vm); 409 } catch (JDWPException exc) { 410 throw exc.toJDIException(); 411 } 412 notifySuspend(); 413 } 414 415 public void resume() { 416 validateVM(); 417 CommandSender sender = 418 new CommandSender() { 419 public PacketStream send() { 420 return JDWP.VirtualMachine.Resume.enqueueCommand(vm); 421 } 422 }; 423 try { 424 PacketStream stream = state.thawCommand(sender); 425 JDWP.VirtualMachine.Resume.waitForReply(vm, stream); 426 } catch (VMDisconnectedException exc) { 427 /* 428 * If the debugger makes a VMDeathRequest with SUSPEND_ALL, 429 * then when it does an EventSet.resume after getting the 430 * VMDeathEvent, the normal flow of events is that the 431 * BE shuts down, but the waitForReply comes back ok. In this 432 * case, the run loop in TargetVM that is waiting for a packet 433 * gets an EOF because the socket closes. It generates a 434 * VMDisconnectedEvent and everyone is happy. 435 * However, sometimes, the BE gets shutdown before this 436 * waitForReply completes. In this case, TargetVM.waitForReply 437 * gets awakened with no reply and so gens a VMDisconnectedException 438 * which is not what we want. It might be possible to fix this 439 * in the BE, but it is ok to just ignore the VMDisconnectedException 440 * here. This will allow the VMDisconnectedEvent to be generated 441 * correctly. And, if the debugger should happen to make another 442 * request, it will get a VMDisconnectedException at that time. 443 */ 444 } catch (JDWPException exc) { 445 switch (exc.errorCode()) { 446 case JDWP.Error.VM_DEAD: 447 return; 448 default: 449 throw exc.toJDIException(); 450 } 451 } 452 } 453 454 public EventQueue eventQueue() { 455 /* 456 * No VM validation here. We allow access to the event queue 457 * after disconnection, so that there is access to the terminating 458 * events. 459 */ 460 return eventQueue; 461 } 462 463 public EventRequestManager eventRequestManager() { 464 validateVM(); 465 return eventRequestManager; 466 } 467 468 EventRequestManagerImpl eventRequestManagerImpl() { 469 return eventRequestManager; 470 } 471 472 public BooleanValue mirrorOf(boolean value) { 473 validateVM(); 474 return new BooleanValueImpl(this,value); 475 } 476 477 public ByteValue mirrorOf(byte value) { 478 validateVM(); 479 return new ByteValueImpl(this,value); 480 } 481 482 public CharValue mirrorOf(char value) { 483 validateVM(); 484 return new CharValueImpl(this,value); 485 } 486 487 public ShortValue mirrorOf(short value) { 488 validateVM(); 489 return new ShortValueImpl(this,value); 490 } 491 492 public IntegerValue mirrorOf(int value) { 493 validateVM(); 494 return new IntegerValueImpl(this,value); 495 } 496 497 public LongValue mirrorOf(long value) { 498 validateVM(); 499 return new LongValueImpl(this,value); 500 } 501 502 public FloatValue mirrorOf(float value) { 503 validateVM(); 504 return new FloatValueImpl(this,value); 505 } 506 507 public DoubleValue mirrorOf(double value) { 508 validateVM(); 509 return new DoubleValueImpl(this,value); 510 } 511 512 public StringReference mirrorOf(String value) { 513 validateVM(); 514 try { 515 return (StringReference)JDWP.VirtualMachine.CreateString. 516 process(vm, value).stringObject; 517 } catch (JDWPException exc) { 518 throw exc.toJDIException(); 519 } 520 } 521 522 public VoidValue mirrorOfVoid() { 523 if (voidVal == null) { 524 voidVal = new VoidValueImpl(this); 525 } 526 return voidVal; 527 } 528 529 public long[] instanceCounts(List<? extends ReferenceType> classes) { 530 if (!canGetInstanceInfo()) { 531 throw new UnsupportedOperationException( 532 "target does not support getting instances"); 533 } 534 long[] retValue ; 535 ReferenceTypeImpl[] rtArray = new ReferenceTypeImpl[classes.size()]; 536 int ii = 0; 537 for (ReferenceType rti: classes) { 538 validateMirror(rti); 539 rtArray[ii++] = (ReferenceTypeImpl)rti; 540 } 541 try { 542 retValue = JDWP.VirtualMachine.InstanceCounts. 543 process(vm, rtArray).counts; 544 } catch (JDWPException exc) { 545 throw exc.toJDIException(); 546 } 547 548 return retValue; 549 } 550 551 public void dispose() { 552 validateVM(); 553 shutdown = true; 554 try { 555 JDWP.VirtualMachine.Dispose.process(vm); 556 } catch (JDWPException exc) { 557 throw exc.toJDIException(); 558 } 559 target.stopListening(); 560 } 561 562 public void exit(int exitCode) { 563 validateVM(); 564 shutdown = true; 565 try { 566 JDWP.VirtualMachine.Exit.process(vm, exitCode); 567 } catch (JDWPException exc) { 568 throw exc.toJDIException(); 569 } 570 target.stopListening(); 571 } 572 573 public Process process() { 574 validateVM(); 575 return process; 576 } 577 578 private JDWP.VirtualMachine.Version versionInfo() { 579 try { 580 if (versionInfo == null) { 581 // Need not be synchronized since it is static information 582 versionInfo = JDWP.VirtualMachine.Version.process(vm); 583 } 584 return versionInfo; 585 } catch (JDWPException exc) { 586 throw exc.toJDIException(); 587 } 588 } 589 public String description() { 590 validateVM(); 591 592 return MessageFormat.format(vmManager.getString("version_format"), 593 "" + vmManager.majorInterfaceVersion(), 594 "" + vmManager.minorInterfaceVersion(), 595 versionInfo().description); 596 } 597 598 public String version() { 599 validateVM(); 600 return versionInfo().vmVersion; 601 } 602 603 public String name() { 604 validateVM(); 605 return versionInfo().vmName; 606 } 607 608 public boolean canWatchFieldModification() { 609 validateVM(); 610 return capabilities().canWatchFieldModification; 611 } 612 public boolean canWatchFieldAccess() { 613 validateVM(); 614 return capabilities().canWatchFieldAccess; 615 } 616 public boolean canGetBytecodes() { 617 validateVM(); 618 return capabilities().canGetBytecodes; 619 } 620 public boolean canGetSyntheticAttribute() { 621 validateVM(); 622 return capabilities().canGetSyntheticAttribute; 623 } 624 public boolean canGetOwnedMonitorInfo() { 625 validateVM(); 626 return capabilities().canGetOwnedMonitorInfo; 627 } 628 public boolean canGetCurrentContendedMonitor() { 629 validateVM(); 630 return capabilities().canGetCurrentContendedMonitor; 631 } 632 public boolean canGetMonitorInfo() { 633 validateVM(); 634 return capabilities().canGetMonitorInfo; 635 } 636 637 private boolean hasNewCapabilities() { 638 return versionInfo().jdwpMajor > 1 || 639 versionInfo().jdwpMinor >= 4; 640 } 641 642 boolean canGet1_5LanguageFeatures() { 643 return versionInfo().jdwpMajor > 1 || 644 versionInfo().jdwpMinor >= 5; 645 } 646 647 public boolean canUseInstanceFilters() { 648 validateVM(); 649 return hasNewCapabilities() && 650 capabilitiesNew().canUseInstanceFilters; 651 } 652 public boolean canRedefineClasses() { 653 validateVM(); 654 return hasNewCapabilities() && 655 capabilitiesNew().canRedefineClasses; 656 } 657 public boolean canAddMethod() { 658 validateVM(); 659 return hasNewCapabilities() && 660 capabilitiesNew().canAddMethod; 661 } 662 public boolean canUnrestrictedlyRedefineClasses() { 663 validateVM(); 664 return hasNewCapabilities() && 665 capabilitiesNew().canUnrestrictedlyRedefineClasses; 666 } 667 public boolean canPopFrames() { 668 validateVM(); 669 return hasNewCapabilities() && 670 capabilitiesNew().canPopFrames; 671 } 672 public boolean canGetMethodReturnValues() { 673 return versionInfo().jdwpMajor > 1 || 674 versionInfo().jdwpMinor >= 6; 675 } 676 public boolean canGetInstanceInfo() { 677 if (versionInfo().jdwpMajor < 1 || 678 versionInfo().jdwpMinor < 6) { 679 return false; 680 } 681 validateVM(); 682 return hasNewCapabilities() && 683 capabilitiesNew().canGetInstanceInfo; 684 } 685 public boolean canUseSourceNameFilters() { 686 if (versionInfo().jdwpMajor < 1 || 687 versionInfo().jdwpMinor < 6) { 688 return false; 689 } 690 return true; 691 } 692 public boolean canForceEarlyReturn() { 693 validateVM(); 694 return hasNewCapabilities() && 695 capabilitiesNew().canForceEarlyReturn; 696 } 697 public boolean canBeModified() { 698 return true; 699 } 700 public boolean canGetSourceDebugExtension() { 701 validateVM(); 702 return hasNewCapabilities() && 703 capabilitiesNew().canGetSourceDebugExtension; 704 } 705 public boolean canGetClassFileVersion() { 706 if ( versionInfo().jdwpMajor < 1 && 707 versionInfo().jdwpMinor < 6) { 708 return false; 709 } else { 710 return true; 711 } 712 } 713 public boolean canGetConstantPool() { 714 validateVM(); 715 return hasNewCapabilities() && 716 capabilitiesNew().canGetConstantPool; 717 } 718 public boolean canRequestVMDeathEvent() { 719 validateVM(); 720 return hasNewCapabilities() && 721 capabilitiesNew().canRequestVMDeathEvent; 722 } 723 public boolean canRequestMonitorEvents() { 724 validateVM(); 725 return hasNewCapabilities() && 726 capabilitiesNew().canRequestMonitorEvents; 727 } 728 public boolean canGetMonitorFrameInfo() { 729 validateVM(); 730 return hasNewCapabilities() && 731 capabilitiesNew().canGetMonitorFrameInfo; 732 } 733 734 public void setDebugTraceMode(int traceFlags) { 735 validateVM(); 736 this.traceFlags = traceFlags; 737 this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0; 738 } 739 740 void printTrace(String string) { 741 System.err.println("[JDI: " + string + "]"); 742 } 743 744 void printReceiveTrace(int depth, String string) { 745 StringBuffer sb = new StringBuffer("Receiving:"); 746 for (int i = depth; i > 0; --i) { 747 sb.append(" "); 748 } 749 sb.append(string); 750 printTrace(sb.toString()); 751 } 752 753 private synchronized ReferenceTypeImpl addReferenceType(long id, 754 int tag, 755 String signature) { 756 if (typesByID == null) { 757 initReferenceTypes(); 758 } 759 ReferenceTypeImpl type = null; 760 switch(tag) { 761 case JDWP.TypeTag.CLASS: 762 type = new ClassTypeImpl(vm, id); 763 break; 764 case JDWP.TypeTag.INTERFACE: 765 type = new InterfaceTypeImpl(vm, id); 766 break; 767 case JDWP.TypeTag.ARRAY: 768 type = new ArrayTypeImpl(vm, id); 769 break; 770 default: 771 throw new InternalException("Invalid reference type tag"); 772 } 773 774 /* 775 * If a signature was specified, make sure to set it ASAP, to 776 * prevent any needless JDWP command to retrieve it. (for example, 777 * typesBySignature.add needs the signature, to maintain proper 778 * ordering. 779 */ 780 if (signature != null) { 781 type.setSignature(signature); 782 } 783 784 typesByID.put(new Long(id), type); 785 typesBySignature.add(type); 786 787 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 788 vm.printTrace("Caching new ReferenceType, sig=" + signature + 789 ", id=" + id); 790 } 791 792 return type; 793 } 794 795 synchronized void removeReferenceType(String signature) { 796 if (typesByID == null) { 797 return; 798 } 799 /* 800 * There can be multiple classes with the same name. Since 801 * we can't differentiate here, we first remove all 802 * matching classes from our cache... 803 */ 804 Iterator<ReferenceType> iter = typesBySignature.iterator(); 805 int matches = 0; 806 while (iter.hasNext()) { 807 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 808 int comp = signature.compareTo(type.signature()); 809 if (comp == 0) { 810 matches++; 811 iter.remove(); 812 typesByID.remove(new Long(type.ref())); 813 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 814 vm.printTrace("Uncaching ReferenceType, sig=" + signature + 815 ", id=" + type.ref()); 816 } 817 /* fix for 4359077 , don't break out. list is no longer sorted 818 in the order we think 819 */ 820 } 821 } 822 823 /* 824 * ...and if there was more than one, re-retrieve the classes 825 * with that name 826 */ 827 if (matches > 1) { 828 retrieveClassesBySignature(signature); 829 } 830 } 831 832 private synchronized List<ReferenceType> findReferenceTypes(String signature) { 833 if (typesByID == null) { 834 return new ArrayList<ReferenceType>(0); 835 } 836 Iterator<ReferenceType> iter = typesBySignature.iterator(); 837 List<ReferenceType> list = new ArrayList<ReferenceType>(); 838 while (iter.hasNext()) { 839 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 840 int comp = signature.compareTo(type.signature()); 841 if (comp == 0) { 842 list.add(type); 843 /* fix for 4359077 , don't break out. list is no longer sorted 844 in the order we think 845 */ 846 } 847 } 848 return list; 849 } 850 851 private void initReferenceTypes() { 852 typesByID = new HashMap<Long, ReferenceType>(300); 853 typesBySignature = new TreeSet<ReferenceType>(); 854 } 855 856 ReferenceTypeImpl referenceType(long ref, byte tag) { 857 return referenceType(ref, tag, null); 858 } 859 860 ClassTypeImpl classType(long ref) { 861 return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null); 862 } 863 864 InterfaceTypeImpl interfaceType(long ref) { 865 return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null); 866 } 867 868 ArrayTypeImpl arrayType(long ref) { 869 return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null); 870 } 871 872 ReferenceTypeImpl referenceType(long id, int tag, 873 String signature) { 874 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 875 StringBuffer sb = new StringBuffer(); 876 sb.append("Looking up "); 877 if (tag == JDWP.TypeTag.CLASS) { 878 sb.append("Class"); 879 } else if (tag == JDWP.TypeTag.INTERFACE) { 880 sb.append("Interface"); 881 } else if (tag == JDWP.TypeTag.ARRAY) { 882 sb.append("ArrayType"); 883 } else { 884 sb.append("UNKNOWN TAG: " + tag); 885 } 886 if (signature != null) { 887 sb.append(", signature='" + signature + "'"); 888 } 889 sb.append(", id=" + id); 890 vm.printTrace(sb.toString()); 891 } 892 if (id == 0) { 893 return null; 894 } else { 895 ReferenceTypeImpl retType = null; 896 synchronized (this) { 897 if (typesByID != null) { 898 retType = (ReferenceTypeImpl)typesByID.get(new Long(id)); 899 } 900 if (retType == null) { 901 retType = addReferenceType(id, tag, signature); 902 } 903 } 904 return retType; 905 } 906 } 907 908 private JDWP.VirtualMachine.Capabilities capabilities() { 909 if (capabilities == null) { 910 try { 911 capabilities = JDWP.VirtualMachine 912 .Capabilities.process(vm); 913 } catch (JDWPException exc) { 914 throw exc.toJDIException(); 915 } 916 } 917 return capabilities; 918 } 919 920 private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() { 921 if (capabilitiesNew == null) { 922 try { 923 capabilitiesNew = JDWP.VirtualMachine 924 .CapabilitiesNew.process(vm); 925 } catch (JDWPException exc) { 926 throw exc.toJDIException(); 927 } 928 } 929 return capabilitiesNew; 930 } 931 932 private List<ReferenceType> retrieveClassesBySignature(String signature) { 933 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 934 vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature); 935 } 936 JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos; 937 try { 938 cinfos = JDWP.VirtualMachine.ClassesBySignature. 939 process(vm, signature).classes; 940 } catch (JDWPException exc) { 941 throw exc.toJDIException(); 942 } 943 944 int count = cinfos.length; 945 List<ReferenceType> list = new ArrayList<ReferenceType>(count); 946 947 // Hold lock during processing to improve performance 948 synchronized (this) { 949 for (int i = 0; i < count; i++) { 950 JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci = 951 cinfos[i]; 952 ReferenceTypeImpl type = referenceType(ci.typeID, 953 ci.refTypeTag, 954 signature); 955 type.setStatus(ci.status); 956 list.add(type); 957 } 958 } 959 return list; 960 } 961 962 private void retrieveAllClasses1_4() { 963 JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos; 964 try { 965 cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes; 966 } catch (JDWPException exc) { 967 throw exc.toJDIException(); 968 } 969 970 // Hold lock during processing to improve performance 971 // and to have safe check/set of retrievedAllTypes 972 synchronized (this) { 973 if (!retrievedAllTypes) { 974 // Number of classes 975 int count = cinfos.length; 976 for (int i=0; i<count; i++) { 977 JDWP.VirtualMachine.AllClasses.ClassInfo ci = 978 cinfos[i]; 979 ReferenceTypeImpl type = referenceType(ci.typeID, 980 ci.refTypeTag, 981 ci.signature); 982 type.setStatus(ci.status); 983 } 984 retrievedAllTypes = true; 985 } 986 } 987 } 988 989 private void retrieveAllClasses() { 990 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 991 vm.printTrace("Retrieving all ReferenceTypes"); 992 } 993 994 if (!vm.canGet1_5LanguageFeatures()) { 995 retrieveAllClasses1_4(); 996 return; 997 } 998 999 /* 1000 * To save time (assuming the caller will be 1001 * using then) we will get the generic sigs too. 1002 */ 1003 1004 JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos; 1005 try { 1006 cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes; 1007 } catch (JDWPException exc) { 1008 throw exc.toJDIException(); 1009 } 1010 1011 // Hold lock during processing to improve performance 1012 // and to have safe check/set of retrievedAllTypes 1013 synchronized (this) { 1014 if (!retrievedAllTypes) { 1015 // Number of classes 1016 int count = cinfos.length; 1017 for (int i=0; i<count; i++) { 1018 JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci = 1019 cinfos[i]; 1020 ReferenceTypeImpl type = referenceType(ci.typeID, 1021 ci.refTypeTag, 1022 ci.signature); 1023 type.setGenericSignature(ci.genericSignature); 1024 type.setStatus(ci.status); 1025 } 1026 retrievedAllTypes = true; 1027 } 1028 } 1029 } 1030 1031 void sendToTarget(Packet packet) { 1032 target.send(packet); 1033 } 1034 1035 void waitForTargetReply(Packet packet) { 1036 target.waitForReply(packet); 1037 /* 1038 * If any object disposes have been batched up, send them now. 1039 */ 1040 processBatchedDisposes(); 1041 } 1042 1043 Type findBootType(String signature) throws ClassNotLoadedException { 1044 List<ReferenceType> types = retrieveClassesBySignature(signature); 1045 Iterator<ReferenceType> iter = types.iterator(); 1046 while (iter.hasNext()) { 1047 ReferenceType type = iter.next(); 1048 if (type.classLoader() == null) { 1049 return type; 1050 } 1051 } 1052 JNITypeParser parser = new JNITypeParser(signature); 1053 throw new ClassNotLoadedException(parser.typeName(), 1054 "Type " + parser.typeName() + " not loaded"); 1055 } 1056 1057 BooleanType theBooleanType() { 1058 if (theBooleanType == null) { 1059 synchronized(this) { 1060 if (theBooleanType == null) { 1061 theBooleanType = new BooleanTypeImpl(this); 1062 } 1063 } 1064 } 1065 return theBooleanType; 1066 } 1067 1068 ByteType theByteType() { 1069 if (theByteType == null) { 1070 synchronized(this) { 1071 if (theByteType == null) { 1072 theByteType = new ByteTypeImpl(this); 1073 } 1074 } 1075 } 1076 return theByteType; 1077 } 1078 1079 CharType theCharType() { 1080 if (theCharType == null) { 1081 synchronized(this) { 1082 if (theCharType == null) { 1083 theCharType = new CharTypeImpl(this); 1084 } 1085 } 1086 } 1087 return theCharType; 1088 } 1089 1090 ShortType theShortType() { 1091 if (theShortType == null) { 1092 synchronized(this) { 1093 if (theShortType == null) { 1094 theShortType = new ShortTypeImpl(this); 1095 } 1096 } 1097 } 1098 return theShortType; 1099 } 1100 1101 IntegerType theIntegerType() { 1102 if (theIntegerType == null) { 1103 synchronized(this) { 1104 if (theIntegerType == null) { 1105 theIntegerType = new IntegerTypeImpl(this); 1106 } 1107 } 1108 } 1109 return theIntegerType; 1110 } 1111 1112 LongType theLongType() { 1113 if (theLongType == null) { 1114 synchronized(this) { 1115 if (theLongType == null) { 1116 theLongType = new LongTypeImpl(this); 1117 } 1118 } 1119 } 1120 return theLongType; 1121 } 1122 1123 FloatType theFloatType() { 1124 if (theFloatType == null) { 1125 synchronized(this) { 1126 if (theFloatType == null) { 1127 theFloatType = new FloatTypeImpl(this); 1128 } 1129 } 1130 } 1131 return theFloatType; 1132 } 1133 1134 DoubleType theDoubleType() { 1135 if (theDoubleType == null) { 1136 synchronized(this) { 1137 if (theDoubleType == null) { 1138 theDoubleType = new DoubleTypeImpl(this); 1139 } 1140 } 1141 } 1142 return theDoubleType; 1143 } 1144 1145 VoidType theVoidType() { 1146 if (theVoidType == null) { 1147 synchronized(this) { 1148 if (theVoidType == null) { 1149 theVoidType = new VoidTypeImpl(this); 1150 } 1151 } 1152 } 1153 return theVoidType; 1154 } 1155 1156 PrimitiveType primitiveTypeMirror(byte tag) { 1157 switch (tag) { 1158 case JDWP.Tag.BOOLEAN: 1159 return theBooleanType(); 1160 case JDWP.Tag.BYTE: 1161 return theByteType(); 1162 case JDWP.Tag.CHAR: 1163 return theCharType(); 1164 case JDWP.Tag.SHORT: 1165 return theShortType(); 1166 case JDWP.Tag.INT: 1167 return theIntegerType(); 1168 case JDWP.Tag.LONG: 1169 return theLongType(); 1170 case JDWP.Tag.FLOAT: 1171 return theFloatType(); 1172 case JDWP.Tag.DOUBLE: 1173 return theDoubleType(); 1174 default: 1175 throw new IllegalArgumentException("Unrecognized primitive tag " + tag); 1176 } 1177 } 1178 1179 private void processBatchedDisposes() { 1180 if (shutdown) { 1181 return; 1182 } 1183 1184 JDWP.VirtualMachine.DisposeObjects.Request[] requests = null; 1185 synchronized(batchedDisposeRequests) { 1186 int size = batchedDisposeRequests.size(); 1187 if (size >= DISPOSE_THRESHOLD) { 1188 if ((traceFlags & TRACE_OBJREFS) != 0) { 1189 printTrace("Dispose threashold reached. Will dispose " 1190 + size + " object references..."); 1191 } 1192 requests = new JDWP.VirtualMachine.DisposeObjects.Request[size]; 1193 for (int i = 0; i < requests.length; i++) { 1194 SoftObjectReference ref = batchedDisposeRequests.get(i); 1195 if ((traceFlags & TRACE_OBJREFS) != 0) { 1196 printTrace("Disposing object " + ref.key().longValue() + 1197 " (ref count = " + ref.count() + ")"); 1198 } 1199 1200 // This is kludgy. We temporarily re-create an object 1201 // reference so that we can correctly pass its id to the 1202 // JDWP command. 1203 requests[i] = 1204 new JDWP.VirtualMachine.DisposeObjects.Request( 1205 new ObjectReferenceImpl(this, ref.key().longValue()), 1206 ref.count()); 1207 } 1208 batchedDisposeRequests.clear(); 1209 } 1210 } 1211 if (requests != null) { 1212 try { 1213 JDWP.VirtualMachine.DisposeObjects.process(vm, requests); 1214 } catch (JDWPException exc) { 1215 throw exc.toJDIException(); 1216 } 1217 } 1218 } 1219 1220 private void batchForDispose(SoftObjectReference ref) { 1221 if ((traceFlags & TRACE_OBJREFS) != 0) { 1222 printTrace("Batching object " + ref.key().longValue() + 1223 " for dispose (ref count = " + ref.count() + ")"); 1224 } 1225 batchedDisposeRequests.add(ref); 1226 } 1227 1228 private void processQueue() { 1229 Reference<?> ref; 1230 //if ((traceFlags & TRACE_OBJREFS) != 0) { 1231 // printTrace("Checking for softly reachable objects"); 1232 //} 1233 while ((ref = referenceQueue.poll()) != null) { 1234 SoftObjectReference softRef = (SoftObjectReference)ref; 1235 removeObjectMirror(softRef); 1236 batchForDispose(softRef); 1237 } 1238 } 1239 1240 synchronized ObjectReferenceImpl objectMirror(long id, int tag) { 1241 1242 // Handle any queue elements that are not strongly reachable 1243 processQueue(); 1244 1245 if (id == 0) { 1246 return null; 1247 } 1248 ObjectReferenceImpl object = null; 1249 Long key = new Long(id); 1250 1251 /* 1252 * Attempt to retrieve an existing object object reference 1253 */ 1254 SoftObjectReference ref = objectsByID.get(key); 1255 if (ref != null) { 1256 object = ref.object(); 1257 } 1258 1259 /* 1260 * If the object wasn't in the table, or it's soft reference was 1261 * cleared, create a new instance. 1262 */ 1263 if (object == null) { 1264 switch (tag) { 1265 case JDWP.Tag.OBJECT: 1266 object = new ObjectReferenceImpl(vm, id); 1267 break; 1268 case JDWP.Tag.STRING: 1269 object = new StringReferenceImpl(vm, id); 1270 break; 1271 case JDWP.Tag.ARRAY: 1272 object = new ArrayReferenceImpl(vm, id); 1273 break; 1274 case JDWP.Tag.THREAD: 1275 ThreadReferenceImpl thread = 1276 new ThreadReferenceImpl(vm, id); 1277 thread.addListener(this); 1278 object = thread; 1279 break; 1280 case JDWP.Tag.THREAD_GROUP: 1281 object = new ThreadGroupReferenceImpl(vm, id); 1282 break; 1283 case JDWP.Tag.CLASS_LOADER: 1284 object = new ClassLoaderReferenceImpl(vm, id); 1285 break; 1286 case JDWP.Tag.CLASS_OBJECT: 1287 object = new ClassObjectReferenceImpl(vm, id); 1288 break; 1289 default: 1290 throw new IllegalArgumentException("Invalid object tag: " + tag); 1291 } 1292 ref = new SoftObjectReference(key, object, referenceQueue); 1293 1294 /* 1295 * If there was no previous entry in the table, we add one here 1296 * If the previous entry was cleared, we replace it here. 1297 */ 1298 objectsByID.put(key, ref); 1299 if ((traceFlags & TRACE_OBJREFS) != 0) { 1300 printTrace("Creating new " + 1301 object.getClass().getName() + " (id = " + id + ")"); 1302 } 1303 } else { 1304 ref.incrementCount(); 1305 } 1306 1307 return object; 1308 } 1309 1310 synchronized void removeObjectMirror(ObjectReferenceImpl object) { 1311 1312 // Handle any queue elements that are not strongly reachable 1313 processQueue(); 1314 1315 SoftObjectReference ref = objectsByID.remove(new Long(object.ref())); 1316 if (ref != null) { 1317 batchForDispose(ref); 1318 } else { 1319 /* 1320 * If there's a live ObjectReference about, it better be part 1321 * of the cache. 1322 */ 1323 throw new InternalException("ObjectReference " + object.ref() + 1324 " not found in object cache"); 1325 } 1326 } 1327 1328 synchronized void removeObjectMirror(SoftObjectReference ref) { 1329 /* 1330 * This will remove the soft reference if it has not been 1331 * replaced in the cache. 1332 */ 1333 objectsByID.remove(ref.key()); 1334 } 1335 1336 ObjectReferenceImpl objectMirror(long id) { 1337 return objectMirror(id, JDWP.Tag.OBJECT); 1338 } 1339 1340 StringReferenceImpl stringMirror(long id) { 1341 return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING); 1342 } 1343 1344 ArrayReferenceImpl arrayMirror(long id) { 1345 return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY); 1346 } 1347 1348 ThreadReferenceImpl threadMirror(long id) { 1349 return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD); 1350 } 1351 1352 ThreadGroupReferenceImpl threadGroupMirror(long id) { 1353 return (ThreadGroupReferenceImpl)objectMirror(id, 1354 JDWP.Tag.THREAD_GROUP); 1355 } 1356 1357 ClassLoaderReferenceImpl classLoaderMirror(long id) { 1358 return (ClassLoaderReferenceImpl)objectMirror(id, 1359 JDWP.Tag.CLASS_LOADER); 1360 } 1361 1362 ClassObjectReferenceImpl classObjectMirror(long id) { 1363 return (ClassObjectReferenceImpl)objectMirror(id, 1364 JDWP.Tag.CLASS_OBJECT); 1365 } 1366 1367 /* 1368 * Implementation of PathSearchingVirtualMachine 1369 */ 1370 private JDWP.VirtualMachine.ClassPaths getClasspath() { 1371 if (pathInfo == null) { 1372 try { 1373 pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm); 1374 } catch (JDWPException exc) { 1375 throw exc.toJDIException(); 1376 } 1377 } 1378 return pathInfo; 1379 } 1380 1381 public List<String> classPath() { 1382 return Arrays.asList(getClasspath().classpaths); 1383 } 1384 1385 public List<String> bootClassPath() { 1386 return Arrays.asList(getClasspath().bootclasspaths); 1387 } 1388 1389 public String baseDirectory() { 1390 return getClasspath().baseDir; 1391 } 1392 1393 public void setDefaultStratum(String stratum) { 1394 defaultStratum = stratum; 1395 if (stratum == null) { 1396 stratum = ""; 1397 } 1398 try { 1399 JDWP.VirtualMachine.SetDefaultStratum.process(vm, 1400 stratum); 1401 } catch (JDWPException exc) { 1402 throw exc.toJDIException(); 1403 } 1404 } 1405 1406 public String getDefaultStratum() { 1407 return defaultStratum; 1408 } 1409 1410 ThreadGroup threadGroupForJDI() { 1411 return threadGroupForJDI; 1412 } 1413 1414 static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> { 1415 int count; 1416 Long key; 1417 1418 SoftObjectReference(Long key, ObjectReferenceImpl mirror, 1419 ReferenceQueue<ObjectReferenceImpl> queue) { 1420 super(mirror, queue); 1421 this.count = 1; 1422 this.key = key; 1423 } 1424 1425 int count() { 1426 return count; 1427 } 1428 1429 void incrementCount() { 1430 count++; 1431 } 1432 1433 Long key() { 1434 return key; 1435 } 1436 1437 ObjectReferenceImpl object() { 1438 return get(); 1439 } 1440 } 1441 } 1442