/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package com.sun.tools.jdi;import com.sun.jdi.*;import com.sun.jdi.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 = 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); } } } }}
/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdi;
import com.sun.jdi.*;
import com.sun.jdi.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 = 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);
}
}
}
}
}