blob: 54d77305ae2cfa8cd03898bb5143ebbbb406a633 [file] [log] [blame]
Brian Carlstrom34c06312011-12-05 09:38:27 -08001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * JDWP spy. This is a rearranged version of the JDWP code from the VM.
5 */
6#include "Common.h"
7#include "jdwp/jdwp_constants.h"
8
9#include <stdlib.h>
10#include <unistd.h>
11#include <stdio.h>
12#include <string.h>
13#include <sys/types.h>
14#include <sys/socket.h>
15#include <netinet/in.h>
16#include <netinet/tcp.h>
17#include <arpa/inet.h>
18#include <netdb.h>
19#include <time.h>
20#include <errno.h>
21#include <assert.h>
22
23#include <iostream>
24#include <sstream>
25
26#define kInputBufferSize (256*1024)
27
28#define kMagicHandshakeLen 14 /* "JDWP-Handshake" */
29#define kJDWPHeaderLen 11
30#define kJDWPFlagReply 0x80
31
32
33/*
34 * Information about the remote end.
35 */
36typedef struct Peer {
37 char label[2]; /* 'D' or 'V' */
38
39 int sock;
40 unsigned char inputBuffer[kInputBufferSize];
41 int inputCount;
42
43 bool awaitingHandshake; /* waiting for "JDWP-Handshake" */
44} Peer;
45
46
47/*
48 * Network state.
49 */
50typedef struct NetState {
51 /* listen here for connection from debugger */
52 int listenSock;
53
54 /* connect here to contact VM */
55 struct in_addr vmAddr;
56 short vmPort;
57
58 Peer dbg;
59 Peer vm;
60} NetState;
61
62/*
63 * Function names.
64 */
65typedef struct {
66 u1 cmdSet;
67 u1 cmd;
68 const char* descr;
69} JdwpHandlerMap;
70
71/*
72 * Map commands to names.
73 *
74 * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
75 * and 128-256 are vendor-defined.
76 */
77static const JdwpHandlerMap gHandlerMap[] = {
78 /* VirtualMachine command set (1) */
79 { 1, 1, "VirtualMachine.Version" },
80 { 1, 2, "VirtualMachine.ClassesBySignature" },
81 { 1, 3, "VirtualMachine.AllClasses" },
82 { 1, 4, "VirtualMachine.AllThreads" },
83 { 1, 5, "VirtualMachine.TopLevelThreadGroups" },
84 { 1, 6, "VirtualMachine.Dispose" },
85 { 1, 7, "VirtualMachine.IDSizes" },
86 { 1, 8, "VirtualMachine.Suspend" },
87 { 1, 9, "VirtualMachine.Resume" },
88 { 1, 10, "VirtualMachine.Exit" },
89 { 1, 11, "VirtualMachine.CreateString" },
90 { 1, 12, "VirtualMachine.Capabilities" },
91 { 1, 13, "VirtualMachine.ClassPaths" },
92 { 1, 14, "VirtualMachine.DisposeObjects" },
93 { 1, 15, "VirtualMachine.HoldEvents" },
94 { 1, 16, "VirtualMachine.ReleaseEvents" },
95 { 1, 17, "VirtualMachine.CapabilitiesNew" },
96 { 1, 18, "VirtualMachine.RedefineClasses" },
97 { 1, 19, "VirtualMachine.SetDefaultStratum" },
98 { 1, 20, "VirtualMachine.AllClassesWithGeneric"},
99 { 1, 21, "VirtualMachine.InstanceCounts"},
100
101 /* ReferenceType command set (2) */
102 { 2, 1, "ReferenceType.Signature" },
103 { 2, 2, "ReferenceType.ClassLoader" },
104 { 2, 3, "ReferenceType.Modifiers" },
105 { 2, 4, "ReferenceType.Fields" },
106 { 2, 5, "ReferenceType.Methods" },
107 { 2, 6, "ReferenceType.GetValues" },
108 { 2, 7, "ReferenceType.SourceFile" },
109 { 2, 8, "ReferenceType.NestedTypes" },
110 { 2, 9, "ReferenceType.Status" },
111 { 2, 10, "ReferenceType.Interfaces" },
112 { 2, 11, "ReferenceType.ClassObject" },
113 { 2, 12, "ReferenceType.SourceDebugExtension" },
114 { 2, 13, "ReferenceType.SignatureWithGeneric" },
115 { 2, 14, "ReferenceType.FieldsWithGeneric" },
116 { 2, 15, "ReferenceType.MethodsWithGeneric" },
117 { 2, 16, "ReferenceType.Instances" },
118 { 2, 17, "ReferenceType.ClassFileVersion" },
119 { 2, 18, "ReferenceType.ConstantPool" },
120
121 /* ClassType command set (3) */
122 { 3, 1, "ClassType.Superclass" },
123 { 3, 2, "ClassType.SetValues" },
124 { 3, 3, "ClassType.InvokeMethod" },
125 { 3, 4, "ClassType.NewInstance" },
126
127 /* ArrayType command set (4) */
128 { 4, 1, "ArrayType.NewInstance" },
129
130 /* InterfaceType command set (5) */
131
132 /* Method command set (6) */
133 { 6, 1, "Method.LineTable" },
134 { 6, 2, "Method.VariableTable" },
135 { 6, 3, "Method.Bytecodes" },
136 { 6, 4, "Method.IsObsolete" },
137 { 6, 5, "Method.VariableTableWithGeneric" },
138
139 /* Field command set (8) */
140
141 /* ObjectReference command set (9) */
142 { 9, 1, "ObjectReference.ReferenceType" },
143 { 9, 2, "ObjectReference.GetValues" },
144 { 9, 3, "ObjectReference.SetValues" },
145 { 9, 4, "ObjectReference.UNUSED" },
146 { 9, 5, "ObjectReference.MonitorInfo" },
147 { 9, 6, "ObjectReference.InvokeMethod" },
148 { 9, 7, "ObjectReference.DisableCollection" },
149 { 9, 8, "ObjectReference.EnableCollection" },
150 { 9, 9, "ObjectReference.IsCollected" },
151 { 9, 10, "ObjectReference.ReferringObjects" },
152
153 /* StringReference command set (10) */
154 { 10, 1, "StringReference.Value" },
155
156 /* ThreadReference command set (11) */
157 { 11, 1, "ThreadReference.Name" },
158 { 11, 2, "ThreadReference.Suspend" },
159 { 11, 3, "ThreadReference.Resume" },
160 { 11, 4, "ThreadReference.Status" },
161 { 11, 5, "ThreadReference.ThreadGroup" },
162 { 11, 6, "ThreadReference.Frames" },
163 { 11, 7, "ThreadReference.FrameCount" },
164 { 11, 8, "ThreadReference.OwnedMonitors" },
165 { 11, 9, "ThreadReference.CurrentContendedMonitor" },
166 { 11, 10, "ThreadReference.Stop" },
167 { 11, 11, "ThreadReference.Interrupt" },
168 { 11, 12, "ThreadReference.SuspendCount" },
169 { 11, 13, "ThreadReference.OwnedMonitorsStackDepthInfo" },
170 { 11, 14, "ThreadReference.ForceEarlyReturn" },
171
172 /* ThreadGroupReference command set (12) */
173 { 12, 1, "ThreadGroupReference.Name" },
174 { 12, 2, "ThreadGroupReference.Parent" },
175 { 12, 3, "ThreadGroupReference.Children" },
176
177 /* ArrayReference command set (13) */
178 { 13, 1, "ArrayReference.Length" },
179 { 13, 2, "ArrayReference.GetValues" },
180 { 13, 3, "ArrayReference.SetValues" },
181
182 /* ClassLoaderReference command set (14) */
183 { 14, 1, "ArrayReference.VisibleClasses" },
184
185 /* EventRequest command set (15) */
186 { 15, 1, "EventRequest.Set" },
187 { 15, 2, "EventRequest.Clear" },
188 { 15, 3, "EventRequest.ClearAllBreakpoints" },
189
190 /* StackFrame command set (16) */
191 { 16, 1, "StackFrame.GetValues" },
192 { 16, 2, "StackFrame.SetValues" },
193 { 16, 3, "StackFrame.ThisObject" },
194 { 16, 4, "StackFrame.PopFrames" },
195
196 /* ClassObjectReference command set (17) */
197 { 17, 1, "ClassObjectReference.ReflectedType" },
198
199 /* Event command set (64) */
200 { 64, 100, "Event.Composite" },
201
202 /* DDMS */
203 { 199, 1, "DDMS.Chunk" },
204};
205
206/*
207 * Look up a command's name.
208 */
209static const char* getCommandName(int cmdSet, int cmd)
210{
211 for (int i = 0; i < (int) NELEM(gHandlerMap); i++) {
212 if (gHandlerMap[i].cmdSet == cmdSet &&
213 gHandlerMap[i].cmd == cmd)
214 {
215 return gHandlerMap[i].descr;
216 }
217 }
218
219 return "?UNKNOWN?";
220}
221
222
223void jdwpNetFree(NetState* netState); /* fwd */
224
225/*
226 * Allocate state structure and bind to the listen port.
227 *
228 * Returns 0 on success.
229 */
230NetState* jdwpNetStartup(unsigned short listenPort, const char* connectHost,
231 unsigned short connectPort)
232{
233 NetState* netState = (NetState*) malloc(sizeof(*netState));
234 memset(netState, 0, sizeof(*netState));
235 netState->listenSock = -1;
236 netState->dbg.sock = netState->vm.sock = -1;
237
238 strcpy(netState->dbg.label, "D");
239 strcpy(netState->vm.label, "V");
240
241 /*
242 * Set up a socket to listen for connections from the debugger.
243 */
244
245 netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
246 if (netState->listenSock < 0) {
247 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
248 goto fail;
249 }
250
251 /* allow immediate re-use if we die */
252 {
253 int one = 1;
254 if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
255 sizeof(one)) < 0)
256 {
257 fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n",
258 strerror(errno));
259 goto fail;
260 }
261 }
262
263 struct sockaddr_in addr;
264 addr.sin_family = AF_INET;
265 addr.sin_port = htons(listenPort);
266 addr.sin_addr.s_addr = INADDR_ANY;
267
268 if (bind(netState->listenSock, (struct sockaddr*) &addr, sizeof(addr)) != 0)
269 {
270 fprintf(stderr, "attempt to bind to port %u failed: %s\n",
271 listenPort, strerror(errno));
272 goto fail;
273 }
274
275 fprintf(stderr, "+++ bound to port %u\n", listenPort);
276
277 if (listen(netState->listenSock, 5) != 0) {
278 fprintf(stderr, "Listen failed: %s\n", strerror(errno));
279 goto fail;
280 }
281
282 /*
283 * Do the hostname lookup for the VM.
284 */
285 struct hostent* pHost;
286
287 pHost = gethostbyname(connectHost);
288 if (pHost == NULL) {
289 fprintf(stderr, "Name lookup of '%s' failed: %s\n",
290 connectHost, strerror(h_errno));
291 goto fail;
292 }
293
294 netState->vmAddr = *((struct in_addr*) pHost->h_addr_list[0]);
295 netState->vmPort = connectPort;
296
297 fprintf(stderr, "+++ connect host resolved to %s\n",
298 inet_ntoa(netState->vmAddr));
299
300 return netState;
301
302fail:
303 jdwpNetFree(netState);
304 return NULL;
305}
306
307/*
308 * Shut down JDWP listener. Don't free state.
309 *
310 * Note that "netState" may be partially initialized if "startup" failed.
311 */
312void jdwpNetShutdown(NetState* netState)
313{
314 int listenSock = netState->listenSock;
315 int dbgSock = netState->dbg.sock;
316 int vmSock = netState->vm.sock;
317
318 /* clear these out so it doesn't wake up and try to reuse them */
319 /* (important when multi-threaded) */
320 netState->listenSock = netState->dbg.sock = netState->vm.sock = -1;
321
322 if (listenSock >= 0) {
323 shutdown(listenSock, SHUT_RDWR);
324 close(listenSock);
325 }
326 if (dbgSock >= 0) {
327 shutdown(dbgSock, SHUT_RDWR);
328 close(dbgSock);
329 }
330 if (vmSock >= 0) {
331 shutdown(vmSock, SHUT_RDWR);
332 close(vmSock);
333 }
334}
335
336/*
337 * Shut down JDWP listener and free its state.
338 */
339void jdwpNetFree(NetState* netState)
340{
341 if (netState == NULL)
342 return;
343
344 jdwpNetShutdown(netState);
345 free(netState);
346}
347
348/*
349 * Disable the TCP Nagle algorithm, which delays transmission of outbound
350 * packets until the previous transmissions have been acked. JDWP does a
351 * lot of back-and-forth with small packets, so this may help.
352 */
353static int setNoDelay(int fd)
354{
355 int cc, on = 1;
356
357 cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
358 assert(cc == 0);
359 return cc;
360}
361
362/*
363 * Accept a connection. This will block waiting for somebody to show up.
364 */
365bool jdwpAcceptConnection(NetState* netState)
366{
367 struct sockaddr_in addr;
368 socklen_t addrlen;
369 int sock;
370
371 if (netState->listenSock < 0)
372 return false; /* you're not listening! */
373
374 assert(netState->dbg.sock < 0); /* must not already be talking */
375
376 addrlen = sizeof(addr);
377 do {
378 sock = accept(netState->listenSock, (struct sockaddr*) &addr, &addrlen);
379 if (sock < 0 && errno != EINTR) {
380 fprintf(stderr, "accept failed: %s\n", strerror(errno));
381 return false;
382 }
383 } while (sock < 0);
384
385 fprintf(stderr, "+++ accepted connection from %s:%u\n",
386 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
387
388 netState->dbg.sock = sock;
389 netState->dbg.awaitingHandshake = true;
390 netState->dbg.inputCount = 0;
391
392 setNoDelay(sock);
393
394 return true;
395}
396
397/*
398 * Close the connections to the debugger and VM.
399 *
400 * Reset the state so we're ready to receive a new connection.
401 */
402void jdwpCloseConnection(NetState* netState)
403{
404 if (netState->dbg.sock >= 0) {
405 fprintf(stderr, "+++ closing connection to debugger\n");
406 close(netState->dbg.sock);
407 netState->dbg.sock = -1;
408 }
409 if (netState->vm.sock >= 0) {
410 fprintf(stderr, "+++ closing connection to vm\n");
411 close(netState->vm.sock);
412 netState->vm.sock = -1;
413 }
414}
415
416/*
417 * Figure out if we have a full packet in the buffer.
418 */
419static bool haveFullPacket(Peer* pPeer)
420{
421 long length;
422
423 if (pPeer->awaitingHandshake)
424 return (pPeer->inputCount >= kMagicHandshakeLen);
425
426 if (pPeer->inputCount < 4)
427 return false;
428
429 length = get4BE(pPeer->inputBuffer);
430 return (pPeer->inputCount >= length);
431}
432
433/*
434 * Consume bytes from the buffer.
435 *
436 * This would be more efficient with a circular buffer. However, we're
437 * usually only going to find one packet, which is trivial to handle.
438 */
439static void consumeBytes(Peer* pPeer, int count)
440{
441 assert(count > 0);
442 assert(count <= pPeer->inputCount);
443
444 if (count == pPeer->inputCount) {
445 pPeer->inputCount = 0;
446 return;
447 }
448
449 memmove(pPeer->inputBuffer, pPeer->inputBuffer + count,
450 pPeer->inputCount - count);
451 pPeer->inputCount -= count;
452}
453
454/*
455 * Get the current time.
456 */
457static void getCurrentTime(int* pMin, int* pSec)
458{
459 time_t now;
460 struct tm* ptm;
461
462 now = time(NULL);
463 ptm = localtime(&now);
464 *pMin = ptm->tm_min;
465 *pSec = ptm->tm_sec;
466}
467
468/*
469 * Dump the contents of a packet to stdout.
470 */
471static void dumpPacket(const unsigned char* packetBuf, const char* srcName,
472 const char* dstName)
473{
474 const unsigned char* buf = packetBuf;
475 char prefix[3];
476 u4 length, id;
477 u1 flags, cmdSet=0, cmd=0;
478 art::JDWP::JdwpError error = art::JDWP::ERR_NONE;
479 bool reply;
480 int dataLen;
481
482 length = get4BE(buf+0);
483 id = get4BE(buf+4);
484 flags = get1(buf+8);
485 if ((flags & kJDWPFlagReply) != 0) {
486 reply = true;
487 error = static_cast<art::JDWP::JdwpError>(get2BE(buf+9));
488 } else {
489 reply = false;
490 cmdSet = get1(buf+9);
491 cmd = get1(buf+10);
492 }
493
494 buf += kJDWPHeaderLen;
495 dataLen = length - (buf - packetBuf);
496
497 if (!reply) {
498 prefix[0] = srcName[0];
499 prefix[1] = '>';
500 } else {
501 prefix[0] = dstName[0];
502 prefix[1] = '<';
503 }
504 prefix[2] = '\0';
505
506 int min, sec;
507 getCurrentTime(&min, &sec);
508
509 if (!reply) {
510 printf("%s REQUEST dataLen=%-5u id=0x%08x flags=0x%02x cmd=%d/%d [%02d:%02d]\n",
511 prefix, dataLen, id, flags, cmdSet, cmd, min, sec);
512 printf("%s --> %s\n", prefix, getCommandName(cmdSet, cmd));
513 } else {
514 std::ostringstream ss;
515 ss << error;
516 printf("%s REPLY dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n",
517 prefix, dataLen, id, flags, error, ss.str().c_str(), min,sec);
518 }
519 if (dataLen > 0)
520 printHexDump2(buf, dataLen, prefix);
521 printf("%s ----------\n", prefix);
522}
523
524/*
525 * Handle a packet. Returns "false" if we encounter a connection-fatal error.
526 */
527static bool handlePacket(Peer* pDst, Peer* pSrc)
528{
529 const unsigned char* buf = pSrc->inputBuffer;
530 u4 length;
531 u1 flags;
532 int cc;
533
534 length = get4BE(buf+0);
535 flags = get1(buf+9);
536
537 assert((int) length <= pSrc->inputCount);
538
539 dumpPacket(buf, pSrc->label, pDst->label);
540
541 cc = write(pDst->sock, buf, length);
542 if (cc != (int) length) {
543 fprintf(stderr, "Failed sending packet: %s\n", strerror(errno));
544 return false;
545 }
546 /*printf("*** wrote %d bytes from %c to %c\n",
547 cc, pSrc->label[0], pDst->label[0]);*/
548
549 consumeBytes(pSrc, length);
550 return true;
551}
552
553/*
554 * Handle incoming data. If we have a full packet in the buffer, process it.
555 */
556static bool handleIncoming(Peer* pWritePeer, Peer* pReadPeer)
557{
558 if (haveFullPacket(pReadPeer)) {
559 if (pReadPeer->awaitingHandshake) {
560 printf("Handshake [%c]: %.14s\n",
561 pReadPeer->label[0], pReadPeer->inputBuffer);
562 if (write(pWritePeer->sock, pReadPeer->inputBuffer,
563 kMagicHandshakeLen) != kMagicHandshakeLen)
564 {
565 fprintf(stderr,
566 "+++ [%c] handshake write failed\n", pReadPeer->label[0]);
567 goto fail;
568 }
569 consumeBytes(pReadPeer, kMagicHandshakeLen);
570 pReadPeer->awaitingHandshake = false;
571 } else {
572 if (!handlePacket(pWritePeer, pReadPeer))
573 goto fail;
574 }
575 } else {
576 /*printf("*** %c not full yet\n", pReadPeer->label[0]);*/
577 }
578
579 return true;
580
581fail:
582 return false;
583}
584
585/*
586 * Process incoming data. If no data is available, this will block until
587 * some arrives.
588 *
589 * Returns "false" on error (indicating that the connection has been severed).
590 */
591bool jdwpProcessIncoming(NetState* netState)
592{
593 int cc;
594
595 assert(netState->dbg.sock >= 0);
596 assert(netState->vm.sock >= 0);
597
598 while (!haveFullPacket(&netState->dbg) && !haveFullPacket(&netState->vm)) {
599 /* read some more */
600 int highFd;
601 fd_set readfds;
602
603 highFd = (netState->dbg.sock > netState->vm.sock) ?
604 netState->dbg.sock+1 : netState->vm.sock+1;
605 FD_ZERO(&readfds);
606 FD_SET(netState->dbg.sock, &readfds);
607 FD_SET(netState->vm.sock, &readfds);
608
609 errno = 0;
610 cc = select(highFd, &readfds, NULL, NULL, NULL);
611 if (cc < 0) {
612 if (errno == EINTR) {
613 fprintf(stderr, "+++ EINTR on select\n");
614 continue;
615 }
616 fprintf(stderr, "+++ select failed: %s\n", strerror(errno));
617 goto fail;
618 }
619
620 if (FD_ISSET(netState->dbg.sock, &readfds)) {
621 cc = read(netState->dbg.sock,
622 netState->dbg.inputBuffer + netState->dbg.inputCount,
623 sizeof(netState->dbg.inputBuffer) - netState->dbg.inputCount);
624 if (cc < 0) {
625 if (errno == EINTR) {
626 fprintf(stderr, "+++ EINTR on read\n");
627 continue;
628 }
629 fprintf(stderr, "+++ dbg read failed: %s\n", strerror(errno));
630 goto fail;
631 }
632 if (cc == 0) {
633 if (sizeof(netState->dbg.inputBuffer) ==
634 netState->dbg.inputCount)
635 fprintf(stderr, "+++ debugger sent huge message\n");
636 else
637 fprintf(stderr, "+++ debugger disconnected\n");
638 goto fail;
639 }
640
641 /*printf("*** %d bytes from dbg\n", cc);*/
642 netState->dbg.inputCount += cc;
643 }
644
645 if (FD_ISSET(netState->vm.sock, &readfds)) {
646 cc = read(netState->vm.sock,
647 netState->vm.inputBuffer + netState->vm.inputCount,
648 sizeof(netState->vm.inputBuffer) - netState->vm.inputCount);
649 if (cc < 0) {
650 if (errno == EINTR) {
651 fprintf(stderr, "+++ EINTR on read\n");
652 continue;
653 }
654 fprintf(stderr, "+++ vm read failed: %s\n", strerror(errno));
655 goto fail;
656 }
657 if (cc == 0) {
658 if (sizeof(netState->vm.inputBuffer) ==
659 netState->vm.inputCount)
660 fprintf(stderr, "+++ vm sent huge message\n");
661 else
662 fprintf(stderr, "+++ vm disconnected\n");
663 goto fail;
664 }
665
666 /*printf("*** %d bytes from vm\n", cc);*/
667 netState->vm.inputCount += cc;
668 }
669 }
670
671 if (!handleIncoming(&netState->dbg, &netState->vm))
672 goto fail;
673 if (!handleIncoming(&netState->vm, &netState->dbg))
674 goto fail;
675
676 return true;
677
678fail:
679 jdwpCloseConnection(netState);
680 return false;
681}
682
683/*
684 * Connect to the VM.
685 */
686bool jdwpConnectToVm(NetState* netState)
687{
688 struct sockaddr_in addr;
689 int sock = -1;
690
691 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
692 if (sock < 0) {
693 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
694 goto fail;
695 }
696
697 addr.sin_family = AF_INET;
698 addr.sin_addr = netState->vmAddr;
699 addr.sin_port = htons(netState->vmPort);
700 if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
701 fprintf(stderr, "Connection to %s:%u failed: %s\n",
702 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), strerror(errno));
703 goto fail;
704 }
705 fprintf(stderr, "+++ connected to VM %s:%u\n",
706 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
707
708 netState->vm.sock = sock;
709 netState->vm.awaitingHandshake = true;
710 netState->vm.inputCount = 0;
711
712 setNoDelay(netState->vm.sock);
713 return true;
714
715fail:
716 if (sock >= 0)
717 close(sock);
718 return false;
719}
720
721/*
722 * Establish network connections and start things running.
723 *
724 * We wait for a new connection from the debugger. When one arrives we
725 * open a connection to the VM. If one side or the other goes away, we
726 * drop both ends and go back to listening.
727 */
728int run(const char* connectHost, int connectPort, int listenPort)
729{
730 NetState* state;
731
732 state = jdwpNetStartup(listenPort, connectHost, connectPort);
733 if (state == NULL)
734 return -1;
735
736 while (true) {
737 if (!jdwpAcceptConnection(state))
738 break;
739
740 if (jdwpConnectToVm(state)) {
741 while (true) {
742 if (!jdwpProcessIncoming(state))
743 break;
744 }
745 }
746
747 jdwpCloseConnection(state);
748 }
749
750 jdwpNetFree(state);
751
752 return 0;
753}