1442 lines · 47.9 KB · Java
RawDownload
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.jdi; import com.sun.jdi.*;import com.sun.jdi.connect.spi.Connection;import com.sun.jdi.request.EventRequestManager;import com.sun.jdi.request.EventRequest;import com.sun.jdi.request.BreakpointRequest;import com.sun.jdi.event.EventQueue; import java.util.*;import java.text.MessageFormat;import java.lang.ref.ReferenceQueue;import java.lang.ref.Reference;import java.lang.ref.SoftReference;import java.lang.ref.WeakReference; class VirtualMachineImpl extends MirrorImpl             implements PathSearchingVirtualMachine, ThreadListener {    // VM Level exported variables, these    // are unique to a given vm    public final int sizeofFieldRef;    public final int sizeofMethodRef;    public final int sizeofObjectRef;    public final int sizeofClassRef;    public final int sizeofFrameRef;     final int sequenceNumber;     private final TargetVM target;    private final EventQueueImpl eventQueue;    private final EventRequestManagerImpl internalEventRequestManager;    private final EventRequestManagerImpl eventRequestManager;    final VirtualMachineManagerImpl vmManager;    private final ThreadGroup threadGroupForJDI;     // Allow direct access to this field so that that tracing code slows down    // JDI as little as possible when not enabled.    int traceFlags = TRACE_NONE;     static int TRACE_RAW_SENDS     = 0x01000000;    static int TRACE_RAW_RECEIVES  = 0x02000000;     boolean traceReceives = false;   // pre-compute because of frequency     // ReferenceType access - updated with class prepare and unload events    // Protected by "synchronized(this)". "retrievedAllTypes" may be    // tested unsynchronized (since once true, it stays true), but must    // be set synchronously    private Map<Long, ReferenceType> typesByID;    private TreeSet<ReferenceType> typesBySignature;    private boolean retrievedAllTypes = false;     // For other languages support    private String defaultStratum = null;     // ObjectReference cache    // "objectsByID" protected by "synchronized(this)".    private final Map<Long, SoftObjectReference> objectsByID = new HashMap<Long, SoftObjectReference>();    private final ReferenceQueue<ObjectReferenceImpl> referenceQueue = new ReferenceQueue<ObjectReferenceImpl>();    static private final int DISPOSE_THRESHOLD = 50;    private final List<SoftObjectReference> batchedDisposeRequests =            Collections.synchronizedList(new ArrayList<SoftObjectReference>(DISPOSE_THRESHOLD + 10));     // These are cached once for the life of the VM    private JDWP.VirtualMachine.Version versionInfo;    private JDWP.VirtualMachine.ClassPaths pathInfo;    private JDWP.VirtualMachine.Capabilities capabilities = null;    private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew = null;     // Per-vm singletons for primitive types and for void.    // singleton-ness protected by "synchronized(this)".    private BooleanType theBooleanType;    private ByteType    theByteType;    private CharType    theCharType;    private ShortType   theShortType;    private IntegerType theIntegerType;    private LongType    theLongType;    private FloatType   theFloatType;    private DoubleType  theDoubleType;     private VoidType    theVoidType;     private VoidValue voidVal;     // Launched debuggee process    private Process process;     // coordinates state changes and corresponding listener notifications    private VMState state = new VMState(this);     private Object initMonitor = new Object();    private boolean initComplete = false;    private boolean shutdown = false;     private void notifyInitCompletion() {        synchronized(initMonitor) {            initComplete = true;            initMonitor.notifyAll();        }    }     void waitInitCompletion() {        synchronized(initMonitor) {            while (!initComplete) {                try {                    initMonitor.wait();                } catch (InterruptedException e) {                    // ignore                }            }        }    }     VMState state() {        return state;    }     /*     * ThreadListener implementation     */    public boolean threadResumable(ThreadAction action) {        /*         * If any thread is resumed, the VM is considered not suspended.         * Just one thread is being resumed so pass it to thaw.         */        state.thaw(action.thread());        return true;    }     VirtualMachineImpl(VirtualMachineManager manager,                       Connection connection, Process process,                       int sequenceNumber) {        super(null);  // Can't use super(this)        vm = this;         this.vmManager = (VirtualMachineManagerImpl)manager;        this.process = process;        this.sequenceNumber = sequenceNumber;         /* Create ThreadGroup to be used by all threads servicing         * this VM.         */        threadGroupForJDI = new ThreadGroup(vmManager.mainGroupForJDI(),                                            "JDI [" +                                            this.hashCode() + "]");         /*         * Set up a thread to communicate with the target VM over         * the specified transport.         */        target = new TargetVM(this, connection);         /*         * Set up a thread to handle events processed internally         * the JDI implementation.         */        EventQueueImpl internalEventQueue = new EventQueueImpl(this, target);        new InternalEventHandler(this, internalEventQueue);        /*         * Initialize client access to event setting and handling         */        eventQueue = new EventQueueImpl(this, target);        eventRequestManager = new EventRequestManagerImpl(this);         target.start();         /*         * Many ids are variably sized, depending on target VM.         * Find out the sizes right away.         */        JDWP.VirtualMachine.IDSizes idSizes;        try {            idSizes = JDWP.VirtualMachine.IDSizes.process(vm);        } catch (JDWPException exc) {            throw exc.toJDIException();        }        sizeofFieldRef  = idSizes.fieldIDSize;        sizeofMethodRef = idSizes.methodIDSize;        sizeofObjectRef = idSizes.objectIDSize;        sizeofClassRef = idSizes.referenceTypeIDSize;        sizeofFrameRef  = idSizes.frameIDSize;         /**         * Set up requests needed by internal event handler.         * Make sure they are distinguished by creating them with         * an internal event request manager.         *         * Warning: create events only with SUSPEND_NONE policy.         * In the current implementation other policies will not         * be handled correctly when the event comes in. (notfiySuspend()         * will not be properly called, and if the event is combined         * with external events in the same set, suspend policy is not         * correctly determined for the internal vs. external event sets)         */        internalEventRequestManager = new EventRequestManagerImpl(this);        EventRequest er = internalEventRequestManager.createClassPrepareRequest();        er.setSuspendPolicy(EventRequest.SUSPEND_NONE);        er.enable();        er = internalEventRequestManager.createClassUnloadRequest();        er.setSuspendPolicy(EventRequest.SUSPEND_NONE);        er.enable();         /*         * Tell other threads, notably TargetVM, that initialization         * is complete.         */        notifyInitCompletion();    }     EventRequestManagerImpl getInternalEventRequestManager() {        return internalEventRequestManager;    }     void validateVM() {        /*         * We no longer need to do this.  The spec now says         * that a VMDisconnected _may_ be thrown in these         * cases, not that it _will_ be thrown.         * So, to simplify things we will just let the         * caller's of this method proceed with their business.         * If the debuggee is disconnected, either because it         * crashed or finished or something, or because the         * debugger called exit() or dispose(), then if         * we end up trying to communicate with the debuggee,         * code in TargetVM will throw a VMDisconnectedException.         * This means that if we can satisfy a request without         * talking to the debuggee, (eg, with cached data) then         * VMDisconnectedException will _not_ be thrown.         * if (shutdown) {         *    throw new VMDisconnectedException();         * }         */    }     public boolean equals(Object obj) {        return this == obj;    }     public int hashCode() {        return System.identityHashCode(this);    }     public List<ReferenceType> classesByName(String className) {        validateVM();        String signature = JNITypeParser.typeNameToSignature(className);        List<ReferenceType> list;        if (retrievedAllTypes) {           list = findReferenceTypes(signature);        } else {           list = retrieveClassesBySignature(signature);        }        return Collections.unmodifiableList(list);    }     public List<ReferenceType> allClasses() {        validateVM();         if (!retrievedAllTypes) {            retrieveAllClasses();        }        ArrayList<ReferenceType> a;        synchronized (this) {            a = new ArrayList<ReferenceType>(typesBySignature);        }        return Collections.unmodifiableList(a);    }     public void        redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes)    {        int cnt = classToBytes.size();        JDWP.VirtualMachine.RedefineClasses.ClassDef[] defs =            new JDWP.VirtualMachine.RedefineClasses.ClassDef[cnt];        validateVM();        if (!canRedefineClasses()) {            throw new UnsupportedOperationException();        }        Iterator it = classToBytes.entrySet().iterator();        for (int i = 0; it.hasNext(); i++) {            Map.Entry entry = (Map.Entry)it.next();            ReferenceTypeImpl refType = (ReferenceTypeImpl)entry.getKey();            validateMirror(refType);            defs[i] = new JDWP.VirtualMachine.RedefineClasses                       .ClassDef(refType, (byte[])entry.getValue());        }         // flush caches and disable caching until the next suspend        vm.state().thaw();         try {            JDWP.VirtualMachine.RedefineClasses.                process(vm, defs);        } catch (JDWPException exc) {            switch (exc.errorCode()) {            case JDWP.Error.INVALID_CLASS_FORMAT :                throw new ClassFormatError(                        "class not in class file format");            case JDWP.Error.CIRCULAR_CLASS_DEFINITION :                throw new ClassCircularityError(       "circularity has been detected while initializing a class");            case JDWP.Error.FAILS_VERIFICATION :                throw new VerifyError(   "verifier detected internal inconsistency or security problem");            case JDWP.Error.UNSUPPORTED_VERSION :                throw new UnsupportedClassVersionError(                    "version numbers of class are not supported");            case JDWP.Error.ADD_METHOD_NOT_IMPLEMENTED:                throw new UnsupportedOperationException(                              "add method not implemented");            case JDWP.Error.SCHEMA_CHANGE_NOT_IMPLEMENTED :                throw new UnsupportedOperationException(                              "schema change not implemented");            case JDWP.Error.HIERARCHY_CHANGE_NOT_IMPLEMENTED:                throw new UnsupportedOperationException(                              "hierarchy change not implemented");            case JDWP.Error.DELETE_METHOD_NOT_IMPLEMENTED :                throw new UnsupportedOperationException(                              "delete method not implemented");            case JDWP.Error.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:                throw new UnsupportedOperationException(                       "changes to class modifiers not implemented");            case JDWP.Error.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED :                throw new UnsupportedOperationException(                       "changes to method modifiers not implemented");            case JDWP.Error.NAMES_DONT_MATCH :                throw new NoClassDefFoundError(                              "class names do not match");            default:                throw exc.toJDIException();            }        }         // Delete any record of the breakpoints        List<BreakpointRequest> toDelete = new ArrayList<BreakpointRequest>();        EventRequestManager erm = eventRequestManager();        it = erm.breakpointRequests().iterator();        while (it.hasNext()) {            BreakpointRequest req = (BreakpointRequest)it.next();            if (classToBytes.containsKey(req.location().declaringType())) {                toDelete.add(req);            }        }        erm.deleteEventRequests(toDelete);         // Invalidate any information cached for the classes just redefined.        it = classToBytes.keySet().iterator();        while (it.hasNext()) {            ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next();            rti.noticeRedefineClass();        }    }     public List<ThreadReference> allThreads() {        validateVM();        return state.allThreads();    }     public List<ThreadGroupReference> topLevelThreadGroups() {        validateVM();        return state.topLevelThreadGroups();    }     /*     * Sends a command to the back end which is defined to do an     * implicit vm-wide resume. The VM can no longer be considered     * suspended, so certain cached data must be invalidated.     */    PacketStream sendResumingCommand(CommandSender sender) {        return state.thawCommand(sender);    }     /*     * The VM has been suspended. Additional caching can be done     * as long as there are no pending resumes.     */    void notifySuspend() {        state.freeze();    }     public void suspend() {        validateVM();        try {            JDWP.VirtualMachine.Suspend.process(vm);        } catch (JDWPException exc) {            throw exc.toJDIException();        }        notifySuspend();    }     public void resume() {        validateVM();        CommandSender sender =            new CommandSender() {                public PacketStream send() {                    return JDWP.VirtualMachine.Resume.enqueueCommand(vm);                }        };        try {            PacketStream stream = state.thawCommand(sender);            JDWP.VirtualMachine.Resume.waitForReply(vm, stream);        } catch (VMDisconnectedException exc) {            /*             * If the debugger makes a VMDeathRequest with SUSPEND_ALL,             * then when it does an EventSet.resume after getting the             * VMDeathEvent, the normal flow of events is that the             * BE shuts down, but the waitForReply comes back ok.  In this             * case, the run loop in TargetVM that is waiting for a packet             * gets an EOF because the socket closes. It generates a             * VMDisconnectedEvent and everyone is happy.             * However, sometimes, the BE gets shutdown before this             * waitForReply completes.  In this case, TargetVM.waitForReply             * gets awakened with no reply and so gens a VMDisconnectedException             * which is not what we want.  It might be possible to fix this             * in the BE, but it is ok to just ignore the VMDisconnectedException             * here.  This will allow the VMDisconnectedEvent to be generated             * correctly.  And, if the debugger should happen to make another             * request, it will get a VMDisconnectedException at that time.             */        } catch (JDWPException exc) {            switch (exc.errorCode()) {                case JDWP.Error.VM_DEAD:                    return;                default:                    throw exc.toJDIException();            }        }    }     public EventQueue eventQueue() {        /*         * No VM validation here. We allow access to the event queue         * after disconnection, so that there is access to the terminating         * events.         */        return eventQueue;    }     public EventRequestManager eventRequestManager() {        validateVM();        return eventRequestManager;    }     EventRequestManagerImpl eventRequestManagerImpl() {        return eventRequestManager;    }     public BooleanValue mirrorOf(boolean value) {        validateVM();        return new BooleanValueImpl(this,value);    }     public ByteValue mirrorOf(byte value) {        validateVM();        return new ByteValueImpl(this,value);    }     public CharValue mirrorOf(char value) {        validateVM();        return new CharValueImpl(this,value);    }     public ShortValue mirrorOf(short value) {        validateVM();        return new ShortValueImpl(this,value);    }     public IntegerValue mirrorOf(int value) {        validateVM();        return new IntegerValueImpl(this,value);    }     public LongValue mirrorOf(long value) {        validateVM();        return new LongValueImpl(this,value);    }

Lines 1–500 of 1442.