Move jdwpspy from development to art (2 of 2)
Change-Id: I7741875e96730372d7623b1327a1fcbca7cb2476
diff --git a/jdwpspy/Android.mk b/jdwpspy/Android.mk
new file mode 100644
index 0000000..60ccb38
--- /dev/null
+++ b/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 += \
+ art/src
+
+LOCAL_MODULE := jdwpspy
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/jdwpspy/Common.h b/jdwpspy/Common.h
new file mode 100644
index 0000000..ddaba9c
--- /dev/null
+++ b/jdwpspy/Common.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * jdwpspy common stuff.
+ */
+#ifndef _JDWPSPY_COMMON
+#define _JDWPSPY_COMMON
+
+#include <stdio.h>
+#include <sys/types.h>
+
+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/jdwpspy/Main.cpp b/jdwpspy/Main.cpp
new file mode 100644
index 0000000..0f68d52
--- /dev/null
+++ b/jdwpspy/Main.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * JDWP spy.
+ */
+#define _JDWP_MISC_INLINE
+#include "Common.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+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<const unsigned char*>(vaddr);
+ char out[77]; /* exact fit */
+ unsigned int offset; /* offset to show while printing */
+ char* hex;
+ char* asc;
+ int gap;
+
+ if (mode == kHexDumpLocal)
+ offset = 0;
+ else
+ offset = (int) 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/jdwpspy/Net.cpp b/jdwpspy/Net.cpp
new file mode 100644
index 0000000..54d7730
--- /dev/null
+++ b/jdwpspy/Net.cpp
@@ -0,0 +1,753 @@
+/*
+ * 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/jdwp_constants.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <iostream>
+#include <sstream>
+
+#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;
+ art::JDWP::JdwpError error = art::JDWP::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<art::JDWP::JdwpError>(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 {
+ std::ostringstream ss;
+ ss << error;
+ printf("%s REPLY dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n",
+ prefix, dataLen, id, flags, error, ss.str().c_str(), 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/jdwpspy/find_JdwpConstants.cpp b/jdwpspy/find_JdwpConstants.cpp
new file mode 100644
index 0000000..d035d2e
--- /dev/null
+++ b/jdwpspy/find_JdwpConstants.cpp
@@ -0,0 +1 @@
+#include "jdwp/jdwp_constants.cc"