From: Brian Carlstrom Date: Wed, 22 May 2013 04:07:08 +0000 (-0700) Subject: Move jdwpspy from development to dalvik (2 of 2) X-Git-Tag: android-x86-4.4-r1~28^2~9^2~14^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=6b900439bc9e54c3bd3e1f4d15967010f6892467;p=android-x86%2Fdalvik.git Move jdwpspy from development to dalvik (2 of 2) Change-Id: I3e7a255fddcec1380fce143004cbad83a69d2c0d --- diff --git a/tools/jdwpspy/Android.mk b/tools/jdwpspy/Android.mk new file mode 100644 index 000000000..2201aab2d --- /dev/null +++ b/tools/jdwpspy/Android.mk @@ -0,0 +1,17 @@ +# Copyright 2006 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Main.cpp \ + Net.cpp \ + find_JdwpConstants.cpp + +LOCAL_C_INCLUDES += \ + dalvik/vm + +LOCAL_MODULE := jdwpspy + +include $(BUILD_HOST_EXECUTABLE) + diff --git a/tools/jdwpspy/Common.h b/tools/jdwpspy/Common.h new file mode 100644 index 000000000..ddaba9c59 --- /dev/null +++ b/tools/jdwpspy/Common.h @@ -0,0 +1,101 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * jdwpspy common stuff. + */ +#ifndef _JDWPSPY_COMMON +#define _JDWPSPY_COMMON + +#include +#include + +typedef unsigned char u1; +typedef unsigned short u2; +typedef unsigned int u4; +typedef unsigned long long u8; + +#define NELEM(x) (sizeof(x) / sizeof((x)[0])) + +#ifndef _JDWP_MISC_INLINE +# define INLINE extern inline +#else +# define INLINE +#endif + +/* + * Get 1 byte. (Included to make the code more legible.) + */ +INLINE u1 get1(unsigned const char* pSrc) +{ + return *pSrc; +} + +/* + * Get 2 big-endian bytes. + */ +INLINE u2 get2BE(unsigned char const* pSrc) +{ + u2 result; + + result = *pSrc++ << 8; + result |= *pSrc++; + + return result; +} + +/* + * Get 4 big-endian bytes. + */ +INLINE u4 get4BE(unsigned char const* pSrc) +{ + u4 result; + + result = *pSrc++ << 24; + result |= *pSrc++ << 16; + result |= *pSrc++ << 8; + result |= *pSrc++; + + return result; +} + +/* + * Get 8 big-endian bytes. + */ +INLINE u8 get8BE(unsigned char const* pSrc) +{ + u8 result; + + result = (u8) *pSrc++ << 56; + result |= (u8) *pSrc++ << 48; + result |= (u8) *pSrc++ << 40; + result |= (u8) *pSrc++ << 32; + result |= (u8) *pSrc++ << 24; + result |= (u8) *pSrc++ << 16; + result |= (u8) *pSrc++ << 8; + result |= (u8) *pSrc++; + + return result; +} + + +/* + * Start here. + */ +int run(const char* connectHost, int connectPort, int listenPort); + +/* + * Print a hex dump to the specified file pointer. + * + * "local" mode prints a hex dump starting from offset 0 (roughly equivalent + * to "xxd -g1"). + * + * "mem" mode shows the actual memory address, and will offset the start + * so that the low nibble of the address is always zero. + */ +typedef enum { kHexDumpLocal, kHexDumpMem } HexDumpMode; +void printHexDump(const void* vaddr, size_t length); +void printHexDump2(const void* vaddr, size_t length, const char* prefix); +void printHexDumpEx(FILE* fp, const void* vaddr, size_t length, + HexDumpMode mode, const char* prefix); + +#endif /*_JDWPSPY_COMMON*/ diff --git a/tools/jdwpspy/Main.cpp b/tools/jdwpspy/Main.cpp new file mode 100644 index 000000000..b53f99b8c --- /dev/null +++ b/tools/jdwpspy/Main.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * JDWP spy. + */ +#define _JDWP_MISC_INLINE +#include "Common.h" +#include +#include +#include +#include +#include +#include + +static const char gHexDigit[] = "0123456789abcdef"; + +/* + * Print a hex dump. Just hands control off to the fancy version. + */ +void printHexDump(const void* vaddr, size_t length) +{ + printHexDumpEx(stdout, vaddr, length, kHexDumpLocal, ""); +} +void printHexDump2(const void* vaddr, size_t length, const char* prefix) +{ + printHexDumpEx(stdout, vaddr, length, kHexDumpLocal, prefix); +} + +/* + * Print a hex dump in this format: + * +01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef\n + */ +void printHexDumpEx(FILE* fp, const void* vaddr, size_t length, + HexDumpMode mode, const char* prefix) +{ + const unsigned char* addr = reinterpret_cast(vaddr); + char out[77]; /* exact fit */ + uintptr_t offset; /* offset to show while printing */ + char* hex; + char* asc; + int gap; + + if (mode == kHexDumpLocal) + offset = 0; + else + offset = (uintptr_t) addr; + + memset(out, ' ', sizeof(out)-1); + out[8] = ':'; + out[sizeof(out)-2] = '\n'; + out[sizeof(out)-1] = '\0'; + + gap = (int) offset & 0x0f; + while (length) { + unsigned int lineOffset = offset & ~0x0f; + char* hex = out; + char* asc = out + 59; + + for (int i = 0; i < 8; i++) { + *hex++ = gHexDigit[lineOffset >> 28]; + lineOffset <<= 4; + } + hex++; + hex++; + + int count = ((int)length > 16-gap) ? 16-gap : (int) length; /* cap length */ + assert(count != 0); + assert(count+gap <= 16); + + if (gap) { + /* only on first line */ + hex += gap * 3; + asc += gap; + } + + int i; + for (i = gap ; i < count+gap; i++) { + *hex++ = gHexDigit[*addr >> 4]; + *hex++ = gHexDigit[*addr & 0x0f]; + hex++; + if (isprint(*addr)) + *asc++ = *addr; + else + *asc++ = '.'; + addr++; + } + for ( ; i < 16; i++) { + /* erase extra stuff; only happens on last line */ + *hex++ = ' '; + *hex++ = ' '; + hex++; + *asc++ = ' '; + } + + fprintf(fp, "%s%s", prefix, out); + + gap = 0; + length -= count; + offset += count; + } +} + + +/* + * Explain it. + */ +static void usage(const char* progName) +{ + fprintf(stderr, "Usage: %s VM-port [debugger-listen-port]\n\n", progName); + fprintf(stderr, +"When a debugger connects to the debugger-listen-port, jdwpspy will connect\n"); + fprintf(stderr, "to the VM on the VM-port.\n"); +} + +/* + * Parse args. + */ +int main(int argc, char* argv[]) +{ + if (argc < 2 || argc > 3) { + usage("jdwpspy"); + return 2; + } + + setvbuf(stdout, NULL, _IONBF, 0); + + /* may want this to be host:port */ + int connectPort = atoi(argv[1]); + + int listenPort; + if (argc > 2) + listenPort = atoi(argv[2]); + else + listenPort = connectPort + 1; + + int cc = run("localhost", connectPort, listenPort); + + return (cc != 0); +} diff --git a/tools/jdwpspy/Net.cpp b/tools/jdwpspy/Net.cpp new file mode 100644 index 000000000..b92300623 --- /dev/null +++ b/tools/jdwpspy/Net.cpp @@ -0,0 +1,748 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * JDWP spy. This is a rearranged version of the JDWP code from the VM. + */ +#include "Common.h" +#include "jdwp/JdwpConstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define kInputBufferSize (256*1024) + +#define kMagicHandshakeLen 14 /* "JDWP-Handshake" */ +#define kJDWPHeaderLen 11 +#define kJDWPFlagReply 0x80 + + +/* + * Information about the remote end. + */ +typedef struct Peer { + char label[2]; /* 'D' or 'V' */ + + int sock; + unsigned char inputBuffer[kInputBufferSize]; + int inputCount; + + bool awaitingHandshake; /* waiting for "JDWP-Handshake" */ +} Peer; + + +/* + * Network state. + */ +typedef struct NetState { + /* listen here for connection from debugger */ + int listenSock; + + /* connect here to contact VM */ + struct in_addr vmAddr; + short vmPort; + + Peer dbg; + Peer vm; +} NetState; + +/* + * Function names. + */ +typedef struct { + u1 cmdSet; + u1 cmd; + const char* descr; +} JdwpHandlerMap; + +/* + * Map commands to names. + * + * Command sets 0-63 are incoming requests, 64-127 are outbound requests, + * and 128-256 are vendor-defined. + */ +static const JdwpHandlerMap gHandlerMap[] = { + /* VirtualMachine command set (1) */ + { 1, 1, "VirtualMachine.Version" }, + { 1, 2, "VirtualMachine.ClassesBySignature" }, + { 1, 3, "VirtualMachine.AllClasses" }, + { 1, 4, "VirtualMachine.AllThreads" }, + { 1, 5, "VirtualMachine.TopLevelThreadGroups" }, + { 1, 6, "VirtualMachine.Dispose" }, + { 1, 7, "VirtualMachine.IDSizes" }, + { 1, 8, "VirtualMachine.Suspend" }, + { 1, 9, "VirtualMachine.Resume" }, + { 1, 10, "VirtualMachine.Exit" }, + { 1, 11, "VirtualMachine.CreateString" }, + { 1, 12, "VirtualMachine.Capabilities" }, + { 1, 13, "VirtualMachine.ClassPaths" }, + { 1, 14, "VirtualMachine.DisposeObjects" }, + { 1, 15, "VirtualMachine.HoldEvents" }, + { 1, 16, "VirtualMachine.ReleaseEvents" }, + { 1, 17, "VirtualMachine.CapabilitiesNew" }, + { 1, 18, "VirtualMachine.RedefineClasses" }, + { 1, 19, "VirtualMachine.SetDefaultStratum" }, + { 1, 20, "VirtualMachine.AllClassesWithGeneric"}, + { 1, 21, "VirtualMachine.InstanceCounts"}, + + /* ReferenceType command set (2) */ + { 2, 1, "ReferenceType.Signature" }, + { 2, 2, "ReferenceType.ClassLoader" }, + { 2, 3, "ReferenceType.Modifiers" }, + { 2, 4, "ReferenceType.Fields" }, + { 2, 5, "ReferenceType.Methods" }, + { 2, 6, "ReferenceType.GetValues" }, + { 2, 7, "ReferenceType.SourceFile" }, + { 2, 8, "ReferenceType.NestedTypes" }, + { 2, 9, "ReferenceType.Status" }, + { 2, 10, "ReferenceType.Interfaces" }, + { 2, 11, "ReferenceType.ClassObject" }, + { 2, 12, "ReferenceType.SourceDebugExtension" }, + { 2, 13, "ReferenceType.SignatureWithGeneric" }, + { 2, 14, "ReferenceType.FieldsWithGeneric" }, + { 2, 15, "ReferenceType.MethodsWithGeneric" }, + { 2, 16, "ReferenceType.Instances" }, + { 2, 17, "ReferenceType.ClassFileVersion" }, + { 2, 18, "ReferenceType.ConstantPool" }, + + /* ClassType command set (3) */ + { 3, 1, "ClassType.Superclass" }, + { 3, 2, "ClassType.SetValues" }, + { 3, 3, "ClassType.InvokeMethod" }, + { 3, 4, "ClassType.NewInstance" }, + + /* ArrayType command set (4) */ + { 4, 1, "ArrayType.NewInstance" }, + + /* InterfaceType command set (5) */ + + /* Method command set (6) */ + { 6, 1, "Method.LineTable" }, + { 6, 2, "Method.VariableTable" }, + { 6, 3, "Method.Bytecodes" }, + { 6, 4, "Method.IsObsolete" }, + { 6, 5, "Method.VariableTableWithGeneric" }, + + /* Field command set (8) */ + + /* ObjectReference command set (9) */ + { 9, 1, "ObjectReference.ReferenceType" }, + { 9, 2, "ObjectReference.GetValues" }, + { 9, 3, "ObjectReference.SetValues" }, + { 9, 4, "ObjectReference.UNUSED" }, + { 9, 5, "ObjectReference.MonitorInfo" }, + { 9, 6, "ObjectReference.InvokeMethod" }, + { 9, 7, "ObjectReference.DisableCollection" }, + { 9, 8, "ObjectReference.EnableCollection" }, + { 9, 9, "ObjectReference.IsCollected" }, + { 9, 10, "ObjectReference.ReferringObjects" }, + + /* StringReference command set (10) */ + { 10, 1, "StringReference.Value" }, + + /* ThreadReference command set (11) */ + { 11, 1, "ThreadReference.Name" }, + { 11, 2, "ThreadReference.Suspend" }, + { 11, 3, "ThreadReference.Resume" }, + { 11, 4, "ThreadReference.Status" }, + { 11, 5, "ThreadReference.ThreadGroup" }, + { 11, 6, "ThreadReference.Frames" }, + { 11, 7, "ThreadReference.FrameCount" }, + { 11, 8, "ThreadReference.OwnedMonitors" }, + { 11, 9, "ThreadReference.CurrentContendedMonitor" }, + { 11, 10, "ThreadReference.Stop" }, + { 11, 11, "ThreadReference.Interrupt" }, + { 11, 12, "ThreadReference.SuspendCount" }, + { 11, 13, "ThreadReference.OwnedMonitorsStackDepthInfo" }, + { 11, 14, "ThreadReference.ForceEarlyReturn" }, + + /* ThreadGroupReference command set (12) */ + { 12, 1, "ThreadGroupReference.Name" }, + { 12, 2, "ThreadGroupReference.Parent" }, + { 12, 3, "ThreadGroupReference.Children" }, + + /* ArrayReference command set (13) */ + { 13, 1, "ArrayReference.Length" }, + { 13, 2, "ArrayReference.GetValues" }, + { 13, 3, "ArrayReference.SetValues" }, + + /* ClassLoaderReference command set (14) */ + { 14, 1, "ArrayReference.VisibleClasses" }, + + /* EventRequest command set (15) */ + { 15, 1, "EventRequest.Set" }, + { 15, 2, "EventRequest.Clear" }, + { 15, 3, "EventRequest.ClearAllBreakpoints" }, + + /* StackFrame command set (16) */ + { 16, 1, "StackFrame.GetValues" }, + { 16, 2, "StackFrame.SetValues" }, + { 16, 3, "StackFrame.ThisObject" }, + { 16, 4, "StackFrame.PopFrames" }, + + /* ClassObjectReference command set (17) */ + { 17, 1, "ClassObjectReference.ReflectedType" }, + + /* Event command set (64) */ + { 64, 100, "Event.Composite" }, + + /* DDMS */ + { 199, 1, "DDMS.Chunk" }, +}; + +/* + * Look up a command's name. + */ +static const char* getCommandName(int cmdSet, int cmd) +{ + for (int i = 0; i < (int) NELEM(gHandlerMap); i++) { + if (gHandlerMap[i].cmdSet == cmdSet && + gHandlerMap[i].cmd == cmd) + { + return gHandlerMap[i].descr; + } + } + + return "?UNKNOWN?"; +} + + +void jdwpNetFree(NetState* netState); /* fwd */ + +/* + * Allocate state structure and bind to the listen port. + * + * Returns 0 on success. + */ +NetState* jdwpNetStartup(unsigned short listenPort, const char* connectHost, + unsigned short connectPort) +{ + NetState* netState = (NetState*) malloc(sizeof(*netState)); + memset(netState, 0, sizeof(*netState)); + netState->listenSock = -1; + netState->dbg.sock = netState->vm.sock = -1; + + strcpy(netState->dbg.label, "D"); + strcpy(netState->vm.label, "V"); + + /* + * Set up a socket to listen for connections from the debugger. + */ + + netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (netState->listenSock < 0) { + fprintf(stderr, "Socket create failed: %s\n", strerror(errno)); + goto fail; + } + + /* allow immediate re-use if we die */ + { + int one = 1; + if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one, + sizeof(one)) < 0) + { + fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n", + strerror(errno)); + goto fail; + } + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(listenPort); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(netState->listenSock, (struct sockaddr*) &addr, sizeof(addr)) != 0) + { + fprintf(stderr, "attempt to bind to port %u failed: %s\n", + listenPort, strerror(errno)); + goto fail; + } + + fprintf(stderr, "+++ bound to port %u\n", listenPort); + + if (listen(netState->listenSock, 5) != 0) { + fprintf(stderr, "Listen failed: %s\n", strerror(errno)); + goto fail; + } + + /* + * Do the hostname lookup for the VM. + */ + struct hostent* pHost; + + pHost = gethostbyname(connectHost); + if (pHost == NULL) { + fprintf(stderr, "Name lookup of '%s' failed: %s\n", + connectHost, strerror(h_errno)); + goto fail; + } + + netState->vmAddr = *((struct in_addr*) pHost->h_addr_list[0]); + netState->vmPort = connectPort; + + fprintf(stderr, "+++ connect host resolved to %s\n", + inet_ntoa(netState->vmAddr)); + + return netState; + +fail: + jdwpNetFree(netState); + return NULL; +} + +/* + * Shut down JDWP listener. Don't free state. + * + * Note that "netState" may be partially initialized if "startup" failed. + */ +void jdwpNetShutdown(NetState* netState) +{ + int listenSock = netState->listenSock; + int dbgSock = netState->dbg.sock; + int vmSock = netState->vm.sock; + + /* clear these out so it doesn't wake up and try to reuse them */ + /* (important when multi-threaded) */ + netState->listenSock = netState->dbg.sock = netState->vm.sock = -1; + + if (listenSock >= 0) { + shutdown(listenSock, SHUT_RDWR); + close(listenSock); + } + if (dbgSock >= 0) { + shutdown(dbgSock, SHUT_RDWR); + close(dbgSock); + } + if (vmSock >= 0) { + shutdown(vmSock, SHUT_RDWR); + close(vmSock); + } +} + +/* + * Shut down JDWP listener and free its state. + */ +void jdwpNetFree(NetState* netState) +{ + if (netState == NULL) + return; + + jdwpNetShutdown(netState); + free(netState); +} + +/* + * Disable the TCP Nagle algorithm, which delays transmission of outbound + * packets until the previous transmissions have been acked. JDWP does a + * lot of back-and-forth with small packets, so this may help. + */ +static int setNoDelay(int fd) +{ + int cc, on = 1; + + cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + assert(cc == 0); + return cc; +} + +/* + * Accept a connection. This will block waiting for somebody to show up. + */ +bool jdwpAcceptConnection(NetState* netState) +{ + struct sockaddr_in addr; + socklen_t addrlen; + int sock; + + if (netState->listenSock < 0) + return false; /* you're not listening! */ + + assert(netState->dbg.sock < 0); /* must not already be talking */ + + addrlen = sizeof(addr); + do { + sock = accept(netState->listenSock, (struct sockaddr*) &addr, &addrlen); + if (sock < 0 && errno != EINTR) { + fprintf(stderr, "accept failed: %s\n", strerror(errno)); + return false; + } + } while (sock < 0); + + fprintf(stderr, "+++ accepted connection from %s:%u\n", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + netState->dbg.sock = sock; + netState->dbg.awaitingHandshake = true; + netState->dbg.inputCount = 0; + + setNoDelay(sock); + + return true; +} + +/* + * Close the connections to the debugger and VM. + * + * Reset the state so we're ready to receive a new connection. + */ +void jdwpCloseConnection(NetState* netState) +{ + if (netState->dbg.sock >= 0) { + fprintf(stderr, "+++ closing connection to debugger\n"); + close(netState->dbg.sock); + netState->dbg.sock = -1; + } + if (netState->vm.sock >= 0) { + fprintf(stderr, "+++ closing connection to vm\n"); + close(netState->vm.sock); + netState->vm.sock = -1; + } +} + +/* + * Figure out if we have a full packet in the buffer. + */ +static bool haveFullPacket(Peer* pPeer) +{ + long length; + + if (pPeer->awaitingHandshake) + return (pPeer->inputCount >= kMagicHandshakeLen); + + if (pPeer->inputCount < 4) + return false; + + length = get4BE(pPeer->inputBuffer); + return (pPeer->inputCount >= length); +} + +/* + * Consume bytes from the buffer. + * + * This would be more efficient with a circular buffer. However, we're + * usually only going to find one packet, which is trivial to handle. + */ +static void consumeBytes(Peer* pPeer, int count) +{ + assert(count > 0); + assert(count <= pPeer->inputCount); + + if (count == pPeer->inputCount) { + pPeer->inputCount = 0; + return; + } + + memmove(pPeer->inputBuffer, pPeer->inputBuffer + count, + pPeer->inputCount - count); + pPeer->inputCount -= count; +} + +/* + * Get the current time. + */ +static void getCurrentTime(int* pMin, int* pSec) +{ + time_t now; + struct tm* ptm; + + now = time(NULL); + ptm = localtime(&now); + *pMin = ptm->tm_min; + *pSec = ptm->tm_sec; +} + +/* + * Dump the contents of a packet to stdout. + */ +static void dumpPacket(const unsigned char* packetBuf, const char* srcName, + const char* dstName) +{ + const unsigned char* buf = packetBuf; + char prefix[3]; + u4 length, id; + u1 flags, cmdSet=0, cmd=0; + JdwpError error = ERR_NONE; + bool reply; + int dataLen; + + length = get4BE(buf+0); + id = get4BE(buf+4); + flags = get1(buf+8); + if ((flags & kJDWPFlagReply) != 0) { + reply = true; + error = static_cast(get2BE(buf+9)); + } else { + reply = false; + cmdSet = get1(buf+9); + cmd = get1(buf+10); + } + + buf += kJDWPHeaderLen; + dataLen = length - (buf - packetBuf); + + if (!reply) { + prefix[0] = srcName[0]; + prefix[1] = '>'; + } else { + prefix[0] = dstName[0]; + prefix[1] = '<'; + } + prefix[2] = '\0'; + + int min, sec; + getCurrentTime(&min, &sec); + + if (!reply) { + printf("%s REQUEST dataLen=%-5u id=0x%08x flags=0x%02x cmd=%d/%d [%02d:%02d]\n", + prefix, dataLen, id, flags, cmdSet, cmd, min, sec); + printf("%s --> %s\n", prefix, getCommandName(cmdSet, cmd)); + } else { + printf("%s REPLY dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n", + prefix, dataLen, id, flags, error, dvmJdwpErrorStr(error), min,sec); + } + if (dataLen > 0) + printHexDump2(buf, dataLen, prefix); + printf("%s ----------\n", prefix); +} + +/* + * Handle a packet. Returns "false" if we encounter a connection-fatal error. + */ +static bool handlePacket(Peer* pDst, Peer* pSrc) +{ + const unsigned char* buf = pSrc->inputBuffer; + u4 length; + u1 flags; + int cc; + + length = get4BE(buf+0); + flags = get1(buf+9); + + assert((int) length <= pSrc->inputCount); + + dumpPacket(buf, pSrc->label, pDst->label); + + cc = write(pDst->sock, buf, length); + if (cc != (int) length) { + fprintf(stderr, "Failed sending packet: %s\n", strerror(errno)); + return false; + } + /*printf("*** wrote %d bytes from %c to %c\n", + cc, pSrc->label[0], pDst->label[0]);*/ + + consumeBytes(pSrc, length); + return true; +} + +/* + * Handle incoming data. If we have a full packet in the buffer, process it. + */ +static bool handleIncoming(Peer* pWritePeer, Peer* pReadPeer) +{ + if (haveFullPacket(pReadPeer)) { + if (pReadPeer->awaitingHandshake) { + printf("Handshake [%c]: %.14s\n", + pReadPeer->label[0], pReadPeer->inputBuffer); + if (write(pWritePeer->sock, pReadPeer->inputBuffer, + kMagicHandshakeLen) != kMagicHandshakeLen) + { + fprintf(stderr, + "+++ [%c] handshake write failed\n", pReadPeer->label[0]); + goto fail; + } + consumeBytes(pReadPeer, kMagicHandshakeLen); + pReadPeer->awaitingHandshake = false; + } else { + if (!handlePacket(pWritePeer, pReadPeer)) + goto fail; + } + } else { + /*printf("*** %c not full yet\n", pReadPeer->label[0]);*/ + } + + return true; + +fail: + return false; +} + +/* + * Process incoming data. If no data is available, this will block until + * some arrives. + * + * Returns "false" on error (indicating that the connection has been severed). + */ +bool jdwpProcessIncoming(NetState* netState) +{ + int cc; + + assert(netState->dbg.sock >= 0); + assert(netState->vm.sock >= 0); + + while (!haveFullPacket(&netState->dbg) && !haveFullPacket(&netState->vm)) { + /* read some more */ + int highFd; + fd_set readfds; + + highFd = (netState->dbg.sock > netState->vm.sock) ? + netState->dbg.sock+1 : netState->vm.sock+1; + FD_ZERO(&readfds); + FD_SET(netState->dbg.sock, &readfds); + FD_SET(netState->vm.sock, &readfds); + + errno = 0; + cc = select(highFd, &readfds, NULL, NULL, NULL); + if (cc < 0) { + if (errno == EINTR) { + fprintf(stderr, "+++ EINTR on select\n"); + continue; + } + fprintf(stderr, "+++ select failed: %s\n", strerror(errno)); + goto fail; + } + + if (FD_ISSET(netState->dbg.sock, &readfds)) { + cc = read(netState->dbg.sock, + netState->dbg.inputBuffer + netState->dbg.inputCount, + sizeof(netState->dbg.inputBuffer) - netState->dbg.inputCount); + if (cc < 0) { + if (errno == EINTR) { + fprintf(stderr, "+++ EINTR on read\n"); + continue; + } + fprintf(stderr, "+++ dbg read failed: %s\n", strerror(errno)); + goto fail; + } + if (cc == 0) { + if (sizeof(netState->dbg.inputBuffer) == + netState->dbg.inputCount) + fprintf(stderr, "+++ debugger sent huge message\n"); + else + fprintf(stderr, "+++ debugger disconnected\n"); + goto fail; + } + + /*printf("*** %d bytes from dbg\n", cc);*/ + netState->dbg.inputCount += cc; + } + + if (FD_ISSET(netState->vm.sock, &readfds)) { + cc = read(netState->vm.sock, + netState->vm.inputBuffer + netState->vm.inputCount, + sizeof(netState->vm.inputBuffer) - netState->vm.inputCount); + if (cc < 0) { + if (errno == EINTR) { + fprintf(stderr, "+++ EINTR on read\n"); + continue; + } + fprintf(stderr, "+++ vm read failed: %s\n", strerror(errno)); + goto fail; + } + if (cc == 0) { + if (sizeof(netState->vm.inputBuffer) == + netState->vm.inputCount) + fprintf(stderr, "+++ vm sent huge message\n"); + else + fprintf(stderr, "+++ vm disconnected\n"); + goto fail; + } + + /*printf("*** %d bytes from vm\n", cc);*/ + netState->vm.inputCount += cc; + } + } + + if (!handleIncoming(&netState->dbg, &netState->vm)) + goto fail; + if (!handleIncoming(&netState->vm, &netState->dbg)) + goto fail; + + return true; + +fail: + jdwpCloseConnection(netState); + return false; +} + +/* + * Connect to the VM. + */ +bool jdwpConnectToVm(NetState* netState) +{ + struct sockaddr_in addr; + int sock = -1; + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) { + fprintf(stderr, "Socket create failed: %s\n", strerror(errno)); + goto fail; + } + + addr.sin_family = AF_INET; + addr.sin_addr = netState->vmAddr; + addr.sin_port = htons(netState->vmPort); + if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) { + fprintf(stderr, "Connection to %s:%u failed: %s\n", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), strerror(errno)); + goto fail; + } + fprintf(stderr, "+++ connected to VM %s:%u\n", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + netState->vm.sock = sock; + netState->vm.awaitingHandshake = true; + netState->vm.inputCount = 0; + + setNoDelay(netState->vm.sock); + return true; + +fail: + if (sock >= 0) + close(sock); + return false; +} + +/* + * Establish network connections and start things running. + * + * We wait for a new connection from the debugger. When one arrives we + * open a connection to the VM. If one side or the other goes away, we + * drop both ends and go back to listening. + */ +int run(const char* connectHost, int connectPort, int listenPort) +{ + NetState* state; + + state = jdwpNetStartup(listenPort, connectHost, connectPort); + if (state == NULL) + return -1; + + while (true) { + if (!jdwpAcceptConnection(state)) + break; + + if (jdwpConnectToVm(state)) { + while (true) { + if (!jdwpProcessIncoming(state)) + break; + } + } + + jdwpCloseConnection(state); + } + + jdwpNetFree(state); + + return 0; +} diff --git a/tools/jdwpspy/find_JdwpConstants.cpp b/tools/jdwpspy/find_JdwpConstants.cpp new file mode 100644 index 000000000..57b7dbbfc --- /dev/null +++ b/tools/jdwpspy/find_JdwpConstants.cpp @@ -0,0 +1 @@ +#include "jdwp/JdwpConstants.cpp"