blob: 5c789d81ab6f30b5641ab4a2f4d745451f2d0f8b [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 */
Elliott Hughes719ace42012-03-09 18:06:03 -080036struct Peer {
Brian Carlstrom34c06312011-12-05 09:38:27 -080037 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" */
Elliott Hughes719ace42012-03-09 18:06:03 -080044};
Brian Carlstrom34c06312011-12-05 09:38:27 -080045
46
47/*
48 * Network state.
49 */
Elliott Hughes719ace42012-03-09 18:06:03 -080050struct NetState {
Brian Carlstrom34c06312011-12-05 09:38:27 -080051 /* listen here for connection from debugger */
52 int listenSock;
53
54 /* connect here to contact VM */
Elliott Hughes7b9d9962012-04-20 18:48:18 -070055 in_addr vmAddr;
Elliott Hughes6d8dd472012-01-17 18:27:41 -080056 uint16_t vmPort;
Brian Carlstrom34c06312011-12-05 09:38:27 -080057
58 Peer dbg;
59 Peer vm;
Elliott Hughes719ace42012-03-09 18:06:03 -080060};
Brian Carlstrom34c06312011-12-05 09:38:27 -080061
62/*
63 * Function names.
64 */
Elliott Hughes719ace42012-03-09 18:06:03 -080065struct JdwpHandlerMap {
Brian Carlstrom34c06312011-12-05 09:38:27 -080066 u1 cmdSet;
67 u1 cmd;
68 const char* descr;
Elliott Hughes719ace42012-03-09 18:06:03 -080069};
Brian Carlstrom34c06312011-12-05 09:38:27 -080070
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 */
Elliott Hughes6d8dd472012-01-17 18:27:41 -0800230NetState* jdwpNetStartup(uint16_t listenPort, const char* connectHost, uint16_t connectPort) {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800231 NetState* netState = new NetState;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800232 memset(netState, 0, sizeof(*netState));
233 netState->listenSock = -1;
234 netState->dbg.sock = netState->vm.sock = -1;
235
236 strcpy(netState->dbg.label, "D");
237 strcpy(netState->vm.label, "V");
238
239 /*
240 * Set up a socket to listen for connections from the debugger.
241 */
242
243 netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
244 if (netState->listenSock < 0) {
245 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
246 goto fail;
247 }
248
249 /* allow immediate re-use if we die */
250 {
251 int one = 1;
252 if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
253 sizeof(one)) < 0)
254 {
255 fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n",
256 strerror(errno));
257 goto fail;
258 }
259 }
260
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700261 sockaddr_in addr;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800262 addr.sin_family = AF_INET;
263 addr.sin_port = htons(listenPort);
264 addr.sin_addr.s_addr = INADDR_ANY;
265
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700266 if (bind(netState->listenSock, (sockaddr*) &addr, sizeof(addr)) != 0)
Brian Carlstrom34c06312011-12-05 09:38:27 -0800267 {
268 fprintf(stderr, "attempt to bind to port %u failed: %s\n",
269 listenPort, strerror(errno));
270 goto fail;
271 }
272
273 fprintf(stderr, "+++ bound to port %u\n", listenPort);
274
275 if (listen(netState->listenSock, 5) != 0) {
276 fprintf(stderr, "Listen failed: %s\n", strerror(errno));
277 goto fail;
278 }
279
280 /*
281 * Do the hostname lookup for the VM.
282 */
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700283 hostent* pHost;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800284
285 pHost = gethostbyname(connectHost);
286 if (pHost == NULL) {
287 fprintf(stderr, "Name lookup of '%s' failed: %s\n",
288 connectHost, strerror(h_errno));
289 goto fail;
290 }
291
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700292 netState->vmAddr = *((in_addr*) pHost->h_addr_list[0]);
Brian Carlstrom34c06312011-12-05 09:38:27 -0800293 netState->vmPort = connectPort;
294
295 fprintf(stderr, "+++ connect host resolved to %s\n",
296 inet_ntoa(netState->vmAddr));
297
298 return netState;
299
300fail:
301 jdwpNetFree(netState);
302 return NULL;
303}
304
305/*
306 * Shut down JDWP listener. Don't free state.
307 *
308 * Note that "netState" may be partially initialized if "startup" failed.
309 */
310void jdwpNetShutdown(NetState* netState)
311{
312 int listenSock = netState->listenSock;
313 int dbgSock = netState->dbg.sock;
314 int vmSock = netState->vm.sock;
315
316 /* clear these out so it doesn't wake up and try to reuse them */
317 /* (important when multi-threaded) */
318 netState->listenSock = netState->dbg.sock = netState->vm.sock = -1;
319
320 if (listenSock >= 0) {
321 shutdown(listenSock, SHUT_RDWR);
322 close(listenSock);
323 }
324 if (dbgSock >= 0) {
325 shutdown(dbgSock, SHUT_RDWR);
326 close(dbgSock);
327 }
328 if (vmSock >= 0) {
329 shutdown(vmSock, SHUT_RDWR);
330 close(vmSock);
331 }
332}
333
334/*
335 * Shut down JDWP listener and free its state.
336 */
337void jdwpNetFree(NetState* netState)
338{
339 if (netState == NULL)
340 return;
341
342 jdwpNetShutdown(netState);
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800343 delete netState;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800344}
345
346/*
347 * Disable the TCP Nagle algorithm, which delays transmission of outbound
348 * packets until the previous transmissions have been acked. JDWP does a
349 * lot of back-and-forth with small packets, so this may help.
350 */
351static int setNoDelay(int fd)
352{
353 int cc, on = 1;
354
355 cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
356 assert(cc == 0);
357 return cc;
358}
359
360/*
361 * Accept a connection. This will block waiting for somebody to show up.
362 */
363bool jdwpAcceptConnection(NetState* netState)
364{
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700365 sockaddr_in addr;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800366 socklen_t addrlen;
367 int sock;
368
369 if (netState->listenSock < 0)
370 return false; /* you're not listening! */
371
372 assert(netState->dbg.sock < 0); /* must not already be talking */
373
374 addrlen = sizeof(addr);
375 do {
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700376 sock = accept(netState->listenSock, (sockaddr*) &addr, &addrlen);
Brian Carlstrom34c06312011-12-05 09:38:27 -0800377 if (sock < 0 && errno != EINTR) {
378 fprintf(stderr, "accept failed: %s\n", strerror(errno));
379 return false;
380 }
381 } while (sock < 0);
382
383 fprintf(stderr, "+++ accepted connection from %s:%u\n",
384 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
385
386 netState->dbg.sock = sock;
387 netState->dbg.awaitingHandshake = true;
388 netState->dbg.inputCount = 0;
389
390 setNoDelay(sock);
391
392 return true;
393}
394
395/*
396 * Close the connections to the debugger and VM.
397 *
398 * Reset the state so we're ready to receive a new connection.
399 */
400void jdwpCloseConnection(NetState* netState)
401{
402 if (netState->dbg.sock >= 0) {
403 fprintf(stderr, "+++ closing connection to debugger\n");
404 close(netState->dbg.sock);
405 netState->dbg.sock = -1;
406 }
407 if (netState->vm.sock >= 0) {
408 fprintf(stderr, "+++ closing connection to vm\n");
409 close(netState->vm.sock);
410 netState->vm.sock = -1;
411 }
412}
413
414/*
415 * Figure out if we have a full packet in the buffer.
416 */
417static bool haveFullPacket(Peer* pPeer)
418{
419 long length;
420
421 if (pPeer->awaitingHandshake)
422 return (pPeer->inputCount >= kMagicHandshakeLen);
423
424 if (pPeer->inputCount < 4)
425 return false;
426
427 length = get4BE(pPeer->inputBuffer);
428 return (pPeer->inputCount >= length);
429}
430
431/*
432 * Consume bytes from the buffer.
433 *
434 * This would be more efficient with a circular buffer. However, we're
435 * usually only going to find one packet, which is trivial to handle.
436 */
437static void consumeBytes(Peer* pPeer, int count)
438{
439 assert(count > 0);
440 assert(count <= pPeer->inputCount);
441
442 if (count == pPeer->inputCount) {
443 pPeer->inputCount = 0;
444 return;
445 }
446
447 memmove(pPeer->inputBuffer, pPeer->inputBuffer + count,
448 pPeer->inputCount - count);
449 pPeer->inputCount -= count;
450}
451
452/*
453 * Get the current time.
454 */
455static void getCurrentTime(int* pMin, int* pSec)
456{
457 time_t now;
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700458 tm* ptm;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800459
460 now = time(NULL);
461 ptm = localtime(&now);
462 *pMin = ptm->tm_min;
463 *pSec = ptm->tm_sec;
464}
465
466/*
467 * Dump the contents of a packet to stdout.
468 */
469static void dumpPacket(const unsigned char* packetBuf, const char* srcName,
470 const char* dstName)
471{
472 const unsigned char* buf = packetBuf;
473 char prefix[3];
474 u4 length, id;
475 u1 flags, cmdSet=0, cmd=0;
476 art::JDWP::JdwpError error = art::JDWP::ERR_NONE;
477 bool reply;
478 int dataLen;
479
480 length = get4BE(buf+0);
481 id = get4BE(buf+4);
482 flags = get1(buf+8);
483 if ((flags & kJDWPFlagReply) != 0) {
484 reply = true;
485 error = static_cast<art::JDWP::JdwpError>(get2BE(buf+9));
486 } else {
487 reply = false;
488 cmdSet = get1(buf+9);
489 cmd = get1(buf+10);
490 }
491
492 buf += kJDWPHeaderLen;
493 dataLen = length - (buf - packetBuf);
494
495 if (!reply) {
496 prefix[0] = srcName[0];
497 prefix[1] = '>';
498 } else {
499 prefix[0] = dstName[0];
500 prefix[1] = '<';
501 }
502 prefix[2] = '\0';
503
504 int min, sec;
505 getCurrentTime(&min, &sec);
506
507 if (!reply) {
508 printf("%s REQUEST dataLen=%-5u id=0x%08x flags=0x%02x cmd=%d/%d [%02d:%02d]\n",
509 prefix, dataLen, id, flags, cmdSet, cmd, min, sec);
510 printf("%s --> %s\n", prefix, getCommandName(cmdSet, cmd));
511 } else {
512 std::ostringstream ss;
Elliott Hughes460384f2012-04-04 16:53:10 -0700513 ss << "TODO"; // get access to the operator<<, or regenerate it for jdwpspy?
Brian Carlstrom34c06312011-12-05 09:38:27 -0800514 printf("%s REPLY dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n",
515 prefix, dataLen, id, flags, error, ss.str().c_str(), min,sec);
516 }
517 if (dataLen > 0)
518 printHexDump2(buf, dataLen, prefix);
519 printf("%s ----------\n", prefix);
520}
521
522/*
523 * Handle a packet. Returns "false" if we encounter a connection-fatal error.
524 */
525static bool handlePacket(Peer* pDst, Peer* pSrc)
526{
527 const unsigned char* buf = pSrc->inputBuffer;
528 u4 length;
529 u1 flags;
530 int cc;
531
532 length = get4BE(buf+0);
533 flags = get1(buf+9);
534
535 assert((int) length <= pSrc->inputCount);
536
537 dumpPacket(buf, pSrc->label, pDst->label);
538
539 cc = write(pDst->sock, buf, length);
540 if (cc != (int) length) {
541 fprintf(stderr, "Failed sending packet: %s\n", strerror(errno));
542 return false;
543 }
544 /*printf("*** wrote %d bytes from %c to %c\n",
545 cc, pSrc->label[0], pDst->label[0]);*/
546
547 consumeBytes(pSrc, length);
548 return true;
549}
550
551/*
552 * Handle incoming data. If we have a full packet in the buffer, process it.
553 */
554static bool handleIncoming(Peer* pWritePeer, Peer* pReadPeer)
555{
556 if (haveFullPacket(pReadPeer)) {
557 if (pReadPeer->awaitingHandshake) {
558 printf("Handshake [%c]: %.14s\n",
559 pReadPeer->label[0], pReadPeer->inputBuffer);
560 if (write(pWritePeer->sock, pReadPeer->inputBuffer,
561 kMagicHandshakeLen) != kMagicHandshakeLen)
562 {
563 fprintf(stderr,
564 "+++ [%c] handshake write failed\n", pReadPeer->label[0]);
565 goto fail;
566 }
567 consumeBytes(pReadPeer, kMagicHandshakeLen);
568 pReadPeer->awaitingHandshake = false;
569 } else {
570 if (!handlePacket(pWritePeer, pReadPeer))
571 goto fail;
572 }
573 } else {
574 /*printf("*** %c not full yet\n", pReadPeer->label[0]);*/
575 }
576
577 return true;
578
579fail:
580 return false;
581}
582
583/*
584 * Process incoming data. If no data is available, this will block until
585 * some arrives.
586 *
587 * Returns "false" on error (indicating that the connection has been severed).
588 */
589bool jdwpProcessIncoming(NetState* netState)
590{
591 int cc;
592
593 assert(netState->dbg.sock >= 0);
594 assert(netState->vm.sock >= 0);
595
596 while (!haveFullPacket(&netState->dbg) && !haveFullPacket(&netState->vm)) {
597 /* read some more */
598 int highFd;
599 fd_set readfds;
600
601 highFd = (netState->dbg.sock > netState->vm.sock) ?
602 netState->dbg.sock+1 : netState->vm.sock+1;
603 FD_ZERO(&readfds);
604 FD_SET(netState->dbg.sock, &readfds);
605 FD_SET(netState->vm.sock, &readfds);
606
607 errno = 0;
608 cc = select(highFd, &readfds, NULL, NULL, NULL);
609 if (cc < 0) {
610 if (errno == EINTR) {
611 fprintf(stderr, "+++ EINTR on select\n");
612 continue;
613 }
614 fprintf(stderr, "+++ select failed: %s\n", strerror(errno));
615 goto fail;
616 }
617
618 if (FD_ISSET(netState->dbg.sock, &readfds)) {
619 cc = read(netState->dbg.sock,
620 netState->dbg.inputBuffer + netState->dbg.inputCount,
621 sizeof(netState->dbg.inputBuffer) - netState->dbg.inputCount);
622 if (cc < 0) {
623 if (errno == EINTR) {
624 fprintf(stderr, "+++ EINTR on read\n");
625 continue;
626 }
627 fprintf(stderr, "+++ dbg read failed: %s\n", strerror(errno));
628 goto fail;
629 }
630 if (cc == 0) {
631 if (sizeof(netState->dbg.inputBuffer) ==
632 netState->dbg.inputCount)
633 fprintf(stderr, "+++ debugger sent huge message\n");
634 else
635 fprintf(stderr, "+++ debugger disconnected\n");
636 goto fail;
637 }
638
639 /*printf("*** %d bytes from dbg\n", cc);*/
640 netState->dbg.inputCount += cc;
641 }
642
643 if (FD_ISSET(netState->vm.sock, &readfds)) {
644 cc = read(netState->vm.sock,
645 netState->vm.inputBuffer + netState->vm.inputCount,
646 sizeof(netState->vm.inputBuffer) - netState->vm.inputCount);
647 if (cc < 0) {
648 if (errno == EINTR) {
649 fprintf(stderr, "+++ EINTR on read\n");
650 continue;
651 }
652 fprintf(stderr, "+++ vm read failed: %s\n", strerror(errno));
653 goto fail;
654 }
655 if (cc == 0) {
656 if (sizeof(netState->vm.inputBuffer) ==
657 netState->vm.inputCount)
658 fprintf(stderr, "+++ vm sent huge message\n");
659 else
660 fprintf(stderr, "+++ vm disconnected\n");
661 goto fail;
662 }
663
664 /*printf("*** %d bytes from vm\n", cc);*/
665 netState->vm.inputCount += cc;
666 }
667 }
668
669 if (!handleIncoming(&netState->dbg, &netState->vm))
670 goto fail;
671 if (!handleIncoming(&netState->vm, &netState->dbg))
672 goto fail;
673
674 return true;
675
676fail:
677 jdwpCloseConnection(netState);
678 return false;
679}
680
681/*
682 * Connect to the VM.
683 */
684bool jdwpConnectToVm(NetState* netState)
685{
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700686 sockaddr_in addr;
Brian Carlstrom34c06312011-12-05 09:38:27 -0800687 int sock = -1;
688
689 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
690 if (sock < 0) {
691 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
692 goto fail;
693 }
694
695 addr.sin_family = AF_INET;
696 addr.sin_addr = netState->vmAddr;
697 addr.sin_port = htons(netState->vmPort);
698 if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
699 fprintf(stderr, "Connection to %s:%u failed: %s\n",
700 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), strerror(errno));
701 goto fail;
702 }
703 fprintf(stderr, "+++ connected to VM %s:%u\n",
704 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
705
706 netState->vm.sock = sock;
707 netState->vm.awaitingHandshake = true;
708 netState->vm.inputCount = 0;
709
710 setNoDelay(netState->vm.sock);
711 return true;
712
713fail:
714 if (sock >= 0)
715 close(sock);
716 return false;
717}
718
719/*
720 * Establish network connections and start things running.
721 *
722 * We wait for a new connection from the debugger. When one arrives we
723 * open a connection to the VM. If one side or the other goes away, we
724 * drop both ends and go back to listening.
725 */
726int run(const char* connectHost, int connectPort, int listenPort)
727{
728 NetState* state;
729
730 state = jdwpNetStartup(listenPort, connectHost, connectPort);
731 if (state == NULL)
732 return -1;
733
734 while (true) {
735 if (!jdwpAcceptConnection(state))
736 break;
737
738 if (jdwpConnectToVm(state)) {
739 while (true) {
740 if (!jdwpProcessIncoming(state))
741 break;
742 }
743 }
744
745 jdwpCloseConnection(state);
746 }
747
748 jdwpNetFree(state);
749
750 return 0;
751}