diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,877 @@ +/* + * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.jdi; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import java.util.*; +enum EventDestination {UNKNOWN_EVENT, INTERNAL_EVENT, CLIENT_EVENT}; + +/* + * An EventSet is normally created by the transport reader thread when + * it reads a JDWP Composite command. The constructor doesn't unpack + * the events contained in the Composite command and create EventImpls + * for them because that process might involve calling back into the back-end + * which should not be done by the transport reader thread. Instead, + * the raw bytes of the packet are read and stored in the EventSet. + * The EventSet is then added to each EventQueue. When an EventSet is + * removed from an EventQueue, the EventSetImpl.build() method is called. + * This method reads the packet bytes and creates the actual EventImpl objects. + * build() also filters out events for our internal handler and puts them in + * their own EventSet. This means that the EventImpls that are in the EventSet + * that is on the queues are all for client requests. + */ +public class EventSetImpl extends ArrayList implements EventSet { + + private VirtualMachineImpl vm; // we implement Mirror + private Packet pkt; + private byte suspendPolicy; + private EventSetImpl internalEventSet; + + public String toString() { + String string = "event set, policy:" + suspendPolicy + + ", count:" + this.size() + " = {"; + Iterator iter = this.iterator(); + boolean first = true; + while (iter.hasNext()) { + Event event = (Event)iter.next(); + if (!first) { + string += ", "; + } + string += event.toString(); + first = false; + } + string += "}"; + return string; + } + + abstract class EventImpl extends MirrorImpl implements Event { + + private final byte eventCmd; + private final int requestID; + // This is set only for client requests, not internal requests. + private final EventRequest request; + + /** + * Constructor for events. + */ + protected EventImpl(JDWP.Event.Composite.Events.EventsCommon evt, + int requestID) { + super(EventSetImpl.this.vm); + this.eventCmd = evt.eventKind(); + this.requestID = requestID; + EventRequestManagerImpl ermi = EventSetImpl.this. + vm.eventRequestManagerImpl(); + this.request = ermi.request(eventCmd, requestID); + } + + /* + * Override superclass back to default equality + */ + public boolean equals(Object obj) { + return this == obj; + } + + public int hashCode() { + return System.identityHashCode(this); + } + + /** + * Constructor for VM disconnected events. + */ + protected EventImpl(byte eventCmd) { + super(EventSetImpl.this.vm); + this.eventCmd = eventCmd; + this.requestID = 0; + this.request = null; + } + + public EventRequest request() { + return request; + } + + int requestID() { + return requestID; + } + + EventDestination destination() { + /* + * We need to decide if this event is for + * 1. an internal request + * 2. a client request that is no longer available, ie + * it has been deleted, or disabled and re-enabled + * which gives it a new ID. + * 3. a current client request that is disabled + * 4. a current enabled client request. + * + * We will filter this set into a set + * that contains only 1s for our internal queue + * and a set that contains only 4s for our client queue. + * If we get an EventSet that contains only 2 and 3 + * then we have to resume it if it is not SUSPEND_NONE + * because no one else will. + */ + if (requestID == 0) { + /* An unsolicited event. These have traditionally + * been treated as client events. + */ + return EventDestination.CLIENT_EVENT; + } + + // Is this an event for a current client request? + if (request == null) { + // Nope. Is it an event for an internal request? + EventRequestManagerImpl ermi = this.vm.getInternalEventRequestManager(); + if (ermi.request(eventCmd, requestID) != null) { + // Yep + return EventDestination.INTERNAL_EVENT; + } + return EventDestination.UNKNOWN_EVENT; + } + + // We found a client request + if (request.isEnabled()) { + return EventDestination.CLIENT_EVENT; + } + return EventDestination.UNKNOWN_EVENT; + } + + abstract String eventName(); + + public String toString() { + return eventName(); + } + + } + + abstract class ThreadedEventImpl extends EventImpl { + private ThreadReference thread; + + ThreadedEventImpl(JDWP.Event.Composite.Events.EventsCommon evt, + int requestID, ThreadReference thread) { + super(evt, requestID); + this.thread = thread; + } + + public ThreadReference thread() { + return thread; + } + + public String toString() { + return eventName() + " in thread " + thread.name(); + } + } + + abstract class LocatableEventImpl extends ThreadedEventImpl + implements Locatable { + private Location location; + + LocatableEventImpl(JDWP.Event.Composite.Events.EventsCommon evt, + int requestID, + ThreadReference thread, Location location) { + super(evt, requestID, thread); + this.location = location; + } + + public Location location() { + return location; + } + + /** + * For MethodEntry and MethodExit + */ + public Method method() { + return location.method(); + } + + public String toString() { + return eventName() + "@" + location().toString() + + " in thread " + thread().name(); + } + } + + class BreakpointEventImpl extends LocatableEventImpl + implements BreakpointEvent { + BreakpointEventImpl(JDWP.Event.Composite.Events.Breakpoint evt) { + super(evt, evt.requestID, evt.thread, evt.location); + } + + String eventName() { + return "BreakpointEvent"; + } + } + + class StepEventImpl extends LocatableEventImpl implements StepEvent { + StepEventImpl(JDWP.Event.Composite.Events.SingleStep evt) { + super(evt, evt.requestID, evt.thread, evt.location); + } + + String eventName() { + return "StepEvent"; + } + } + + class MethodEntryEventImpl extends LocatableEventImpl + implements MethodEntryEvent { + MethodEntryEventImpl(JDWP.Event.Composite.Events.MethodEntry evt) { + super(evt, evt.requestID, evt.thread, evt.location); + } + + String eventName() { + return "MethodEntryEvent"; + } + } + + class MethodExitEventImpl extends LocatableEventImpl + implements MethodExitEvent { + private Value returnVal = null; + + MethodExitEventImpl(JDWP.Event.Composite.Events.MethodExit evt) { + super(evt, evt.requestID, evt.thread, evt.location); + } + + MethodExitEventImpl(JDWP.Event.Composite.Events.MethodExitWithReturnValue evt) { + super(evt, evt.requestID, evt.thread, evt.location); + returnVal = evt.value; + } + + String eventName() { + return "MethodExitEvent"; + } + + public Value returnValue() { + if (!this.vm.canGetMethodReturnValues()) { + throw new UnsupportedOperationException( + "target does not support return values in MethodExit events"); + } + return returnVal; + } + + } + + class MonitorContendedEnterEventImpl extends LocatableEventImpl + implements MonitorContendedEnterEvent { + private ObjectReference monitor = null; + + MonitorContendedEnterEventImpl(JDWP.Event.Composite.Events.MonitorContendedEnter evt) { + super(evt, evt.requestID, evt.thread, evt.location); + this.monitor = evt.object; + } + + String eventName() { + return "MonitorContendedEnter"; + } + + public ObjectReference monitor() { + return monitor; + }; + + } + + class MonitorContendedEnteredEventImpl extends LocatableEventImpl + implements MonitorContendedEnteredEvent { + private ObjectReference monitor = null; + + MonitorContendedEnteredEventImpl(JDWP.Event.Composite.Events.MonitorContendedEntered evt) { + super(evt, evt.requestID, evt.thread, evt.location); + this.monitor = evt.object; + } + + String eventName() { + return "MonitorContendedEntered"; + } + + public ObjectReference monitor() { + return monitor; + }; + + } + + class MonitorWaitEventImpl extends LocatableEventImpl + implements MonitorWaitEvent { + private ObjectReference monitor = null; + private long timeout; + + MonitorWaitEventImpl(JDWP.Event.Composite.Events.MonitorWait evt) { + super(evt, evt.requestID, evt.thread, evt.location); + this.monitor = evt.object; + this.timeout = evt.timeout; + } + + String eventName() { + return "MonitorWait"; + } + + public ObjectReference monitor() { + return monitor; + }; + + public long timeout() { + return timeout; + } + } + + class MonitorWaitedEventImpl extends LocatableEventImpl + implements MonitorWaitedEvent { + private ObjectReference monitor = null; + private boolean timed_out; + + MonitorWaitedEventImpl(JDWP.Event.Composite.Events.MonitorWaited evt) { + super(evt, evt.requestID, evt.thread, evt.location); + this.monitor = evt.object; + this.timed_out = evt.timed_out; + } + + String eventName() { + return "MonitorWaited"; + } + + public ObjectReference monitor() { + return monitor; + }; + + public boolean timedout() { + return timed_out; + } + } + + class ClassPrepareEventImpl extends ThreadedEventImpl + implements ClassPrepareEvent { + private ReferenceType referenceType; + + ClassPrepareEventImpl(JDWP.Event.Composite.Events.ClassPrepare evt) { + super(evt, evt.requestID, evt.thread); + referenceType = this.vm.referenceType(evt.typeID, evt.refTypeTag, + evt.signature); + ((ReferenceTypeImpl)referenceType).setStatus(evt.status); + } + + public ReferenceType referenceType() { + return referenceType; + } + + String eventName() { + return "ClassPrepareEvent"; + } + } + + class ClassUnloadEventImpl extends EventImpl implements ClassUnloadEvent { + private String classSignature; + + ClassUnloadEventImpl(JDWP.Event.Composite.Events.ClassUnload evt) { + super(evt, evt.requestID); + this.classSignature = evt.signature; + } + + public String className() { + return classSignature.substring(1, classSignature.length()-1) + .replace('/', '.'); + } + + public String classSignature() { + return classSignature; + } + + String eventName() { + return "ClassUnloadEvent"; + } + } + + class ExceptionEventImpl extends LocatableEventImpl + implements ExceptionEvent { + private ObjectReference exception; + private Location catchLocation; + + ExceptionEventImpl(JDWP.Event.Composite.Events.Exception evt) { + super(evt, evt.requestID, evt.thread, evt.location); + this.exception = evt.exception; + this.catchLocation = evt.catchLocation; + } + + public ObjectReference exception() { + return exception; + } + + public Location catchLocation() { + return catchLocation; + } + + String eventName() { + return "ExceptionEvent"; + } + } + + class ThreadDeathEventImpl extends ThreadedEventImpl + implements ThreadDeathEvent { + ThreadDeathEventImpl(JDWP.Event.Composite.Events.ThreadDeath evt) { + super(evt, evt.requestID, evt.thread); + } + + String eventName() { + return "ThreadDeathEvent"; + } + } + + class ThreadStartEventImpl extends ThreadedEventImpl + implements ThreadStartEvent { + ThreadStartEventImpl(JDWP.Event.Composite.Events.ThreadStart evt) { + super(evt, evt.requestID, evt.thread); + } + + String eventName() { + return "ThreadStartEvent"; + } + } + + class VMStartEventImpl extends ThreadedEventImpl + implements VMStartEvent { + VMStartEventImpl(JDWP.Event.Composite.Events.VMStart evt) { + super(evt, evt.requestID, evt.thread); + } + + String eventName() { + return "VMStartEvent"; + } + } + + class VMDeathEventImpl extends EventImpl implements VMDeathEvent { + + VMDeathEventImpl(JDWP.Event.Composite.Events.VMDeath evt) { + super(evt, evt.requestID); + } + + String eventName() { + return "VMDeathEvent"; + } + } + + class VMDisconnectEventImpl extends EventImpl + implements VMDisconnectEvent { + + VMDisconnectEventImpl() { + super((byte)JDWP.EventKind.VM_DISCONNECTED); + } + + String eventName() { + return "VMDisconnectEvent"; + } + } + + abstract class WatchpointEventImpl extends LocatableEventImpl + implements WatchpointEvent { + private final ReferenceTypeImpl refType; + private final long fieldID; + private final ObjectReference object; + private Field field = null; + + WatchpointEventImpl(JDWP.Event.Composite.Events.EventsCommon evt, + int requestID, + ThreadReference thread, Location location, + byte refTypeTag, long typeID, long fieldID, + ObjectReference object) { + super(evt, requestID, thread, location); + this.refType = this.vm.referenceType(typeID, refTypeTag); + this.fieldID = fieldID; + this.object = object; + } + + public Field field() { + if (field == null) { + field = refType.getFieldMirror(fieldID); + } + return field; + } + + public ObjectReference object() { + return object; + } + + public Value valueCurrent() { + if (object == null) { + return refType.getValue(field()); + } else { + return object.getValue(field()); + } + } + } + + class AccessWatchpointEventImpl extends WatchpointEventImpl + implements AccessWatchpointEvent { + + AccessWatchpointEventImpl(JDWP.Event.Composite.Events.FieldAccess evt) { + super(evt, evt.requestID, evt.thread, evt.location, + evt.refTypeTag, evt.typeID, evt.fieldID, evt.object); + } + + String eventName() { + return "AccessWatchpoint"; + } + } + + class ModificationWatchpointEventImpl extends WatchpointEventImpl + implements ModificationWatchpointEvent { + Value newValue; + + ModificationWatchpointEventImpl( + JDWP.Event.Composite.Events.FieldModification evt) { + super(evt, evt.requestID, evt.thread, evt.location, + evt.refTypeTag, evt.typeID, evt.fieldID, evt.object); + this.newValue = evt.valueToBe; + } + + public Value valueToBe() { + return newValue; + } + + String eventName() { + return "ModificationWatchpoint"; + } + } + + /** + * Events are constructed on the thread which reads all data from the + * transport. This means that the packet cannot be converted to real + * JDI objects as that may involve further communications with the + * back end which would deadlock. + * + * Hence the {@link #build()} method below called by EventQueue. + */ + EventSetImpl(VirtualMachine aVm, Packet pkt) { + super(); + + // From "MirrorImpl": + // Yes, its a bit of a hack. But by doing it this + // way, this is the only place we have to change + // typing to substitute a new impl. + vm = (VirtualMachineImpl)aVm; + + this.pkt = pkt; + } + + /** + * Constructor for special events like VM disconnected + */ + EventSetImpl(VirtualMachine aVm, byte eventCmd) { + this(aVm, null); + suspendPolicy = JDWP.SuspendPolicy.NONE; + switch (eventCmd) { + case JDWP.EventKind.VM_DISCONNECTED: + addEvent(new VMDisconnectEventImpl()); + break; + + default: + throw new InternalException("Bad singleton event code"); + } + } + + private void addEvent(EventImpl evt) { + // Note that this class has a public add method that throws + // an exception so that clients can't modify the EventSet + super.add(evt); + } + + /* + * Complete the construction of an EventSet. This is called from + * an event handler thread. It upacks the JDWP events inside + * the packet and creates EventImpls for them. The EventSet is already + * on EventQueues when this is called, so it has to be synch. + */ + synchronized void build() { + if (pkt == null) { + return; + } + PacketStream ps = new PacketStream(vm, pkt); + JDWP.Event.Composite compEvt = new JDWP.Event.Composite(vm, ps); + suspendPolicy = compEvt.suspendPolicy; + if ((vm.traceFlags & vm.TRACE_EVENTS) != 0) { + switch(suspendPolicy) { + case JDWP.SuspendPolicy.ALL: + vm.printTrace("EventSet: SUSPEND_ALL"); + break; + + case JDWP.SuspendPolicy.EVENT_THREAD: + vm.printTrace("EventSet: SUSPEND_EVENT_THREAD"); + break; + + case JDWP.SuspendPolicy.NONE: + vm.printTrace("EventSet: SUSPEND_NONE"); + break; + } + } + + ThreadReference fix6485605 = null; + for (int i = 0; i < compEvt.events.length; i++) { + EventImpl evt = createEvent(compEvt.events[i]); + if ((vm.traceFlags & vm.TRACE_EVENTS) != 0) { + try { + vm.printTrace("Event: " + evt); + } catch (VMDisconnectedException ee) { + // ignore - see bug 6502716 + } + } + + switch (evt.destination()) { + case UNKNOWN_EVENT: + // Ignore disabled, deleted, unknown events, but + // save the thread if there is one since we might + // have to resume it. Note that events for different + // threads can't be in the same event set. + if (evt instanceof ThreadedEventImpl && + suspendPolicy == JDWP.SuspendPolicy.EVENT_THREAD) { + fix6485605 = ((ThreadedEventImpl)evt).thread(); + } + continue; + case CLIENT_EVENT: + addEvent(evt); + break; + case INTERNAL_EVENT: + if (internalEventSet == null) { + internalEventSet = new EventSetImpl(this.vm, null); + } + internalEventSet.addEvent(evt); + break; + default: + throw new InternalException("Invalid event destination"); + } + } + pkt = null; // No longer needed - free it up + + // Avoid hangs described in 6296125, 6293795 + if (super.size() == 0) { + // This set has no client events. If we don't do + // needed resumes, no one else is going to. + if (suspendPolicy == JDWP.SuspendPolicy.ALL) { + vm.resume(); + } else if (suspendPolicy == JDWP.SuspendPolicy.EVENT_THREAD) { + // See bug 6485605. + if (fix6485605 != null) { + fix6485605.resume(); + } else { + // apparently, there is nothing to resume. + } + } + suspendPolicy = JDWP.SuspendPolicy.NONE; + + } + + } + + /** + * Filter out internal events + */ + EventSet userFilter() { + return this; + } + + /** + * Filter out user events. + */ + EventSet internalFilter() { + return this.internalEventSet; + } + + EventImpl createEvent(JDWP.Event.Composite.Events evt) { + JDWP.Event.Composite.Events.EventsCommon comm = evt.aEventsCommon; + switch (evt.eventKind) { + case JDWP.EventKind.THREAD_START: + return new ThreadStartEventImpl( + (JDWP.Event.Composite.Events.ThreadStart)comm); + + case JDWP.EventKind.THREAD_END: + return new ThreadDeathEventImpl( + (JDWP.Event.Composite.Events.ThreadDeath)comm); + + case JDWP.EventKind.EXCEPTION: + return new ExceptionEventImpl( + (JDWP.Event.Composite.Events.Exception)comm); + + case JDWP.EventKind.BREAKPOINT: + return new BreakpointEventImpl( + (JDWP.Event.Composite.Events.Breakpoint)comm); + + case JDWP.EventKind.METHOD_ENTRY: + return new MethodEntryEventImpl( + (JDWP.Event.Composite.Events.MethodEntry)comm); + + case JDWP.EventKind.METHOD_EXIT: + return new MethodExitEventImpl( + (JDWP.Event.Composite.Events.MethodExit)comm); + + case JDWP.EventKind.METHOD_EXIT_WITH_RETURN_VALUE: + return new MethodExitEventImpl( + (JDWP.Event.Composite.Events.MethodExitWithReturnValue)comm); + + case JDWP.EventKind.FIELD_ACCESS: + return new AccessWatchpointEventImpl( + (JDWP.Event.Composite.Events.FieldAccess)comm); + + case JDWP.EventKind.FIELD_MODIFICATION: + return new ModificationWatchpointEventImpl( + (JDWP.Event.Composite.Events.FieldModification)comm); + + case JDWP.EventKind.SINGLE_STEP: + return new StepEventImpl( + (JDWP.Event.Composite.Events.SingleStep)comm); + + case JDWP.EventKind.CLASS_PREPARE: + return new ClassPrepareEventImpl( + (JDWP.Event.Composite.Events.ClassPrepare)comm); + + case JDWP.EventKind.CLASS_UNLOAD: + return new ClassUnloadEventImpl( + (JDWP.Event.Composite.Events.ClassUnload)comm); + + case JDWP.EventKind.MONITOR_CONTENDED_ENTER: + return new MonitorContendedEnterEventImpl( + (JDWP.Event.Composite.Events.MonitorContendedEnter)comm); + + case JDWP.EventKind.MONITOR_CONTENDED_ENTERED: + return new MonitorContendedEnteredEventImpl( + (JDWP.Event.Composite.Events.MonitorContendedEntered)comm); + + case JDWP.EventKind.MONITOR_WAIT: + return new MonitorWaitEventImpl( + (JDWP.Event.Composite.Events.MonitorWait)comm); + + case JDWP.EventKind.MONITOR_WAITED: + return new MonitorWaitedEventImpl( + (JDWP.Event.Composite.Events.MonitorWaited)comm); + + case JDWP.EventKind.VM_START: + return new VMStartEventImpl( + (JDWP.Event.Composite.Events.VMStart)comm); + + case JDWP.EventKind.VM_DEATH: + return new VMDeathEventImpl( + (JDWP.Event.Composite.Events.VMDeath)comm); + + default: + // Ignore unknown event types + System.err.println("Ignoring event cmd " + + evt.eventKind + " from the VM"); + return null; + } + } + + public VirtualMachine virtualMachine() { + return vm; + } + + public int suspendPolicy() { + return EventRequestManagerImpl.JDWPtoJDISuspendPolicy(suspendPolicy); + } + + private ThreadReference eventThread() { + Iterator iter = this.iterator(); + while (iter.hasNext()) { + Event event = (Event)iter.next(); + if (event instanceof ThreadedEventImpl) { + return ((ThreadedEventImpl)event).thread(); + } + } + return null; + } + + public void resume() { + switch (suspendPolicy()) { + case EventRequest.SUSPEND_ALL: + vm.resume(); + break; + case EventRequest.SUSPEND_EVENT_THREAD: + ThreadReference thread = eventThread(); + if (thread == null) { + throw new InternalException("Inconsistent suspend policy"); + } + thread.resume(); + break; + case EventRequest.SUSPEND_NONE: + // Do nothing + break; + default: + throw new InternalException("Invalid suspend policy"); + } + } + + public Iterator iterator() { + return new Itr(); + } + + public EventIterator eventIterator() { + return new Itr(); + } + + public class Itr implements EventIterator { + /** + * Index of element to be returned by subsequent call to next. + */ + int cursor = 0; + + public boolean hasNext() { + return cursor != size(); + } + + public Event next() { + try { + Event nxt = get(cursor); + ++cursor; + return nxt; + } catch(IndexOutOfBoundsException e) { + throw new NoSuchElementException(); + } + } + + public Event nextEvent() { + return (Event)next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /* below make this unmodifiable */ + + public boolean add(Event o){ + throw new UnsupportedOperationException(); + } + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + public boolean addAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public boolean removeAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public boolean retainAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public void clear() { + throw new UnsupportedOperationException(); + } +}