--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/tools/jdi/TargetVM.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,375 @@
+/*
+ * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.jdi;
+
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+import com.sun.jdi.connect.spi.Connection;
+import com.sun.jdi.event.EventSet;
+
+import java.util.*;
+import java.io.IOException;
+
+public class TargetVM implements Runnable {
+ private Map<String, Packet> waitingQueue = new HashMap<String, Packet>(32,0.75f);
+ private boolean shouldListen = true;
+ private List<EventQueue> eventQueues = Collections.synchronizedList(new ArrayList<EventQueue>(2));
+ private VirtualMachineImpl vm;
+ private Connection connection;
+ private Thread readerThread;
+ private EventController eventController = null;
+ private boolean eventsHeld = false;
+
+ /*
+ * TO DO: The limit numbers below are somewhat arbitrary and should
+ * be configurable in the future.
+ */
+ static private final int OVERLOADED_QUEUE = 2000;
+ static private final int UNDERLOADED_QUEUE = 100;
+
+ TargetVM(VirtualMachineImpl vm, Connection connection) {
+ this.vm = vm;
+ this.connection = connection;
+ this.readerThread = new Thread(vm.threadGroupForJDI(),
+ this, "JDI Target VM Interface");
+ this.readerThread.setDaemon(true);
+ }
+
+ void start() {
+ readerThread.start();
+ }
+
+ private void dumpPacket(Packet packet, boolean sending) {
+ String direction = sending ? "Sending" : "Receiving";
+ if (sending) {
+ vm.printTrace(direction + " Command. id=" + packet.id +
+ ", length=" + packet.data.length +
+ ", commandSet=" + packet.cmdSet +
+ ", command=" + packet.cmd +
+ ", flags=" + packet.flags);
+ } else {
+ String type = (packet.flags & Packet.Reply) != 0 ?
+ "Reply" : "Event";
+ vm.printTrace(direction + " " + type + ". id=" + packet.id +
+ ", length=" + packet.data.length +
+ ", errorCode=" + packet.errorCode +
+ ", flags=" + packet.flags);
+ }
+ StringBuffer line = new StringBuffer(80);
+ line.append("0000: ");
+ for (int i = 0; i < packet.data.length; i++) {
+ if ((i > 0) && (i % 16 == 0)) {
+ vm.printTrace(line.toString());
+ line.setLength(0);
+ line.append(String.valueOf(i));
+ line.append(": ");
+ int len = line.length();
+ for (int j = 0; j < 6 - len; j++) {
+ line.insert(0, '0');
+ }
+ }
+ int val = 0xff & packet.data[i];
+ String str = Integer.toHexString(val);
+ if (str.length() == 1) {
+ line.append('0');
+ }
+ line.append(str);
+ line.append(' ');
+ }
+ if (line.length() > 6) {
+ vm.printTrace(line.toString());
+ }
+ }
+
+ public void run() {
+ if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) {
+ vm.printTrace("Target VM interface thread running");
+ }
+ Packet p=null,p2;
+ String idString;
+
+ while(shouldListen) {
+
+ boolean done = false;
+ try {
+ byte b[] = connection.readPacket();
+ if (b.length == 0) {
+ done = true;
+ }
+ p = Packet.fromByteArray(b);
+ } catch (IOException e) {
+ done = true;
+ }
+
+ if (done) {
+ shouldListen = false;
+ try {
+ connection.close();
+ } catch (IOException ioe) { }
+ break;
+ }
+
+ if ((vm.traceFlags & VirtualMachineImpl.TRACE_RAW_RECEIVES) != 0) {
+ dumpPacket(p, false);
+ }
+
+ if((p.flags & Packet.Reply) == 0) {
+ // It's a command
+ handleVMCommand(p);
+ } else {
+ /*if(p.errorCode != Packet.ReplyNoError) {
+ System.err.println("Packet " + p.id + " returned failure = " + p.errorCode);
+ }*/
+
+ vm.state().notifyCommandComplete(p.id);
+ idString = String.valueOf(p.id);
+
+ synchronized(waitingQueue) {
+ p2 = (Packet)waitingQueue.get(idString);
+
+ if (p2 != null)
+ waitingQueue.remove(idString);
+ }
+
+ if(p2 == null) {
+ // Whoa! a reply without a sender. Problem.
+ // FIX ME! Need to post an error.
+
+ System.err.println("Recieved reply with no sender!");
+ continue;
+ }
+ p2.errorCode = p.errorCode;
+ p2.data = p.data;
+ p2.replied = true;
+
+ synchronized(p2) {
+ p2.notify();
+ }
+ }
+ }
+
+ // inform the VM mamager that this VM is history
+ vm.vmManager.disposeVirtualMachine(vm);
+
+ // close down all the event queues
+ // Closing a queue causes a VMDisconnectEvent to
+ // be put onto the queue.
+ synchronized(eventQueues) {
+ Iterator iter = eventQueues.iterator();
+ while (iter.hasNext()) {
+ ((EventQueueImpl)iter.next()).close();
+ }
+ }
+
+ // indirectly throw VMDisconnectedException to
+ // command requesters.
+ synchronized(waitingQueue) {
+ Iterator iter = waitingQueue.values().iterator();
+ while (iter.hasNext()) {
+ Packet packet = (Packet)iter.next();
+ synchronized(packet) {
+ packet.notify();
+ }
+ }
+ waitingQueue.clear();
+ }
+
+ if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) {
+ vm.printTrace("Target VM interface thread exiting");
+ }
+ }
+
+ protected void handleVMCommand(Packet p) {
+ switch (p.cmdSet) {
+ case JDWP.Event.COMMAND_SET:
+ handleEventCmdSet(p);
+ break;
+
+ default:
+ System.err.println("Ignoring cmd " + p.id + "/" +
+ p.cmdSet + "/" + p.cmd + " from the VM");
+ return;
+ }
+ }
+
+ /* Events should not be constructed on this thread (the thread
+ * which reads all data from the transport). This means that the
+ * packet cannot be converted to real JDI objects as that may
+ * involve further communications with the back end which would
+ * deadlock.
+ *
+ * Instead the whole packet is passed for lazy eval by a queue
+ * reading thread.
+ */
+ protected void handleEventCmdSet(Packet p) {
+ EventSet eventSet = new EventSetImpl(vm, p);
+
+ if (eventSet != null) {
+ queueEventSet(eventSet);
+ }
+ }
+
+ private EventController eventController() {
+ if (eventController == null) {
+ eventController = new EventController(vm);
+ }
+ return eventController;
+ }
+
+ private synchronized void controlEventFlow(int maxQueueSize) {
+ if (!eventsHeld && (maxQueueSize > OVERLOADED_QUEUE)) {
+ eventController().hold();
+ eventsHeld = true;
+ } else if (eventsHeld && (maxQueueSize < UNDERLOADED_QUEUE)) {
+ eventController().release();
+ eventsHeld = false;
+ }
+ }
+
+ void notifyDequeueEventSet() {
+ int maxQueueSize = 0;
+ synchronized(eventQueues) {
+ Iterator iter = eventQueues.iterator();
+ while (iter.hasNext()) {
+ EventQueueImpl queue = (EventQueueImpl)iter.next();
+ maxQueueSize = Math.max(maxQueueSize, queue.size());
+ }
+ }
+ controlEventFlow(maxQueueSize);
+ }
+
+ private void queueEventSet(EventSet eventSet) {
+ int maxQueueSize = 0;
+
+ synchronized(eventQueues) {
+ Iterator iter = eventQueues.iterator();
+ while (iter.hasNext()) {
+ EventQueueImpl queue = (EventQueueImpl)iter.next();
+ queue.enqueue(eventSet);
+ maxQueueSize = Math.max(maxQueueSize, queue.size());
+ }
+ }
+
+ controlEventFlow(maxQueueSize);
+ }
+
+ void send(Packet packet) {
+ String id = String.valueOf(packet.id);
+
+ synchronized(waitingQueue) {
+ waitingQueue.put(id, packet);
+ }
+
+ if ((vm.traceFlags & VirtualMachineImpl.TRACE_RAW_SENDS) != 0) {
+ dumpPacket(packet, true);
+ }
+
+ try {
+ connection.writePacket(packet.toByteArray());
+ } catch (IOException e) {
+ throw new VMDisconnectedException(e.getMessage());
+ }
+ }
+
+ void waitForReply(Packet packet) {
+ synchronized(packet) {
+ while ((!packet.replied) && shouldListen) {
+ try { packet.wait(); } catch (InterruptedException e) {;}
+ }
+
+ if (!packet.replied) {
+ throw new VMDisconnectedException();
+ }
+ }
+ }
+
+ void addEventQueue(EventQueueImpl queue) {
+ if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) {
+ vm.printTrace("New event queue added");
+ }
+ eventQueues.add(queue);
+ }
+
+ void stopListening() {
+ if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) {
+ vm.printTrace("Target VM i/f closing event queues");
+ }
+ shouldListen = false;
+ try {
+ connection.close();
+ } catch (IOException ioe) { }
+ }
+
+ static private class EventController extends Thread {
+ VirtualMachineImpl vm;
+ int controlRequest = 0;
+
+ EventController(VirtualMachineImpl vm) {
+ super(vm.threadGroupForJDI(), "JDI Event Control Thread");
+ this.vm = vm;
+ setDaemon(true);
+ setPriority((MAX_PRIORITY + NORM_PRIORITY)/2);
+ super.start();
+ }
+
+ synchronized void hold() {
+ controlRequest++;
+ notifyAll();
+ }
+
+ synchronized void release() {
+ controlRequest--;
+ notifyAll();
+ }
+
+ public void run() {
+ while(true) {
+ int currentRequest;
+ synchronized(this) {
+ while (controlRequest == 0) {
+ try {wait();} catch (InterruptedException e) {}
+ }
+ currentRequest = controlRequest;
+ controlRequest = 0;
+ }
+ try {
+ if (currentRequest > 0) {
+ JDWP.VirtualMachine.HoldEvents.process(vm);
+ } else {
+ JDWP.VirtualMachine.ReleaseEvents.process(vm);
+ }
+ } catch (JDWPException e) {
+ /*
+ * Don't want to terminate the thread, so the
+ * stack trace is printed and we continue.
+ */
+ e.toJDIException().printStackTrace(System.err);
+ }
+ }
+ }
+ }
+
+}