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 26 package com.sun.tools.jdi; 27 28 import com.sun.jdi.*; 29 import com.sun.jdi.event.*; 30 import com.sun.jdi.connect.spi.Connection; 31 import com.sun.jdi.event.EventSet; 32 33 import java.util.*; 34 import java.io.IOException; 35 36 public 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 TargetVM(VirtualMachineImpl vm, Connection connection)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 start()61 void start() { 62 readerThread.start(); 63 } 64 dumpPacket(Packet packet, boolean sending)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 StringBuffer line = new StringBuffer(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 run()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 if (eventController != null) { 178 eventController.release(); 179 } 180 181 // close down all the event queues 182 // Closing a queue causes a VMDisconnectEvent to 183 // be put onto the queue. 184 synchronized(eventQueues) { 185 Iterator<EventQueue> iter = eventQueues.iterator(); 186 while (iter.hasNext()) { 187 ((EventQueueImpl)iter.next()).close(); 188 } 189 } 190 191 // indirectly throw VMDisconnectedException to 192 // command requesters. 193 synchronized(waitingQueue) { 194 Iterator<Packet> iter = waitingQueue.values().iterator(); 195 while (iter.hasNext()) { 196 Packet packet = iter.next(); 197 synchronized(packet) { 198 packet.notify(); 199 } 200 } 201 waitingQueue.clear(); 202 } 203 204 if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) { 205 vm.printTrace("Target VM interface thread exiting"); 206 } 207 } 208 handleVMCommand(Packet p)209 protected void handleVMCommand(Packet p) { 210 switch (p.cmdSet) { 211 case JDWP.Event.COMMAND_SET: 212 handleEventCmdSet(p); 213 break; 214 215 default: 216 System.err.println("Ignoring cmd " + p.id + "/" + 217 p.cmdSet + "/" + p.cmd + " from the VM"); 218 return; 219 } 220 } 221 222 /* Events should not be constructed on this thread (the thread 223 * which reads all data from the transport). This means that the 224 * packet cannot be converted to real JDI objects as that may 225 * involve further communications with the back end which would 226 * deadlock. 227 * 228 * Instead the whole packet is passed for lazy eval by a queue 229 * reading thread. 230 */ handleEventCmdSet(Packet p)231 protected void handleEventCmdSet(Packet p) { 232 EventSet eventSet = new EventSetImpl(vm, p); 233 234 if (eventSet != null) { 235 queueEventSet(eventSet); 236 } 237 } 238 eventController()239 private EventController eventController() { 240 if (eventController == null) { 241 eventController = new EventController(vm); 242 } 243 return eventController; 244 } 245 controlEventFlow(int maxQueueSize)246 private synchronized void controlEventFlow(int maxQueueSize) { 247 if (!eventsHeld && (maxQueueSize > OVERLOADED_QUEUE)) { 248 eventController().hold(); 249 eventsHeld = true; 250 } else if (eventsHeld && (maxQueueSize < UNDERLOADED_QUEUE)) { 251 eventController().release(); 252 eventsHeld = false; 253 } 254 } 255 notifyDequeueEventSet()256 void notifyDequeueEventSet() { 257 int maxQueueSize = 0; 258 synchronized(eventQueues) { 259 Iterator<EventQueue> iter = eventQueues.iterator(); 260 while (iter.hasNext()) { 261 EventQueueImpl queue = (EventQueueImpl)iter.next(); 262 maxQueueSize = Math.max(maxQueueSize, queue.size()); 263 } 264 } 265 controlEventFlow(maxQueueSize); 266 } 267 queueEventSet(EventSet eventSet)268 private void queueEventSet(EventSet eventSet) { 269 int maxQueueSize = 0; 270 271 synchronized(eventQueues) { 272 Iterator<EventQueue> iter = eventQueues.iterator(); 273 while (iter.hasNext()) { 274 EventQueueImpl queue = (EventQueueImpl)iter.next(); 275 queue.enqueue(eventSet); 276 maxQueueSize = Math.max(maxQueueSize, queue.size()); 277 } 278 } 279 280 controlEventFlow(maxQueueSize); 281 } 282 send(Packet packet)283 void send(Packet packet) { 284 String id = String.valueOf(packet.id); 285 286 synchronized(waitingQueue) { 287 waitingQueue.put(id, packet); 288 } 289 290 if ((vm.traceFlags & VirtualMachineImpl.TRACE_RAW_SENDS) != 0) { 291 dumpPacket(packet, true); 292 } 293 294 try { 295 connection.writePacket(packet.toByteArray()); 296 } catch (IOException e) { 297 throw new VMDisconnectedException(e.getMessage()); 298 } 299 } 300 waitForReply(Packet packet)301 void waitForReply(Packet packet) { 302 synchronized(packet) { 303 while ((!packet.replied) && shouldListen) { 304 try { packet.wait(); } catch (InterruptedException e) {;} 305 } 306 307 if (!packet.replied) { 308 throw new VMDisconnectedException(); 309 } 310 } 311 } 312 addEventQueue(EventQueueImpl queue)313 void addEventQueue(EventQueueImpl queue) { 314 if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) { 315 vm.printTrace("New event queue added"); 316 } 317 eventQueues.add(queue); 318 } 319 stopListening()320 void stopListening() { 321 if ((vm.traceFlags & VirtualMachine.TRACE_EVENTS) != 0) { 322 vm.printTrace("Target VM i/f closing event queues"); 323 } 324 shouldListen = false; 325 try { 326 connection.close(); 327 } catch (IOException ioe) { } 328 } 329 330 private class EventController extends Thread { 331 int controlRequest = 0; 332 EventController(VirtualMachineImpl vm)333 EventController(VirtualMachineImpl vm) { 334 super(vm.threadGroupForJDI(), "JDI Event Control Thread"); 335 setDaemon(true); 336 setPriority((MAX_PRIORITY + NORM_PRIORITY)/2); 337 super.start(); 338 } 339 hold()340 synchronized void hold() { 341 controlRequest++; 342 notifyAll(); 343 } 344 release()345 synchronized void release() { 346 controlRequest--; 347 notifyAll(); 348 } 349 run()350 public void run() { 351 while(true) { 352 int currentRequest; 353 synchronized(this) { 354 while (controlRequest == 0) { 355 try {wait();} catch (InterruptedException e) {} 356 if (!shouldListen) return; 357 } 358 currentRequest = controlRequest; 359 controlRequest = 0; 360 } 361 try { 362 if (currentRequest > 0) { 363 JDWP.VirtualMachine.HoldEvents.process(vm); 364 } else { 365 JDWP.VirtualMachine.ReleaseEvents.process(vm); 366 } 367 } catch (JDWPException e) { 368 /* 369 * Don't want to terminate the thread, so the 370 * stack trace is printed and we continue. 371 */ 372 e.toJDIException().printStackTrace(System.err); 373 } 374 } 375 } 376 } 377 378 } 379