| /* |
| * 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. |
| * <p> |
| * This class is an mirror of debugee VM that is constructed by |
| * <code>Binder</code> and uses <code>Transport</code> object |
| * to interact with debugee VM. |
| * <p> |
| * In addition to the general abities to control of debugee VM process, |
| * provided by the base class <code>DebugeeProcess</code>, 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<EventPacket> eventQueue = new LinkedList<EventPacket>(); |
| |
| protected Transport transport = null; |
| |
| /** Make new <code>Debugee</code> object for the given binder. */ |
| protected Debugee (Binder binder) { |
| super(binder); |
| this.argumentHandler = binder.getArgumentHandler(); |
| this.binder = binder; |
| prefix = "Debugee> "; |
| } |
| |
| /** Return <code>Binder</code> of the debugee object. */ |
| public Binder getBinder() { |
| return binder; |
| } |
| |
| /** Return <code>Transport</code> of the debugee object. */ |
| public Transport getTransport() { |
| return transport; |
| } |
| |
| /** |
| * Prepare transport object for establishing connection. |
| * This may change connection options in <code>argumentHandler</code>. |
| * |
| * @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<Long> vector = new Vector<Long>(); |
| 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<Long> vector = new Vector<Long>(); |
| 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<Long> vector = new Vector<Long>(); |
| 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<Long> vector = new Vector<Long>(); |
| 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<Long> list = new Vector<Long>(); |
| 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<String> list = new Vector<String>(); |
| 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<String> list = new Vector<String>(); |
| 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<Long> IDs = new Vector<Long>(); |
| Vector<String> names = new Vector<String>(); |
| 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<Long> list = new Vector<Long>(); |
| 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<String> list = new Vector<String>(); |
| 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<String> list = new Vector<String>(); |
| 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<Long> IDs = new Vector<Long>(); |
| Vector<String> names = new Vector<String>(); |
| 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 <code>approximate</code> is <it>true</i> 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<Long> supertypes, |
| Vector<Long> interfaces, Vector<Long> 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<Long> IDs, Vector<String> names, |
| Vector<String> signatures, Vector<Integer> modifiers, |
| boolean interfaceOnly, boolean declared) { |
| |
| if (!declared) { |
| Vector<Long> supertypes = new Vector<Long>(); |
| 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<Long> IDs, Vector<String> names, |
| Vector<String> signatures, Vector<Integer> modifiers, |
| boolean interfaceOnly, boolean declared) { |
| |
| if (!declared) { |
| Vector<Long> supertypes = new Vector<Long>(); |
| 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<Long> 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<String> 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(); |
| } |
| |
| } |