1/* 2 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.jdi; 27 28import com.sun.jdi.*; 29import com.sun.jdi.event.*; 30import com.sun.jdi.connect.spi.Connection; 31import com.sun.jdi.event.EventSet; 32 33import java.util.*; 34import java.io.IOException; 35 36public class TargetVM implements Runnable { 37 private Map<String, Packet> waitingQueue = new HashMap<String, Packet>(32,0.75f); 38 private boolean shouldListen = true; 39 private List<EventQueue> eventQueues = Collections.synchronizedList(new ArrayList<EventQueue>(2)); 40 private VirtualMachineImpl vm; 41 private Connection connection; 42 private Thread readerThread; 43 private EventController eventController = null; 44 private boolean eventsHeld = false; 45 46 /* 47 * TO DO: The limit numbers below are somewhat arbitrary and should 48 * be configurable in the future. 49 */ 50 static private final int OVERLOADED_QUEUE = 2000; 51 static private final int UNDERLOADED_QUEUE = 100; 52 53 TargetVM(VirtualMachineImpl vm, Connection connection) { 54 this.vm = vm; 55 this.connection = connection; 56 this.readerThread = new Thread(vm.threadGroupForJDI(), 57 this, "JDI Target VM Interface"); 58 this.readerThread.setDaemon(true); 59 } 60 61 void start() { 62 readerThread.start(); 63 } 64 65 private void dumpPacket(Packet packet, boolean sending) { 66 String direction = sending ? "Sending" : "Receiving"; 67 if (sending) { 68 vm.printTrace(direction + " Command. id=" + packet.id + 69 ", length=" + packet.data.length + 70 ", commandSet=" + packet.cmdSet + 71 ", command=" + packet.cmd + 72 ", flags=" + packet.flags); 73 } else { 74 String type = (packet.flags & Packet.Reply) != 0 ? 75 "Reply" : "Event"; 76 vm.printTrace(direction + " " + type + ". id=" + packet.id + 77 ", length=" + packet.data.length + 78 ", errorCode=" + packet.errorCode + 79 ", flags=" + packet.flags); 80 } 81 StringBuilder line = new StringBuilder(80); 82 line.append("0000: "); 83 for (int i = 0; i < packet.data.length; i++) { 84 if ((i > 0) && (i % 16 == 0)) { 85 vm.printTrace(line.toString()); 86 line.setLength(0); 87 line.append(String.valueOf(i)); 88 line.append(": "); 89 int len = line.length(); 90 for (int j = 0; j < 6 - len; j++) { 91 line.insert(0, '0'); 92 } 93 } 94 int val = 0xff & packet.data[i]; 95 String str = Integer.toHexString(val); 96 if (str.length() == 1) { 97 line.append('0'); 98 } 99 line.append(str); 100 line.append(' '); 101 } 102 if (line.length() > 6) { 103 vm.printTrace(line.toString()); 104 } 105 } 106 107 public void run() { 108 if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) { 109 vm.printTrace("Target VM interface thread running"); 110 } 111 Packet p=null,p2; 112 String idString; 113 114 while(shouldListen) { 115 116 boolean done = false; 117 try { 118 byte b[] = connection.readPacket(); 119 if (b.length == 0) { 120 done = true; 121 } 122 p = Packet.fromByteArray(b); 123 } catch (IOException e) { 124 done = true; 125 } 126 127 if (done) { 128 shouldListen = false; 129 try { 130 connection.close(); 131 } catch (IOException ioe) { } 132 break; 133 } 134 135 if ((vm.traceFlags & VirtualMachineImpl.TRACE_RAW_RECEIVES) != 0) { 136 dumpPacket(p, false); 137 } 138 139 if((p.flags & Packet.Reply) == 0) { 140 // It's a command 141 handleVMCommand(p); 142 } else { 143 /*if(p.errorCode != Packet.ReplyNoError) { 144 System.err.println("Packet " + p.id + " returned failure = " + p.errorCode); 145 }*/ 146 147 vm.state().notifyCommandComplete(p.id); 148 idString = String.valueOf(p.id); 149 150 synchronized(waitingQueue) { 151 p2 = waitingQueue.get(idString); 152 153 if (p2 != null) 154 waitingQueue.remove(idString); 155 } 156 157 if(p2 == null) { 158 // Whoa! a reply without a sender. Problem. 159 // FIX ME! Need to post an error. 160 161 System.err.println("Recieved reply with no sender!"); 162 continue; 163 } 164 p2.errorCode = p.errorCode; 165 p2.data = p.data; 166 p2.replied = true; 167 168 synchronized(p2) { 169 p2.notify(); 170 } 171 } 172 } 173 174 // inform the VM mamager that this VM is history 175 vm.vmManager.disposeVirtualMachine(vm); 176 177 // close down all the event queues 178 // Closing a queue causes a VMDisconnectEvent to 179 // be put onto the queue. 180 synchronized(eventQueues) { 181 Iterator<EventQueue> iter = eventQueues.iterator(); 182 while (iter.hasNext()) { 183 ((EventQueueImpl)iter.next()).close(); 184 } 185 } 186 187 // indirectly throw VMDisconnectedException to 188 // command requesters. 189 synchronized(waitingQueue) { 190 Iterator<Packet> iter = waitingQueue.values().iterator(); 191 while (iter.hasNext()) { 192 Packet packet = iter.next(); 193 synchronized(packet) { 194 packet.notify(); 195 } 196 } 197 waitingQueue.clear(); 198 } 199 200 if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) { 201 vm.printTrace("Target VM interface thread exiting"); 202 } 203 } 204 205 protected void handleVMCommand(Packet p) { 206 switch (p.cmdSet) { 207 case JDWP.Event.COMMAND_SET: 208 handleEventCmdSet(p); 209 break; 210 211 default: 212 System.err.println("Ignoring cmd " + p.id + "/" + 213 p.cmdSet + "/" + p.cmd + " from the VM"); 214 return; 215 } 216 } 217 218 /* Events should not be constructed on this thread (the thread 219 * which reads all data from the transport). This means that the 220 * packet cannot be converted to real JDI objects as that may 221 * involve further communications with the back end which would 222 * deadlock. 223 * 224 * Instead the whole packet is passed for lazy eval by a queue 225 * reading thread. 226 */ 227 protected void handleEventCmdSet(Packet p) { 228 EventSet eventSet = new EventSetImpl(vm, p); 229 230 if (eventSet != null) { 231 queueEventSet(eventSet); 232 } 233 } 234 235 private EventController eventController() { 236 if (eventController == null) { 237 eventController = new EventController(vm); 238 } 239 return eventController; 240 } 241 242 private synchronized void controlEventFlow(int maxQueueSize) { 243 if (!eventsHeld && (maxQueueSize > OVERLOADED_QUEUE)) { 244 eventController().hold(); 245 eventsHeld = true; 246 } else if (eventsHeld && (maxQueueSize < UNDERLOADED_QUEUE)) { 247 eventController().release(); 248 eventsHeld = false; 249 } 250 } 251 252 void notifyDequeueEventSet() { 253 int maxQueueSize = 0; 254 synchronized(eventQueues) { 255 Iterator<EventQueue> iter = eventQueues.iterator(); 256 while (iter.hasNext()) { 257 EventQueueImpl queue = (EventQueueImpl)iter.next(); 258 maxQueueSize = Math.max(maxQueueSize, queue.size()); 259 } 260 } 261 controlEventFlow(maxQueueSize); 262 } 263 264 private void queueEventSet(EventSet eventSet) { 265 int maxQueueSize = 0; 266 267 synchronized(eventQueues) { 268 Iterator<EventQueue> iter = eventQueues.iterator(); 269 while (iter.hasNext()) { 270 EventQueueImpl queue = (EventQueueImpl)iter.next(); 271 queue.enqueue(eventSet); 272 maxQueueSize = Math.max(maxQueueSize, queue.size()); 273 } 274 } 275 276 controlEventFlow(maxQueueSize); 277 } 278 279 void send(Packet packet) { 280 String id = String.valueOf(packet.id); 281 282 synchronized(waitingQueue) { 283 waitingQueue.put(id, packet); 284 } 285 286 if ((vm.traceFlags & VirtualMachineImpl.TRACE_RAW_SENDS) != 0) { 287 dumpPacket(packet, true); 288 } 289 290 try { 291 connection.writePacket(packet.toByteArray()); 292 } catch (IOException e) { 293 throw new VMDisconnectedException(e.getMessage()); 294 } 295 } 296 297 void waitForReply(Packet packet) { 298 synchronized(packet) { 299 while ((!packet.replied) && shouldListen) { 300 try { packet.wait(); } catch (InterruptedException e) {;} 301 } 302 303 if (!packet.replied) { 304 throw new VMDisconnectedException(); 305 } 306 } 307 } 308 309 void addEventQueue(EventQueueImpl queue) { 310 if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) { 311 vm.printTrace("New event queue added"); 312 } 313 eventQueues.add(queue); 314 } 315 316 void stopListening() { 317 if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) { 318 vm.printTrace("Target VM i/f closing event queues"); 319 } 320 shouldListen = false; 321 try { 322 connection.close(); 323 } catch (IOException ioe) { } 324 } 325 326 static private class EventController extends Thread { 327 VirtualMachineImpl vm; 328 int controlRequest = 0; 329 330 EventController(VirtualMachineImpl vm) { 331 super(vm.threadGroupForJDI(), "JDI Event Control Thread"); 332 this.vm = vm; 333 setDaemon(true); 334 setPriority((MAX_PRIORITY + NORM_PRIORITY)/2); 335 super.start(); 336 } 337 338 synchronized void hold() { 339 controlRequest++; 340 notifyAll(); 341 } 342 343 synchronized void release() { 344 controlRequest--; 345 notifyAll(); 346 } 347 348 public void run() { 349 while(true) { 350 int currentRequest; 351 synchronized(this) { 352 while (controlRequest == 0) { 353 try {wait();} catch (InterruptedException e) {} 354 } 355 currentRequest = controlRequest; 356 controlRequest = 0; 357 } 358 try { 359 if (currentRequest > 0) { 360 JDWP.VirtualMachine.HoldEvents.process(vm); 361 } else { 362 JDWP.VirtualMachine.ReleaseEvents.process(vm); 363 } 364 } catch (JDWPException e) { 365 /* 366 * Don't want to terminate the thread, so the 367 * stack trace is printed and we continue. 368 */ 369 e.toJDIException().printStackTrace(System.err); 370 } 371 } 372 } 373 } 374 375} 376