Use Tree Navigation
public class

TargetVM

extends Object
implements Runnable
/*
 * 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);
               
}
           
}
       
}
   
}

}