/* * Copyright (c) 2001, 2018, 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. * * 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 nsk.share.jdwp; import nsk.share.*; import nsk.share.jpda.*; import java.util.*; import java.io.*; /** * This class is used to interact with debugee VM using JDWP features. *

* This class is an mirror of debugee VM that is constructed by * Binder and uses Transport object * to interact with debugee VM. *

* In addition to the general abities to control of debugee VM process, * provided by the base class DebugeeProcess, this class * adds some service methods that uses JDWP protocol to simplify interaction * with debugee VM (such as finding classes, setting breakpoints, * handling events, and so on.). * * @see Binder * @see Transport * @see DebugeeProcess */ abstract public class Debugee extends DebugeeProcess { /** Binder that creates this debugee. */ protected Binder binder = null; protected LinkedList eventQueue = new LinkedList(); protected Transport transport = null; /** Make new Debugee object for the given binder. */ protected Debugee (Binder binder) { super(binder); this.argumentHandler = binder.getArgumentHandler(); this.binder = binder; prefix = "Debugee> "; } /** Return Binder of the debugee object. */ public Binder getBinder() { return binder; } /** Return Transport of the debugee object. */ public Transport getTransport() { return transport; } /** * Prepare transport object for establishing connection. * This may change connection options in argumentHandler. * * @return specific address string if listening has started or null otherwise */ public String prepareTransport(ArgumentHandler argumentHandler) { String address = null; try { if (argumentHandler.isSocketTransport()) { SocketTransport socket_transport = new SocketTransport(log); if (argumentHandler.isListeningConnector()) { int port = 0; if (argumentHandler.isTransportAddressDynamic()) { port = socket_transport.bind(0); // argumentHandler.setTransportPortNumber(port); } else { port = argumentHandler.getTransportPortNumber(); socket_transport.bind(port); } address = argumentHandler.getTestHost() + ":" + port; } transport = socket_transport; /* } else if (argumentHandler.isShmemTransport()) { ShmemTransport shmem_transport = new ShmemTransport(log); if (argumentHandler.isListeningConnector()) { String sharedName = agrHandler.getTransportSharedName(); shmem_transport.bind(sharedName); address = sharedName; } transport = shmem_transport; */ } else { throw new TestBug("Unexpected transport type: " + argumentHandler.getTransportType()); } } catch (IOException e) { e.printStackTrace(log.getOutStream()); throw new Failure("Caught IOException while preparing for JDWP transport connection:\n\t" + e); } return address; } /** * Establish connection to debugee VM. */ public Transport connect() { if (transport == null) { throw new Failure("Attemt to establish JDWP connection for not prepared transport"); } try { if (argumentHandler.isSocketTransport()) { display("Establishing JDWP socket connection"); SocketTransport socket_transport = (SocketTransport)transport; int transportPort = argumentHandler.getTransportPortNumber(); if (argumentHandler.isAttachingConnector()) { String debugeeHost = argumentHandler.getDebugeeHost(); display("Attaching to debugee: " + debugeeHost + ":" + transportPort); socket_transport.attach(debugeeHost, transportPort); } else if (argumentHandler.isListeningConnector()) { display("Listening from debugee"); socket_transport.accept(); } else { throw new TestBug("Unexpected connector type: " + argumentHandler.getConnectorType()); } /* } else if (argumentHandler.isShmemTransport()) { display("Establishing JDWP shared-memory connection"); ShmemTransport shmem_transport = (ShmemTransport)transport; String sharedName = argumentHandler.getTransportSharedName(); if (argumentHandler.isAttachingConnector()) { display("Attaching to debugee: " + sharedName); shmem_transport.attach(sharedName); } else if (argumentHandler.isListeningConnector()) { display("Listening from debugee"); shmem_transport.accept(); } else { throw new TestBug("Unexpected connector type: " + argumentHandler.getConnectorType()); } */ } else { throw new TestBug("Unexpected transport type: " + argumentHandler.getTransportType()); } transport.handshake(); } catch (IOException e) { e.printStackTrace(log.getOutStream()); throw new Failure("Caught IOException while establishing JDWP transport connection:\n\t" + e); } return transport; } // --------------------------------------------------- // /** * Waits for VM_INIT event from debugee VM. */ public void waitForVMInit() { String eventName = "VirtualMachine.VM_START"; EventPacket packet = receiveEventFor(JDWP.EventKind.VM_START, eventName); // String versionInfo = getVersionInfo(); // display("Target VM started:\n" + versionInfo); } /** * Waits for VM_DEATH event from debugee VM. */ public void waitForVMDeath() { String eventName = "VirtualMachine.VM_DEATH"; EventPacket packet = receiveEventFor(JDWP.EventKind.VM_DEATH, eventName); } /** * Wait for class loaded on debugee start up and return its classID. * Debuggee should be initially suspended and it will also left suspended * by the CLASS_PREPARE event request. */ public long waitForClassLoaded(String className, byte suspendPolicy) { // make request for CLASS_PREPARE_EVENT for this class name int requestID = requestClassPrepareEvent(className, suspendPolicy); // resume initially suspended debugee resume(); // wait for CLASS_PREPARE_EVENT return waitForClassPrepareEvent(requestID, className); } /** * Wait for classes loaded on debugee start up and return their classIDs. * Debuggee should be initially suspended and it will also left suspended * by the CLASS_PREPARE event request. */ public long[] waitForClassesLoaded(String classNames[], byte suspendPolicy) { int count = classNames.length; // make requests for CLASS_PREPARE_EVENT for these class names int[] requestIDs = new int[count]; for (int i = 0; i < count; i++) { requestIDs[i] = requestClassPrepareEvent(classNames[i], suspendPolicy); } // resume initially suspended debugee resume(); return waitForClassPrepareEvents(requestIDs, classNames); } /** * Wait for breakpoint reached and return threadIDs. * Debuggee should be initially suspended and it will also left suspended * by the BREAKPOINT event request. */ public long waitForBreakpointReached(long classID, String methodName, int line, byte suspendPolicy) { // query debuggee for methodID long methodID = getMethodID(classID, methodName, true); // create BREAKPOINT event request int requestID = requestBreakpointEvent(JDWP.TypeTag.CLASS, classID, methodID, line, suspendPolicy); // resume initially suspended debugee resume(); // wait for BREAKPOINT event return waitForBreakpointEvent(requestID); } // --------------------------------------------------- // /** * Wait for CLASS_PREPARE event made by given request received * and return classID. * Debuggee will be left suspended by the CLASS_PREPARE event. */ public long waitForClassPrepareEvent(int requestID, String className) { String error = "Error occured while waiting for CLASS_PREPARE event for class:\n\t" + className; String signature = "L" + className.replace('.', '/') + ";"; long classID = 0; // wait for CLASS_PREPARE event for(;;) { EventPacket event = receiveEvent(); byte eventSuspendPolicy = 0; long eventThreadID = 0; try { eventSuspendPolicy = event.getByte(); int events = event.getInt(); for (int i = 0; i < events; i++) { // check event kind byte eventKind = event.getByte(); if (eventKind == JDWP.EventKind.VM_DEATH) { complain("Unexpected VM_DEATH event received: " + eventKind + " (expected: " + JDWP.EventKind.CLASS_PREPARE +")"); throw new Failure(error); } else if (eventKind != JDWP.EventKind.CLASS_PREPARE) { complain("Unexpected event kind received: " + eventKind + " (expected: " + JDWP.EventKind.CLASS_PREPARE +")"); throw new Failure(error); } // extract CLASS_PREPARE event specific data int eventRequestID = event.getInt(); eventThreadID = event.getObjectID(); byte eventRefTypeTag = event.getByte(); long eventClassID = event.getReferenceTypeID(); String eventClassSignature = event.getString(); int eventClassStatus = event.getInt(); // check if event was single if (events > 1) { complain("Not single CLASS_PREPARE event received for class:\n\t" + eventClassSignature); throw new Failure(error); } // check if event is for expected class if (eventClassSignature.equals(signature)) { // check if event is because of expected request if (eventRequestID != requestID) { complain("CLASS_PREPARE event with unexpected requestID (" + eventRequestID + ") received for class:\n\t" + eventClassSignature); throw new Failure(error); } // remove event request clearEventRequest(JDWP.EventKind.CLASS_PREPARE, requestID); return eventClassID; } else { complain("Unexpected CLASS_PREPARE event received with class signature:\n" + " " + eventClassSignature); } } } catch (BoundException e) { complain("Unable to extract data from event packet while waiting for CLASS_PREPARE event:\n\t" + e.getMessage() + "\n" + event); throw new Failure(error); } // resume debuggee according to event suspend policy resumeEvent(eventSuspendPolicy, eventThreadID); } } /** * Wait for CLASS_PREPARE events made by given requests received * and return classIDs. * Debuggee will be left suspended by the CLASS_PREPARE event. */ public long[] waitForClassPrepareEvents(int requestIDs[], String classNames[]) { int count = classNames.length; String error = "Error occured while waiting for " + count + " CLASS_PREPARE events"; // prepare expected class signatures String[] signatures = new String[count]; for (int i = 0; i < count; i++) { signatures[i] = "L" + classNames[i].replace('.', '/') + ";"; } // clear list of classIDs long[] classIDs = new long[count]; for (int i = 0; i < count; i++) { classIDs[i] = 0; } // wait for all expected CLASS_PREPARE events int received = 0; for(;;) { EventPacket event = receiveEvent(); byte eventSuspendPolicy = 0; long eventThreadID = 0; try { eventSuspendPolicy = event.getByte(); int events = event.getInt(); for (int i = 0; i < events; i++) { // check event kind byte eventKind = event.getByte(); if (eventKind == JDWP.EventKind.VM_DEATH) { complain("Unexpected VM_DEATH event received: " + eventKind + " (expected: " + JDWP.EventKind.CLASS_PREPARE +")"); throw new Failure(error); } else if (eventKind != JDWP.EventKind.CLASS_PREPARE) { complain("Unexpected event kind received: " + eventKind + " (expected: " + JDWP.EventKind.CLASS_PREPARE +")"); throw new Failure(error); } // extracy CLASS_PREPARE event specific data int eventRequestID = event.getInt(); eventThreadID = event.getObjectID(); byte eventRefTypeTag = event.getByte(); long eventClassID = event.getReferenceTypeID(); String eventClassSignature = event.getString(); int eventClassStatus = event.getInt(); // check if event was single if (events > 1) { complain("Not single CLASS_PREPARE event received for class:\n\t" + eventClassSignature); } // find appropriate class by signature boolean found = false; for (int j = 0; j < count; j++) { if (eventClassSignature.equals(signatures[j])) { found = true; // check if event is not duplicated if (classIDs[j] != 0) { complain("Extra CLASS_PREPARE event recieved for class:\n\t" + eventClassSignature); } else { classIDs[j] = eventClassID; received ++; } // check if event is because of expected request if (eventRequestID != requestIDs[j]) { complain("CLASS_PREPARE event with unexpected requestID (" + requestIDs[j] + ") received for class:\n\t" + eventClassSignature); } else { clearEventRequest(JDWP.EventKind.CLASS_PREPARE, requestIDs[j]); } } } if (!found) { log.complain("Unexpected CLASS_PREPARE event received with class signature:\n" + " " + eventClassSignature); } } } catch (BoundException e) { complain("Unable to extract data from event packet while waiting for CLASS_PREPARE event:\n\t" + e.getMessage() + "\n" + event); throw new Failure(error); } // if all events received return without resuming if (received >= count) return classIDs; // resume debuggee according to events suspend policy resumeEvent(eventSuspendPolicy, eventThreadID); } } /** * Wait for BREAKPOINT event made by the given request and return threadID. * Debuggee will be left suspended by the BREAKPOINT event. */ public long waitForBreakpointEvent(int requestID) { String error = "Error occured while waiting for BREAKPOINT event for "; for(;;) { EventPacket event = receiveEvent(); byte eventSuspendPolicy = 0; long eventThreadID = 0; try { eventSuspendPolicy = event.getByte(); int events = event.getInt(); for (int i = 0; i < events; i++) { // check event kind byte eventKind = event.getByte(); if (eventKind == JDWP.EventKind.VM_DEATH) { complain("Unexpected VM_DEATH event received: " + eventKind + " (expected: " + JDWP.EventKind.BREAKPOINT +")"); throw new Failure(error); } else if (eventKind != JDWP.EventKind.BREAKPOINT) { complain("Unexpected event kind received: " + eventKind + " (expected: " + JDWP.EventKind.BREAKPOINT +")"); throw new Failure(error); } // extrack specific BREAKPOINT event data int eventRequestID = event.getInt(); eventThreadID = event.getObjectID(); JDWP.Location eventLocation = event.getLocation(); if (eventRequestID == requestID) { clearEventRequest(JDWP.EventKind.BREAKPOINT, requestID); return eventThreadID; } else { complain("Unexpected BREAKPOINT event received with requestID: " + eventRequestID + " (expected: " + requestID + ")"); } } } catch (BoundException e) { complain("Unable to extract data from event packet while waiting for BREAKPOINT event:\n\t" + e.getMessage() + "\n" + event); throw new Failure(error); } resumeEvent(eventSuspendPolicy, eventThreadID); } } /** * Resume debuggee according given event suspend policy. */ public void resumeEvent(byte suspendPolicy, long threadID) { if (suspendPolicy == JDWP.SuspendPolicy.NONE) { // do nothing } else if (suspendPolicy == JDWP.SuspendPolicy.EVENT_THREAD) { resumeThread(threadID); } else if (suspendPolicy == JDWP.SuspendPolicy.ALL) { resume(); } else { throw new Failure("Unexpected event suspend policy while resuming debuggee: " + suspendPolicy); } } // --------------------------------------------------- // /** * Query target VM for version info. */ public String getVersionInfo() { String commandName = "VirtualMachine.Version"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.Version); ReplyPacket reply = receiveReplyFor(command, commandName); try { String description = reply.getString(); int jdwpMajor = reply.getInt(); int jdwpMinor = reply.getInt(); String vmVersion = reply.getString(); String vmName = reply.getString(); return description; } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting JDWP and VM version info"); } } /** * Query target VM about VM dependent ID sizes. */ public void queryForIDSizes() { String commandName = "VirtualMachine.IDSizes"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.IDSizes); ReplyPacket reply = receiveReplyFor(command); try { reply.resetPosition(); JDWP.TypeSize.FIELD_ID = reply.getInt(); JDWP.TypeSize.METHOD_ID = reply.getInt(); JDWP.TypeSize.OBJECT_ID = reply.getInt(); JDWP.TypeSize.REFERENCE_TYPE_ID = reply.getInt(); JDWP.TypeSize.FRAME_ID = reply.getInt(); } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting VM dependent ID sizes"); } JDWP.TypeSize.CalculateSizes(); } // --------------------------------------------------- // /** * Suspend the debugee VM by sending VirtualMachine.Suspend command. */ public void suspend() { String commandName = "VirtualMachine.Suspend"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.Suspend); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Resume the debugee VM by sending VirtualMachine.Resume command. */ public void resume() { String commandName = "VirtualMachine.Resume"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.Resume); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Dispose the debugee VM by sending VirtualMachine.Dispose command. */ public void dispose() { String commandName = "VirtualMachine.Dispose"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.Dispose); ReplyPacket reply = receiveReplyFor(command, commandName); } // --------------------------------------------------- // /** * Sends JDWP command packet. */ public void sendCommand(CommandPacket packet, String commandName) { try { transport.write(packet); } catch (IOException e) { e.printStackTrace(log.getOutStream()); complain("Caught IOException while sending command packet for " + commandName + ":\n\t" + e); display("Command packet:\n" + packet); throw new Failure("Error occured while sending command: " + commandName); } } /** * Receive next JDWP packet. */ /* public Packet receivePacket() { try { ReplyPacket packet = new ReplyPacket(); transport.read(packet); return packet; } catch (IOException e) { e.printStackTrace(log.getOutStream()); throw new Failure("Caught IOException while receiving reply packet:\n\t" + e); } } */ /** * Receive next JDWP reply packet. */ public ReplyPacket receiveReply() { try { for (;;) { Packet packet = new Packet(); transport.read(packet); if (packet.getFlags() == JDWP.Flag.REPLY_PACKET) { ReplyPacket reply = new ReplyPacket(packet); return reply; } EventPacket event = new EventPacket(packet); display("Placing received event packet into queue"); eventQueue.add(event); } } catch (IOException e) { e.printStackTrace(log.getOutStream()); throw new Failure("Caught IOException while receiving reply packet:\n\t" + e); } } /** * Get next JDWP event packet by reading from transport or getting stored * event in the event queue. */ public EventPacket getEventPacket() throws IOException { // check events queue first if (!eventQueue.isEmpty()) { EventPacket event = (EventPacket)(eventQueue.removeFirst()); return event; } // read from transport Packet packet = new Packet(); transport.read(packet); EventPacket event = new EventPacket(packet); return event; } /** * Get next JDWP event packet by reading from transport for specified timeout * or getting stored event in the event queue. */ public EventPacket getEventPacket(long timeout) throws IOException { transport.setReadTimeout(timeout); return getEventPacket(); } /** * Receive next JDWP event packet. */ public EventPacket receiveEvent() { EventPacket packet = null; try { packet = getEventPacket(); } catch (IOException e) { e.printStackTrace(log.getOutStream()); throw new Failure("Caught IOException while receiving event packet:\n\t" + e); } if (packet.getFlags() == JDWP.Flag.REPLY_PACKET) { ReplyPacket reply = new ReplyPacket(packet); log.complain("Unexpected reply packet received with id: " + reply.getPacketID()); log.display("Reply packet:\n" + reply); throw new Failure("Unexpected reply packet received instead of event packet"); } return packet; } /** * Send specified command packet, receive and check reply packet. * * @throws Failure if exception caught in sending and reading packets */ public ReplyPacket receiveReplyFor(CommandPacket command) { return receiveReplyFor(command, Packet.toHexString(command.getCommand(), 4)); } /** * Send specified command packet, receive and check reply packet. * * @throws Failure if exception caught in sending and reading packets */ public ReplyPacket receiveReplyFor(CommandPacket command, String commandName) { ReplyPacket reply = null; sendCommand(command, commandName); reply = receiveReply(); try { reply.checkHeader(command.getPacketID()); } catch (BoundException e) { complain("Wrong header of reply packet for command "+ commandName + ":\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Wrong reply packet received for command: " + commandName); } return reply; } /** * Receive and check event packet for specified event kind. * * @throws Failure if exception caught in sending and reading packets */ public EventPacket receiveEventFor(int eventKind, String eventName) { EventPacket event = null; event = receiveEvent(); try { event.checkHeader(eventKind); } catch (BoundException e) { complain("Wrong header of event packet for expected "+ eventName + " event:\n\t" + e.getMessage()); display("Event packet:\n" + event); throw new Failure("Wrong event packet received for expected event: " + eventName); } return event; } // --------------------------------------------------- // /** * Check common VM capability. */ public boolean getCapability(int capability, String name) { String commandName = "VirtualMachine.Capabilities"; int count = JDWP.Capability.CAN_GET_MONITOR_INFO + 1; if (capability < 0 || capability >= count) { throw new TestBug("Illegal capability number (" + capability + ") while checking for VM capability: " + name); } CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.Capabilities); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); for (int i = 0; i < count; i++) { byte value = reply.getByte(); if (i == capability) { return (value != 0); } } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting VM capability: " + name); } throw new TestBug("Illegal capability number (" + capability + ") while checking for VM capability: " + name); } /** * Check new VM capability (since JDWP version 1.4). */ public boolean getNewCapability(int capability, String name) { String commandName = "VirtualMachine.CapabilitiesNew"; int count = JDWP.Capability.CAN_SET_DEFAULT_STRATUM + 1; if (capability < 0 || capability >= count) { throw new TestBug("Illegal capability number (" + capability + ") while checking for VM new capability: " + name); } CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.CapabilitiesNew); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); for (int i = 0; i < count; i++) { byte value = reply.getByte(); if (i == capability) { return (value != 0); } } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting VM new capability: " + name); } throw new TestBug("Illegal capability number (" + capability + ") while checking for VM new capability: " + name); } // --------------------------------------------------- // /** * Return ReferenceTypeID for requested class by given signature. */ public long getReferenceTypeID(String classSignature) { String commandName = "VirtualMachine.ClassesBySignature"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.ClassesBySignature); command.addString(classSignature); command.setLength(); ReplyPacket reply = receiveReplyFor(command, commandName); long typeID = 0; try { reply.resetPosition(); int classes = reply.getInt(); for (int i = 0; i < classes; i++) { byte refTypeTag = reply.getByte(); typeID = reply.getReferenceTypeID(); int status = reply.getInt(); } if (classes < 0) { throw new Failure("Negative number (" + classes + ") of referenceTypeIDs received for signature: " + classSignature); } if (classes == 0) { throw new Failure("No any referenceTypeID received for signature: " + classSignature); } if (classes > 1) { throw new Failure("Too many (" + classes + ") referenceTypeIDs received for signature: " + classSignature); } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting referenceTypeID for signature: " + classSignature); } return typeID; } // --------------------------------------------------- // /** * Get list of IDs of supertypes (interfaces and classes) for given class. */ public long[] getSupertypes(long classID, boolean declared) { Vector vector = new Vector(); addSupertypes(classID, vector, null, null, false, declared); return makeListOfLongValues(vector); } /** * Get list of IDs of superclasses for given class. */ public long[] getSuperclasses(long classID, boolean declared) { Vector vector = new Vector(); addSupertypes(classID, null, null, vector, false, declared); return makeListOfLongValues(vector); } /** * Get list of IDs of implemented interfaces for given class. */ public long[] getImplementedInterfaces(long classID, boolean declared) { Vector vector = new Vector(); addSupertypes(classID, null, vector, null, false, declared); return makeListOfLongValues(vector); } /** * Get list of IDs of superinterfaces for given interface. */ public long[] getSuperinterfaces(long interfaceID, boolean declared) { Vector vector = new Vector(); addSupertypes(interfaceID, null, vector, null, true, declared); return makeListOfLongValues(vector); } // --------------------------------------------------- // /** * Get list of IDs of methods of given class. */ public long[] getMethodIDs(long classID, boolean declared) { Vector list = new Vector(); addMethods(classID, list, null, null, null, false, declared); return makeListOfLongValues(list); } /** * Get list of names of methods of given class. */ public String[] getMethodNames(long classID, boolean declared) { Vector list = new Vector(); addMethods(classID, null, list, null, null, false, declared); return makeListOfStringValues(list); } /** * Get list of signatures of methods of given class. */ public String[] getMethodSignatures(long classID, boolean declared) { Vector list = new Vector(); addMethods(classID, null, null, list, null, false, declared); return makeListOfStringValues(list); } /** * Get ID of a method of given class by name. */ public long getMethodID(long classID, String name, boolean declared) { Vector IDs = new Vector(); Vector names = new Vector(); addMethods(classID, IDs, names, null, null, false, declared); int count = names.size(); for (int i = 0; i < count; i++) { if (name.equals(names.elementAt(i))) { return (IDs.elementAt(i)).longValue(); } } throw new Failure("Method \"" + name + "\" not found for classID: " + classID); } // --------------------------------------------------- // /** * Get list of IDs of static fields of given class. */ public long[] getClassFieldIDs(long classID, boolean declared) { Vector list = new Vector(); addFields(classID, list, null, null, null, false, declared); return makeListOfLongValues(list); } /** * Get list of names of static fields of given class. */ public String[] getClassFieldNames(long classID, boolean declared) { Vector list = new Vector(); addFields(classID, null, list, null, null, false, declared); return makeListOfStringValues(list); } /** * Get list of signatures of static fields of given class. */ public String[] getClassFieldSignatures(long classID, boolean declared) { Vector list = new Vector(); addFields(classID, null, null, list, null, false, declared); return makeListOfStringValues(list); } /** * Get ID of a static field of given class by name. */ public long getClassFieldID(long classID, String name, boolean declared) { Vector IDs = new Vector(); Vector names = new Vector(); addFields(classID, IDs, names, null, null, false, declared); int count = names.size(); for (int i = 0; i < count; i++) { if (name.equals((String)names.elementAt(i))) { return ((Long)IDs.elementAt(i)).longValue(); } } throw new Failure("Static field \"" + name + "\" not found for classID: " + classID); } // --------------------------------------------------- // /** * Get value of a static field of given class. */ public JDWP.Value getStaticFieldValue(long typeID, long fieldID) { String commandName = "ReferenceType.GetValues"; CommandPacket command = new CommandPacket(JDWP.Command.ReferenceType.GetValues); command.addReferenceTypeID(typeID); command.addInt(1); command.addFieldID(fieldID); ReplyPacket reply = receiveReplyFor(command, commandName); JDWP.Value value = null; try { reply.resetPosition(); int count = reply.getInt(); if (count < 1) { throw new Failure("No values returned for static fieldID: " + fieldID); } value = reply.getValue(); } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName +" command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting value of static field: " + + fieldID); } return value; } /** * Get value of particular type from a static field of given class. */ public JDWP.Value getStaticFieldValue(long typeID, String fieldName, byte tag) { long fieldID = getClassFieldID(typeID, fieldName, true); JDWP.Value value = getStaticFieldValue(typeID, fieldID); if (value.getTag() != tag) { complain("unexpedted value tag returned from debuggee: " + value.getTag() + " (expected: " + tag + ")"); throw new Failure("Error occured while getting value from static field: " + fieldName); } return value; } /** * Set value of a static field of given class. */ public void setStaticFieldValue(long typeID, long fieldID, JDWP.Value value) { String commandName = "ClassType.SetValues"; CommandPacket command = new CommandPacket(JDWP.Command.ClassType.SetValues); command.addReferenceTypeID(typeID); command.addInt(1); command.addFieldID(fieldID); command.addUntaggedValue(value, value.getTag()); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Get value of a field of given object. */ public JDWP.Value getObjectFieldValue(long objectID, long fieldID) { String commandName = "ObjectReference.GetValues"; CommandPacket command = new CommandPacket(JDWP.Command.ObjectReference.GetValues); command.addObjectID(objectID); command.addInt(1); command.addFieldID(fieldID); ReplyPacket reply = receiveReplyFor(command, commandName); JDWP.Value value = null; try { reply.resetPosition(); int count = reply.getInt(); if (count < 1) { throw new Failure("No values returned for object fieldID: " + fieldID); } value = reply.getValue(); } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting value of object field: " + + fieldID); } return value; } /** * Set value of a static field of given class. */ public void setObjectFieldValue(long objectID, long fieldID, JDWP.Value value) { String commandName = "ObjectReference.SetValues"; CommandPacket command = new CommandPacket(JDWP.Command.ObjectReference.SetValues); command.addObjectID(objectID); command.addInt(1); command.addFieldID(fieldID); command.addUntaggedValue(value, value.getTag()); ReplyPacket reply = receiveReplyFor(command, commandName); } // --------------------------------------------------- // /** * Find threadID for given thread name among all active threads. */ public long getThreadID(String name) { // request list of all threadIDs int threads = 0; long threadIDs[] = null; { String commandName = "VirtualMachine.AllThreads"; CommandPacket command = new CommandPacket(JDWP.Command.VirtualMachine.AllThreads); ReplyPacket reply = receiveReplyFor(command, commandName); reply.resetPosition(); try { threads = reply.getInt(); threadIDs = new long[threads]; for (int i = 0; i < threads; i++) { threadIDs[i] = reply.getObjectID(); } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting threadID for thread name: " + name); } } // request name for each threadID for (int i = 0; i < threads; i++) { String commandName = "ThreadReference.Name"; CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Name); command.addObjectID(threadIDs[i]); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); String threadName = reply.getString(); if (threadName.equals(name)) { return threadIDs[i]; } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting name for threadID: " + threadIDs[i]); } } throw new Failure("No threadID found for thread name: " + name); } /** * Return thread name for the given threadID. */ public String getThreadName(long threadID) { String commandName = "ThreadReference.Name"; CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Name); command.addObjectID(threadID); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); String threadName = reply.getString(); return threadName; } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting name for threadID: " + threadID); } } /** * Suspend thread for the given threadID. */ public void suspendThread(long threadID) { String commandName = "ThreadReference.Suspend"; CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Suspend); command.addObjectID(threadID); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Resume thread for the given threadID. */ public void resumeThread(long threadID) { String commandName = "ThreadReference.resume"; CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Resume); command.addObjectID(threadID); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Return frameID for the current frame of the thread. * Thread must be suspended. */ public long getCurrentFrameID(long threadID) { String commandName = "ThreadReference.Frames"; CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Frames); command.addObjectID(threadID); // threadID command.addInt(0); // startFrame command.addInt(1); // length ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); int frames = reply.getInt(); if (frames != 1) { throw new Failure("Not only one current frame returned for threadID " + threadID + ": " + frames); } long frameID = reply.getFrameID(); JDWP.Location location = reply.getLocation(); return frameID; } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting current frame for threadID: " + threadID); } } // --------------------------------------------------- // /** * Find line number for the given location from the method line table. * If approximate is true the found line should begin * with code index of the given location. Otherwise, the found line will * just cover code index of the given location. */ public int getLineNumber(JDWP.Location location, boolean approximate) { String commandName = "Method.LineTable"; CommandPacket command = new CommandPacket(JDWP.Command.Method.LineTable); command.addReferenceTypeID(location.getClassID()); command.addMethodID(location.getMethodID()); long index = location.getIndex(); ReplyPacket reply = receiveReplyFor(command, commandName); String msg = "Error occured while getting line number for location: " + location; try { reply.resetPosition(); long start = reply.getLong(); if (index < start) { complain("Location index (" + index + ") is less than start method index (" + start); throw new Failure(msg); } long end = reply.getLong(); if (index > end) { complain("Location index (" + index + ") is greater than end method index (" + end); throw new Failure(msg); } int lines = reply.getInt(); if (!approximate) { for (int i = 0; i < lines; i++) { long lineCodeIndex = reply.getLong(); int lineNumber = reply.getInt(); if (lineCodeIndex == index) { return lineNumber; } } throw new Failure("No exact line number exactly for location: " + location); } else { int prevLine = -1; for (int i = 0; i < lines; i++) { long lineCodeIndex = reply.getLong(); int lineNumber = reply.getInt(); if (lineCodeIndex == index) { return lineNumber; } else if (lineCodeIndex > index) { break; } prevLine = lineNumber; } if (prevLine < 0) throw new Failure("No approximate line number found for location: " + location); return prevLine; } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure(msg); } } /** * Find line index for the given line number from the method line table. */ public long getCodeIndex(long classID, long methodID, int lineNumber) { String commandName = "Method.LineTable"; CommandPacket command = new CommandPacket(JDWP.Command.Method.LineTable); command.addReferenceTypeID(classID); command.addMethodID(methodID); ReplyPacket reply = receiveReplyFor(command, commandName); String msg = "Error occured while getting code index for line number: " + lineNumber; try { reply.resetPosition(); long start = reply.getLong(); long end = reply.getLong(); int lines = reply.getInt(); for (int i = 0; i < lines; i++) { long lineCodeIndex = reply.getLong(); int line = reply.getInt(); if (lineNumber == line) { return lineCodeIndex; } } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure(msg); } throw new Failure("No code index found for line number: " + lineNumber); } // --------------------------------------------------- // /** * Make the specified event request into debuggee. */ public int requestEvent(CommandPacket requestCommand, String name) { String commandName = "EventRequest.Set"; ReplyPacket reply = receiveReplyFor(requestCommand, name); try { reply.resetPosition(); int requestID = reply.getInt(); return requestID; } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Request command packet:\n" + requestCommand); display("Reply packet:\n" + reply); throw new Failure("Error occured while making event request: " + name); } } /** * Remove existing event request from debuggee. */ public void clearEventRequest(byte eventKind, int requestID) { String commandName = "EventRequest.Clear"; CommandPacket command = new CommandPacket(JDWP.Command.EventRequest.Clear); command.addByte(eventKind); command.addInt(requestID); ReplyPacket reply = receiveReplyFor(command, commandName); } /** * Make request for CLASS_PREPARE event for specified class into debuggee. */ public int requestClassPrepareEvent(String className, byte suspendPolicy) { CommandPacket command = new CommandPacket(JDWP.Command.EventRequest.Set); command.addByte(JDWP.EventKind.CLASS_PREPARE); command.addByte(suspendPolicy); command.addInt(1); command.addByte(JDWP.EventModifierKind.CLASS_MATCH); command.addString(className); return requestEvent(command, "CLASS_PREPARE"); } /** * Make request for BREAKPOINT event for the specified location into debuggee. */ public int requestBreakpointEvent(JDWP.Location location, byte suspendPolicy) { CommandPacket command = new CommandPacket(JDWP.Command.EventRequest.Set); command.addByte(JDWP.EventKind.BREAKPOINT); command.addByte(suspendPolicy); command.addInt(1); command.addByte(JDWP.EventModifierKind.LOCATION_ONLY); command.addLocation(location); return requestEvent(command, "BREAKPOINT"); } /** * Make request for BREAKPOINT event for the specified line of the given method. */ public int requestBreakpointEvent(byte typeTag, long classID, long methodID, int lineNumber, byte suspendPolicy) { long codeIndex = getCodeIndex(classID, methodID, lineNumber); JDWP.Location location = new JDWP.Location(typeTag, classID, methodID, codeIndex); return requestBreakpointEvent(location, suspendPolicy); } /** * Remove all existing BREAKPOINT event requests from debuggee. */ public void clearAllBreakpoints() { String commandName = "EventRequest.ClearAllBreakpoints"; CommandPacket command = new CommandPacket(JDWP.Command.EventRequest.ClearAllBreakpoints); ReplyPacket reply = receiveReplyFor(command, commandName); } // --------------------------------------------------- // /** * Add IDs of supertypes (interfaces and classes) for given class to the lists. */ private void addSupertypes(long referenceTypeID, Vector supertypes, Vector interfaces, Vector superclasses, boolean interfaceOnly, boolean declared) { if (supertypes != null || interfaces != null) { // obtain list of direct implemented interfaces String commandName = "ReferenceType.Interfaces"; CommandPacket command = new CommandPacket(JDWP.Command.ReferenceType.Interfaces); command.addReferenceTypeID(referenceTypeID); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); int count = reply.getInt(); if (count < 0) { throw new Failure("Negative number (" + count + ") of declared interfaces received for referenceTypeID: " + referenceTypeID); } for (int i = 0; i < count; i++) { long typeID = reply.getReferenceTypeID(); if (!declared) { addSupertypes(typeID, supertypes, interfaces, superclasses, true, declared); } Long value = new Long(typeID); if (supertypes != null) { supertypes.add(value); } if (interfaces != null) { interfaces.add(value); } } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting interfeceIDs for referenceTypeID: " + + referenceTypeID); } } if (!interfaceOnly) { String commandName = "ClassType.Superclasses"; CommandPacket command = new CommandPacket(JDWP.Command.ClassType.Superclass); command.addReferenceTypeID(referenceTypeID); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); long typeID = reply.getReferenceTypeID(); if (typeID != 0) { if (!declared) { addSupertypes(typeID, supertypes, interfaces, superclasses, false, declared); } Long value = new Long(typeID); if (supertypes != null) { supertypes.add(value); } if (superclasses != null) { superclasses.add(value); } } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting superclass ID for classID: " + + referenceTypeID); } } } /** * Add attributes of fields of given class to the lists. */ private void addFields(long referenceTypeID, Vector IDs, Vector names, Vector signatures, Vector modifiers, boolean interfaceOnly, boolean declared) { if (!declared) { Vector supertypes = new Vector(); addSupertypes(referenceTypeID, supertypes, null, null, interfaceOnly, declared); int count = supertypes.size(); for (int i = 0; i < count; i++) { long typeID = (supertypes.elementAt(i)).longValue(); addFields(typeID, IDs, names, signatures, modifiers, interfaceOnly, declared); } } String commandName = "ReferenceType.Fields"; CommandPacket command = new CommandPacket(JDWP.Command.ReferenceType.Fields); command.addReferenceTypeID(referenceTypeID); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); int count = reply.getInt(); if (count < 0) { throw new Failure("Negative number (" + count + ") of declared fields received for referenceTypeID: " + referenceTypeID); } for (int i = 0; i < count; i++) { long id = reply.getFieldID(); String name = reply.getString(); String signature = reply.getString(); int modBits = reply.getInt(); if (IDs != null) IDs.add(new Long(id)); if (names != null) names.add(name); if (signatures != null) signatures.add(signature); if (modifiers != null) modifiers.add(new Integer(modBits)); } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting fieldIDs for referenceTypeID: " + + referenceTypeID); } } /** * Add attributes of methods of given class to the lists. */ private void addMethods(long referenceTypeID, Vector IDs, Vector names, Vector signatures, Vector modifiers, boolean interfaceOnly, boolean declared) { if (!declared) { Vector supertypes = new Vector(); addSupertypes(referenceTypeID, supertypes, null, null, interfaceOnly, declared); int count = supertypes.size(); for (int i = 0; i < count; i++) { long typeID = (supertypes.elementAt(i)).longValue(); addMethods(typeID, IDs, names, signatures, modifiers, interfaceOnly, declared); } } String commandName = "ReferenceType.Methods"; CommandPacket command = new CommandPacket(JDWP.Command.ReferenceType.Methods); command.addReferenceTypeID(referenceTypeID); ReplyPacket reply = receiveReplyFor(command, commandName); try { reply.resetPosition(); int count = reply.getInt(); if (count < 0) { throw new Failure("Negative number (" + count + ") of declared fields received for referenceTypeID: " + referenceTypeID); } for (int i = 0; i < count; i++) { long id = reply.getMethodID(); String name = reply.getString(); String signature = reply.getString(); int modBits = reply.getInt(); if (IDs != null) IDs.add(new Long(id)); if (names != null) names.add(name); if (signatures != null) signatures.add(signature); if (modifiers != null) modifiers.add(new Integer(modBits)); } } catch (BoundException e) { complain("Unable to parse reply packet for " + commandName + " command:\n\t" + e.getMessage()); display("Reply packet:\n" + reply); throw new Failure("Error occured while getting methodIDs for referenceTypeID: " + + referenceTypeID); } } // --------------------------------------------------- // private static long[] makeListOfLongValues(Vector vector) { int count = vector.size(); long[] list = new long[count]; for (int i = 0; i < count; i++) { list[i] = (vector.elementAt(i)).longValue(); } return list; } private static String[] makeListOfStringValues(Vector vector) { int count = vector.size(); String[] list = new String[count]; for (int i = 0; i < count; i++) { list[i] = vector.elementAt(i); } return list; } // --------------------------------------------------- // /** * Force debugge VM to exit using JDWP connection if possible. */ protected void killDebugee() { // ignore } /** * Close transport channel and kill the debugee VM if it is not terminated yet. */ public void close() { if (transport != null) { try { transport.close(); } catch (IOException e) { log.display("WARNING: Caught IOException while closing JDWP connection:\n\t" + e); } } super.close(); } }