| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdi.internal; |
| |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdi.Bootstrap; |
| import org.eclipse.jdi.internal.connect.PacketReceiveManager; |
| import org.eclipse.jdi.internal.connect.PacketSendManager; |
| import org.eclipse.jdi.internal.event.EventQueueImpl; |
| import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket; |
| import org.eclipse.jdi.internal.jdwp.JdwpObjectID; |
| import org.eclipse.jdi.internal.jdwp.JdwpReferenceTypeID; |
| import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket; |
| import org.eclipse.jdi.internal.request.EventRequestManagerImpl; |
| |
| import com.sun.jdi.BooleanValue; |
| import com.sun.jdi.ByteValue; |
| import com.sun.jdi.CharValue; |
| import com.sun.jdi.DoubleValue; |
| import com.sun.jdi.FloatValue; |
| import com.sun.jdi.IntegerValue; |
| import com.sun.jdi.LongValue; |
| import com.sun.jdi.ObjectCollectedException; |
| import com.sun.jdi.ShortValue; |
| import com.sun.jdi.StringReference; |
| import com.sun.jdi.VMDisconnectedException; |
| import com.sun.jdi.VirtualMachine; |
| import com.sun.jdi.connect.spi.Connection; |
| import com.sun.jdi.event.EventQueue; |
| import com.sun.jdi.request.EventRequestManager; |
| |
| /** |
| * This class implements the corresponding interfaces |
| * declared by the JDI specification. See the com.sun.jdi package |
| * for more information. |
| * |
| */ |
| public class VirtualMachineImpl extends MirrorImpl implements VirtualMachine, org.eclipse.jdi.hcr.VirtualMachine, org.eclipse.jdi.VirtualMachine { |
| /** Result flags for Classes Have Changed command. */ |
| public static final byte HCR_RELOAD_SUCCESS = 0; |
| public static final byte HCR_RELOAD_FAILURE = 1; |
| public static final byte HCR_RELOAD_IGNORED = 2; |
| |
| /* Indexes in HCR capabilities list.*/ |
| private static final int HCR_CAN_RELOAD_CLASSES = 0; |
| private static final int HCR_CAN_GET_CLASS_VERSION = 1; |
| private static final int HCR_CAN_DO_RETURN = 2; |
| private static final int HCR_CAN_REENTER_ON_EXIT = 3; |
| |
| protected static final String JAVA_STRATUM_NAME= "Java"; //$NON-NLS-1$ |
| |
| /** Timeout value for requests to VM if not overriden for a particular VM. */ |
| private int fRequestTimeout; |
| /** Mapping of command codes to strings. */ |
| |
| private static Map fgHCRResultMap = null; |
| |
| /** EventRequestManager that creates event objects on request. */ |
| private EventRequestManagerImpl fEventReqMgr; |
| /** EventQueue that returns EventSets from the Virtual Manager. */ |
| private EventQueueImpl fEventQueue; |
| |
| /** If a launchingconnector is used, we store the process. */ |
| private Process fLaunchedProcess; |
| |
| /** |
| * The following field contains cached Mirrors. |
| * Note that these are optional: their only purpose is to speed up the debugger by |
| * being able to use the stored results of JDWP calls. |
| */ |
| private ValueCache fCachedReftypes = new ValueCache(); |
| private ValueCache fCachedObjects = new ValueCache(); |
| |
| /** The following are the stored results of JDWP calls. */ |
| private String fVersionDescription = null; // Text information on the VM version. |
| private int fJdwpMajorVersion; |
| private int fJdwpMinorVersion; |
| private String fVMVersion; // Target VM JRE version, as in the java.version property. |
| private String fVMName; // Target VM name, as in the java.vm.name property. |
| private boolean fGotIDSizes = false; |
| private int fFieldIDSize; |
| private int fMethodIDSize; |
| private int fObjectIDSize; |
| private int fReferenceTypeIDSize; |
| private int fFrameIDSize; |
| |
| private boolean fGotCapabilities = false; |
| private boolean fCanWatchFieldModification; |
| private boolean fCanWatchFieldAccess; |
| private boolean fCanGetBytecodes; |
| private boolean fCanGetSyntheticAttribute; |
| private boolean fCanGetOwnedMonitorInfo; |
| private boolean fCanGetCurrentContendedMonitor; |
| private boolean fCanGetMonitorInfo; |
| private boolean fCanRedefineClasses; |
| private boolean fCanAddMethod; |
| private boolean fCanUnrestrictedlyRedefineClasses; |
| private boolean fCanPopFrames; |
| private boolean fCanUseInstanceFilters; |
| private boolean fCanGetSourceDebugExtension; |
| private boolean fCanRequestVMDeathEvent; |
| private boolean fCanSetDefaultStratum; |
| private boolean[] fHcrCapabilities = null; |
| |
| /* |
| * singletons for primitive types |
| */ |
| private BooleanTypeImpl fBooleanType; |
| private ByteTypeImpl fByteType; |
| private CharTypeImpl fCharType; |
| private DoubleTypeImpl fDoubleType; |
| private FloatTypeImpl fFloatType; |
| private IntegerTypeImpl fIntegerType; |
| private LongTypeImpl fLongType; |
| private ShortTypeImpl fShortType; |
| |
| /** |
| * Disconnected flag |
| */ |
| private boolean fIsDisconnected = false; |
| |
| /** |
| * The name of the current default stratum. |
| */ |
| private String fDefaultStratum; |
| private PacketReceiveManager fPacketReceiveManager; |
| private PacketSendManager fPacketSendManager; |
| |
| /** |
| * Creates a new Virtual Machine. |
| */ |
| public VirtualMachineImpl(Connection connection) { |
| super("VirtualMachine"); //$NON-NLS-1$ |
| fEventReqMgr = new EventRequestManagerImpl(this); |
| fEventQueue = new EventQueueImpl(this); |
| fRequestTimeout = ((VirtualMachineManagerImpl) Bootstrap.virtualMachineManager()).getGlobalRequestTimeout(); |
| |
| fPacketReceiveManager = new PacketReceiveManager(connection, this); |
| Thread receiveThread = new Thread(fPacketReceiveManager, JDIMessages.VirtualMachineImpl_0); |
| receiveThread.setDaemon(true); |
| fPacketReceiveManager.setPartnerThread(receiveThread); |
| receiveThread.start(); |
| |
| fPacketSendManager = new PacketSendManager(connection); |
| Thread sendThread = new Thread(fPacketSendManager, JDIMessages.VirtualMachineImpl_1); |
| sendThread.setDaemon(true); |
| fPacketReceiveManager.setPartnerThread(sendThread); |
| sendThread.start(); |
| } |
| |
| /** |
| * @return Returns size of JDWP ID. |
| */ |
| public final int fieldIDSize() { |
| return fFieldIDSize; |
| } |
| |
| /** |
| * @return Returns size of JDWP ID. |
| */ |
| public final int methodIDSize() { |
| return fMethodIDSize; |
| } |
| |
| /** |
| * @return Returns size of JDWP ID. |
| */ |
| public final int objectIDSize() { |
| return fObjectIDSize; |
| } |
| |
| /** |
| * @return Returns size of JDWP ID. |
| */ |
| public final int referenceTypeIDSize() { |
| return fReferenceTypeIDSize; |
| } |
| |
| /** |
| * @return Returns size of JDWP ID. |
| */ |
| public final int frameIDSize() { |
| return fFrameIDSize; |
| } |
| |
| /** |
| * @return Returns cached mirror object, or null if method is not in cache. |
| */ |
| public ReferenceTypeImpl getCachedMirror(JdwpReferenceTypeID ID) { |
| return (ReferenceTypeImpl)fCachedReftypes.get(ID); |
| } |
| |
| /** |
| * @return Returns cached mirror object, or null if method is not in cache. |
| */ |
| public ObjectReferenceImpl getCachedMirror(JdwpObjectID ID) { |
| return (ObjectReferenceImpl)fCachedObjects.get(ID); |
| } |
| |
| /** |
| * Adds mirror object to cache. |
| */ |
| public void addCachedMirror(ReferenceTypeImpl mirror) { |
| fCachedReftypes.put(mirror.getRefTypeID(), mirror); |
| // tbd: It is now yet possible to only ask for unload events for |
| // classes that we know of due to a limitation in the J9 VM. |
| // eventRequestManagerImpl().enableInternalClasUnloadEvent(mirror); |
| } |
| |
| /** |
| * Adds mirror object to cache. |
| */ |
| public void addCachedMirror(ObjectReferenceImpl mirror) { |
| fCachedObjects.put(mirror.getObjectID(), mirror); |
| } |
| |
| /** |
| * Flushes all stored Jdwp results. |
| */ |
| public void flushStoredJdwpResults() { |
| // All known classes also become invalid. |
| Iterator iter = fCachedReftypes.values().iterator(); |
| while (iter.hasNext()) { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl)iter.next(); |
| refType.flushStoredJdwpResults(); |
| } |
| |
| fVersionDescription = null; |
| fGotIDSizes = false; |
| fHcrCapabilities = null; |
| } |
| |
| /* |
| * Removes a known class. |
| * A class/interface is known if we have ever received its ReferenceTypeID and we have |
| * not received an unload event for it. |
| */ |
| public final void removeKnownRefType(String signature) { |
| List refTypeList = classesBySignature(signature); |
| if (refTypeList.isEmpty()) |
| return; |
| |
| // If we have only one known class for this signature, we known that this is the class |
| // to be removed. |
| if (refTypeList.size() == 1) { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl)refTypeList.get(0); |
| refType.flushStoredJdwpResults(); |
| fCachedReftypes.remove(refType.getRefTypeID()); |
| return; |
| } |
| |
| // We have more than one known class for the signature, let's find the unloaded one(s). |
| Iterator iter = refTypeList.iterator(); |
| while (iter.hasNext()) { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl)iter.next(); |
| boolean prepared= false; |
| try { |
| prepared= refType.isPrepared(); |
| } catch (ObjectCollectedException exception) { |
| // The type is unloaded. Fall through |
| } |
| if (!prepared) { |
| refType.flushStoredJdwpResults(); |
| iter.remove(); |
| fCachedReftypes.remove(refType.getRefTypeID()); |
| } |
| } |
| } |
| |
| /* |
| * @exception Throws UnsupportedOperationException if VM does not support J9 HCR. |
| */ |
| public void checkHCRSupported() throws UnsupportedOperationException { |
| if (!isHCRSupported()) |
| throw new UnsupportedOperationException(MessageFormat.format(JDIMessages.VirtualMachineImpl_Target_VM__0__does_not_support_Hot_Code_Replacement_1, new String[]{name()})); |
| } |
| |
| /* |
| * Returns whether J9 HCR is supported |
| */ |
| public boolean isHCRSupported() throws UnsupportedOperationException { |
| return name().equals("j9"); //$NON-NLS-1$ |
| } |
| |
| /* |
| * @return Returns Manager for receiving packets from the Virtual Machine. |
| */ |
| public final PacketReceiveManager packetReceiveManager() { |
| return fPacketReceiveManager; |
| } |
| |
| /* |
| * @return Returns Manager for sending packets to the Virtual Machine. |
| */ |
| public final PacketSendManager packetSendManager() { |
| /* |
| * Before we send out first bytes to the VM by JDI calls, we need some initial requests: |
| * - Get the sizes of the IDs (fieldID, method ID etc.) that the VM uses; |
| * - Request class prepare and unload events. We used these to cache classes/interfaces and map their signatures. |
| */ |
| if (!fGotIDSizes) { |
| getIDSizes(); |
| if (!fGotIDSizes) { // We can't do much without them. |
| disconnectVM(); |
| throw new VMDisconnectedException(JDIMessages.VirtualMachineImpl_Failed_to_get_ID_sizes_2); |
| } |
| |
| // tbd: This call should be moved to addKnownRefType() when it can be made specific |
| // for a referencetype. |
| eventRequestManagerImpl().enableInternalClasUnloadEvent(); |
| } |
| |
| return fPacketSendManager; |
| } |
| |
| /** |
| * Returns all loaded types (classes, interfaces, and array types). |
| * For each loaded type in the target VM a ReferenceType will be placed in the returned list. |
| */ |
| public List allClasses() { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| boolean withGenericSignature= virtualMachineImpl().isJdwpVersionGreaterOrEqual(1, 5); |
| int jdwpCommand= withGenericSignature ? JdwpCommandPacket.VM_ALL_CLASSES_WITH_GENERIC : JdwpCommandPacket.VM_ALL_CLASSES; |
| JdwpReplyPacket replyPacket = requestVM(jdwpCommand); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| List elements = new ArrayList(nrOfElements); |
| for (int i = 0; i < nrOfElements; i++) { |
| ReferenceTypeImpl elt = ReferenceTypeImpl.readWithTypeTagAndSignature(this, withGenericSignature, replyData); |
| if (elt == null) { |
| continue; |
| } |
| readInt("status", ReferenceTypeImpl.classStatusStrings(), replyData); //$NON-NLS-1$ |
| elements.add(elt); |
| } |
| return elements; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| |
| } |
| |
| /** |
| * @return Returns an iterator over all loaded classes. |
| */ |
| protected final Iterator allRefTypes() { |
| return allClasses().iterator(); |
| } |
| |
| /** |
| * @return Returns an iterator over all cached classes. |
| */ |
| protected final Iterator allCachedRefTypes() { |
| return fCachedReftypes.values().iterator(); |
| } |
| |
| /** |
| * Returns a list of the currently running threads. |
| * For each running thread in the target VM, a ThreadReference that mirrors it is placed in the list. |
| */ |
| public List allThreads() { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_ALL_THREADS); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| List elements = new ArrayList(nrOfElements); |
| for (int i = 0; i < nrOfElements; i++) { |
| ThreadReferenceImpl elt = ThreadReferenceImpl.read(this, replyData); |
| if (elt == null) { |
| continue; |
| } |
| elements.add(elt); |
| } |
| return elements; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Retrieve this VM's capabilities. |
| */ |
| public void getCapabilities() { |
| if (fGotCapabilities) |
| return; |
| |
| int command = JdwpCommandPacket.VM_CAPABILITIES; |
| if (isJdwpVersionGreaterOrEqual(1, 4)) { |
| command = JdwpCommandPacket.VM_CAPABILITIES_NEW; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(command); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| |
| fCanWatchFieldModification = readBoolean("watch field modification", replyData); //$NON-NLS-1$ |
| fCanWatchFieldAccess = readBoolean("watch field access", replyData); //$NON-NLS-1$ |
| fCanGetBytecodes = readBoolean("get bytecodes", replyData); //$NON-NLS-1$ |
| fCanGetSyntheticAttribute = readBoolean("synth. attr", replyData); //$NON-NLS-1$ |
| fCanGetOwnedMonitorInfo = readBoolean("owned monitor info", replyData); //$NON-NLS-1$ |
| fCanGetCurrentContendedMonitor = readBoolean("curr. contended monitor", replyData); //$NON-NLS-1$ |
| fCanGetMonitorInfo = readBoolean("monitor info", replyData); //$NON-NLS-1$ |
| if (command == JdwpCommandPacket.VM_CAPABILITIES_NEW) { |
| // extended capabilities |
| fCanRedefineClasses = readBoolean("redefine classes", replyData); //$NON-NLS-1$ |
| fCanAddMethod = readBoolean("add method", replyData); //$NON-NLS-1$ |
| fCanUnrestrictedlyRedefineClasses = readBoolean("unrestrictedly redefine classes", replyData); //$NON-NLS-1$ |
| fCanPopFrames = readBoolean("pop frames", replyData); //$NON-NLS-1$ |
| fCanUseInstanceFilters = readBoolean("use instance filters", replyData); //$NON-NLS-1$ |
| fCanGetSourceDebugExtension = readBoolean("get source debug extension", replyData); //$NON-NLS-1$ |
| fCanRequestVMDeathEvent = readBoolean("request vm death", replyData); //$NON-NLS-1$ |
| fCanSetDefaultStratum= readBoolean("set default stratum", replyData); //$NON-NLS-1$ |
| } else { |
| fCanRedefineClasses = false; |
| fCanAddMethod = false; |
| fCanUnrestrictedlyRedefineClasses = false; |
| fCanPopFrames = false; |
| fCanUseInstanceFilters = false; |
| fCanGetSourceDebugExtension = false; |
| fCanRequestVMDeathEvent = false; |
| fCanSetDefaultStratum= false; |
| } |
| fGotCapabilities = true; |
| } catch (IOException e) { |
| fGotIDSizes = false; |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns true if this implementation supports the retrieval of a method's bytecodes. |
| */ |
| public boolean canGetBytecodes() { |
| getCapabilities(); |
| return fCanGetBytecodes; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports the retrieval of the monitor for which a thread is currently waiting. |
| */ |
| public boolean canGetCurrentContendedMonitor() { |
| getCapabilities(); |
| return fCanGetCurrentContendedMonitor; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports the retrieval of the monitor information for an object. |
| */ |
| public boolean canGetMonitorInfo() { |
| getCapabilities(); |
| return fCanGetMonitorInfo; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports the retrieval of the monitors owned by a thread. |
| */ |
| public boolean canGetOwnedMonitorInfo() { |
| getCapabilities(); |
| return fCanGetOwnedMonitorInfo; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports the query of the synthetic attribute of a method or field. |
| |
| */ |
| public boolean canGetSyntheticAttribute() { |
| getCapabilities(); |
| return fCanGetSyntheticAttribute; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports watchpoints for field access. |
| */ |
| public boolean canWatchFieldAccess() { |
| getCapabilities(); |
| return fCanWatchFieldAccess; |
| } |
| |
| /** |
| * @return Returns true if this implementation supports watchpoints for field modification. |
| */ |
| public boolean canWatchFieldModification() { |
| getCapabilities(); |
| return fCanWatchFieldModification; |
| } |
| |
| /** |
| * @return Returns the loaded reference types that match a given signature. |
| */ |
| public List classesBySignature(String signature) { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeString(signature, "signature", outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_CLASSES_BY_SIGNATURE, outBytes); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| List elements = new ArrayList(nrOfElements); |
| for (int i = 0; i < nrOfElements; i++) { |
| ReferenceTypeImpl elt = ReferenceTypeImpl.readWithTypeTag(this, replyData); |
| readInt("status", ReferenceTypeImpl.classStatusStrings(), replyData); //$NON-NLS-1$ |
| if (elt == null) { |
| continue; |
| } |
| elements.add(elt); |
| } |
| return elements; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns the loaded reference types that match a given name. |
| */ |
| public List classesByName(String name) { |
| String signature = TypeImpl.classNameToSignature(name); |
| return classesBySignature(signature); |
| } |
| |
| /** |
| * Invalidates this virtual machine mirror. |
| */ |
| public void dispose() { |
| initJdwpRequest(); |
| try { |
| requestVM(JdwpCommandPacket.VM_DISPOSE); |
| disconnectVM(); |
| } catch (VMDisconnectedException e) { |
| // The VM can exit before we receive the reply. |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns EventQueue that returns EventSets from the Virtual Manager. |
| */ |
| public EventQueue eventQueue() { |
| return fEventQueue; |
| } |
| |
| /** |
| * @return Returns EventRequestManager that creates all event objects on request. |
| */ |
| public EventRequestManager eventRequestManager() { |
| return fEventReqMgr; |
| } |
| |
| /** |
| * @return Returns EventRequestManagerImpl that creates all event objects on request. |
| */ |
| public EventRequestManagerImpl eventRequestManagerImpl() { |
| return fEventReqMgr; |
| } |
| |
| /** |
| * Causes the mirrored VM to terminate with the given error code. |
| */ |
| public void exit(int exitCode) { |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeInt(exitCode, "exit code", outData); //$NON-NLS-1$ |
| requestVM(JdwpCommandPacket.VM_EXIT, outBytes); |
| disconnectVM(); |
| } catch (VMDisconnectedException e) { |
| // The VM can exit before we receive the reply. |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns newly created ByteValue for the given value. |
| */ |
| public ByteValue mirrorOf(byte value) { |
| return new ByteValueImpl(virtualMachineImpl(), new Byte(value)); |
| } |
| |
| /** |
| * @return Returns newly created CharValue for the given value. |
| */ |
| public CharValue mirrorOf(char value) { |
| return new CharValueImpl(virtualMachineImpl(), new Character(value)); |
| } |
| |
| /** |
| * @return Returns newly created DoubleValue for the given value. |
| */ |
| public DoubleValue mirrorOf(double value) { |
| return new DoubleValueImpl(virtualMachineImpl(), new Double(value)); |
| } |
| |
| /** |
| * @return Returns newly created FloatValue for the given value. |
| */ |
| public FloatValue mirrorOf(float value) { |
| return new FloatValueImpl(virtualMachineImpl(), new Float(value)); |
| } |
| |
| /** |
| * @return Returns newly created IntegerValue for the given value. |
| */ |
| public IntegerValue mirrorOf(int value) { |
| return new IntegerValueImpl(virtualMachineImpl(), new Integer(value)); |
| } |
| |
| /** |
| * @return Returns newly created LongValue for the given value. |
| */ |
| public LongValue mirrorOf(long value) { |
| return new LongValueImpl(virtualMachineImpl(), new Long(value)); |
| } |
| |
| /** |
| * @return Returns newly created ShortValue for the given value. |
| */ |
| public ShortValue mirrorOf(short value) { |
| return new ShortValueImpl(virtualMachineImpl(), new Short(value)); |
| } |
| |
| /** |
| * @return Returns newly created BooleanValue for the given value. |
| */ |
| public BooleanValue mirrorOf(boolean value) { |
| return new BooleanValueImpl(virtualMachineImpl(), Boolean.valueOf(value)); |
| } |
| |
| /** |
| * @return Returns newly created StringReference for the given value. |
| */ |
| public StringReference mirrorOf(String value) { |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeString(value, "string value", outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_CREATE_STRING, outBytes); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| StringReference result = StringReferenceImpl.read(this, replyData); |
| return result; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns the Process object for this virtual machine if launched by a LaunchingConnector. |
| */ |
| public Process process() { |
| return fLaunchedProcess; |
| } |
| |
| /** |
| * Sets Process object for this virtual machine if launched by a LaunchingConnector. |
| */ |
| public void setLaunchedProcess(Process proc) { |
| fLaunchedProcess = proc; |
| } |
| |
| /** |
| * Continues the execution of the application running in this virtual machine. |
| */ |
| public void resume() { |
| initJdwpRequest(); |
| try { |
| resetThreadEventFlags(); |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_RESUME); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| public void setDebugTraceMode(int traceFlags) { |
| // We don't have trace info. |
| } |
| |
| /** |
| * Suspends all threads. |
| */ |
| public void suspend() { |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_SUSPEND); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| public List topLevelThreadGroups() { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_TOP_LEVEL_THREAD_GROUPS); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int nrGroups = readInt("nr of groups", replyData); //$NON-NLS-1$ |
| ArrayList result = new ArrayList(nrGroups); |
| for (int i = 0; i < nrGroups; i++) { |
| ThreadGroupReferenceImpl threadGroup = ThreadGroupReferenceImpl.read(this, replyData); |
| result.add(threadGroup); |
| } |
| return result; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns the name of the target VM as reported by the property java.vm.name. |
| */ |
| public String name() { |
| getVersionInfo(); |
| return fVMName; |
| } |
| |
| /** |
| * @return Returns the version of the Java Runtime Environment in the target VM as reported by the property java.version. |
| */ |
| public String version() { |
| getVersionInfo(); |
| return fVMVersion; |
| } |
| |
| /** |
| * @return Returns text information on the target VM and the debugger support that mirrors it. |
| */ |
| public String description() { |
| getVersionInfo(); |
| return fVersionDescription; |
| } |
| |
| /** |
| * Reset event flags of all threads. |
| */ |
| private void resetThreadEventFlags() { |
| Iterator iter = allThreads().iterator(); |
| ThreadReferenceImpl thread; |
| while (iter.hasNext()) { |
| thread = (ThreadReferenceImpl)iter.next(); |
| thread.resetEventFlags(); |
| } |
| } |
| |
| /** |
| * Request and fetch ID sizes of Virtual Machine. |
| */ |
| private void getIDSizes() { |
| if (fGotIDSizes) |
| return; |
| |
| /* |
| * fGotIDSizes must first be assigned true to prevent an invinite loop |
| * because getIDSizes() calls requestVM which calls packetSendManager. |
| */ |
| fGotIDSizes = true; |
| |
| // We use a different mirror to avoid having verbose output mixed with the initiating command. |
| MirrorImpl mirror = new VoidValueImpl(this); |
| |
| mirror.initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = mirror.requestVM(JdwpCommandPacket.VM_ID_SIZES); |
| mirror.defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| |
| fFieldIDSize = mirror.readInt("field ID size", replyData); //$NON-NLS-1$ |
| fMethodIDSize = mirror.readInt("method ID size", replyData); //$NON-NLS-1$ |
| fObjectIDSize = mirror.readInt("object ID size", replyData); //$NON-NLS-1$ |
| fReferenceTypeIDSize = mirror.readInt("refType ID size", replyData); //$NON-NLS-1$ |
| fFrameIDSize = mirror.readInt("frame ID size", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| fGotIDSizes = false; |
| mirror.defaultIOExceptionHandler(e); |
| } finally { |
| mirror.handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Retrieves version info of the VM. |
| */ |
| public void getVersionInfo() { |
| if (fVersionDescription != null) |
| return; |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_VERSION); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| |
| fVersionDescription = readString("version descr.", replyData); //$NON-NLS-1$ |
| fJdwpMajorVersion = readInt("major version", replyData); //$NON-NLS-1$ |
| fJdwpMinorVersion = readInt("minor version", replyData); //$NON-NLS-1$ |
| fVMVersion = readString("version", replyData); //$NON-NLS-1$ |
| fVMName = readString("name", replyData); //$NON-NLS-1$ |
| |
| if ((fVMName != null) && fVMName.equals("KVM")) { //$NON-NLS-1$ |
| // KVM requires class preparation events in order |
| // to resolve things correctly |
| eventRequestManagerImpl().enableInternalClassPrepareEvent(); |
| } |
| |
| } catch (IOException e) { |
| e.printStackTrace(); |
| fVersionDescription = null; |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Retrieves the HCR capabilities of the VM. |
| */ |
| public void getHCRCapabilities() { |
| if (fHcrCapabilities != null) |
| return; |
| fHcrCapabilities = new boolean[HCR_CAN_REENTER_ON_EXIT + 1]; |
| |
| if (isHCRSupported()) { |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.HCR_CAPABILITIES); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| |
| fHcrCapabilities[HCR_CAN_RELOAD_CLASSES] = readBoolean("reload classes", replyData); //$NON-NLS-1$ |
| fHcrCapabilities[HCR_CAN_GET_CLASS_VERSION] = readBoolean("get class version", replyData); //$NON-NLS-1$ |
| fHcrCapabilities[HCR_CAN_DO_RETURN] = readBoolean("do return", replyData); //$NON-NLS-1$ |
| fHcrCapabilities[HCR_CAN_REENTER_ON_EXIT] = readBoolean("reenter on exit", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| fHcrCapabilities = null; |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } else { |
| for (int i = 0; i < fHcrCapabilities.length; i++) { |
| fHcrCapabilities[i] = false; |
| } |
| } |
| } |
| |
| /** |
| * @return Returns Whether VM can deal with the 'Classes have Changed' command. |
| */ |
| public boolean canReloadClasses() { |
| getHCRCapabilities(); |
| return fHcrCapabilities[HCR_CAN_RELOAD_CLASSES]; |
| } |
| |
| /** |
| * @return Returns Whether VM can get the version of a given class file. |
| */ |
| public boolean canGetClassFileVersion() { |
| getHCRCapabilities(); |
| return fHcrCapabilities[HCR_CAN_GET_CLASS_VERSION]; |
| } |
| |
| /** |
| * @return Returns Whether VM can do a return in the middle of executing a method. |
| */ |
| public boolean canDoReturn() { |
| getHCRCapabilities(); |
| return fHcrCapabilities[HCR_CAN_DO_RETURN]; |
| } |
| |
| /** |
| * @return Returns Whether VM can reenter a method on exit. |
| */ |
| public boolean canReenterOnExit() { |
| getHCRCapabilities(); |
| return fHcrCapabilities[HCR_CAN_REENTER_ON_EXIT]; |
| } |
| |
| /** |
| * Notify the VM that classes have changed due to Hot Code Replacement. |
| * @return Returns RELOAD_SUCCESS, RELOAD_FAILURE or RELOAD_IGNORED. |
| */ |
| public int classesHaveChanged(String[] names) { |
| checkHCRSupported(); |
| // We convert the class/interface names to signatures. |
| String[] signatures = new String[names.length]; |
| |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeInt(names.length, "length", outData); //$NON-NLS-1$ |
| for (int i = 0; i < names.length; i++) { |
| signatures[i] = TypeImpl.classNameToSignature(names[i]); |
| writeString(signatures[i], "signature", outData); //$NON-NLS-1$ |
| } |
| |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.HCR_CLASSES_HAVE_CHANGED, outBytes); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| |
| byte resultFlag = readByte("result", resultHCRMap(), replyData); //$NON-NLS-1$ |
| switch (resultFlag) { |
| case HCR_RELOAD_SUCCESS: |
| return RELOAD_SUCCESS; |
| case HCR_RELOAD_FAILURE: |
| return RELOAD_FAILURE; |
| case HCR_RELOAD_IGNORED: |
| return RELOAD_IGNORED; |
| } |
| throw new InternalError(JDIMessages.VirtualMachineImpl_Invalid_result_flag_in_Classes_Have_Changed_response___3 + resultFlag + JDIMessages.VirtualMachineImpl__4); // |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns description of Mirror object. |
| */ |
| public String toString() { |
| try { |
| return name(); |
| } catch (Exception e) { |
| return fDescription; |
| } |
| } |
| |
| /** |
| * Retrieves constant mappings. |
| */ |
| public static void getConstantMaps() { |
| if (fgHCRResultMap != null) { |
| return; |
| } |
| |
| Field[] fields = VirtualMachineImpl.class.getDeclaredFields(); |
| fgHCRResultMap = new HashMap(); |
| for (int i = 0; i < fields.length; i++) { |
| Field field = fields[i]; |
| if ((field.getModifiers() & Modifier.PUBLIC) == 0 || (field.getModifiers() & Modifier.STATIC) == 0 || (field.getModifiers() & Modifier.FINAL) == 0) { |
| continue; |
| } |
| |
| try { |
| String name = field.getName(); |
| if (name.startsWith("HCR_RELOAD_")) { //$NON-NLS-1$ |
| Integer intValue = new Integer(field.getInt(null)); |
| name = name.substring(4); |
| fgHCRResultMap.put(intValue, name); |
| } |
| } catch (IllegalAccessException e) { |
| // Will not occur for own class. |
| } catch (IllegalArgumentException e) { |
| // Should not occur. |
| // We should take care that all public static final constants |
| // in this class are numbers that are convertible to int. |
| } |
| } |
| } |
| |
| /** |
| * @return Returns a map with string representations of tags. |
| */ |
| public static Map resultHCRMap() { |
| getConstantMaps(); |
| return fgHCRResultMap; |
| } |
| |
| /** |
| * Sets request timeout in ms. |
| */ |
| public void setRequestTimeout(int timeout) { |
| fRequestTimeout = timeout; |
| } |
| |
| /** |
| * @return Returns request timeout in ms. |
| */ |
| public int getRequestTimeout() { |
| return fRequestTimeout; |
| } |
| |
| /** |
| * Returns whether the JDWP version is greater |
| * than or equal to the specified major/minor |
| * version numbers. |
| * |
| * @return whether the JDWP version is greater |
| * than or equal to the specified major/minor |
| * version numbers |
| */ |
| public boolean isJdwpVersionGreaterOrEqual(int major, int minor) { |
| getVersionInfo(); |
| return (fJdwpMajorVersion > major) || |
| (fJdwpMajorVersion == major && fJdwpMinorVersion >= minor); |
| } |
| |
| /* |
| * @see VirtualMachine#redefineClasses(Map) |
| */ |
| public void redefineClasses(Map typesToBytes) { |
| if (!canRedefineClasses()) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeInt(typesToBytes.size(), "classes", outData); //$NON-NLS-1$ |
| |
| Set types = typesToBytes.keySet(); |
| Iterator iter = types.iterator(); |
| while (iter.hasNext()) { |
| ReferenceTypeImpl type = (ReferenceTypeImpl) iter.next(); |
| type.write(this, outData); |
| byte[] bytes = (byte[]) typesToBytes.get(type); |
| writeInt(bytes.length, "classfile", outData); //$NON-NLS-1$ |
| for (int i=0; i < bytes.length; i++) { |
| writeByte(bytes[i], "classByte", outData); //$NON-NLS-1$ |
| } |
| fCachedReftypes.remove(type.getRefTypeID()); // flush local cache of redefined types |
| } |
| |
| JdwpReplyPacket reply = requestVM(JdwpCommandPacket.VM_REDEFINE_CLASSES, outBytes); |
| switch (reply.errorCode()) { |
| case JdwpReplyPacket.UNSUPPORTED_VERSION: |
| throw new UnsupportedClassVersionError(); |
| case JdwpReplyPacket.INVALID_CLASS_FORMAT: |
| throw new ClassFormatError(); |
| case JdwpReplyPacket.CIRCULAR_CLASS_DEFINITION: |
| throw new ClassCircularityError(); |
| case JdwpReplyPacket.FAILS_VERIFICATION: |
| throw new VerifyError(); |
| case JdwpReplyPacket.NAMES_DONT_MATCH: |
| throw new NoClassDefFoundError(); |
| case JdwpReplyPacket.ADD_METHOD_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Add_method_not_implemented_1); |
| case JdwpReplyPacket.SCHEMA_CHANGE_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Scheme_change_not_implemented_2); |
| case JdwpReplyPacket.HIERARCHY_CHANGE_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Hierarchy_change_not_implemented_3); |
| case JdwpReplyPacket.DELETE_METHOD_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Delete_method_not_implemented_4); |
| case JdwpReplyPacket.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Class_modifiers_change_not_implemented_5); |
| case JdwpReplyPacket.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.VirtualMachineImpl_Method_modifiers_change_not_implemented_6); |
| default: |
| defaultReplyErrorHandler(reply.errorCode()); |
| } |
| } catch (IOException ioe) { |
| defaultIOExceptionHandler(ioe); |
| return; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /* |
| * @see VirtualMachine#canRedefineClasses() |
| */ |
| public boolean canRedefineClasses() { |
| getCapabilities(); |
| return fCanRedefineClasses; |
| } |
| |
| /* |
| * @see VirtualMachine#canUseInstanceFilters() |
| */ |
| public boolean canUseInstanceFilters() { |
| getCapabilities(); |
| return fCanUseInstanceFilters; |
| } |
| |
| /* |
| * @see VirtualMachine#canAddMethod() |
| */ |
| public boolean canAddMethod() { |
| getCapabilities(); |
| return fCanAddMethod; |
| } |
| |
| /* |
| * @see VirtualMachine#canUnrestrictedlyRedefineClasses() |
| */ |
| public boolean canUnrestrictedlyRedefineClasses() { |
| getCapabilities(); |
| return fCanUnrestrictedlyRedefineClasses; |
| } |
| |
| /* |
| * @see VirtualMachine#canPopFrames() |
| */ |
| public boolean canPopFrames() { |
| getCapabilities(); |
| return fCanPopFrames; |
| } |
| |
| /* |
| * @see VirtualMachine#canGetSourceDebugExtension() |
| */ |
| public boolean canGetSourceDebugExtension() { |
| getCapabilities(); |
| return fCanGetSourceDebugExtension; |
| } |
| |
| /* |
| * @see VirtualMachine#canRequestVMDeathEvent() |
| */ |
| public boolean canRequestVMDeathEvent() { |
| getCapabilities(); |
| return fCanRequestVMDeathEvent; |
| } |
| |
| public boolean canSetDefaultStratum() { |
| getCapabilities(); |
| return fCanSetDefaultStratum; |
| } |
| |
| /* |
| * @see VirtualMachine#setDefaultStratum(String) |
| */ |
| public void setDefaultStratum(String stratum) { |
| fDefaultStratum= stratum; |
| |
| if (!canSetDefaultStratum()) { |
| // TODO: how to inform the user that the VM doesn't manage setDefaultStartum ? |
| return; |
| } |
| if (stratum == null) { |
| stratum= ""; //$NON-NLS-1$ |
| } |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| writeString(stratum, "stratum ID", outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_SET_DEFAULT_STRATUM, outBytes); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /* |
| * @see VirtualMachine#getDefaultStratum() |
| */ |
| public String getDefaultStratum() { |
| return fDefaultStratum; |
| } |
| |
| /** |
| * Returns whether this VM is disconnected. |
| * |
| * @return whether this VM is disconnected |
| */ |
| public boolean isDisconnected() { |
| return fIsDisconnected; |
| } |
| |
| /** |
| * Sets whether this VM is disconnected. |
| * |
| * @param disconected whether this VM is disconnected |
| */ |
| public synchronized void setDisconnected(boolean disconnected) { |
| fIsDisconnected = disconnected; |
| } |
| |
| /** |
| * Return the boolean type for this VM. |
| */ |
| protected BooleanTypeImpl getBooleanType() { |
| if (fBooleanType == null) { |
| fBooleanType= new BooleanTypeImpl(this); |
| } |
| return fBooleanType; |
| } |
| |
| /** |
| * Return the byte type for this VM. |
| */ |
| protected ByteTypeImpl getByteType() { |
| if (fByteType == null) { |
| fByteType= new ByteTypeImpl(this); |
| } |
| return fByteType; |
| } |
| |
| /** |
| * Return the char type for this VM. |
| */ |
| protected CharTypeImpl getCharType() { |
| if (fCharType == null) { |
| fCharType= new CharTypeImpl(this); |
| } |
| return fCharType; |
| } |
| |
| /** |
| * Return the double type for this VM. |
| */ |
| protected DoubleTypeImpl getDoubleType() { |
| if (fDoubleType == null) { |
| fDoubleType= new DoubleTypeImpl(this); |
| } |
| return fDoubleType; |
| } |
| |
| /** |
| * Return the float type for this VM. |
| */ |
| protected FloatTypeImpl getFloatType() { |
| if (fFloatType == null) { |
| fFloatType= new FloatTypeImpl(this); |
| } |
| return fFloatType; |
| } |
| |
| /** |
| * Return the integer type for this VM. |
| */ |
| protected IntegerTypeImpl getIntegerType() { |
| if (fIntegerType == null) { |
| fIntegerType= new IntegerTypeImpl(this); |
| } |
| return fIntegerType; |
| } |
| |
| /** |
| * Return the long type for this VM. |
| */ |
| protected LongTypeImpl getLongType() { |
| if (fLongType == null) { |
| fLongType= new LongTypeImpl(this); |
| } |
| return fLongType; |
| } |
| |
| /** |
| * Return the short type for this VM. |
| */ |
| protected ShortTypeImpl getShortType() { |
| if (fShortType == null) { |
| fShortType= new ShortTypeImpl(this); |
| } |
| return fShortType; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see com.sun.jdi.VirtualMachine#canBeModified() |
| */ |
| public boolean canBeModified() { |
| return true; |
| } |
| |
| } |