summaryrefslogtreecommitdiffstats
path: root/vm/jdwp
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2014-08-05 12:46:17 -0700
committerBrian Carlstrom <bdc@google.com>2014-08-05 12:51:13 -0700
commit870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch)
tree7487dad3970556a040f88a49852a3dc9ed19bebd /vm/jdwp
parent76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff)
downloadandroid_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.gz
android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.bz2
android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.zip
Dalvik is dead, long live Dalvik!
croot cd dalvik repo start dalvik-is-dead-long-live-dalvik . repo sync -c . git rm -r README.txt git rm -r dexopt git rm -r tools/deadcode.py git rm -r tools/dex-preopt git rm -r tools/dexcheck git rm -r tools/gdbjithelper git rm -r unit-tests git rm -r vm git checkout HEAD vm/Common.h (needed by libdex) git checkout HEAD vm/DalvikVersion.h (needed by libdex) git checkout HEAD vm/Profile.h (needed by dmtracedump) git add Android.mk (after removing vm, dexopt, and unit-tests references) git commit -a -m 'Dalvik is dead, long live Dalvik!' Bug: 14298175 Change-Id: I9dd13053677629d13496d4238af4374452cda415
Diffstat (limited to 'vm/jdwp')
-rw-r--r--vm/jdwp/ExpandBuf.cpp175
-rw-r--r--vm/jdwp/ExpandBuf.h56
-rw-r--r--vm/jdwp/Jdwp.h236
-rw-r--r--vm/jdwp/JdwpAdb.cpp746
-rw-r--r--vm/jdwp/JdwpConstants.cpp260
-rw-r--r--vm/jdwp/JdwpConstants.h230
-rw-r--r--vm/jdwp/JdwpEvent.cpp1278
-rw-r--r--vm/jdwp/JdwpEvent.h129
-rw-r--r--vm/jdwp/JdwpHandler.cpp1973
-rw-r--r--vm/jdwp/JdwpHandler.h47
-rw-r--r--vm/jdwp/JdwpMain.cpp449
-rw-r--r--vm/jdwp/JdwpPriv.h192
-rw-r--r--vm/jdwp/JdwpSocket.cpp910
-rw-r--r--vm/jdwp/README.txt12
14 files changed, 0 insertions, 6693 deletions
diff --git a/vm/jdwp/ExpandBuf.cpp b/vm/jdwp/ExpandBuf.cpp
deleted file mode 100644
index cc3557e47..000000000
--- a/vm/jdwp/ExpandBuf.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Implementation of an expandable byte buffer. Designed for serializing
- * primitive values, e.g. JDWP replies.
- */
-#include "jdwp/ExpandBuf.h"
-#include "Bits.h"
-#include "Common.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Data structure used to track buffer use.
- */
-struct ExpandBuf {
- u1* storage;
- int curLen;
- int maxLen;
-};
-
-#define kInitialStorage 64
-
-/*
- * Allocate a JdwpBuf and some initial storage.
- */
-ExpandBuf* expandBufAlloc()
-{
- ExpandBuf* newBuf;
-
- newBuf = (ExpandBuf*) malloc(sizeof(*newBuf));
- newBuf->storage = (u1*) malloc(kInitialStorage);
- newBuf->curLen = 0;
- newBuf->maxLen = kInitialStorage;
-
- return newBuf;
-}
-
-/*
- * Free a JdwpBuf and associated storage.
- */
-void expandBufFree(ExpandBuf* pBuf)
-{
- if (pBuf == NULL)
- return;
-
- free(pBuf->storage);
- free(pBuf);
-}
-
-/*
- * Get a pointer to the start of the buffer.
- */
-u1* expandBufGetBuffer(ExpandBuf* pBuf)
-{
- return pBuf->storage;
-}
-
-/*
- * Get the amount of data currently in the buffer.
- */
-size_t expandBufGetLength(ExpandBuf* pBuf)
-{
- return pBuf->curLen;
-}
-
-
-/*
- * Ensure that the buffer has enough space to hold incoming data. If it
- * doesn't, resize the buffer.
- */
-static void ensureSpace(ExpandBuf* pBuf, int newCount)
-{
- u1* newPtr;
-
- if (pBuf->curLen + newCount <= pBuf->maxLen)
- return;
-
- while (pBuf->curLen + newCount > pBuf->maxLen)
- pBuf->maxLen *= 2;
-
- newPtr = (u1*) realloc(pBuf->storage, pBuf->maxLen);
- if (newPtr == NULL) {
- ALOGE("realloc(%d) failed", pBuf->maxLen);
- abort();
- }
-
- pBuf->storage = newPtr;
-}
-
-/*
- * Allocate some space in the buffer.
- */
-u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize)
-{
- u1* gapStart;
-
- ensureSpace(pBuf, gapSize);
- gapStart = pBuf->storage + pBuf->curLen;
- /* do we want to garbage-fill the gap for debugging? */
- pBuf->curLen += gapSize;
-
- return gapStart;
-}
-
-/*
- * Append a byte.
- */
-void expandBufAdd1(ExpandBuf* pBuf, u1 val)
-{
- ensureSpace(pBuf, sizeof(val));
- *(pBuf->storage + pBuf->curLen) = val;
- pBuf->curLen++;
-}
-
-/*
- * Append two big-endian bytes.
- */
-void expandBufAdd2BE(ExpandBuf* pBuf, u2 val)
-{
- ensureSpace(pBuf, sizeof(val));
- set2BE(pBuf->storage + pBuf->curLen, val);
- pBuf->curLen += sizeof(val);
-}
-
-/*
- * Append four big-endian bytes.
- */
-void expandBufAdd4BE(ExpandBuf* pBuf, u4 val)
-{
- ensureSpace(pBuf, sizeof(val));
- set4BE(pBuf->storage + pBuf->curLen, val);
- pBuf->curLen += sizeof(val);
-}
-
-/*
- * Append eight big-endian bytes.
- */
-void expandBufAdd8BE(ExpandBuf* pBuf, u8 val)
-{
- ensureSpace(pBuf, sizeof(val));
- set8BE(pBuf->storage + pBuf->curLen, val);
- pBuf->curLen += sizeof(val);
-}
-
-/*
- * Add a UTF8 string as a 4-byte length followed by a non-NULL-terminated
- * string.
- *
- * Because these strings are coming out of the VM, it's safe to assume that
- * they can be null-terminated (either they don't have null bytes or they
- * have stored null bytes in a multi-byte encoding).
- */
-void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str)
-{
- int strLen = strlen((const char*)str);
-
- ensureSpace(pBuf, sizeof(u4) + strLen);
- setUtf8String(pBuf->storage + pBuf->curLen, str);
- pBuf->curLen += sizeof(u4) + strLen;
-}
diff --git a/vm/jdwp/ExpandBuf.h b/vm/jdwp/ExpandBuf.h
deleted file mode 100644
index 0bbc0c6de..000000000
--- a/vm/jdwp/ExpandBuf.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Expanding byte buffer, with primitives for appending basic data types.
- */
-#ifndef DALVIK_JDWP_EXPANDBUF_H_
-#define DALVIK_JDWP_EXPANDBUF_H_
-
-#include "Common.h" // need u1/u2/u4/u8 types
-
-struct ExpandBuf; /* private */
-
-/* create a new struct */
-ExpandBuf* expandBufAlloc(void);
-/* free storage */
-void expandBufFree(ExpandBuf* pBuf);
-
-/*
- * Accessors. The buffer pointer and length will only be valid until more
- * data is added.
- */
-u1* expandBufGetBuffer(ExpandBuf* pBuf);
-size_t expandBufGetLength(ExpandBuf* pBuf);
-
-/*
- * The "add" operations allocate additional storage and append the data.
- *
- * There are no "get" operations included with this "class", other than
- * GetBuffer(). If you want to get or set data from a position other
- * than the end, get a pointer to the buffer and use the inline functions
- * defined elsewhere.
- *
- * expandBufAddSpace() returns a pointer to the *start* of the region
- * added.
- */
-u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize);
-void expandBufAdd1(ExpandBuf* pBuf, u1 val);
-void expandBufAdd2BE(ExpandBuf* pBuf, u2 val);
-void expandBufAdd4BE(ExpandBuf* pBuf, u4 val);
-void expandBufAdd8BE(ExpandBuf* pBuf, u8 val);
-void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str);
-
-#endif // DALVIK_JDWP_EXPANDBUF_H_
diff --git a/vm/jdwp/Jdwp.h b/vm/jdwp/Jdwp.h
deleted file mode 100644
index 467cecd47..000000000
--- a/vm/jdwp/Jdwp.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * JDWP "public" interface. The main body of the VM should only use JDWP
- * structures and functions declared here.
- *
- * The JDWP code follows the DalvikVM rules for naming conventions, but
- * attempts to remain independent of VM innards (e.g. it doesn't access VM
- * data structures directly). All calls go through Debugger.c.
- */
-#ifndef DALVIK_JDWP_JDWP_H_
-#define DALVIK_JDWP_JDWP_H_
-
-#include "jdwp/JdwpConstants.h"
-#include "jdwp/ExpandBuf.h"
-#include "Common.h"
-#include "Bits.h"
-#include <pthread.h>
-
-struct JdwpState; /* opaque */
-
-/*
- * Fundamental types.
- *
- * ObjectId and RefTypeId must be the same size.
- */
-typedef u4 FieldId; /* static or instance field */
-typedef u4 MethodId; /* any kind of method, including constructors */
-typedef u8 ObjectId; /* any object (threadID, stringID, arrayID, etc) */
-typedef u8 RefTypeId; /* like ObjectID, but unique for Class objects */
-typedef u8 FrameId; /* short-lived stack frame ID */
-
-/*
- * Match these with the type sizes. This way we don't have to pass
- * a value and a length.
- */
-INLINE FieldId dvmReadFieldId(const u1** pBuf) { return read4BE(pBuf); }
-INLINE MethodId dvmReadMethodId(const u1** pBuf) { return read4BE(pBuf); }
-INLINE ObjectId dvmReadObjectId(const u1** pBuf) { return read8BE(pBuf); }
-INLINE RefTypeId dvmReadRefTypeId(const u1** pBuf) { return read8BE(pBuf); }
-INLINE FrameId dvmReadFrameId(const u1** pBuf) { return read8BE(pBuf); }
-INLINE void dvmSetFieldId(u1* buf, FieldId val) { return set4BE(buf, val); }
-INLINE void dvmSetMethodId(u1* buf, MethodId val) { return set4BE(buf, val); }
-INLINE void dvmSetObjectId(u1* buf, ObjectId val) { return set8BE(buf, val); }
-INLINE void dvmSetRefTypeId(u1* buf, RefTypeId val) { return set8BE(buf, val); }
-INLINE void dvmSetFrameId(u1* buf, FrameId val) { return set8BE(buf, val); }
-INLINE void expandBufAddFieldId(ExpandBuf* pReply, FieldId id) {
- expandBufAdd4BE(pReply, id);
-}
-INLINE void expandBufAddMethodId(ExpandBuf* pReply, MethodId id) {
- expandBufAdd4BE(pReply, id);
-}
-INLINE void expandBufAddObjectId(ExpandBuf* pReply, ObjectId id) {
- expandBufAdd8BE(pReply, id);
-}
-INLINE void expandBufAddRefTypeId(ExpandBuf* pReply, RefTypeId id) {
- expandBufAdd8BE(pReply, id);
-}
-INLINE void expandBufAddFrameId(ExpandBuf* pReply, FrameId id) {
- expandBufAdd8BE(pReply, id);
-}
-
-
-/*
- * Holds a JDWP "location".
- */
-struct JdwpLocation {
- u1 typeTag; /* class or interface? */
- RefTypeId classId; /* method->clazz */
- MethodId methodId; /* method in which "idx" resides */
- u8 idx; /* relative index into code block */
-};
-
-/*
- * How we talk to the debugger.
- */
-enum JdwpTransportType {
- kJdwpTransportUnknown = 0,
- kJdwpTransportSocket, /* transport=dt_socket */
- kJdwpTransportAndroidAdb, /* transport=dt_android_adb */
-};
-
-/*
- * Holds collection of JDWP initialization parameters.
- */
-struct JdwpStartupParams {
- JdwpTransportType transport;
- bool server;
- bool suspend;
- char host[64];
- short port;
- /* more will be here someday */
-};
-
-/*
- * Perform one-time initialization.
- *
- * Among other things, this binds to a port to listen for a connection from
- * the debugger.
- *
- * Returns a newly-allocated JdwpState struct on success, or NULL on failure.
- */
-JdwpState* dvmJdwpStartup(const JdwpStartupParams* params);
-
-/*
- * Shut everything down.
- */
-void dvmJdwpShutdown(JdwpState* state);
-
-/*
- * Returns "true" if a debugger or DDM is connected.
- */
-bool dvmJdwpIsActive(JdwpState* state);
-
-/*
- * Return the debugger thread's handle, or 0 if the debugger thread isn't
- * running.
- */
-pthread_t dvmJdwpGetDebugThread(JdwpState* state);
-
-/*
- * Get time, in milliseconds, since the last debugger activity.
- */
-s8 dvmJdwpLastDebuggerActivity(JdwpState* state);
-
-/*
- * When we hit a debugger event that requires suspension, it's important
- * that we wait for the thread to suspend itself before processing any
- * additional requests. (Otherwise, if the debugger immediately sends a
- * "resume thread" command, the resume might arrive before the thread has
- * suspended itself.)
- *
- * The thread should call the "set" function before sending the event to
- * the debugger. The main JDWP handler loop calls "get" before processing
- * an event, and will wait for thread suspension if it's set. Once the
- * thread has suspended itself, the JDWP handler calls "clear" and
- * continues processing the current event. This works in the suspend-all
- * case because the event thread doesn't suspend itself until everything
- * else has suspended.
- *
- * It's possible that multiple threads could encounter thread-suspending
- * events at the same time, so we grab a mutex in the "set" call, and
- * release it in the "clear" call.
- */
-//ObjectId dvmJdwpGetWaitForEventThread(JdwpState* state);
-void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId);
-void dvmJdwpClearWaitForEventThread(JdwpState* state);
-
-/*
- * Network functions.
- */
-bool dvmJdwpCheckConnection(JdwpState* state);
-bool dvmJdwpAcceptConnection(JdwpState* state);
-bool dvmJdwpEstablishConnection(JdwpState* state);
-void dvmJdwpCloseConnection(JdwpState* state);
-bool dvmJdwpProcessIncoming(JdwpState* state);
-
-
-/*
- * These notify the debug code that something interesting has happened. This
- * could be a thread starting or ending, an exception, or an opportunity
- * for a breakpoint. These calls do not mean that an event the debugger
- * is interested has happened, just that something has happened that the
- * debugger *might* be interested in.
- *
- * The item of interest may trigger multiple events, some or all of which
- * are grouped together in a single response.
- *
- * The event may cause the current thread or all threads (except the
- * JDWP support thread) to be suspended.
- */
-
-/*
- * The VM has finished initializing. Only called when the debugger is
- * connected at the time initialization completes.
- */
-bool dvmJdwpPostVMStart(JdwpState* state, bool suspend);
-
-/*
- * A location of interest has been reached. This is used for breakpoints,
- * single-stepping, and method entry/exit. (JDWP requires that these four
- * events are grouped together in a single response.)
- *
- * In some cases "*pLoc" will just have a method and class name, e.g. when
- * issuing a MethodEntry on a native method.
- *
- * "eventFlags" indicates the types of events that have occurred.
- */
-bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
- ObjectId thisPtr, int eventFlags);
-
-/*
- * An exception has been thrown.
- *
- * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
- */
-bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
- ObjectId excepId, RefTypeId excepClassId, const JdwpLocation* pCatchLoc,
- ObjectId thisPtr);
-
-/*
- * A thread has started or stopped.
- */
-bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start);
-
-/*
- * Class has been prepared.
- */
-bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
- const char* signature, int status);
-
-/*
- * The VM is about to stop.
- */
-bool dvmJdwpPostVMDeath(JdwpState* state);
-
-/*
- * Send up a chunk of DDM data.
- */
-void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
- int iovcnt);
-
-#endif // DALVIK_JDWP_JDWP_H_
diff --git a/vm/jdwp/JdwpAdb.cpp b/vm/jdwp/JdwpAdb.cpp
deleted file mode 100644
index 8fb539134..000000000
--- a/vm/jdwp/JdwpAdb.cpp
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jdwp/JdwpPriv.h"
-#include "jdwp/JdwpHandler.h"
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-#include <unistd.h>
-#include <cutils/sockets.h>
-
-/*
- * The JDWP <-> ADB transport protocol is explained in detail
- * in system/core/adb/jdwp_service.c. Here's a summary.
- *
- * 1/ when the JDWP thread starts, it tries to connect to a Unix
- * domain stream socket (@jdwp-control) that is opened by the
- * ADB daemon.
- *
- * 2/ it then sends the current process PID as a string of 4 hexadecimal
- * chars (no terminating zero)
- *
- * 3/ then, it uses recvmsg to receive file descriptors from the
- * daemon. each incoming file descriptor is a pass-through to
- * a given JDWP debugger, that can be used to read the usual
- * JDWP-handshake, etc...
- */
-
-#define kInputBufferSize 8192
-
-#define kMagicHandshake "JDWP-Handshake"
-#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1)
-
-#define kJdwpControlName "\0jdwp-control"
-#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
-
-struct JdwpNetState : public JdwpNetStateBase {
- int controlSock;
- bool awaitingHandshake;
- bool shuttingDown;
- int wakeFds[2];
-
- int inputCount;
- unsigned char inputBuffer[kInputBufferSize];
-
- socklen_t controlAddrLen;
- union {
- struct sockaddr_un controlAddrUn;
- struct sockaddr controlAddrPlain;
- } controlAddr;
-
- JdwpNetState()
- {
- controlSock = -1;
- awaitingHandshake = false;
- shuttingDown = false;
- wakeFds[0] = -1;
- wakeFds[1] = -1;
-
- inputCount = 0;
-
- controlAddr.controlAddrUn.sun_family = AF_UNIX;
- controlAddrLen = sizeof(controlAddr.controlAddrUn.sun_family) +
- kJdwpControlNameLen;
- memcpy(controlAddr.controlAddrUn.sun_path, kJdwpControlName,
- kJdwpControlNameLen);
- }
-};
-
-static void
-adbStateFree( JdwpNetState* netState )
-{
- if (netState == NULL)
- return;
-
- if (netState->clientSock >= 0) {
- shutdown(netState->clientSock, SHUT_RDWR);
- close(netState->clientSock);
- }
- if (netState->controlSock >= 0) {
- shutdown(netState->controlSock, SHUT_RDWR);
- close(netState->controlSock);
- }
- if (netState->wakeFds[0] >= 0) {
- close(netState->wakeFds[0]);
- netState->wakeFds[0] = -1;
- }
- if (netState->wakeFds[1] >= 0) {
- close(netState->wakeFds[1]);
- netState->wakeFds[1] = -1;
- }
-
- delete netState;
-}
-
-/*
- * Do initial prep work, e.g. binding to ports and opening files. This
- * runs in the main thread, before the JDWP thread starts, so it shouldn't
- * do anything that might block forever.
- */
-static bool startup(struct JdwpState* state, const JdwpStartupParams* pParams)
-{
- JdwpNetState* netState;
-
- ALOGV("ADB transport startup");
-
- state->netState = netState = new JdwpNetState;
- if (netState == NULL)
- return false;
-
- return true;
-}
-
-/*
- * Receive a file descriptor from ADB. The fd can be used to communicate
- * directly with a debugger or DDMS.
- *
- * Returns the file descriptor on success. On failure, returns -1 and
- * closes netState->controlSock.
- */
-static int receiveClientFd(JdwpNetState* netState)
-{
- struct msghdr msg;
- struct cmsghdr* cmsg;
- struct iovec iov;
- char dummy = '!';
- union {
- struct cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
- int ret;
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)(void*)CMSG_DATA(cmsg))[0] = -1;
-
- do {
- ret = recvmsg(netState->controlSock, &msg, 0);
- } while (ret < 0 && errno == EINTR);
-
- if (ret <= 0) {
- if (ret < 0) {
- ALOGW("receiving file descriptor from ADB failed (socket %d): %s",
- netState->controlSock, strerror(errno));
- }
- close(netState->controlSock);
- netState->controlSock = -1;
- return -1;
- }
-
- return ((int*)(void*)CMSG_DATA(cmsg))[0];
-}
-
-/*
- * Block forever, waiting for a debugger to connect to us. Called from the
- * JDWP thread.
- *
- * This needs to un-block and return "false" if the VM is shutting down. It
- * should return "true" when it successfully accepts a connection.
- */
-static bool acceptConnection(struct JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- int retryCount = 0;
-
- /* first, ensure that we get a connection to the ADB daemon */
-
-retry:
- if (netState->shuttingDown)
- return false;
-
- if (netState->controlSock < 0) {
- int sleep_ms = 500;
- const int sleep_max_ms = 2*1000;
- char buff[5];
-
- netState->controlSock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (netState->controlSock < 0) {
- ALOGE("Could not create ADB control socket:%s",
- strerror(errno));
- return false;
- }
-
- if (pipe(netState->wakeFds) < 0) {
- ALOGE("pipe failed");
- return false;
- }
-
- snprintf(buff, sizeof(buff), "%04x", getpid());
- buff[4] = 0;
-
- for (;;) {
- /*
- * If adbd isn't running, because USB debugging was disabled or
- * perhaps the system is restarting it for "adb root", the
- * connect() will fail. We loop here forever waiting for it
- * to come back.
- *
- * Waking up and polling every couple of seconds is generally a
- * bad thing to do, but we only do this if the application is
- * debuggable *and* adbd isn't running. Still, for the sake
- * of battery life, we should consider timing out and giving
- * up after a few minutes in case somebody ships an app with
- * the debuggable flag set.
- */
- int ret = connect(netState->controlSock,
- &netState->controlAddr.controlAddrPlain,
- netState->controlAddrLen);
- if (!ret) {
- if (!socket_peer_is_trusted(netState->controlSock)) {
- if (shutdown(netState->controlSock, SHUT_RDWR)) {
- ALOGE("trouble shutting down socket: %s", strerror(errno));
- }
- return false;
- }
-
- /* now try to send our pid to the ADB daemon */
- do {
- ret = send( netState->controlSock, buff, 4, 0 );
- } while (ret < 0 && errno == EINTR);
-
- if (ret >= 0) {
- ALOGV("PID sent as '%.*s' to ADB", 4, buff);
- break;
- }
-
- ALOGE("Weird, can't send JDWP process pid to ADB: %s",
- strerror(errno));
- return false;
- }
- ALOGV("Can't connect to ADB control socket:%s",
- strerror(errno));
-
- usleep( sleep_ms*1000 );
-
- sleep_ms += (sleep_ms >> 1);
- if (sleep_ms > sleep_max_ms)
- sleep_ms = sleep_max_ms;
-
- if (netState->shuttingDown)
- return false;
- }
- }
-
- ALOGV("trying to receive file descriptor from ADB");
- /* now we can receive a client file descriptor */
- netState->clientSock = receiveClientFd(netState);
- if (netState->shuttingDown)
- return false; // suppress logs and additional activity
-
- if (netState->clientSock < 0) {
- if (++retryCount > 5) {
- ALOGE("adb connection max retries exceeded");
- return false;
- }
- goto retry;
- } else {
- ALOGV("received file descriptor %d from ADB", netState->clientSock);
- netState->awaitingHandshake = 1;
- netState->inputCount = 0;
- return true;
- }
-}
-
-/*
- * Connect out to a debugger (for server=n). Not required.
- */
-static bool establishConnection(struct JdwpState* state)
-{
- return false;
-}
-
-/*
- * Close a connection from a debugger (which may have already dropped us).
- * Only called from the JDWP thread.
- */
-static void closeConnection(struct JdwpState* state)
-{
- JdwpNetState* netState;
-
- assert(state != NULL && state->netState != NULL);
-
- netState = state->netState;
- if (netState->clientSock < 0)
- return;
-
- ALOGV("+++ closed JDWP <-> ADB connection");
-
- close(netState->clientSock);
- netState->clientSock = -1;
-}
-
-/*
- * Close all network stuff, including the socket we use to listen for
- * new connections.
- *
- * May be called from a non-JDWP thread, e.g. when the VM is shutting down.
- */
-static void adbStateShutdown(struct JdwpNetState* netState)
-{
- int controlSock;
- int clientSock;
-
- if (netState == NULL)
- return;
-
- netState->shuttingDown = true;
-
- clientSock = netState->clientSock;
- if (clientSock >= 0) {
- shutdown(clientSock, SHUT_RDWR);
- netState->clientSock = -1;
- }
-
- controlSock = netState->controlSock;
- if (controlSock >= 0) {
- shutdown(controlSock, SHUT_RDWR);
- netState->controlSock = -1;
- }
-
- if (netState->wakeFds[1] >= 0) {
- ALOGV("+++ writing to wakePipe");
- TEMP_FAILURE_RETRY(write(netState->wakeFds[1], "", 1));
- }
-}
-
-static void netShutdown(JdwpState* state)
-{
- adbStateShutdown(state->netState);
-}
-
-/*
- * Free up anything we put in state->netState. This is called after
- * "netShutdown", after the JDWP thread has stopped.
- */
-static void netFree(struct JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
-
- adbStateFree(netState);
-}
-
-/*
- * Is a debugger connected to us?
- */
-static bool isConnected(struct JdwpState* state)
-{
- return (state->netState != NULL &&
- state->netState->clientSock >= 0);
-}
-
-/*
- * Are we still waiting for the JDWP handshake?
- */
-static bool awaitingHandshake(struct JdwpState* state)
-{
- return state->netState->awaitingHandshake;
-}
-
-/*
- * Figure out if we have a full packet in the buffer.
- */
-static bool haveFullPacket(JdwpNetState* netState)
-{
- long length;
-
- if (netState->awaitingHandshake)
- return (netState->inputCount >= (int) kMagicHandshakeLen);
-
- if (netState->inputCount < 4)
- return false;
-
- length = get4BE(netState->inputBuffer);
- return (netState->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(JdwpNetState* netState, int count)
-{
- assert(count > 0);
- assert(count <= netState->inputCount);
-
- if (count == netState->inputCount) {
- netState->inputCount = 0;
- return;
- }
-
- memmove(netState->inputBuffer, netState->inputBuffer + count,
- netState->inputCount - count);
- netState->inputCount -= count;
-}
-
-/*
- * Handle a packet. Returns "false" if we encounter a connection-fatal error.
- */
-static bool handlePacket(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- const unsigned char* buf = netState->inputBuffer;
- JdwpReqHeader hdr;
- u4 length, id;
- u1 flags, cmdSet, cmd;
- u2 error;
- bool reply;
- int dataLen;
-
- cmd = cmdSet = 0; // shut up gcc
-
- length = read4BE(&buf);
- id = read4BE(&buf);
- flags = read1(&buf);
- if ((flags & kJDWPFlagReply) != 0) {
- reply = true;
- error = read2BE(&buf);
- } else {
- reply = false;
- cmdSet = read1(&buf);
- cmd = read1(&buf);
- }
-
- assert((int) length <= netState->inputCount);
- dataLen = length - (buf - netState->inputBuffer);
-
- if (!reply) {
- ExpandBuf* pReply = expandBufAlloc();
-
- hdr.length = length;
- hdr.id = id;
- hdr.cmdSet = cmdSet;
- hdr.cmd = cmd;
- dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
- if (expandBufGetLength(pReply) > 0) {
- ssize_t cc = netState->writePacket(pReply);
-
- if (cc != (ssize_t) expandBufGetLength(pReply)) {
- ALOGE("Failed sending reply to debugger: %s", strerror(errno));
- expandBufFree(pReply);
- return false;
- }
- } else {
- ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
- }
- expandBufFree(pReply);
- } else {
- ALOGV("reply?!");
- assert(false);
- }
-
- ALOGV("----------");
-
- consumeBytes(netState, length);
- return true;
-}
-
-/*
- * Process incoming data. If no data is available, this will block until
- * some arrives.
- *
- * If we get a full packet, handle it.
- *
- * To take some of the mystery out of life, we want to reject incoming
- * connections if we already have a debugger attached. If we don't, the
- * debugger will just mysteriously hang until it times out. We could just
- * close the listen socket, but there's a good chance we won't be able to
- * bind to the same port again, which would confuse utilities.
- *
- * Returns "false" on error (indicating that the connection has been severed),
- * "true" if things are still okay.
- */
-static bool processIncoming(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- int readCount;
-
- assert(netState->clientSock >= 0);
-
- if (!haveFullPacket(netState)) {
- /* read some more, looping until we have data */
- errno = 0;
- while (1) {
- int selCount;
- fd_set readfds;
- int maxfd = -1;
- int fd;
-
- FD_ZERO(&readfds);
-
- /* configure fds; note these may get zapped by another thread */
- fd = netState->controlSock;
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- }
- fd = netState->clientSock;
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- }
- fd = netState->wakeFds[0];
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- if (maxfd < fd)
- maxfd = fd;
- } else {
- ALOGI("NOTE: entering select w/o wakepipe");
- }
-
- if (maxfd < 0) {
- ALOGV("+++ all fds are closed");
- return false;
- }
-
- /*
- * Select blocks until it sees activity on the file descriptors.
- * Closing the local file descriptor does not count as activity,
- * so we can't rely on that to wake us up (it works for read()
- * and accept(), but not select()).
- *
- * We can do one of three things: (1) send a signal and catch
- * EINTR, (2) open an additional fd ("wakePipe") and write to
- * it when it's time to exit, or (3) time out periodically and
- * re-issue the select. We're currently using #2, as it's more
- * reliable than #1 and generally better than #3. Wastes two fds.
- */
- selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
- if (selCount < 0) {
- if (errno == EINTR)
- continue;
- ALOGE("select failed: %s", strerror(errno));
- goto fail;
- }
-
- if (netState->wakeFds[0] >= 0 &&
- FD_ISSET(netState->wakeFds[0], &readfds))
- {
- ALOGD("Got wake-up signal, bailing out of select");
- goto fail;
- }
- if (netState->controlSock >= 0 &&
- FD_ISSET(netState->controlSock, &readfds))
- {
- int sock = receiveClientFd(netState);
- if (sock >= 0) {
- ALOGI("Ignoring second debugger -- accepting and dropping");
- close(sock);
- } else {
- assert(netState->controlSock < 0);
- /*
- * Remote side most likely went away, so our next read
- * on netState->clientSock will fail and throw us out
- * of the loop.
- */
- }
- }
- if (netState->clientSock >= 0 &&
- FD_ISSET(netState->clientSock, &readfds))
- {
- readCount = read(netState->clientSock,
- netState->inputBuffer + netState->inputCount,
- sizeof(netState->inputBuffer) - netState->inputCount);
- if (readCount < 0) {
- /* read failed */
- if (errno != EINTR)
- goto fail;
- ALOGD("+++ EINTR hit");
- return true;
- } else if (readCount == 0) {
- /* EOF hit -- far end went away */
- ALOGV("+++ peer disconnected");
- goto fail;
- } else
- break;
- }
- }
-
- netState->inputCount += readCount;
- if (!haveFullPacket(netState))
- return true; /* still not there yet */
- }
-
- /*
- * Special-case the initial handshake. For some bizarre reason we're
- * expected to emulate bad tty settings by echoing the request back
- * exactly as it was sent. Note the handshake is always initiated by
- * the debugger, no matter who connects to whom.
- *
- * Other than this one case, the protocol [claims to be] stateless.
- */
- if (netState->awaitingHandshake) {
- int cc;
-
- if (memcmp(netState->inputBuffer,
- kMagicHandshake, kMagicHandshakeLen) != 0)
- {
- ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
- goto fail;
- }
-
- errno = 0;
- cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer,
- kMagicHandshakeLen));
- if (cc != kMagicHandshakeLen) {
- ALOGE("Failed writing handshake bytes: %s (%d of %d)",
- strerror(errno), cc, (int) kMagicHandshakeLen);
- goto fail;
- }
-
- consumeBytes(netState, kMagicHandshakeLen);
- netState->awaitingHandshake = false;
- ALOGV("+++ handshake complete");
- return true;
- }
-
- /*
- * Handle this packet.
- */
- return handlePacket(state);
-
-fail:
- closeConnection(state);
- return false;
-}
-
-/*
- * Send a request.
- *
- * The entire packet must be sent with a single write() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
-{
- JdwpNetState* netState = state->netState;
-
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- errno = 0;
-
- ssize_t cc = netState->writePacket(pReq);
-
- if (cc != (ssize_t) expandBufGetLength(pReq)) {
- ALOGE("Failed sending req to debugger: %s (%d of %d)",
- strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
- return false;
- }
-
- return true;
-}
-
-/*
- * Send a request that was split into multiple buffers.
- *
- * The entire packet must be sent with a single writev() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
- int iovcnt)
-{
- JdwpNetState* netState = state->netState;
-
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- size_t expected = 0;
- int i;
- for (i = 0; i < iovcnt; i++)
- expected += iov[i].iov_len;
-
- ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
-
- if ((size_t)actual != expected) {
- ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
- strerror(errno), (int) actual, expected);
- return false;
- }
-
- return true;
-}
-
-
-/*
- * Our functions.
- */
-static const JdwpTransport socketTransport = {
- startup,
- acceptConnection,
- establishConnection,
- closeConnection,
- netShutdown,
- netFree,
- isConnected,
- awaitingHandshake,
- processIncoming,
- sendRequest,
- sendBufferedRequest
-};
-
-/*
- * Return our set.
- */
-const JdwpTransport* dvmJdwpAndroidAdbTransport()
-{
- return &socketTransport;
-}
diff --git a/vm/jdwp/JdwpConstants.cpp b/vm/jdwp/JdwpConstants.cpp
deleted file mode 100644
index e98ff432f..000000000
--- a/vm/jdwp/JdwpConstants.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * String constants to go along with enumerated values. (Pity we don't
- * have enumerated constant reflection in C.) These are only needed for
- * making the output human-readable.
- */
-#include "jdwp/JdwpConstants.h"
-
-/*
- * Return a string for the error code.
- */
-const char* dvmJdwpErrorStr(JdwpError error)
-{
- switch (error) {
- case ERR_NONE:
- return "NONE";
- case ERR_INVALID_THREAD:
- return "INVALID_THREAD";
- case ERR_INVALID_THREAD_GROUP:
- return "INVALID_THREAD_GROUP";
- case ERR_INVALID_PRIORITY:
- return "INVALID_PRIORITY";
- case ERR_THREAD_NOT_SUSPENDED:
- return "THREAD_NOT_SUSPENDED";
- case ERR_THREAD_SUSPENDED:
- return "THREAD_SUSPENDED";
- case ERR_INVALID_OBJECT:
- return "INVALID_OBJEC";
- case ERR_INVALID_CLASS:
- return "INVALID_CLASS";
- case ERR_CLASS_NOT_PREPARED:
- return "CLASS_NOT_PREPARED";
- case ERR_INVALID_METHODID:
- return "INVALID_METHODID";
- case ERR_INVALID_LOCATION:
- return "INVALID_LOCATION";
- case ERR_INVALID_FIELDID:
- return "INVALID_FIELDID";
- case ERR_INVALID_FRAMEID:
- return "INVALID_FRAMEID";
- case ERR_NO_MORE_FRAMES:
- return "NO_MORE_FRAMES";
- case ERR_OPAQUE_FRAME:
- return "OPAQUE_FRAME";
- case ERR_NOT_CURRENT_FRAME:
- return "NOT_CURRENT_FRAME";
- case ERR_TYPE_MISMATCH:
- return "TYPE_MISMATCH";
- case ERR_INVALID_SLOT:
- return "INVALID_SLOT";
- case ERR_DUPLICATE:
- return "DUPLICATE";
- case ERR_NOT_FOUND:
- return "NOT_FOUND";
- case ERR_INVALID_MONITOR:
- return "INVALID_MONITOR";
- case ERR_NOT_MONITOR_OWNER:
- return "NOT_MONITOR_OWNER";
- case ERR_INTERRUPT:
- return "INTERRUPT";
- case ERR_INVALID_CLASS_FORMAT:
- return "INVALID_CLASS_FORMAT";
- case ERR_CIRCULAR_CLASS_DEFINITION:
- return "CIRCULAR_CLASS_DEFINITION";
- case ERR_FAILS_VERIFICATION:
- return "FAILS_VERIFICATION";
- case ERR_ADD_METHOD_NOT_IMPLEMENTED:
- return "ADD_METHOD_NOT_IMPLEMENTED";
- case ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED:
- return "SCHEMA_CHANGE_NOT_IMPLEMENTED";
- case ERR_INVALID_TYPESTATE:
- return "INVALID_TYPESTATE";
- case ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED:
- return "HIERARCHY_CHANGE_NOT_IMPLEMENTED";
- case ERR_DELETE_METHOD_NOT_IMPLEMENTED:
- return "DELETE_METHOD_NOT_IMPLEMENTED";
- case ERR_UNSUPPORTED_VERSION:
- return "UNSUPPORTED_VERSION";
- case ERR_NAMES_DONT_MATCH:
- return "NAMES_DONT_MATCH";
- case ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
- return "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
- case ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
- return "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
- case ERR_NOT_IMPLEMENTED:
- return "NOT_IMPLEMENTED";
- case ERR_NULL_POINTER:
- return "NULL_POINTER";
- case ERR_ABSENT_INFORMATION:
- return "ABSENT_INFORMATION";
- case ERR_INVALID_EVENT_TYPE:
- return "INVALID_EVENT_TYPE";
- case ERR_ILLEGAL_ARGUMENT:
- return "ILLEGAL_ARGUMENT";
- case ERR_OUT_OF_MEMORY:
- return "OUT_OF_MEMORY";
- case ERR_ACCESS_DENIED:
- return "ACCESS_DENIED";
- case ERR_VM_DEAD:
- return "VM_DEAD";
- case ERR_INTERNAL:
- return "INTERNAL";
- case ERR_UNATTACHED_THREAD:
- return "UNATTACHED_THREAD";
- case ERR_INVALID_TAG:
- return "INVALID_TAG";
- case ERR_ALREADY_INVOKING:
- return "ALREADY_INVOKING";
- case ERR_INVALID_INDEX:
- return "INVALID_INDEX";
- case ERR_INVALID_LENGTH:
- return "INVALID_LENGTH";
- case ERR_INVALID_STRING:
- return "INVALID_STRING";
- case ERR_INVALID_CLASS_LOADER:
- return "INVALID_CLASS_LOADER";
- case ERR_INVALID_ARRAY:
- return "INVALID_ARRAY";
- case ERR_TRANSPORT_LOAD:
- return "TRANSPORT_LOAD";
- case ERR_TRANSPORT_INIT:
- return "TRANSPORT_INIT";
- case ERR_NATIVE_METHOD:
- return "NATIVE_METHOD";
- case ERR_INVALID_COUNT:
- return "INVALID_COUNT";
- default:
- return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the EventKind.
- */
-const char* dvmJdwpEventKindStr(JdwpEventKind kind)
-{
- switch (kind) {
- case EK_SINGLE_STEP: return "SINGLE_STEP";
- case EK_BREAKPOINT: return "BREAKPOINT";
- case EK_FRAME_POP: return "FRAME_POP";
- case EK_EXCEPTION: return "EXCEPTION";
- case EK_USER_DEFINED: return "USER_DEFINED";
- case EK_THREAD_START: return "THREAD_START";
- /*case EK_THREAD_END: return "THREAD_END";*/
- case EK_CLASS_PREPARE: return "CLASS_PREPARE";
- case EK_CLASS_UNLOAD: return "CLASS_UNLOAD";
- case EK_CLASS_LOAD: return "CLASS_LOAD";
- case EK_FIELD_ACCESS: return "FIELD_ACCESS";
- case EK_FIELD_MODIFICATION: return "FIELD_MODIFICATION";
- case EK_EXCEPTION_CATCH: return "EXCEPTION_CATCH";
- case EK_METHOD_ENTRY: return "METHOD_ENTRY";
- case EK_METHOD_EXIT: return "METHOD_EXIT";
- case EK_VM_INIT: return "VM_INIT";
- case EK_VM_DEATH: return "VM_DEATH";
- case EK_VM_DISCONNECTED: return "VM_DISCONNECTED";
- /*case EK_VM_START: return "VM_START";*/
- case EK_THREAD_DEATH: return "THREAD_DEATH";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the ModKind.
- */
-const char* dvmJdwpModKindStr(JdwpModKind kind)
-{
- switch (kind) {
- case MK_COUNT: return "COUNT";
- case MK_CONDITIONAL: return "CONDITIONAL";
- case MK_THREAD_ONLY: return "THREAD_ONLY";
- case MK_CLASS_ONLY: return "CLASS_ONLY";
- case MK_CLASS_MATCH: return "CLASS_MATCH";
- case MK_CLASS_EXCLUDE: return "CLASS_EXCLUDE";
- case MK_LOCATION_ONLY: return "LOCATION_ONLY";
- case MK_EXCEPTION_ONLY: return "EXCEPTION_ONLY";
- case MK_FIELD_ONLY: return "FIELD_ONLY";
- case MK_STEP: return "STEP";
- case MK_INSTANCE_ONLY: return "INSTANCE_ONLY";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the StepDepth.
- */
-const char* dvmJdwpStepDepthStr(JdwpStepDepth depth)
-{
- switch (depth) {
- case SD_INTO: return "INTO";
- case SD_OVER: return "OVER";
- case SD_OUT: return "OUT";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the StepSize.
- */
-const char* dvmJdwpStepSizeStr(JdwpStepSize size)
-{
- switch (size) {
- case SS_MIN: return "MIN";
- case SS_LINE: return "LINE";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the SuspendPolicy.
- */
-const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy)
-{
- switch (policy) {
- case SP_NONE: return "NONE";
- case SP_EVENT_THREAD: return "EVENT_THREAD";
- case SP_ALL: return "ALL";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the SuspendStatus.
- */
-const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status)
-{
- switch (status) {
- case SUSPEND_STATUS_NOT_SUSPENDED: return "Not SUSPENDED";
- case SUSPEND_STATUS_SUSPENDED: return "SUSPENDED";
- default: return "?UNKNOWN?";
- }
-}
-
-/*
- * Return a string for the ThreadStatus.
- */
-const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status)
-{
- switch (status) {
- case TS_ZOMBIE: return "ZOMBIE";
- case TS_RUNNING: return "RUNNING";
- case TS_SLEEPING: return "SLEEPING";
- case TS_MONITOR: return "MONITOR";
- case TS_WAIT: return "WAIT";
- default: return "?UNKNOWN?";
- }
-};
diff --git a/vm/jdwp/JdwpConstants.h b/vm/jdwp/JdwpConstants.h
deleted file mode 100644
index 9913759e8..000000000
--- a/vm/jdwp/JdwpConstants.h
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * These come out of the JDWP documentation.
- */
-#ifndef DALVIK_JDWP_JDWPCONSTANTS_H_
-#define DALVIK_JDWP_JDWPCONSTANTS_H_
-
-/*
- * Error constants.
- */
-enum JdwpError {
- ERR_NONE = 0,
- ERR_INVALID_THREAD = 10,
- ERR_INVALID_THREAD_GROUP = 11,
- ERR_INVALID_PRIORITY = 12,
- ERR_THREAD_NOT_SUSPENDED = 13,
- ERR_THREAD_SUSPENDED = 14,
- ERR_INVALID_OBJECT = 20,
- ERR_INVALID_CLASS = 21,
- ERR_CLASS_NOT_PREPARED = 22,
- ERR_INVALID_METHODID = 23,
- ERR_INVALID_LOCATION = 24,
- ERR_INVALID_FIELDID = 25,
- ERR_INVALID_FRAMEID = 30,
- ERR_NO_MORE_FRAMES = 31,
- ERR_OPAQUE_FRAME = 32,
- ERR_NOT_CURRENT_FRAME = 33,
- ERR_TYPE_MISMATCH = 34,
- ERR_INVALID_SLOT = 35,
- ERR_DUPLICATE = 40,
- ERR_NOT_FOUND = 41,
- ERR_INVALID_MONITOR = 50,
- ERR_NOT_MONITOR_OWNER = 51,
- ERR_INTERRUPT = 52,
- ERR_INVALID_CLASS_FORMAT = 60,
- ERR_CIRCULAR_CLASS_DEFINITION = 61,
- ERR_FAILS_VERIFICATION = 62,
- ERR_ADD_METHOD_NOT_IMPLEMENTED = 63,
- ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED = 64,
- ERR_INVALID_TYPESTATE = 65,
- ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED = 66,
- ERR_DELETE_METHOD_NOT_IMPLEMENTED = 67,
- ERR_UNSUPPORTED_VERSION = 68,
- ERR_NAMES_DONT_MATCH = 69,
- ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED = 70,
- ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED = 71,
- ERR_NOT_IMPLEMENTED = 99,
- ERR_NULL_POINTER = 100,
- ERR_ABSENT_INFORMATION = 101,
- ERR_INVALID_EVENT_TYPE = 102,
- ERR_ILLEGAL_ARGUMENT = 103,
- ERR_OUT_OF_MEMORY = 110,
- ERR_ACCESS_DENIED = 111,
- ERR_VM_DEAD = 112,
- ERR_INTERNAL = 113,
- ERR_UNATTACHED_THREAD = 115,
- ERR_INVALID_TAG = 500,
- ERR_ALREADY_INVOKING = 502,
- ERR_INVALID_INDEX = 503,
- ERR_INVALID_LENGTH = 504,
- ERR_INVALID_STRING = 506,
- ERR_INVALID_CLASS_LOADER = 507,
- ERR_INVALID_ARRAY = 508,
- ERR_TRANSPORT_LOAD = 509,
- ERR_TRANSPORT_INIT = 510,
- ERR_NATIVE_METHOD = 511,
- ERR_INVALID_COUNT = 512,
-};
-const char* dvmJdwpErrorStr(JdwpError error);
-
-
-/*
- * ClassStatus constants. These are bit flags that can be ORed together.
- */
-enum JdwpClassStatus {
- CS_VERIFIED = 0x01,
- CS_PREPARED = 0x02,
- CS_INITIALIZED = 0x04,
- CS_ERROR = 0x08,
-};
-
-/*
- * EventKind constants.
- */
-enum JdwpEventKind {
- EK_SINGLE_STEP = 1,
- EK_BREAKPOINT = 2,
- EK_FRAME_POP = 3,
- EK_EXCEPTION = 4,
- EK_USER_DEFINED = 5,
- EK_THREAD_START = 6,
- EK_THREAD_END = 7,
- EK_CLASS_PREPARE = 8,
- EK_CLASS_UNLOAD = 9,
- EK_CLASS_LOAD = 10,
- EK_FIELD_ACCESS = 20,
- EK_FIELD_MODIFICATION = 21,
- EK_EXCEPTION_CATCH = 30,
- EK_METHOD_ENTRY = 40,
- EK_METHOD_EXIT = 41,
- EK_VM_INIT = 90,
- EK_VM_DEATH = 99,
- EK_VM_DISCONNECTED = 100, /* "Never sent across JDWP */
- EK_VM_START = EK_VM_INIT,
- EK_THREAD_DEATH = EK_THREAD_END,
-};
-const char* dvmJdwpEventKindStr(JdwpEventKind kind);
-
-/*
- * Values for "modKind" in EventRequest.Set.
- */
-enum JdwpModKind {
- MK_COUNT = 1,
- MK_CONDITIONAL = 2,
- MK_THREAD_ONLY = 3,
- MK_CLASS_ONLY = 4,
- MK_CLASS_MATCH = 5,
- MK_CLASS_EXCLUDE = 6,
- MK_LOCATION_ONLY = 7,
- MK_EXCEPTION_ONLY = 8,
- MK_FIELD_ONLY = 9,
- MK_STEP = 10,
- MK_INSTANCE_ONLY = 11,
-};
-const char* dvmJdwpModKindStr(JdwpModKind kind);
-
-/*
- * InvokeOptions constants (bit flags).
- */
-enum JdwpInvokeOptions {
- INVOKE_SINGLE_THREADED = 0x01,
- INVOKE_NONVIRTUAL = 0x02,
-};
-
-/*
- * StepDepth constants.
- */
-enum JdwpStepDepth {
- SD_INTO = 0, /* step into method calls */
- SD_OVER = 1, /* step over method calls */
- SD_OUT = 2, /* step out of current method */
-};
-const char* dvmJdwpStepDepthStr(JdwpStepDepth depth);
-
-/*
- * StepSize constants.
- */
-enum JdwpStepSize {
- SS_MIN = 0, /* step by minimum (e.g. 1 bytecode inst) */
- SS_LINE = 1, /* if possible, step to next line */
-};
-const char* dvmJdwpStepSizeStr(JdwpStepSize size);
-
-/*
- * SuspendPolicy constants.
- */
-enum JdwpSuspendPolicy {
- SP_NONE = 0, /* suspend no threads */
- SP_EVENT_THREAD = 1, /* suspend event thread */
- SP_ALL = 2, /* suspend all threads */
-};
-const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy);
-
-/*
- * SuspendStatus constants.
- */
-enum JdwpSuspendStatus {
- SUSPEND_STATUS_NOT_SUSPENDED = 0,
- SUSPEND_STATUS_SUSPENDED = 1,
-};
-const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status);
-
-/*
- * ThreadStatus constants.
- */
-enum JdwpThreadStatus {
- TS_ZOMBIE = 0,
- TS_RUNNING = 1, // RUNNING
- TS_SLEEPING = 2, // (in Thread.sleep())
- TS_MONITOR = 3, // WAITING (monitor wait)
- TS_WAIT = 4, // (in Object.wait())
-};
-const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status);
-
-/*
- * TypeTag constants.
- */
-enum JdwpTypeTag {
- TT_CLASS = 1,
- TT_INTERFACE = 2,
- TT_ARRAY = 3,
-};
-
-/*
- * Tag constants.
- */
-enum JdwpType {
- JT_ARRAY = '[',
- JT_BYTE = 'B',
- JT_CHAR = 'C',
- JT_OBJECT = 'L',
- JT_FLOAT = 'F',
- JT_DOUBLE = 'D',
- JT_INT = 'I',
- JT_LONG = 'J',
- JT_SHORT = 'S',
- JT_VOID = 'V',
- JT_BOOLEAN = 'Z',
- JT_STRING = 's',
- JT_THREAD = 't',
- JT_THREAD_GROUP = 'g',
- JT_CLASS_LOADER = 'l',
- JT_CLASS_OBJECT = 'c',
-};
-
-#endif // DALVIK_JDWP_JDWPCONSTANTS_H_
diff --git a/vm/jdwp/JdwpEvent.cpp b/vm/jdwp/JdwpEvent.cpp
deleted file mode 100644
index 27934872e..000000000
--- a/vm/jdwp/JdwpEvent.cpp
+++ /dev/null
@@ -1,1278 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Send events to the debugger.
- */
-#include "jdwp/JdwpPriv.h"
-#include "jdwp/JdwpConstants.h"
-#include "jdwp/JdwpHandler.h"
-#include "jdwp/JdwpEvent.h"
-#include "jdwp/ExpandBuf.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h> /* for offsetof() */
-#include <unistd.h>
-
-/*
-General notes:
-
-The event add/remove stuff usually happens from the debugger thread,
-in response to requests from the debugger, but can also happen as the
-result of an event in an arbitrary thread (e.g. an event with a "count"
-mod expires). It's important to keep the event list locked when processing
-events.
-
-Event posting can happen from any thread. The JDWP thread will not usually
-post anything but VM start/death, but if a JDWP request causes a class
-to be loaded, the ClassPrepare event will come from the JDWP thread.
-
-
-We can have serialization issues when we post an event to the debugger.
-For example, a thread could send an "I hit a breakpoint and am suspending
-myself" message to the debugger. Before it manages to suspend itself, the
-debugger's response ("not interested, resume thread") arrives and is
-processed. We try to resume a thread that hasn't yet suspended.
-
-This means that, after posting an event to the debugger, we need to wait
-for the event thread to suspend itself (and, potentially, all other threads)
-before processing any additional requests from the debugger. While doing
-so we need to be aware that multiple threads may be hitting breakpoints
-or other events simultaneously, so we either need to wait for all of them
-or serialize the events with each other.
-
-The current mechanism works like this:
- Event thread:
- - If I'm going to suspend, grab the "I am posting an event" token. Wait
- for it if it's not currently available.
- - Post the event to the debugger.
- - If appropriate, suspend others and then myself. As part of suspending
- myself, release the "I am posting" token.
- JDWP thread:
- - When an event arrives, see if somebody is posting an event. If so,
- sleep until we can acquire the "I am posting an event" token. Release
- it immediately and continue processing -- the event we have already
- received should not interfere with other events that haven't yet
- been posted.
-
-Some care must be taken to avoid deadlock:
-
- - thread A and thread B exit near-simultaneously, and post thread-death
- events with a "suspend all" clause
- - thread A gets the event token, thread B sits and waits for it
- - thread A wants to suspend all other threads, but thread B is waiting
- for the token and can't be suspended
-
-So we need to mark thread B in such a way that thread A doesn't wait for it.
-
-If we just bracket the "grab event token" call with a change to VMWAIT
-before sleeping, the switch back to RUNNING state when we get the token
-will cause thread B to suspend (remember, thread A's global suspend is
-still in force, even after it releases the token). Suspending while
-holding the event token is very bad, because it prevents the JDWP thread
-from processing incoming messages.
-
-We need to change to VMWAIT state at the *start* of posting an event,
-and stay there until we either finish posting the event or decide to
-put ourselves to sleep. That way we don't interfere with anyone else and
-don't allow anyone else to interfere with us.
-*/
-
-
-#define kJdwpEventCommandSet 64
-#define kJdwpCompositeCommand 100
-
-/*
- * Stuff to compare against when deciding if a mod matches. Only the
- * values for mods valid for the event being evaluated will be filled in.
- * The rest will be zeroed.
- */
-struct ModBasket {
- const JdwpLocation* pLoc; /* LocationOnly */
- const char* className; /* ClassMatch/ClassExclude */
- ObjectId threadId; /* ThreadOnly */
- RefTypeId classId; /* ClassOnly */
- RefTypeId excepClassId; /* ExceptionOnly */
- bool caught; /* ExceptionOnly */
- FieldId field; /* FieldOnly */
- ObjectId thisPtr; /* InstanceOnly */
- /* nothing for StepOnly -- handled differently */
-};
-
-/*
- * Get the next "request" serial number. We use this when sending
- * packets to the debugger.
- */
-u4 dvmJdwpNextRequestSerial(JdwpState* state)
-{
- dvmDbgLockMutex(&state->serialLock);
- u4 result = state->requestSerial++;
- dvmDbgUnlockMutex(&state->serialLock);
-
- return result;
-}
-
-/*
- * Get the next "event" serial number. We use this in the response to
- * message type EventRequest.Set.
- */
-u4 dvmJdwpNextEventSerial(JdwpState* state)
-{
- dvmDbgLockMutex(&state->serialLock);
- u4 result = state->eventSerial++;
- dvmDbgUnlockMutex(&state->serialLock);
-
- return result;
-}
-
-/*
- * Lock the "event" mutex, which guards the list of registered events.
- */
-static void lockEventMutex(JdwpState* state)
-{
- //dvmDbgThreadWaiting();
- dvmDbgLockMutex(&state->eventLock);
- //dvmDbgThreadRunning();
-}
-
-/*
- * Unlock the "event" mutex.
- */
-static void unlockEventMutex(JdwpState* state)
-{
- dvmDbgUnlockMutex(&state->eventLock);
-}
-
-/*
- * Dump an event to the log file.
- */
-static void dumpEvent(const JdwpEvent* pEvent)
-{
- ALOGI("Event id=0x%4x %p (prev=%p next=%p):",
- pEvent->requestId, pEvent, pEvent->prev, pEvent->next);
- ALOGI(" kind=%s susp=%s modCount=%d",
- dvmJdwpEventKindStr(pEvent->eventKind),
- dvmJdwpSuspendPolicyStr(pEvent->suspendPolicy),
- pEvent->modCount);
-
- for (int i = 0; i < pEvent->modCount; i++) {
- const JdwpEventMod* pMod = &pEvent->mods[i];
- JdwpModKind kind = static_cast<JdwpModKind>(pMod->modKind);
- ALOGI(" %s", dvmJdwpModKindStr(kind));
- /* TODO - show details */
- }
-}
-
-/*
- * Add an event to the list. Ordering is not important.
- *
- * If something prevents the event from being registered, e.g. it's a
- * single-step request on a thread that doesn't exist, the event will
- * not be added to the list, and an appropriate error will be returned.
- */
-JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent)
-{
- lockEventMutex(state);
-
- assert(state != NULL);
- assert(pEvent != NULL);
- assert(pEvent->prev == NULL);
- assert(pEvent->next == NULL);
-
- /*
- * If one or more "break"-type mods are used, register them with
- * the interpreter.
- */
- for (int i = 0; i < pEvent->modCount; i++) {
- const JdwpEventMod* pMod = &pEvent->mods[i];
- if (pMod->modKind == MK_LOCATION_ONLY) {
- /* should only be for Breakpoint, Step, and Exception */
- dvmDbgWatchLocation(&pMod->locationOnly.loc);
- } else if (pMod->modKind == MK_STEP) {
- /* should only be for EK_SINGLE_STEP; should only be one */
- JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
- JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth);
- dvmDbgConfigureStep(pMod->step.threadId, size, depth);
- } else if (pMod->modKind == MK_FIELD_ONLY) {
- /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */
- dumpEvent(pEvent); /* TODO - need for field watches */
- }
- }
-
- /*
- * Add to list.
- */
- if (state->eventList != NULL) {
- pEvent->next = state->eventList;
- state->eventList->prev = pEvent;
- }
- state->eventList = pEvent;
- state->numEvents++;
-
- unlockEventMutex(state);
-
- return ERR_NONE;
-}
-
-/*
- * Remove an event from the list. This will also remove the event from
- * any optimization tables, e.g. breakpoints.
- *
- * Does not free the JdwpEvent.
- *
- * Grab the eventLock before calling here.
- */
-static void unregisterEvent(JdwpState* state, JdwpEvent* pEvent)
-{
- if (pEvent->prev == NULL) {
- /* head of the list */
- assert(state->eventList == pEvent);
-
- state->eventList = pEvent->next;
- } else {
- pEvent->prev->next = pEvent->next;
- }
-
- if (pEvent->next != NULL) {
- pEvent->next->prev = pEvent->prev;
- pEvent->next = NULL;
- }
- pEvent->prev = NULL;
-
- /*
- * Unhook us from the interpreter, if necessary.
- */
- for (int i = 0; i < pEvent->modCount; i++) {
- JdwpEventMod* pMod = &pEvent->mods[i];
- if (pMod->modKind == MK_LOCATION_ONLY) {
- /* should only be for Breakpoint, Step, and Exception */
- dvmDbgUnwatchLocation(&pMod->locationOnly.loc);
- }
- if (pMod->modKind == MK_STEP) {
- /* should only be for EK_SINGLE_STEP; should only be one */
- dvmDbgUnconfigureStep(pMod->step.threadId);
- }
- }
-
- state->numEvents--;
- assert(state->numEvents != 0 || state->eventList == NULL);
-}
-
-/*
- * Remove the event with the given ID from the list.
- *
- * Failure to find the event isn't really an error, but it is a little
- * weird. (It looks like Eclipse will try to be extra careful and will
- * explicitly remove one-off single-step events.)
- */
-void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId)
-{
- lockEventMutex(state);
-
- JdwpEvent* pEvent = state->eventList;
- while (pEvent != NULL) {
- if (pEvent->requestId == requestId) {
- unregisterEvent(state, pEvent);
- dvmJdwpEventFree(pEvent);
- goto done; /* there can be only one with a given ID */
- }
-
- pEvent = pEvent->next;
- }
-
- //ALOGD("Odd: no match when removing event reqId=0x%04x", requestId);
-
-done:
- unlockEventMutex(state);
-}
-
-/*
- * Remove all entries from the event list.
- */
-void dvmJdwpUnregisterAll(JdwpState* state)
-{
- lockEventMutex(state);
-
- JdwpEvent* pEvent = state->eventList;
- while (pEvent != NULL) {
- JdwpEvent* pNextEvent = pEvent->next;
-
- unregisterEvent(state, pEvent);
- dvmJdwpEventFree(pEvent);
- pEvent = pNextEvent;
- }
-
- state->eventList = NULL;
-
- unlockEventMutex(state);
-}
-
-
-
-/*
- * Allocate a JdwpEvent struct with enough space to hold the specified
- * number of mod records.
- */
-JdwpEvent* dvmJdwpEventAlloc(int numMods)
-{
- JdwpEvent* newEvent;
- int allocSize = offsetof(JdwpEvent, mods) +
- numMods * sizeof(newEvent->mods[0]);
-
- newEvent = (JdwpEvent*)calloc(1, allocSize);
- return newEvent;
-}
-
-/*
- * Free a JdwpEvent.
- *
- * Do not call this until the event has been removed from the list.
- */
-void dvmJdwpEventFree(JdwpEvent* pEvent)
-{
- if (pEvent == NULL)
- return;
-
- /* make sure it was removed from the list */
- assert(pEvent->prev == NULL);
- assert(pEvent->next == NULL);
- /* want to assert state->eventList != pEvent */
-
- /*
- * Free any hairy bits in the mods.
- */
- for (int i = 0; i < pEvent->modCount; i++) {
- if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
- free(pEvent->mods[i].classMatch.classPattern);
- pEvent->mods[i].classMatch.classPattern = NULL;
- }
- if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
- free(pEvent->mods[i].classExclude.classPattern);
- pEvent->mods[i].classExclude.classPattern = NULL;
- }
- }
-
- free(pEvent);
-}
-
-/*
- * Allocate storage for matching events. To keep things simple we
- * use an array with enough storage for the entire list.
- *
- * The state->eventLock should be held before calling.
- */
-static JdwpEvent** allocMatchList(JdwpState* state)
-{
- return (JdwpEvent**) malloc(sizeof(JdwpEvent*) * state->numEvents);
-}
-
-/*
- * Run through the list and remove any entries with an expired "count" mod
- * from the event list, then free the match list.
- */
-static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList,
- int matchCount)
-{
- JdwpEvent** ppEvent = matchList;
-
- while (matchCount--) {
- JdwpEvent* pEvent = *ppEvent;
-
- for (int i = 0; i < pEvent->modCount; i++) {
- if (pEvent->mods[i].modKind == MK_COUNT &&
- pEvent->mods[i].count.count == 0)
- {
- ALOGV("##### Removing expired event");
- unregisterEvent(state, pEvent);
- dvmJdwpEventFree(pEvent);
- break;
- }
- }
-
- ppEvent++;
- }
-
- free(matchList);
-}
-
-/*
- * Match a string against a "restricted regular expression", which is just
- * a string that may start or end with '*' (e.g. "*.Foo" or "java.*").
- *
- * ("Restricted name globbing" might have been a better term.)
- */
-static bool patternMatch(const char* pattern, const char* target)
-{
- int patLen = strlen(pattern);
-
- if (pattern[0] == '*') {
- int targetLen = strlen(target);
- patLen--;
- // TODO: remove printf when we find a test case to verify this
- ALOGE(">>> comparing '%s' to '%s'",
- pattern+1, target + (targetLen-patLen));
-
- if (targetLen < patLen)
- return false;
- return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
- } else if (pattern[patLen-1] == '*') {
- return strncmp(pattern, target, patLen-1) == 0;
- } else {
- return strcmp(pattern, target) == 0;
- }
-}
-
-/*
- * See if two locations are equal.
- *
- * It's tempting to do a bitwise compare ("struct ==" or memcmp), but if
- * the storage wasn't zeroed out there could be undefined values in the
- * padding. Besides, the odds of "idx" being equal while the others aren't
- * is very small, so this is usually just a simple integer comparison.
- */
-static inline bool locationMatch(const JdwpLocation* pLoc1,
- const JdwpLocation* pLoc2)
-{
- return pLoc1->idx == pLoc2->idx &&
- pLoc1->methodId == pLoc2->methodId &&
- pLoc1->classId == pLoc2->classId &&
- pLoc1->typeTag == pLoc2->typeTag;
-}
-
-/*
- * See if the event's mods match up with the contents of "basket".
- *
- * If we find a Count mod before rejecting an event, we decrement it. We
- * need to do this even if later mods cause us to ignore the event.
- */
-static bool modsMatch(JdwpState* state, JdwpEvent* pEvent, ModBasket* basket)
-{
- JdwpEventMod* pMod = pEvent->mods;
-
- for (int i = pEvent->modCount; i > 0; i--, pMod++) {
- switch (pMod->modKind) {
- case MK_COUNT:
- assert(pMod->count.count > 0);
- pMod->count.count--;
- break;
- case MK_CONDITIONAL:
- assert(false); // should not be getting these
- break;
- case MK_THREAD_ONLY:
- if (pMod->threadOnly.threadId != basket->threadId)
- return false;
- break;
- case MK_CLASS_ONLY:
- if (!dvmDbgMatchType(basket->classId, pMod->classOnly.refTypeId))
- return false;
- break;
- case MK_CLASS_MATCH:
- if (!patternMatch(pMod->classMatch.classPattern,
- basket->className))
- return false;
- break;
- case MK_CLASS_EXCLUDE:
- if (patternMatch(pMod->classMatch.classPattern,
- basket->className))
- return false;
- break;
- case MK_LOCATION_ONLY:
- if (!locationMatch(&pMod->locationOnly.loc, basket->pLoc))
- return false;
- break;
- case MK_EXCEPTION_ONLY:
- if (pMod->exceptionOnly.refTypeId != 0 &&
- !dvmDbgMatchType(basket->excepClassId,
- pMod->exceptionOnly.refTypeId))
- return false;
- if ((basket->caught && !pMod->exceptionOnly.caught) ||
- (!basket->caught && !pMod->exceptionOnly.uncaught))
- return false;
- break;
- case MK_FIELD_ONLY:
- if (!dvmDbgMatchType(basket->classId, pMod->fieldOnly.refTypeId) ||
- pMod->fieldOnly.fieldId != basket->field)
- return false;
- break;
- case MK_STEP:
- if (pMod->step.threadId != basket->threadId)
- return false;
- break;
- case MK_INSTANCE_ONLY:
- if (pMod->instanceOnly.objectId != basket->thisPtr)
- return false;
- break;
- default:
- ALOGE("unhandled mod kind %d", pMod->modKind);
- assert(false);
- break;
- }
- }
-
- return true;
-}
-
-/*
- * Find all events of type "eventKind" with mods that match up with the
- * rest of the arguments.
- *
- * Found events are appended to "matchList", and "*pMatchCount" is advanced,
- * so this may be called multiple times for grouped events.
- *
- * DO NOT call this multiple times for the same eventKind, as Count mods are
- * decremented during the scan.
- */
-static void findMatchingEvents(JdwpState* state, JdwpEventKind eventKind,
- ModBasket* basket, JdwpEvent** matchList, int* pMatchCount)
-{
- /* start after the existing entries */
- matchList += *pMatchCount;
-
- JdwpEvent* pEvent = state->eventList;
- while (pEvent != NULL) {
- if (pEvent->eventKind == eventKind && modsMatch(state, pEvent, basket))
- {
- *matchList++ = pEvent;
- (*pMatchCount)++;
- }
-
- pEvent = pEvent->next;
- }
-}
-
-/*
- * Scan through the list of matches and determine the most severe
- * suspension policy.
- */
-static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList,
- int matchCount)
-{
- JdwpSuspendPolicy policy = SP_NONE;
-
- while (matchCount--) {
- if ((*matchList)->suspendPolicy > policy)
- policy = (*matchList)->suspendPolicy;
- matchList++;
- }
-
- return policy;
-}
-
-/*
- * Three possibilities:
- * SP_NONE - do nothing
- * SP_EVENT_THREAD - suspend ourselves
- * SP_ALL - suspend everybody except JDWP support thread
- */
-static void suspendByPolicy(JdwpState* state, JdwpSuspendPolicy suspendPolicy)
-{
- if (suspendPolicy == SP_NONE)
- return;
-
- if (suspendPolicy == SP_ALL) {
- dvmDbgSuspendVM(true);
- } else {
- assert(suspendPolicy == SP_EVENT_THREAD);
- }
-
- /* this is rare but possible -- see CLASS_PREPARE handling */
- if (dvmDbgGetThreadSelfId() == state->debugThreadId) {
- ALOGI("NOTE: suspendByPolicy not suspending JDWP thread");
- return;
- }
-
- DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
- while (true) {
- pReq->ready = true;
- dvmDbgSuspendSelf();
- pReq->ready = false;
-
- /*
- * The JDWP thread has told us (and possibly all other threads) to
- * resume. See if it has left anything in our DebugInvokeReq mailbox.
- */
- if (!pReq->invokeNeeded) {
- /*LOGD("suspendByPolicy: no invoke needed");*/
- break;
- }
-
- /* grab this before posting/suspending again */
- dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());
-
- /* leave pReq->invokeNeeded raised so we can check reentrancy */
- ALOGV("invoking method...");
- dvmDbgExecuteMethod(pReq);
-
- pReq->err = ERR_NONE;
-
- /* clear this before signaling */
- pReq->invokeNeeded = false;
-
- ALOGV("invoke complete, signaling and self-suspending");
- dvmDbgLockMutex(&pReq->lock);
- dvmDbgCondSignal(&pReq->cv);
- dvmDbgUnlockMutex(&pReq->lock);
- }
-}
-
-/*
- * Determine if there is a method invocation in progress in the current
- * thread.
- *
- * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
- * state. If set, we're in the process of invoking a method.
- */
-static bool invokeInProgress(JdwpState* state)
-{
- DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
- return pReq->invokeNeeded;
-}
-
-/*
- * We need the JDWP thread to hold off on doing stuff while we post an
- * event and then suspend ourselves.
- *
- * Call this with a threadId of zero if you just want to wait for the
- * current thread operation to complete.
- *
- * This could go to sleep waiting for another thread, so it's important
- * that the thread be marked as VMWAIT before calling here.
- */
-void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId)
-{
- bool waited = false;
-
- /* this is held for very brief periods; contention is unlikely */
- dvmDbgLockMutex(&state->eventThreadLock);
-
- /*
- * If another thread is already doing stuff, wait for it. This can
- * go to sleep indefinitely.
- */
- while (state->eventThreadId != 0) {
- ALOGV("event in progress (0x%llx), 0x%llx sleeping",
- state->eventThreadId, threadId);
- waited = true;
- dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
- }
-
- if (waited || threadId != 0)
- ALOGV("event token grabbed (0x%llx)", threadId);
- if (threadId != 0)
- state->eventThreadId = threadId;
-
- dvmDbgUnlockMutex(&state->eventThreadLock);
-}
-
-/*
- * Clear the threadId and signal anybody waiting.
- */
-void dvmJdwpClearWaitForEventThread(JdwpState* state)
-{
- /*
- * Grab the mutex. Don't try to go in/out of VMWAIT mode, as this
- * function is called by dvmSuspendSelf(), and the transition back
- * to RUNNING would confuse it.
- */
- dvmDbgLockMutex(&state->eventThreadLock);
-
- assert(state->eventThreadId != 0);
- ALOGV("cleared event token (0x%llx)", state->eventThreadId);
-
- state->eventThreadId = 0;
-
- dvmDbgCondSignal(&state->eventThreadCond);
-
- dvmDbgUnlockMutex(&state->eventThreadLock);
-}
-
-
-/*
- * Prep an event. Allocates storage for the message and leaves space for
- * the header.
- */
-static ExpandBuf* eventPrep()
-{
- ExpandBuf* pReq = expandBufAlloc();
- expandBufAddSpace(pReq, kJDWPHeaderLen);
-
- return pReq;
-}
-
-/*
- * Write the header into the buffer and send the packet off to the debugger.
- *
- * Takes ownership of "pReq" (currently discards it).
- */
-static void eventFinish(JdwpState* state, ExpandBuf* pReq)
-{
- u1* buf = expandBufGetBuffer(pReq);
-
- set4BE(buf, expandBufGetLength(pReq));
- set4BE(buf+4, dvmJdwpNextRequestSerial(state));
- set1(buf+8, 0); /* flags */
- set1(buf+9, kJdwpEventCommandSet);
- set1(buf+10, kJdwpCompositeCommand);
-
- dvmJdwpSendRequest(state, pReq);
-
- expandBufFree(pReq);
-}
-
-
-/*
- * Tell the debugger that we have finished initializing. This is always
- * sent, even if the debugger hasn't requested it.
- *
- * This should be sent "before the main thread is started and before
- * any application code has been executed". The thread ID in the message
- * must be for the main thread.
- */
-bool dvmJdwpPostVMStart(JdwpState* state, bool suspend)
-{
- JdwpSuspendPolicy suspendPolicy;
- ObjectId threadId = dvmDbgGetThreadSelfId();
-
- if (suspend)
- suspendPolicy = SP_ALL;
- else
- suspendPolicy = SP_NONE;
-
- /* probably don't need this here */
- lockEventMutex(state);
-
- ExpandBuf* pReq = NULL;
- if (true) {
- ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_START));
- ALOGV(" suspendPolicy=%s", dvmJdwpSuspendPolicyStr(suspendPolicy));
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspendPolicy);
- expandBufAdd4BE(pReq, 1);
-
- expandBufAdd1(pReq, EK_VM_START);
- expandBufAdd4BE(pReq, 0); /* requestId */
- expandBufAdd8BE(pReq, threadId);
- }
-
- unlockEventMutex(state);
-
- /* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- int oldStatus = dvmDbgThreadWaiting();
- if (suspendPolicy != SP_NONE)
- dvmJdwpSetWaitForEventThread(state, threadId);
-
- eventFinish(state, pReq);
-
- suspendByPolicy(state, suspendPolicy);
- dvmDbgThreadContinuing(oldStatus);
- }
-
- return true;
-}
-
-/*
- * A location of interest has been reached. This handles:
- * Breakpoint
- * SingleStep
- * MethodEntry
- * MethodExit
- * These four types must be grouped together in a single response. The
- * "eventFlags" indicates the type of event(s) that have happened.
- *
- * Valid mods:
- * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
- * LocationOnly (for breakpoint/step only)
- * Step (for step only)
- *
- * Interesting test cases:
- * - Put a breakpoint on a native method. Eclipse creates METHOD_ENTRY
- * and METHOD_EXIT events with a ClassOnly mod on the method's class.
- * - Use "run to line". Eclipse creates a BREAKPOINT with Count=1.
- * - Single-step to a line with a breakpoint. Should get a single
- * event message with both events in it.
- */
-bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
- ObjectId thisPtr, int eventFlags)
-{
- JdwpSuspendPolicy suspendPolicy = SP_NONE;
- ModBasket basket;
- char* nameAlloc = NULL;
-
- memset(&basket, 0, sizeof(basket));
- basket.pLoc = pLoc;
- basket.classId = pLoc->classId;
- basket.thisPtr = thisPtr;
- basket.threadId = dvmDbgGetThreadSelfId();
- basket.className = nameAlloc =
- dvmDescriptorToName(dvmDbgGetClassDescriptor(pLoc->classId));
-
- /*
- * On rare occasions we may need to execute interpreted code in the VM
- * while handling a request from the debugger. Don't fire breakpoints
- * while doing so. (I don't think we currently do this at all, so
- * this is mostly paranoia.)
- */
- if (basket.threadId == state->debugThreadId) {
- ALOGV("Ignoring location event in JDWP thread");
- free(nameAlloc);
- return false;
- }
-
- /*
- * The debugger variable display tab may invoke the interpreter to format
- * complex objects. We want to ignore breakpoints and method entry/exit
- * traps while working on behalf of the debugger.
- *
- * If we don't ignore them, the VM will get hung up, because we'll
- * suspend on a breakpoint while the debugger is still waiting for its
- * method invocation to complete.
- */
- if (invokeInProgress(state)) {
- ALOGV("Not checking breakpoints during invoke (%s)", basket.className);
- free(nameAlloc);
- return false;
- }
-
- /* don't allow the list to be updated while we scan it */
- lockEventMutex(state);
-
- JdwpEvent** matchList = allocMatchList(state);
- int matchCount = 0;
-
- if ((eventFlags & DBG_BREAKPOINT) != 0)
- findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList,
- &matchCount);
- if ((eventFlags & DBG_SINGLE_STEP) != 0)
- findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList,
- &matchCount);
- if ((eventFlags & DBG_METHOD_ENTRY) != 0)
- findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList,
- &matchCount);
- if ((eventFlags & DBG_METHOD_EXIT) != 0)
- findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList,
- &matchCount);
-
- ExpandBuf* pReq = NULL;
- if (matchCount != 0) {
- ALOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)",
- dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
- basket.className,
- dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
- basket.threadId, pLoc->idx);
-
- suspendPolicy = scanSuspendPolicy(matchList, matchCount);
- ALOGV(" suspendPolicy=%s",
- dvmJdwpSuspendPolicyStr(suspendPolicy));
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspendPolicy);
- expandBufAdd4BE(pReq, matchCount);
-
- for (int i = 0; i < matchCount; i++) {
- expandBufAdd1(pReq, matchList[i]->eventKind);
- expandBufAdd4BE(pReq, matchList[i]->requestId);
- expandBufAdd8BE(pReq, basket.threadId);
- dvmJdwpAddLocation(pReq, pLoc);
- }
- }
-
- cleanupMatchList(state, matchList, matchCount);
- unlockEventMutex(state);
-
- /* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- int oldStatus = dvmDbgThreadWaiting();
- if (suspendPolicy != SP_NONE)
- dvmJdwpSetWaitForEventThread(state, basket.threadId);
-
- eventFinish(state, pReq);
-
- suspendByPolicy(state, suspendPolicy);
- dvmDbgThreadContinuing(oldStatus);
- }
-
- free(nameAlloc);
- return matchCount != 0;
-}
-
-/*
- * A thread is starting or stopping.
- *
- * Valid mods:
- * Count, ThreadOnly
- */
-bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start)
-{
- JdwpSuspendPolicy suspendPolicy = SP_NONE;
-
- assert(threadId == dvmDbgGetThreadSelfId());
-
- /*
- * I don't think this can happen.
- */
- if (invokeInProgress(state)) {
- ALOGW("Not posting thread change during invoke");
- return false;
- }
-
- ModBasket basket;
- memset(&basket, 0, sizeof(basket));
- basket.threadId = threadId;
-
- /* don't allow the list to be updated while we scan it */
- lockEventMutex(state);
-
- JdwpEvent** matchList = allocMatchList(state);
- int matchCount = 0;
-
- if (start)
- findMatchingEvents(state, EK_THREAD_START, &basket, matchList,
- &matchCount);
- else
- findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList,
- &matchCount);
-
- ExpandBuf* pReq = NULL;
- if (matchCount != 0) {
- ALOGV("EVENT: %s(%d total) thread=%llx)",
- dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
- basket.threadId);
-
- suspendPolicy = scanSuspendPolicy(matchList, matchCount);
- ALOGV(" suspendPolicy=%s",
- dvmJdwpSuspendPolicyStr(suspendPolicy));
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspendPolicy);
- expandBufAdd4BE(pReq, matchCount);
-
- for (int i = 0; i < matchCount; i++) {
- expandBufAdd1(pReq, matchList[i]->eventKind);
- expandBufAdd4BE(pReq, matchList[i]->requestId);
- expandBufAdd8BE(pReq, basket.threadId);
- }
-
- }
-
- cleanupMatchList(state, matchList, matchCount);
- unlockEventMutex(state);
-
- /* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- int oldStatus = dvmDbgThreadWaiting();
- if (suspendPolicy != SP_NONE)
- dvmJdwpSetWaitForEventThread(state, basket.threadId);
-
- eventFinish(state, pReq);
-
- suspendByPolicy(state, suspendPolicy);
- dvmDbgThreadContinuing(oldStatus);
- }
-
- return matchCount != 0;
-}
-
-/*
- * Send a polite "VM is dying" message to the debugger.
- *
- * Skips the usual "event token" stuff.
- */
-bool dvmJdwpPostVMDeath(JdwpState* state)
-{
- ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_DEATH));
-
- ExpandBuf* pReq = eventPrep();
- expandBufAdd1(pReq, SP_NONE);
- expandBufAdd4BE(pReq, 1);
-
- expandBufAdd1(pReq, EK_VM_DEATH);
- expandBufAdd4BE(pReq, 0);
- eventFinish(state, pReq);
- return true;
-}
-
-
-/*
- * An exception has been thrown. It may or may not have been caught.
- *
- * Valid mods:
- * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
- * ExceptionOnly, InstanceOnly
- *
- * The "exceptionId" has not been added to the GC-visible object registry,
- * because there's a pretty good chance that we're not going to send it
- * up the debugger.
- */
-bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
- ObjectId exceptionId, RefTypeId exceptionClassId,
- const JdwpLocation* pCatchLoc, ObjectId thisPtr)
-{
- JdwpSuspendPolicy suspendPolicy = SP_NONE;
- ModBasket basket;
- char* nameAlloc = NULL;
-
- memset(&basket, 0, sizeof(basket));
- basket.pLoc = pThrowLoc;
- basket.classId = pThrowLoc->classId;
- basket.threadId = dvmDbgGetThreadSelfId();
- basket.className = nameAlloc =
- dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
- basket.excepClassId = exceptionClassId;
- basket.caught = (pCatchLoc->classId != 0);
- basket.thisPtr = thisPtr;
-
- /* don't try to post an exception caused by the debugger */
- if (invokeInProgress(state)) {
- ALOGV("Not posting exception hit during invoke (%s)",basket.className);
- free(nameAlloc);
- return false;
- }
-
- /* don't allow the list to be updated while we scan it */
- lockEventMutex(state);
-
- JdwpEvent** matchList = allocMatchList(state);
- int matchCount = 0;
-
- findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
-
- ExpandBuf* pReq = NULL;
- if (matchCount != 0) {
- ALOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)",
- dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
- basket.threadId, exceptionId, basket.caught);
- ALOGV(" throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
- pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
- dvmDbgGetClassDescriptor(pThrowLoc->classId),
- dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
- if (pCatchLoc->classId == 0) {
- ALOGV(" catch: (not caught)");
- } else {
- ALOGV(" catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
- pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
- dvmDbgGetClassDescriptor(pCatchLoc->classId),
- dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
- }
-
- suspendPolicy = scanSuspendPolicy(matchList, matchCount);
- ALOGV(" suspendPolicy=%s",
- dvmJdwpSuspendPolicyStr(suspendPolicy));
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspendPolicy);
- expandBufAdd4BE(pReq, matchCount);
-
- for (int i = 0; i < matchCount; i++) {
- expandBufAdd1(pReq, matchList[i]->eventKind);
- expandBufAdd4BE(pReq, matchList[i]->requestId);
- expandBufAdd8BE(pReq, basket.threadId);
-
- dvmJdwpAddLocation(pReq, pThrowLoc);
- expandBufAdd1(pReq, JT_OBJECT);
- expandBufAdd8BE(pReq, exceptionId);
- dvmJdwpAddLocation(pReq, pCatchLoc);
- }
-
- /* don't let the GC discard it */
- dvmDbgRegisterObjectId(exceptionId);
- }
-
- cleanupMatchList(state, matchList, matchCount);
- unlockEventMutex(state);
-
- /* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- int oldStatus = dvmDbgThreadWaiting();
- if (suspendPolicy != SP_NONE)
- dvmJdwpSetWaitForEventThread(state, basket.threadId);
-
- eventFinish(state, pReq);
-
- suspendByPolicy(state, suspendPolicy);
- dvmDbgThreadContinuing(oldStatus);
- }
-
- free(nameAlloc);
- return matchCount != 0;
-}
-
-/*
- * Announce that a class has been loaded.
- *
- * Valid mods:
- * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
- */
-bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
- const char* signature, int status)
-{
- JdwpSuspendPolicy suspendPolicy = SP_NONE;
- ModBasket basket;
- char* nameAlloc = NULL;
-
- memset(&basket, 0, sizeof(basket));
- basket.classId = refTypeId;
- basket.threadId = dvmDbgGetThreadSelfId();
- basket.className = nameAlloc =
- dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
-
- /* suppress class prep caused by debugger */
- if (invokeInProgress(state)) {
- ALOGV("Not posting class prep caused by invoke (%s)",basket.className);
- free(nameAlloc);
- return false;
- }
-
- /* don't allow the list to be updated while we scan it */
- lockEventMutex(state);
-
- JdwpEvent** matchList = allocMatchList(state);
- int matchCount = 0;
-
- findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
- &matchCount);
-
- ExpandBuf* pReq = NULL;
- if (matchCount != 0) {
- ALOGV("EVENT: %s(%d total) thread=%llx)",
- dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
- basket.threadId);
-
- suspendPolicy = scanSuspendPolicy(matchList, matchCount);
- ALOGV(" suspendPolicy=%s",
- dvmJdwpSuspendPolicyStr(suspendPolicy));
-
- if (basket.threadId == state->debugThreadId) {
- /*
- * JDWP says that, for a class prep in the debugger thread, we
- * should set threadId to null and if any threads were supposed
- * to be suspended then we suspend all other threads.
- */
- ALOGV(" NOTE: class prepare in debugger thread!");
- basket.threadId = 0;
- if (suspendPolicy == SP_EVENT_THREAD)
- suspendPolicy = SP_ALL;
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspendPolicy);
- expandBufAdd4BE(pReq, matchCount);
-
- for (int i = 0; i < matchCount; i++) {
- expandBufAdd1(pReq, matchList[i]->eventKind);
- expandBufAdd4BE(pReq, matchList[i]->requestId);
- expandBufAdd8BE(pReq, basket.threadId);
-
- expandBufAdd1(pReq, tag);
- expandBufAdd8BE(pReq, refTypeId);
- expandBufAddUtf8String(pReq, (const u1*) signature);
- expandBufAdd4BE(pReq, status);
- }
- }
-
- cleanupMatchList(state, matchList, matchCount);
-
- unlockEventMutex(state);
-
- /* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- int oldStatus = dvmDbgThreadWaiting();
- if (suspendPolicy != SP_NONE)
- dvmJdwpSetWaitForEventThread(state, basket.threadId);
-
- eventFinish(state, pReq);
-
- suspendByPolicy(state, suspendPolicy);
- dvmDbgThreadContinuing(oldStatus);
- }
-
- free(nameAlloc);
- return matchCount != 0;
-}
-
-/*
- * Unload a class.
- *
- * Valid mods:
- * Count, ClassMatch, ClassExclude
- */
-bool dvmJdwpPostClassUnload(JdwpState* state, RefTypeId refTypeId)
-{
- assert(false); // TODO
- return false;
-}
-
-/*
- * Get or set a field.
- *
- * Valid mods:
- * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, FieldOnly,
- * InstanceOnly
- */
-bool dvmJdwpPostFieldAccess(JdwpState* state, int STUFF, ObjectId thisPtr,
- bool modified, JValue newValue)
-{
- assert(false); // TODO
- return false;
-}
-
-/*
- * Send up a chunk of DDM data.
- *
- * While this takes the form of a JDWP "event", it doesn't interact with
- * other debugger traffic, and can't suspend the VM, so we skip all of
- * the fun event token gymnastics.
- */
-void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
- int iovcnt)
-{
- u1 header[kJDWPHeaderLen + 8];
- size_t dataLen = 0;
-
- assert(iov != NULL);
- assert(iovcnt > 0 && iovcnt < 10);
-
- /*
- * "Wrap" the contents of the iovec with a JDWP/DDMS header. We do
- * this by creating a new copy of the vector with space for the header.
- */
- struct iovec wrapiov[iovcnt+1];
- for (int i = 0; i < iovcnt; i++) {
- wrapiov[i+1].iov_base = iov[i].iov_base;
- wrapiov[i+1].iov_len = iov[i].iov_len;
- dataLen += iov[i].iov_len;
- }
-
- /* form the header (JDWP plus DDMS) */
- set4BE(header, sizeof(header) + dataLen);
- set4BE(header+4, dvmJdwpNextRequestSerial(state));
- set1(header+8, 0); /* flags */
- set1(header+9, kJDWPDdmCmdSet);
- set1(header+10, kJDWPDdmCmd);
- set4BE(header+11, type);
- set4BE(header+15, dataLen);
-
- wrapiov[0].iov_base = header;
- wrapiov[0].iov_len = sizeof(header);
-
- /*
- * Make sure we're in VMWAIT in case the write blocks.
- */
- int oldStatus = dvmDbgThreadWaiting();
- dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
- dvmDbgThreadContinuing(oldStatus);
-}
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
deleted file mode 100644
index c7d3e3c44..000000000
--- a/vm/jdwp/JdwpEvent.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Handle registration of events, and debugger event notification.
- */
-#ifndef DALVIK_JDWP_JDWPEVENT_H_
-#define DALVIK_JDWP_JDWPEVENT_H_
-
-#include "JdwpConstants.h"
-#include "ExpandBuf.h"
-
-/*
- * Event modifiers. A JdwpEvent may have zero or more of these.
- */
-union JdwpEventMod {
- u1 modKind; /* JdwpModKind */
- struct {
- u1 modKind;
- int count;
- } count;
- struct {
- u1 modKind;
- u4 exprId;
- } conditional;
- struct {
- u1 modKind;
- ObjectId threadId;
- } threadOnly;
- struct {
- u1 modKind;
- RefTypeId refTypeId;
- } classOnly;
- struct {
- u1 modKind;
- char* classPattern;
- } classMatch;
- struct {
- u1 modKind;
- char* classPattern;
- } classExclude;
- struct {
- u1 modKind;
- JdwpLocation loc;
- } locationOnly;
- struct {
- u1 modKind;
- u1 caught;
- u1 uncaught;
- RefTypeId refTypeId;
- } exceptionOnly;
- struct {
- u1 modKind;
- RefTypeId refTypeId;
- FieldId fieldId;
- } fieldOnly;
- struct {
- u1 modKind;
- ObjectId threadId;
- int size; /* JdwpStepSize */
- int depth; /* JdwpStepDepth */
- } step;
- struct {
- u1 modKind;
- ObjectId objectId;
- } instanceOnly;
-};
-
-/*
- * One of these for every registered event.
- *
- * We over-allocate the struct to hold the modifiers.
- */
-struct JdwpEvent {
- JdwpEvent* prev; /* linked list */
- JdwpEvent* next;
-
- JdwpEventKind eventKind; /* what kind of event is this? */
- JdwpSuspendPolicy suspendPolicy; /* suspend all, none, or self? */
- int modCount; /* #of entries in mods[] */
- u4 requestId; /* serial#, reported to debugger */
-
- JdwpEventMod mods[1]; /* MUST be last field in struct */
-};
-
-/*
- * Allocate an event structure with enough space.
- */
-JdwpEvent* dvmJdwpEventAlloc(int numMods);
-void dvmJdwpEventFree(JdwpEvent* pEvent);
-
-/*
- * Register an event by adding it to the event list.
- *
- * "*pEvent" must be storage allocated with jdwpEventAlloc(). The caller
- * may discard its pointer after calling this.
- */
-JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent);
-
-/*
- * Unregister an event, given the requestId.
- */
-void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId);
-
-/*
- * Unregister all events.
- */
-void dvmJdwpUnregisterAll(JdwpState* state);
-
-/*
- * Send an event, formatted into "pReq", to the debugger.
- *
- * (Messages are sent asynchronously, and do not receive a reply.)
- */
-bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq);
-
-#endif // DALVIK_JDWP_JDWPEVENT_H_
diff --git a/vm/jdwp/JdwpHandler.cpp b/vm/jdwp/JdwpHandler.cpp
deleted file mode 100644
index 112ac4a0d..000000000
--- a/vm/jdwp/JdwpHandler.cpp
+++ /dev/null
@@ -1,1973 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Handle messages from debugger.
- *
- * GENERAL NOTE: we're not currently testing the message length for
- * correctness. This is usually a bad idea, but here we can probably
- * get away with it so long as the debugger isn't broken. We can
- * change the "read" macros to use "dataLen" to avoid wandering into
- * bad territory, and have a single "is dataLen correct" check at the
- * end of each function. Not needed at this time.
- */
-#include "jdwp/JdwpPriv.h"
-#include "jdwp/JdwpHandler.h"
-#include "jdwp/JdwpEvent.h"
-#include "jdwp/JdwpConstants.h"
-#include "jdwp/ExpandBuf.h"
-
-#include "Bits.h"
-#include "Atomic.h"
-#include "DalvikVersion.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/*
- * Helper function: read a "location" from an input buffer.
- */
-static void jdwpReadLocation(const u1** pBuf, JdwpLocation* pLoc)
-{
- memset(pLoc, 0, sizeof(*pLoc)); /* allows memcmp() later */
- pLoc->typeTag = read1(pBuf);
- pLoc->classId = dvmReadObjectId(pBuf);
- pLoc->methodId = dvmReadMethodId(pBuf);
- pLoc->idx = read8BE(pBuf);
-}
-
-/*
- * Helper function: write a "location" into the reply buffer.
- */
-void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc)
-{
- expandBufAdd1(pReply, pLoc->typeTag);
- expandBufAddObjectId(pReply, pLoc->classId);
- expandBufAddMethodId(pReply, pLoc->methodId);
- expandBufAdd8BE(pReply, pLoc->idx);
-}
-
-/*
- * Helper function: read a variable-width value from the input buffer.
- */
-static u8 jdwpReadValue(const u1** pBuf, int width)
-{
- u8 value;
-
- switch (width) {
- case 1: value = read1(pBuf); break;
- case 2: value = read2BE(pBuf); break;
- case 4: value = read4BE(pBuf); break;
- case 8: value = read8BE(pBuf); break;
- default: value = (u8) -1; assert(false); break;
- }
-
- return value;
-}
-
-/*
- * Helper function: write a variable-width value into the output input buffer.
- */
-static void jdwpWriteValue(ExpandBuf* pReply, int width, u8 value)
-{
- switch (width) {
- case 1: expandBufAdd1(pReply, value); break;
- case 2: expandBufAdd2BE(pReply, value); break;
- case 4: expandBufAdd4BE(pReply, value); break;
- case 8: expandBufAdd8BE(pReply, value); break;
- default: assert(false); break;
- }
-}
-
-/*
- * Common code for *_InvokeMethod requests.
- *
- * If "isConstructor" is set, this returns "objectId" rather than the
- * expected-to-be-void return value of the called function.
- */
-static JdwpError finishInvoke(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply,
- ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
- bool isConstructor)
-{
- assert(!isConstructor || objectId != 0);
-
- u4 numArgs = read4BE(&buf);
-
- ALOGV(" --> threadId=%llx objectId=%llx", threadId, objectId);
- ALOGV(" classId=%llx methodId=%x %s.%s",
- classId, methodId,
- dvmDbgGetClassDescriptor(classId),
- dvmDbgGetMethodName(classId, methodId));
- ALOGV(" %d args:", numArgs);
-
- u8* argArray = NULL;
- if (numArgs > 0)
- argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
-
- for (u4 i = 0; i < numArgs; i++) {
- u1 typeTag = read1(&buf);
- int width = dvmDbgGetTagWidth(typeTag);
- u8 value = jdwpReadValue(&buf, width);
-
- ALOGV(" '%c'(%d): 0x%llx", typeTag, width, value);
- argArray[i] = value;
- }
-
- u4 options = read4BE(&buf); /* enum InvokeOptions bit flags */
- ALOGV(" options=0x%04x%s%s", options,
- (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
- (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
-
-
- u1 resultTag;
- u8 resultValue;
- ObjectId exceptObjId;
- JdwpError err = dvmDbgInvokeMethod(threadId, objectId, classId, methodId,
- numArgs, argArray, options,
- &resultTag, &resultValue, &exceptObjId);
- if (err != ERR_NONE)
- goto bail;
-
- if (err == ERR_NONE) {
- if (isConstructor) {
- expandBufAdd1(pReply, JT_OBJECT);
- expandBufAddObjectId(pReply, objectId);
- } else {
- int width = dvmDbgGetTagWidth(resultTag);
-
- expandBufAdd1(pReply, resultTag);
- if (width != 0)
- jdwpWriteValue(pReply, width, resultValue);
- }
- expandBufAdd1(pReply, JT_OBJECT);
- expandBufAddObjectId(pReply, exceptObjId);
-
- ALOGV(" --> returned '%c' 0x%llx (except=%08llx)",
- resultTag, resultValue, exceptObjId);
-
- /* show detailed debug output */
- if (resultTag == JT_STRING && exceptObjId == 0) {
- if (resultValue != 0) {
- char* str = dvmDbgStringToUtf8(resultValue);
- ALOGV(" string '%s'", str);
- free(str);
- } else {
- ALOGV(" string (null)");
- }
- }
- }
-
-bail:
- free(argArray);
- return err;
-}
-
-
-/*
- * Request for version info.
- */
-static JdwpError handleVM_Version(JdwpState* state, const u1* buf,
- int dataLen, ExpandBuf* pReply)
-{
- char tmpBuf[128];
-
- /* text information on VM version */
- sprintf(tmpBuf, "Android DalvikVM %d.%d.%d",
- DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
- expandBufAddUtf8String(pReply, (const u1*) tmpBuf);
- /* JDWP version numbers */
- expandBufAdd4BE(pReply, 1); // major
- expandBufAdd4BE(pReply, 5); // minor
- /* VM JRE version */
- expandBufAddUtf8String(pReply, (const u1*) "1.5.0"); /* e.g. 1.5.0_04 */
- /* target VM name */
- expandBufAddUtf8String(pReply, (const u1*) "DalvikVM");
-
- return ERR_NONE;
-}
-
-/*
- * Given a class JNI signature (e.g. "Ljava/lang/Error;"), return the
- * referenceTypeID. We need to send back more than one if the class has
- * been loaded by multiple class loaders.
- */
-static JdwpError handleVM_ClassesBySignature(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- size_t strLen;
- char* classDescriptor = readNewUtf8String(&buf, &strLen);
- ALOGV(" Req for class by signature '%s'", classDescriptor);
-
- /*
- * TODO: if a class with the same name has been loaded multiple times
- * (by different class loaders), we're supposed to return each of them.
- *
- * NOTE: this may mangle "className".
- */
- u4 numClasses;
- RefTypeId refTypeId;
- if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
- /* not currently loaded */
- ALOGV(" --> no match!");
- numClasses = 0;
- } else {
- /* just the one */
- numClasses = 1;
- }
-
- expandBufAdd4BE(pReply, numClasses);
-
- if (numClasses > 0) {
- u1 typeTag;
- u4 status;
-
- /* get class vs. interface and status flags */
- dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
-
- expandBufAdd1(pReply, typeTag);
- expandBufAddRefTypeId(pReply, refTypeId);
- expandBufAdd4BE(pReply, status);
- }
-
- free(classDescriptor);
-
- return ERR_NONE;
-}
-
-/*
- * Handle request for the thread IDs of all running threads.
- *
- * We exclude ourselves from the list, because we don't allow ourselves
- * to be suspended, and that violates some JDWP expectations.
- */
-static JdwpError handleVM_AllThreads(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId* pThreadIds;
- u4 threadCount;
- dvmDbgGetAllThreads(&pThreadIds, &threadCount);
-
- expandBufAdd4BE(pReply, threadCount);
-
- ObjectId* walker = pThreadIds;
- for (u4 i = 0; i < threadCount; i++) {
- expandBufAddObjectId(pReply, *walker++);
- }
-
- free(pThreadIds);
-
- return ERR_NONE;
-}
-
-/*
- * List all thread groups that do not have a parent.
- */
-static JdwpError handleVM_TopLevelThreadGroups(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- /*
- * TODO: maintain a list of parentless thread groups in the VM.
- *
- * For now, just return "system". Application threads are created
- * in "main", which is a child of "system".
- */
- u4 groups = 1;
- expandBufAdd4BE(pReply, groups);
- //threadGroupId = debugGetMainThreadGroup();
- //expandBufAdd8BE(pReply, threadGroupId);
- ObjectId threadGroupId = dvmDbgGetSystemThreadGroupId();
- expandBufAddObjectId(pReply, threadGroupId);
-
- return ERR_NONE;
-}
-
-/*
- * Respond with the sizes of the basic debugger types.
- *
- * All IDs are 8 bytes.
- */
-static JdwpError handleVM_IDSizes(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- expandBufAdd4BE(pReply, sizeof(FieldId));
- expandBufAdd4BE(pReply, sizeof(MethodId));
- expandBufAdd4BE(pReply, sizeof(ObjectId));
- expandBufAdd4BE(pReply, sizeof(RefTypeId));
- expandBufAdd4BE(pReply, sizeof(FrameId));
- return ERR_NONE;
-}
-
-/*
- * The debugger is politely asking to disconnect. We're good with that.
- *
- * We could resume threads and clean up pinned references, but we can do
- * that when the TCP connection drops.
- */
-static JdwpError handleVM_Dispose(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- return ERR_NONE;
-}
-
-/*
- * Suspend the execution of the application running in the VM (i.e. suspend
- * all threads).
- *
- * This needs to increment the "suspend count" on all threads.
- */
-static JdwpError handleVM_Suspend(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- dvmDbgSuspendVM(false);
- return ERR_NONE;
-}
-
-/*
- * Resume execution. Decrements the "suspend count" of all threads.
- */
-static JdwpError handleVM_Resume(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- dvmDbgResumeVM();
- return ERR_NONE;
-}
-
-/*
- * The debugger wants the entire VM to exit.
- */
-static JdwpError handleVM_Exit(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- u4 exitCode = get4BE(buf);
-
- ALOGW("Debugger is telling the VM to exit with code=%d", exitCode);
-
- dvmDbgExit(exitCode);
- return ERR_NOT_IMPLEMENTED; // shouldn't get here
-}
-
-/*
- * Create a new string in the VM and return its ID.
- *
- * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
- * string "java.util.Arrays".)
- */
-static JdwpError handleVM_CreateString(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- size_t strLen;
- char* str = readNewUtf8String(&buf, &strLen);
-
- ALOGV(" Req to create string '%s'", str);
-
- ObjectId stringId = dvmDbgCreateString(str);
- free(str);
- if (stringId == 0)
- return ERR_OUT_OF_MEMORY;
-
- expandBufAddObjectId(pReply, stringId);
- return ERR_NONE;
-}
-
-/*
- * Tell the debugger what we are capable of.
- */
-static JdwpError handleVM_Capabilities(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- expandBufAdd1(pReply, false); /* canWatchFieldModification */
- expandBufAdd1(pReply, false); /* canWatchFieldAccess */
- expandBufAdd1(pReply, false); /* canGetBytecodes */
- expandBufAdd1(pReply, true); /* canGetSyntheticAttribute */
- expandBufAdd1(pReply, false); /* canGetOwnedMonitorInfo */
- expandBufAdd1(pReply, false); /* canGetCurrentContendedMonitor */
- expandBufAdd1(pReply, false); /* canGetMonitorInfo */
- return ERR_NONE;
-}
-
-/*
- * Return classpath and bootclasspath.
- */
-static JdwpError handleVM_ClassPaths(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- char baseDir[2] = "/";
-
- /*
- * TODO: make this real. Not important for remote debugging, but
- * might be useful for local debugging.
- */
- u4 classPaths = 1;
- u4 bootClassPaths = 0;
-
- expandBufAddUtf8String(pReply, (const u1*) baseDir);
- expandBufAdd4BE(pReply, classPaths);
- for (u4 i = 0; i < classPaths; i++) {
- expandBufAddUtf8String(pReply, (const u1*) ".");
- }
-
- expandBufAdd4BE(pReply, bootClassPaths);
- for (u4 i = 0; i < classPaths; i++) {
- /* add bootclasspath components as strings */
- }
-
- return ERR_NONE;
-}
-
-/*
- * Release a list of object IDs. (Seen in jdb.)
- *
- * Currently does nothing.
- */
-static JdwpError HandleVM_DisposeObjects(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- return ERR_NONE;
-}
-
-/*
- * Tell the debugger what we are capable of.
- */
-static JdwpError handleVM_CapabilitiesNew(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- expandBufAdd1(pReply, false); /* canWatchFieldModification */
- expandBufAdd1(pReply, false); /* canWatchFieldAccess */
- expandBufAdd1(pReply, false); /* canGetBytecodes */
- expandBufAdd1(pReply, true); /* canGetSyntheticAttribute */
- expandBufAdd1(pReply, false); /* canGetOwnedMonitorInfo */
- expandBufAdd1(pReply, false); /* canGetCurrentContendedMonitor */
- expandBufAdd1(pReply, false); /* canGetMonitorInfo */
- expandBufAdd1(pReply, false); /* canRedefineClasses */
- expandBufAdd1(pReply, false); /* canAddMethod */
- expandBufAdd1(pReply, false); /* canUnrestrictedlyRedefineClasses */
- expandBufAdd1(pReply, false); /* canPopFrames */
- expandBufAdd1(pReply, false); /* canUseInstanceFilters */
- expandBufAdd1(pReply, false); /* canGetSourceDebugExtension */
- expandBufAdd1(pReply, false); /* canRequestVMDeathEvent */
- expandBufAdd1(pReply, false); /* canSetDefaultStratum */
- expandBufAdd1(pReply, false); /* 1.6: canGetInstanceInfo */
- expandBufAdd1(pReply, false); /* 1.6: canRequestMonitorEvents */
- expandBufAdd1(pReply, false); /* 1.6: canGetMonitorFrameInfo */
- expandBufAdd1(pReply, false); /* 1.6: canUseSourceNameFilters */
- expandBufAdd1(pReply, false); /* 1.6: canGetConstantPool */
- expandBufAdd1(pReply, false); /* 1.6: canForceEarlyReturn */
-
- /* fill in reserved22 through reserved32; note count started at 1 */
- for (int i = 22; i <= 32; i++)
- expandBufAdd1(pReply, false); /* reservedN */
- return ERR_NONE;
-}
-
-/*
- * Cough up the complete list of classes.
- */
-static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- u4 numClasses = 0;
- RefTypeId* classRefBuf = NULL;
-
- dvmDbgGetClassList(&numClasses, &classRefBuf);
-
- expandBufAdd4BE(pReply, numClasses);
-
- for (u4 i = 0; i < numClasses; i++) {
- static const u1 genericSignature[1] = "";
- u1 refTypeTag;
- const char* signature;
- u4 status;
-
- dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
-
- expandBufAdd1(pReply, refTypeTag);
- expandBufAddRefTypeId(pReply, classRefBuf[i]);
- expandBufAddUtf8String(pReply, (const u1*) signature);
- expandBufAddUtf8String(pReply, genericSignature);
- expandBufAdd4BE(pReply, status);
- }
-
- free(classRefBuf);
-
- return ERR_NONE;
-}
-
-/*
- * Given a referenceTypeID, return a string with the JNI reference type
- * signature (e.g. "Ljava/lang/Error;").
- */
-static JdwpError handleRT_Signature(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- ALOGV(" Req for signature of refTypeId=0x%llx", refTypeId);
- const char* signature = dvmDbgGetSignature(refTypeId);
- expandBufAddUtf8String(pReply, (const u1*) signature);
-
- return ERR_NONE;
-}
-
-/*
- * Return the modifiers (a/k/a access flags) for a reference type.
- */
-static JdwpError handleRT_Modifiers(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
- u4 modBits = dvmDbgGetAccessFlags(refTypeId);
-
- expandBufAdd4BE(pReply, modBits);
-
- return ERR_NONE;
-}
-
-/*
- * Get values from static fields in a reference type.
- */
-static JdwpError handleRT_GetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
- u4 numFields = read4BE(&buf);
-
- ALOGV(" RT_GetValues %u:", numFields);
-
- expandBufAdd4BE(pReply, numFields);
- for (u4 i = 0; i < numFields; i++) {
- FieldId fieldId = dvmReadFieldId(&buf);
- dvmDbgGetStaticFieldValue(refTypeId, fieldId, pReply);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Get the name of the source file in which a reference type was declared.
- */
-static JdwpError handleRT_SourceFile(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- const char* fileName = dvmDbgGetSourceFile(refTypeId);
- if (fileName != NULL) {
- expandBufAddUtf8String(pReply, (const u1*) fileName);
- return ERR_NONE;
- } else {
- return ERR_ABSENT_INFORMATION;
- }
-}
-
-/*
- * Return the current status of the reference type.
- */
-static JdwpError handleRT_Status(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- /* get status flags */
- u1 typeTag;
- u4 status;
- dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
- expandBufAdd4BE(pReply, status);
- return ERR_NONE;
-}
-
-/*
- * Return interfaces implemented directly by this class.
- */
-static JdwpError handleRT_Interfaces(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- ALOGV(" Req for interfaces in %llx (%s)", refTypeId,
- dvmDbgGetClassDescriptor(refTypeId));
-
- dvmDbgOutputAllInterfaces(refTypeId, pReply);
-
- return ERR_NONE;
-}
-
-/*
- * Return the class object corresponding to this type.
- */
-static JdwpError handleRT_ClassObject(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
- ObjectId classObjId = dvmDbgGetClassObject(refTypeId);
-
- ALOGV(" RefTypeId %llx -> ObjectId %llx", refTypeId, classObjId);
-
- expandBufAddObjectId(pReply, classObjId);
-
- return ERR_NONE;
-}
-
-/*
- * Returns the value of the SourceDebugExtension attribute.
- *
- * JDB seems interested, but DEX files don't currently support this.
- */
-static JdwpError handleRT_SourceDebugExtension(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- /* referenceTypeId in, string out */
- return ERR_ABSENT_INFORMATION;
-}
-
-/*
- * Like RT_Signature but with the possibility of a "generic signature".
- */
-static JdwpError handleRT_SignatureWithGeneric(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- static const u1 genericSignature[1] = "";
-
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- ALOGV(" Req for signature of refTypeId=0x%llx", refTypeId);
- const char* signature = dvmDbgGetSignature(refTypeId);
- if (signature != NULL) {
- expandBufAddUtf8String(pReply, (const u1*) signature);
- } else {
- ALOGW("No signature for refTypeId=0x%llx", refTypeId);
- expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");
- }
- expandBufAddUtf8String(pReply, genericSignature);
-
- return ERR_NONE;
-}
-
-/*
- * Return the instance of java.lang.ClassLoader that loaded the specified
- * reference type, or null if it was loaded by the system loader.
- */
-static JdwpError handleRT_ClassLoader(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- expandBufAddObjectId(pReply, dvmDbgGetClassLoader(refTypeId));
-
- return ERR_NONE;
-}
-
-/*
- * Given a referenceTypeId, return a block of stuff that describes the
- * fields declared by a class.
- */
-static JdwpError handleRT_FieldsWithGeneric(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
- ALOGV(" Req for fields in refTypeId=0x%llx", refTypeId);
- ALOGV(" --> '%s'", dvmDbgGetSignature(refTypeId));
-
- dvmDbgOutputAllFields(refTypeId, true, pReply);
-
- return ERR_NONE;
-}
-
-/*
- * Given a referenceTypeID, return a block of goodies describing the
- * methods declared by a class.
- */
-static JdwpError handleRT_MethodsWithGeneric(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
-
- ALOGV(" Req for methods in refTypeId=0x%llx", refTypeId);
- ALOGV(" --> '%s'", dvmDbgGetSignature(refTypeId));
-
- dvmDbgOutputAllMethods(refTypeId, true, pReply);
-
- return ERR_NONE;
-}
-
-/*
- * Return the immediate superclass of a class.
- */
-static JdwpError handleCT_Superclass(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classId = dvmReadRefTypeId(&buf);
-
- RefTypeId superClassId = dvmDbgGetSuperclass(classId);
-
- expandBufAddRefTypeId(pReply, superClassId);
-
- return ERR_NONE;
-}
-
-/*
- * Set static class values.
- */
-static JdwpError handleCT_SetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classId = dvmReadRefTypeId(&buf);
- u4 values = read4BE(&buf);
-
- ALOGV(" Req to set %d values in classId=%llx", values, classId);
-
- for (u4 i = 0; i < values; i++) {
- FieldId fieldId = dvmReadFieldId(&buf);
- u1 fieldTag = dvmDbgGetStaticFieldBasicTag(classId, fieldId);
- int width = dvmDbgGetTagWidth(fieldTag);
- u8 value = jdwpReadValue(&buf, width);
-
- ALOGV(" --> field=%x tag=%c -> %lld", fieldId, fieldTag, value);
- dvmDbgSetStaticFieldValue(classId, fieldId, value, width);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Invoke a static method.
- *
- * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
- * values in the "variables" display.
- */
-static JdwpError handleCT_InvokeMethod(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classId = dvmReadRefTypeId(&buf);
- ObjectId threadId = dvmReadObjectId(&buf);
- MethodId methodId = dvmReadMethodId(&buf);
-
- return finishInvoke(state, buf, dataLen, pReply,
- threadId, 0, classId, methodId, false);
-}
-
-/*
- * Create a new object of the requested type, and invoke the specified
- * constructor.
- *
- * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
- * see the contents of a byte[] as a string.
- */
-static JdwpError handleCT_NewInstance(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classId = dvmReadRefTypeId(&buf);
- ObjectId threadId = dvmReadObjectId(&buf);
- MethodId methodId = dvmReadMethodId(&buf);
-
- ALOGV("Creating instance of %s", dvmDbgGetClassDescriptor(classId));
- ObjectId objectId = dvmDbgCreateObject(classId);
- if (objectId == 0)
- return ERR_OUT_OF_MEMORY;
-
- return finishInvoke(state, buf, dataLen, pReply,
- threadId, objectId, classId, methodId, true);
-}
-
-/*
- * Create a new array object of the requested type and length.
- */
-static JdwpError handleAT_newInstance(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId arrayTypeId = dvmReadRefTypeId(&buf);
- u4 length = read4BE(&buf);
-
- ALOGV("Creating array %s[%u]",
- dvmDbgGetClassDescriptor(arrayTypeId), length);
- ObjectId objectId = dvmDbgCreateArrayObject(arrayTypeId, length);
- if (objectId == 0)
- return ERR_OUT_OF_MEMORY;
-
- expandBufAdd1(pReply, JT_ARRAY);
- expandBufAddObjectId(pReply, objectId);
- return ERR_NONE;
-}
-
-/*
- * Return line number information for the method, if present.
- */
-static JdwpError handleM_LineTable(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId refTypeId = dvmReadRefTypeId(&buf);
- MethodId methodId = dvmReadMethodId(&buf);
-
- ALOGV(" Req for line table in %s.%s",
- dvmDbgGetClassDescriptor(refTypeId),
- dvmDbgGetMethodName(refTypeId,methodId));
-
- dvmDbgOutputLineTable(refTypeId, methodId, pReply);
-
- return ERR_NONE;
-}
-
-/*
- * Pull out the LocalVariableTable goodies.
- */
-static JdwpError handleM_VariableTableWithGeneric(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classId = dvmReadRefTypeId(&buf);
- MethodId methodId = dvmReadMethodId(&buf);
-
- ALOGV(" Req for LocalVarTab in class=%s method=%s",
- dvmDbgGetClassDescriptor(classId),
- dvmDbgGetMethodName(classId, methodId));
-
- /*
- * We could return ERR_ABSENT_INFORMATION here if the DEX file was
- * built without local variable information. That will cause Eclipse
- * to make a best-effort attempt at displaying local variables
- * anonymously. However, the attempt isn't very good, so we're probably
- * better off just not showing anything.
- */
- dvmDbgOutputVariableTable(classId, methodId, true, pReply);
- return ERR_NONE;
-}
-
-/*
- * Given an object reference, return the runtime type of the object
- * (class or array).
- *
- * This can get called on different things, e.g. threadId gets
- * passed in here.
- */
-static JdwpError handleOR_ReferenceType(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId objectId = dvmReadObjectId(&buf);
- ALOGV(" Req for type of objectId=0x%llx", objectId);
-
- u1 refTypeTag;
- RefTypeId typeId;
- dvmDbgGetObjectType(objectId, &refTypeTag, &typeId);
-
- expandBufAdd1(pReply, refTypeTag);
- expandBufAddRefTypeId(pReply, typeId);
-
- return ERR_NONE;
-}
-
-/*
- * Get values from the fields of an object.
- */
-static JdwpError handleOR_GetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId objectId = dvmReadObjectId(&buf);
- u4 numFields = read4BE(&buf);
-
- ALOGV(" Req for %d fields from objectId=0x%llx", numFields, objectId);
-
- expandBufAdd4BE(pReply, numFields);
-
- for (u4 i = 0; i < numFields; i++) {
- FieldId fieldId = dvmReadFieldId(&buf);
- dvmDbgGetFieldValue(objectId, fieldId, pReply);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Set values in the fields of an object.
- */
-static JdwpError handleOR_SetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId objectId = dvmReadObjectId(&buf);
- u4 numFields = read4BE(&buf);
-
- ALOGV(" Req to set %d fields in objectId=0x%llx", numFields, objectId);
-
- for (u4 i = 0; i < numFields; i++) {
- FieldId fieldId = dvmReadFieldId(&buf);
-
- u1 fieldTag = dvmDbgGetFieldBasicTag(objectId, fieldId);
- int width = dvmDbgGetTagWidth(fieldTag);
- u8 value = jdwpReadValue(&buf, width);
-
- ALOGV(" --> fieldId=%x tag='%c'(%d) value=%lld",
- fieldId, fieldTag, width, value);
-
- dvmDbgSetFieldValue(objectId, fieldId, value, width);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Invoke an instance method. The invocation must occur in the specified
- * thread, which must have been suspended by an event.
- *
- * The call is synchronous. All threads in the VM are resumed, unless the
- * SINGLE_THREADED flag is set.
- *
- * If you ask Eclipse to "inspect" an object (or ask JDB to "print" an
- * object), it will try to invoke the object's toString() function. This
- * feature becomes crucial when examining ArrayLists with Eclipse.
- */
-static JdwpError handleOR_InvokeMethod(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId objectId = dvmReadObjectId(&buf);
- ObjectId threadId = dvmReadObjectId(&buf);
- RefTypeId classId = dvmReadRefTypeId(&buf);
- MethodId methodId = dvmReadMethodId(&buf);
-
- return finishInvoke(state, buf, dataLen, pReply,
- threadId, objectId, classId, methodId, false);
-}
-
-/*
- * Disable garbage collection of the specified object.
- */
-static JdwpError handleOR_DisableCollection(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- // this is currently a no-op
- return ERR_NONE;
-}
-
-/*
- * Enable garbage collection of the specified object.
- */
-static JdwpError handleOR_EnableCollection(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- // this is currently a no-op
- return ERR_NONE;
-}
-
-/*
- * Determine whether an object has been garbage collected.
- */
-static JdwpError handleOR_IsCollected(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId objectId;
-
- objectId = dvmReadObjectId(&buf);
- ALOGV(" Req IsCollected(0x%llx)", objectId);
-
- // TODO: currently returning false; must integrate with GC
- expandBufAdd1(pReply, 0);
-
- return ERR_NONE;
-}
-
-/*
- * Return the string value in a string object.
- */
-static JdwpError handleSR_Value(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId stringObject = dvmReadObjectId(&buf);
- char* str = dvmDbgStringToUtf8(stringObject);
-
- ALOGV(" Req for str %llx --> '%s'", stringObject, str);
-
- expandBufAddUtf8String(pReply, (u1*) str);
- free(str);
-
- return ERR_NONE;
-}
-
-/*
- * Return a thread's name.
- */
-static JdwpError handleTR_Name(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- ALOGV(" Req for name of thread 0x%llx", threadId);
- char* name = dvmDbgGetThreadName(threadId);
- if (name == NULL)
- return ERR_INVALID_THREAD;
-
- expandBufAddUtf8String(pReply, (u1*) name);
- free(name);
-
- return ERR_NONE;
-}
-
-/*
- * Suspend the specified thread.
- *
- * It's supposed to remain suspended even if interpreted code wants to
- * resume it; only the JDI is allowed to resume it.
- */
-static JdwpError handleTR_Suspend(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- if (threadId == dvmDbgGetThreadSelfId()) {
- ALOGI(" Warning: ignoring request to suspend self");
- return ERR_THREAD_NOT_SUSPENDED;
- }
- ALOGV(" Req to suspend thread 0x%llx", threadId);
-
- dvmDbgSuspendThread(threadId);
-
- return ERR_NONE;
-}
-
-/*
- * Resume the specified thread.
- */
-static JdwpError handleTR_Resume(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- if (threadId == dvmDbgGetThreadSelfId()) {
- ALOGI(" Warning: ignoring request to resume self");
- return ERR_NONE;
- }
- ALOGV(" Req to resume thread 0x%llx", threadId);
-
- dvmDbgResumeThread(threadId);
-
- return ERR_NONE;
-}
-
-/*
- * Return status of specified thread.
- */
-static JdwpError handleTR_Status(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- ALOGV(" Req for status of thread 0x%llx", threadId);
-
- u4 threadStatus;
- u4 suspendStatus;
- if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
- return ERR_INVALID_THREAD;
-
- ALOGV(" --> %s, %s",
- dvmJdwpThreadStatusStr((JdwpThreadStatus) threadStatus),
- dvmJdwpSuspendStatusStr((JdwpSuspendStatus) suspendStatus));
-
- expandBufAdd4BE(pReply, threadStatus);
- expandBufAdd4BE(pReply, suspendStatus);
-
- return ERR_NONE;
-}
-
-/*
- * Return the thread group that the specified thread is a member of.
- */
-static JdwpError handleTR_ThreadGroup(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- /* currently not handling these */
- ObjectId threadGroupId = dvmDbgGetThreadGroup(threadId);
- expandBufAddObjectId(pReply, threadGroupId);
-
- return ERR_NONE;
-}
-
-/*
- * Return the current call stack of a suspended thread.
- *
- * If the thread isn't suspended, the error code isn't defined, but should
- * be THREAD_NOT_SUSPENDED.
- */
-static JdwpError handleTR_Frames(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
- u4 startFrame = read4BE(&buf);
- u4 length = read4BE(&buf);
-
- if (!dvmDbgThreadExists(threadId))
- return ERR_INVALID_THREAD;
- if (!dvmDbgIsSuspended(threadId)) {
- ALOGV(" Rejecting req for frames in running thread '%s' (%llx)",
- dvmDbgGetThreadName(threadId), threadId);
- return ERR_THREAD_NOT_SUSPENDED;
- }
-
- int frameCount = dvmDbgGetThreadFrameCount(threadId);
-
- ALOGV(" Request for frames: threadId=%llx start=%d length=%d [count=%d]",
- threadId, startFrame, length, frameCount);
- if (frameCount <= 0)
- return ERR_THREAD_NOT_SUSPENDED; /* == 0 means 100% native */
-
- if (length == (u4) -1)
- length = frameCount;
- assert((int) startFrame >= 0 && (int) startFrame < frameCount);
- assert((int) (startFrame + length) <= frameCount);
-
- u4 frames = length;
- expandBufAdd4BE(pReply, frames);
- for (u4 i = startFrame; i < (startFrame+length); i++) {
- FrameId frameId;
- JdwpLocation loc;
-
- dvmDbgGetThreadFrame(threadId, i, &frameId, &loc);
-
- expandBufAdd8BE(pReply, frameId);
- dvmJdwpAddLocation(pReply, &loc);
-
- LOGVV(" Frame %d: id=%llx loc={type=%d cls=%llx mth=%x loc=%llx}",
- i, frameId, loc.typeTag, loc.classId, loc.methodId, loc.idx);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Returns the #of frames on the specified thread, which must be suspended.
- */
-static JdwpError handleTR_FrameCount(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- if (!dvmDbgThreadExists(threadId))
- return ERR_INVALID_THREAD;
- if (!dvmDbgIsSuspended(threadId)) {
- ALOGV(" Rejecting req for frames in running thread '%s' (%llx)",
- dvmDbgGetThreadName(threadId), threadId);
- return ERR_THREAD_NOT_SUSPENDED;
- }
-
- int frameCount = dvmDbgGetThreadFrameCount(threadId);
- if (frameCount < 0)
- return ERR_INVALID_THREAD;
- expandBufAdd4BE(pReply, (u4)frameCount);
-
- return ERR_NONE;
-}
-
-/*
- * Get the monitor that the thread is waiting on.
- */
-static JdwpError handleTR_CurrentContendedMonitor(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId;
-
- threadId = dvmReadObjectId(&buf);
-
- // TODO: create an Object to represent the monitor (we're currently
- // just using a raw Monitor struct in the VM)
-
- return ERR_NOT_IMPLEMENTED;
-}
-
-/*
- * Return the suspend count for the specified thread.
- *
- * (The thread *might* still be running -- it might not have examined
- * its suspend count recently.)
- */
-static JdwpError handleTR_SuspendCount(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
-
- u4 suspendCount = dvmDbgGetThreadSuspendCount(threadId);
- expandBufAdd4BE(pReply, suspendCount);
-
- return ERR_NONE;
-}
-
-/*
- * Return the name of a thread group.
- *
- * The Eclipse debugger recognizes "main" and "system" as special.
- */
-static JdwpError handleTGR_Name(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadGroupId = dvmReadObjectId(&buf);
- ALOGV(" Req for name of threadGroupId=0x%llx", threadGroupId);
-
- char* name = dvmDbgGetThreadGroupName(threadGroupId);
- if (name != NULL)
- expandBufAddUtf8String(pReply, (u1*) name);
- else {
- expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
- ALOGW("bad thread group ID");
- }
-
- free(name);
-
- return ERR_NONE;
-}
-
-/*
- * Returns the thread group -- if any -- that contains the specified
- * thread group.
- */
-static JdwpError handleTGR_Parent(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId groupId = dvmReadObjectId(&buf);
-
- ObjectId parentGroup = dvmDbgGetThreadGroupParent(groupId);
- expandBufAddObjectId(pReply, parentGroup);
-
- return ERR_NONE;
-}
-
-/*
- * Return the active threads and thread groups that are part of the
- * specified thread group.
- */
-static JdwpError handleTGR_Children(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadGroupId = dvmReadObjectId(&buf);
- ALOGV(" Req for threads in threadGroupId=0x%llx", threadGroupId);
-
- ObjectId* pThreadIds;
- u4 threadCount;
- dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
-
- expandBufAdd4BE(pReply, threadCount);
-
- for (u4 i = 0; i < threadCount; i++)
- expandBufAddObjectId(pReply, pThreadIds[i]);
- free(pThreadIds);
-
- /*
- * TODO: finish support for child groups
- *
- * For now, just show that "main" is a child of "system".
- */
- if (threadGroupId == dvmDbgGetSystemThreadGroupId()) {
- expandBufAdd4BE(pReply, 1);
- expandBufAddObjectId(pReply, dvmDbgGetMainThreadGroupId());
- } else {
- expandBufAdd4BE(pReply, 0);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Return the #of components in the array.
- */
-static JdwpError handleAR_Length(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId arrayId = dvmReadObjectId(&buf);
- ALOGV(" Req for length of array 0x%llx", arrayId);
-
- u4 arrayLength = dvmDbgGetArrayLength(arrayId);
-
- ALOGV(" --> %d", arrayLength);
-
- expandBufAdd4BE(pReply, arrayLength);
-
- return ERR_NONE;
-}
-
-/*
- * Return the values from an array.
- */
-static JdwpError handleAR_GetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId arrayId = dvmReadObjectId(&buf);
- u4 firstIndex = read4BE(&buf);
- u4 length = read4BE(&buf);
-
- u1 tag = dvmDbgGetArrayElementTag(arrayId);
- ALOGV(" Req for array values 0x%llx first=%d len=%d (elem tag=%c)",
- arrayId, firstIndex, length, tag);
-
- expandBufAdd1(pReply, tag);
- expandBufAdd4BE(pReply, length);
-
- if (!dvmDbgOutputArray(arrayId, firstIndex, length, pReply))
- return ERR_INVALID_LENGTH;
-
- return ERR_NONE;
-}
-
-/*
- * Set values in an array.
- */
-static JdwpError handleAR_SetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId arrayId = dvmReadObjectId(&buf);
- u4 firstIndex = read4BE(&buf);
- u4 values = read4BE(&buf);
-
- ALOGV(" Req to set array values 0x%llx first=%d count=%d",
- arrayId, firstIndex, values);
-
- if (!dvmDbgSetArrayElements(arrayId, firstIndex, values, buf))
- return ERR_INVALID_LENGTH;
-
- return ERR_NONE;
-}
-
-/*
- * Return the set of classes visible to a class loader. All classes which
- * have the class loader as a defining or initiating loader are returned.
- */
-static JdwpError handleCLR_VisibleClasses(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId classLoaderObject;
- u4 numClasses = 0;
- RefTypeId* classRefBuf = NULL;
- int i;
-
- classLoaderObject = dvmReadObjectId(&buf);
-
- dvmDbgGetVisibleClassList(classLoaderObject, &numClasses, &classRefBuf);
-
- expandBufAdd4BE(pReply, numClasses);
- for (i = 0; i < (int) numClasses; i++) {
- u1 refTypeTag;
-
- refTypeTag = dvmDbgGetClassObjectType(classRefBuf[i]);
-
- expandBufAdd1(pReply, refTypeTag);
- expandBufAddRefTypeId(pReply, classRefBuf[i]);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Set an event trigger.
- *
- * Reply with a requestID.
- */
-static JdwpError handleER_Set(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- const u1* origBuf = buf;
-
- u1 eventKind = read1(&buf);
- u1 suspendPolicy = read1(&buf);
- u4 modifierCount = read4BE(&buf);
-
- LOGVV(" Set(kind=%s(%u) suspend=%s(%u) mods=%u)",
- dvmJdwpEventKindStr(eventKind), eventKind,
- dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
- modifierCount);
-
- assert(modifierCount < 256); /* reasonableness check */
-
- JdwpEvent* pEvent = dvmJdwpEventAlloc(modifierCount);
- pEvent->eventKind = static_cast<JdwpEventKind>(eventKind);
- pEvent->suspendPolicy = static_cast<JdwpSuspendPolicy>(suspendPolicy);
- pEvent->modCount = modifierCount;
-
- /*
- * Read modifiers. Ordering may be significant (see explanation of Count
- * mods in JDWP doc).
- */
- for (u4 idx = 0; idx < modifierCount; idx++) {
- u1 modKind = read1(&buf);
-
- pEvent->mods[idx].modKind = modKind;
-
- switch (modKind) {
- case MK_COUNT: /* report once, when "--count" reaches 0 */
- {
- u4 count = read4BE(&buf);
- LOGVV(" Count: %u", count);
- if (count == 0)
- return ERR_INVALID_COUNT;
- pEvent->mods[idx].count.count = count;
- }
- break;
- case MK_CONDITIONAL: /* conditional on expression) */
- {
- u4 exprId = read4BE(&buf);
- LOGVV(" Conditional: %d", exprId);
- pEvent->mods[idx].conditional.exprId = exprId;
- }
- break;
- case MK_THREAD_ONLY: /* only report events in specified thread */
- {
- ObjectId threadId = dvmReadObjectId(&buf);
- LOGVV(" ThreadOnly: %llx", threadId);
- pEvent->mods[idx].threadOnly.threadId = threadId;
- }
- break;
- case MK_CLASS_ONLY: /* for ClassPrepare, MethodEntry */
- {
- RefTypeId clazzId = dvmReadRefTypeId(&buf);
- LOGVV(" ClassOnly: %llx (%s)",
- clazzId, dvmDbgGetClassDescriptor(clazzId));
- pEvent->mods[idx].classOnly.refTypeId = clazzId;
- }
- break;
- case MK_CLASS_MATCH: /* restrict events to matching classes */
- {
- char* pattern;
- size_t strLen;
-
- pattern = readNewUtf8String(&buf, &strLen);
- LOGVV(" ClassMatch: '%s'", pattern);
- /* pattern is "java.foo.*", we want "java/foo/ *" */
- pEvent->mods[idx].classMatch.classPattern =
- dvmDotToSlash(pattern);
- free(pattern);
- }
- break;
- case MK_CLASS_EXCLUDE: /* restrict events to non-matching classes */
- {
- char* pattern;
- size_t strLen;
-
- pattern = readNewUtf8String(&buf, &strLen);
- LOGVV(" ClassExclude: '%s'", pattern);
- pEvent->mods[idx].classExclude.classPattern =
- dvmDotToSlash(pattern);
- free(pattern);
- }
- break;
- case MK_LOCATION_ONLY: /* restrict certain events based on loc */
- {
- JdwpLocation loc;
-
- jdwpReadLocation(&buf, &loc);
- LOGVV(" LocationOnly: typeTag=%d classId=%llx methodId=%x idx=%llx",
- loc.typeTag, loc.classId, loc.methodId, loc.idx);
- pEvent->mods[idx].locationOnly.loc = loc;
- }
- break;
- case MK_EXCEPTION_ONLY: /* modifies EK_EXCEPTION events */
- {
- RefTypeId exceptionOrNull; /* null == all exceptions */
- u1 caught, uncaught;
-
- exceptionOrNull = dvmReadRefTypeId(&buf);
- caught = read1(&buf);
- uncaught = read1(&buf);
- LOGVV(" ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
- exceptionOrNull, (exceptionOrNull == 0) ? "null"
- : dvmDbgGetClassDescriptor(exceptionOrNull),
- caught, uncaught);
-
- pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
- pEvent->mods[idx].exceptionOnly.caught = caught;
- pEvent->mods[idx].exceptionOnly.uncaught = uncaught;
- }
- break;
- case MK_FIELD_ONLY: /* for field access/mod events */
- {
- RefTypeId declaring = dvmReadRefTypeId(&buf);
- FieldId fieldId = dvmReadFieldId(&buf);
- LOGVV(" FieldOnly: %llx %x", declaring, fieldId);
- pEvent->mods[idx].fieldOnly.refTypeId = declaring;
- pEvent->mods[idx].fieldOnly.fieldId = fieldId;
- }
- break;
- case MK_STEP: /* for use with EK_SINGLE_STEP */
- {
- ObjectId threadId;
- u4 size, depth;
-
- threadId = dvmReadObjectId(&buf);
- size = read4BE(&buf);
- depth = read4BE(&buf);
- LOGVV(" Step: thread=%llx size=%s depth=%s",
- threadId, dvmJdwpStepSizeStr(size),
- dvmJdwpStepDepthStr(depth));
-
- pEvent->mods[idx].step.threadId = threadId;
- pEvent->mods[idx].step.size = size;
- pEvent->mods[idx].step.depth = depth;
- }
- break;
- case MK_INSTANCE_ONLY: /* report events related to a specific obj */
- {
- ObjectId instance = dvmReadObjectId(&buf);
- LOGVV(" InstanceOnly: %llx", instance);
- pEvent->mods[idx].instanceOnly.objectId = instance;
- }
- break;
- default:
- ALOGW("GLITCH: unsupported modKind=%d", modKind);
- break;
- }
- }
-
- /*
- * Make sure we consumed all data. It is possible that the remote side
- * has sent us bad stuff, but for now we blame ourselves.
- */
- if (buf != origBuf + dataLen) {
- ALOGW("GLITCH: dataLen is %d, we have consumed %d", dataLen,
- (int) (buf - origBuf));
- }
-
- /*
- * We reply with an integer "requestID".
- */
- u4 requestId = dvmJdwpNextEventSerial(state);
- expandBufAdd4BE(pReply, requestId);
-
- pEvent->requestId = requestId;
-
- ALOGV(" --> event requestId=%#x", requestId);
-
- /* add it to the list */
- JdwpError err = dvmJdwpRegisterEvent(state, pEvent);
- if (err != ERR_NONE) {
- /* registration failed, probably because event is bogus */
- dvmJdwpEventFree(pEvent);
- ALOGW("WARNING: event request rejected");
- }
- return err;
-}
-
-/*
- * Clear an event. Failure to find an event with a matching ID is a no-op
- * and does not return an error.
- */
-static JdwpError handleER_Clear(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- u1 eventKind;
- eventKind = read1(&buf);
- u4 requestId = read4BE(&buf);
-
- ALOGV(" Req to clear eventKind=%d requestId=%#x", eventKind, requestId);
-
- dvmJdwpUnregisterEventById(state, requestId);
-
- return ERR_NONE;
-}
-
-/*
- * Return the values of arguments and local variables.
- */
-static JdwpError handleSF_GetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
- FrameId frameId = dvmReadFrameId(&buf);
- u4 slots = read4BE(&buf);
-
- ALOGV(" Req for %d slots in threadId=%llx frameId=%llx",
- slots, threadId, frameId);
-
- expandBufAdd4BE(pReply, slots); /* "int values" */
- for (u4 i = 0; i < slots; i++) {
- u4 slot = read4BE(&buf);
- u1 reqSigByte = read1(&buf);
-
- ALOGV(" --> slot %d '%c'", slot, reqSigByte);
-
- int width = dvmDbgGetTagWidth(reqSigByte);
- u1* ptr = expandBufAddSpace(pReply, width+1);
- dvmDbgGetLocalValue(threadId, frameId, slot, reqSigByte, ptr, width);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Set the values of arguments and local variables.
- */
-static JdwpError handleSF_SetValues(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
- FrameId frameId = dvmReadFrameId(&buf);
- u4 slots = read4BE(&buf);
-
- ALOGV(" Req to set %d slots in threadId=%llx frameId=%llx",
- slots, threadId, frameId);
-
- for (u4 i = 0; i < slots; i++) {
- u4 slot = read4BE(&buf);
- u1 sigByte = read1(&buf);
- int width = dvmDbgGetTagWidth(sigByte);
- u8 value = jdwpReadValue(&buf, width);
-
- ALOGV(" --> slot %d '%c' %llx", slot, sigByte, value);
- dvmDbgSetLocalValue(threadId, frameId, slot, sigByte, value, width);
- }
-
- return ERR_NONE;
-}
-
-/*
- * Returns the value of "this" for the specified frame.
- */
-static JdwpError handleSF_ThisObject(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- ObjectId threadId = dvmReadObjectId(&buf);
- FrameId frameId = dvmReadFrameId(&buf);
-
- ObjectId objectId;
- if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
- return ERR_INVALID_FRAMEID;
-
- u1 objectTag = dvmDbgGetObjectTag(objectId);
- ALOGV(" Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'",
- threadId, frameId, objectId, dvmDbgGetObjectTypeName(objectId),
- (char)objectTag);
-
- expandBufAdd1(pReply, objectTag);
- expandBufAddObjectId(pReply, objectId);
-
- return ERR_NONE;
-}
-
-/*
- * Return the reference type reflected by this class object.
- *
- * This appears to be required because ReferenceTypeId values are NEVER
- * reused, whereas ClassIds can be recycled like any other object. (Either
- * that, or I have no idea what this is for.)
- */
-static JdwpError handleCOR_ReflectedType(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- RefTypeId classObjectId = dvmReadRefTypeId(&buf);
-
- ALOGV(" Req for refTypeId for class=%llx (%s)",
- classObjectId, dvmDbgGetClassDescriptor(classObjectId));
-
- /* just hand the type back to them */
- if (dvmDbgIsInterface(classObjectId))
- expandBufAdd1(pReply, TT_INTERFACE);
- else
- expandBufAdd1(pReply, TT_CLASS);
- expandBufAddRefTypeId(pReply, classObjectId);
-
- return ERR_NONE;
-}
-
-/*
- * Handle a DDM packet with a single chunk in it.
- */
-static JdwpError handleDDM_Chunk(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- u1* replyBuf = NULL;
- int replyLen = -1;
-
- ALOGV(" Handling DDM packet (%.4s)", buf);
-
- /*
- * On first DDM packet, notify all handlers that DDM is running.
- */
- if (!state->ddmActive) {
- state->ddmActive = true;
- dvmDbgDdmConnected();
- }
-
- /*
- * If they want to send something back, we copy it into the buffer.
- * A no-copy approach would be nicer.
- *
- * TODO: consider altering the JDWP stuff to hold the packet header
- * in a separate buffer. That would allow us to writev() DDM traffic
- * instead of copying it into the expanding buffer. The reduction in
- * heap requirements is probably more valuable than the efficiency.
- */
- if (dvmDbgDdmHandlePacket(buf, dataLen, &replyBuf, &replyLen)) {
- memcpy(expandBufAddSpace(pReply, replyLen), replyBuf, replyLen);
- free(replyBuf);
- }
- return ERR_NONE;
-}
-
-/*
- * Handler map decl.
- */
-typedef JdwpError (*JdwpRequestHandler)(JdwpState* state,
- const u1* buf, int dataLen, ExpandBuf* reply);
-
-struct JdwpHandlerMap {
- u1 cmdSet;
- u1 cmd;
- JdwpRequestHandler func;
- const char* descr;
-};
-
-/*
- * Map commands to functions.
- *
- * 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, handleVM_Version, "VirtualMachine.Version" },
- { 1, 2, handleVM_ClassesBySignature,
- "VirtualMachine.ClassesBySignature" },
- //1, 3, VirtualMachine.AllClasses
- { 1, 4, handleVM_AllThreads, "VirtualMachine.AllThreads" },
- { 1, 5, handleVM_TopLevelThreadGroups,
- "VirtualMachine.TopLevelThreadGroups" },
- { 1, 6, handleVM_Dispose, "VirtualMachine.Dispose" },
- { 1, 7, handleVM_IDSizes, "VirtualMachine.IDSizes" },
- { 1, 8, handleVM_Suspend, "VirtualMachine.Suspend" },
- { 1, 9, handleVM_Resume, "VirtualMachine.Resume" },
- { 1, 10, handleVM_Exit, "VirtualMachine.Exit" },
- { 1, 11, handleVM_CreateString, "VirtualMachine.CreateString" },
- { 1, 12, handleVM_Capabilities, "VirtualMachine.Capabilities" },
- { 1, 13, handleVM_ClassPaths, "VirtualMachine.ClassPaths" },
- { 1, 14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
- //1, 15, HoldEvents
- //1, 16, ReleaseEvents
- { 1, 17, handleVM_CapabilitiesNew,
- "VirtualMachine.CapabilitiesNew" },
- //1, 18, RedefineClasses
- //1, 19, SetDefaultStratum
- { 1, 20, handleVM_AllClassesWithGeneric,
- "VirtualMachine.AllClassesWithGeneric"},
- //1, 21, InstanceCounts
-
- /* ReferenceType command set (2) */
- { 2, 1, handleRT_Signature, "ReferenceType.Signature" },
- { 2, 2, handleRT_ClassLoader, "ReferenceType.ClassLoader" },
- { 2, 3, handleRT_Modifiers, "ReferenceType.Modifiers" },
- //2, 4, Fields
- //2, 5, Methods
- { 2, 6, handleRT_GetValues, "ReferenceType.GetValues" },
- { 2, 7, handleRT_SourceFile, "ReferenceType.SourceFile" },
- //2, 8, NestedTypes
- { 2, 9, handleRT_Status, "ReferenceType.Status" },
- { 2, 10, handleRT_Interfaces, "ReferenceType.Interfaces" },
- { 2, 11, handleRT_ClassObject, "ReferenceType.ClassObject" },
- { 2, 12, handleRT_SourceDebugExtension,
- "ReferenceType.SourceDebugExtension" },
- { 2, 13, handleRT_SignatureWithGeneric,
- "ReferenceType.SignatureWithGeneric" },
- { 2, 14, handleRT_FieldsWithGeneric,
- "ReferenceType.FieldsWithGeneric" },
- { 2, 15, handleRT_MethodsWithGeneric,
- "ReferenceType.MethodsWithGeneric" },
- //2, 16, Instances
- //2, 17, ClassFileVersion
- //2, 18, ConstantPool
-
- /* ClassType command set (3) */
- { 3, 1, handleCT_Superclass, "ClassType.Superclass" },
- { 3, 2, handleCT_SetValues, "ClassType.SetValues" },
- { 3, 3, handleCT_InvokeMethod, "ClassType.InvokeMethod" },
- { 3, 4, handleCT_NewInstance, "ClassType.NewInstance" },
-
- /* ArrayType command set (4) */
- { 4, 1, handleAT_newInstance, "ArrayType.NewInstance" },
-
- /* InterfaceType command set (5) */
-
- /* Method command set (6) */
- { 6, 1, handleM_LineTable, "Method.LineTable" },
- //6, 2, VariableTable
- //6, 3, Bytecodes
- //6, 4, IsObsolete
- { 6, 5, handleM_VariableTableWithGeneric,
- "Method.VariableTableWithGeneric" },
-
- /* Field command set (8) */
-
- /* ObjectReference command set (9) */
- { 9, 1, handleOR_ReferenceType, "ObjectReference.ReferenceType" },
- { 9, 2, handleOR_GetValues, "ObjectReference.GetValues" },
- { 9, 3, handleOR_SetValues, "ObjectReference.SetValues" },
- //9, 4, (not defined)
- //9, 5, MonitorInfo
- { 9, 6, handleOR_InvokeMethod, "ObjectReference.InvokeMethod" },
- { 9, 7, handleOR_DisableCollection,
- "ObjectReference.DisableCollection" },
- { 9, 8, handleOR_EnableCollection,
- "ObjectReference.EnableCollection" },
- { 9, 9, handleOR_IsCollected, "ObjectReference.IsCollected" },
- //9, 10, ReferringObjects
-
- /* StringReference command set (10) */
- { 10, 1, handleSR_Value, "StringReference.Value" },
-
- /* ThreadReference command set (11) */
- { 11, 1, handleTR_Name, "ThreadReference.Name" },
- { 11, 2, handleTR_Suspend, "ThreadReference.Suspend" },
- { 11, 3, handleTR_Resume, "ThreadReference.Resume" },
- { 11, 4, handleTR_Status, "ThreadReference.Status" },
- { 11, 5, handleTR_ThreadGroup, "ThreadReference.ThreadGroup" },
- { 11, 6, handleTR_Frames, "ThreadReference.Frames" },
- { 11, 7, handleTR_FrameCount, "ThreadReference.FrameCount" },
- //11, 8, OwnedMonitors
- { 11, 9, handleTR_CurrentContendedMonitor,
- "ThreadReference.CurrentContendedMonitor" },
- //11, 10, Stop
- //11, 11, Interrupt
- { 11, 12, handleTR_SuspendCount, "ThreadReference.SuspendCount" },
- //11, 13, OwnedMonitorsStackDepthInfo
- //11, 14, ForceEarlyReturn
-
- /* ThreadGroupReference command set (12) */
- { 12, 1, handleTGR_Name, "ThreadGroupReference.Name" },
- { 12, 2, handleTGR_Parent, "ThreadGroupReference.Parent" },
- { 12, 3, handleTGR_Children, "ThreadGroupReference.Children" },
-
- /* ArrayReference command set (13) */
- { 13, 1, handleAR_Length, "ArrayReference.Length" },
- { 13, 2, handleAR_GetValues, "ArrayReference.GetValues" },
- { 13, 3, handleAR_SetValues, "ArrayReference.SetValues" },
-
- /* ClassLoaderReference command set (14) */
- { 14, 1, handleCLR_VisibleClasses,
- "ClassLoaderReference.VisibleClasses" },
-
- /* EventRequest command set (15) */
- { 15, 1, handleER_Set, "EventRequest.Set" },
- { 15, 2, handleER_Clear, "EventRequest.Clear" },
- //15, 3, ClearAllBreakpoints
-
- /* StackFrame command set (16) */
- { 16, 1, handleSF_GetValues, "StackFrame.GetValues" },
- { 16, 2, handleSF_SetValues, "StackFrame.SetValues" },
- { 16, 3, handleSF_ThisObject, "StackFrame.ThisObject" },
- //16, 4, PopFrames
-
- /* ClassObjectReference command set (17) */
- { 17, 1, handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },
-
- /* Event command set (64) */
- //64, 100, Composite <-- sent from VM to debugger, never received by VM
-
- { 199, 1, handleDDM_Chunk, "DDM.Chunk" },
-};
-
-
-/*
- * Process a request from the debugger.
- *
- * On entry, the JDWP thread is in VMWAIT.
- */
-void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
- const u1* buf, int dataLen, ExpandBuf* pReply)
-{
- JdwpError result = ERR_NONE;
- int i, respLen;
-
- if (pHeader->cmdSet != kJDWPDdmCmdSet) {
- /*
- * Activity from a debugger, not merely ddms. Mark us as having an
- * active debugger session, and zero out the last-activity timestamp
- * so waitForDebugger() doesn't return if we stall for a bit here.
- */
- dvmDbgActive();
- dvmQuasiAtomicSwap64(0, &state->lastActivityWhen);
- }
-
- /*
- * If a debugger event has fired in another thread, wait until the
- * initiating thread has suspended itself before processing messages
- * from the debugger. Otherwise we (the JDWP thread) could be told to
- * resume the thread before it has suspended.
- *
- * We call with an argument of zero to wait for the current event
- * thread to finish, and then clear the block. Depending on the thread
- * suspend policy, this may allow events in other threads to fire,
- * but those events have no bearing on what the debugger has sent us
- * in the current request.
- *
- * Note that we MUST clear the event token before waking the event
- * thread up, or risk waiting for the thread to suspend after we've
- * told it to resume.
- */
- dvmJdwpSetWaitForEventThread(state, 0);
-
- /*
- * Tell the VM that we're running and shouldn't be interrupted by GC.
- * Do this after anything that can stall indefinitely.
- */
- dvmDbgThreadRunning();
-
- expandBufAddSpace(pReply, kJDWPHeaderLen);
-
- for (i = 0; i < (int) NELEM(gHandlerMap); i++) {
- if (gHandlerMap[i].cmdSet == pHeader->cmdSet &&
- gHandlerMap[i].cmd == pHeader->cmd)
- {
- ALOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)",
- gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
- dataLen, pHeader->id);
- result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
- break;
- }
- }
- if (i == NELEM(gHandlerMap)) {
- ALOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)",
- pHeader->cmdSet, pHeader->cmd, dataLen, pHeader->id);
- if (dataLen > 0)
- dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
- assert(!"command not implemented"); // make it *really* obvious
- result = ERR_NOT_IMPLEMENTED;
- }
-
- /*
- * Set up the reply header.
- *
- * If we encountered an error, only send the header back.
- */
- u1* replyBuf = expandBufGetBuffer(pReply);
- set4BE(replyBuf + 4, pHeader->id);
- set1(replyBuf + 8, kJDWPFlagReply);
- set2BE(replyBuf + 9, result);
- if (result == ERR_NONE)
- set4BE(replyBuf + 0, expandBufGetLength(pReply));
- else
- set4BE(replyBuf + 0, kJDWPHeaderLen);
-
- respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
- IF_ALOG(LOG_VERBOSE, LOG_TAG) {
- ALOGV("reply: dataLen=%d err=%s(%d)%s", respLen,
- dvmJdwpErrorStr(result), result,
- result != ERR_NONE ? " **FAILED**" : "");
- if (respLen > 0)
- dvmPrintHexDumpDbg(expandBufGetBuffer(pReply) + kJDWPHeaderLen,
- respLen, LOG_TAG);
- }
-
- /*
- * Update last-activity timestamp. We really only need this during
- * the initial setup. Only update if this is a non-DDMS packet.
- */
- if (pHeader->cmdSet != kJDWPDdmCmdSet) {
- dvmQuasiAtomicSwap64(dvmJdwpGetNowMsec(), &state->lastActivityWhen);
- }
-
- /* tell the VM that GC is okay again */
- dvmDbgThreadWaiting();
-}
diff --git a/vm/jdwp/JdwpHandler.h b/vm/jdwp/JdwpHandler.h
deleted file mode 100644
index 63812706c..000000000
--- a/vm/jdwp/JdwpHandler.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Handle requests.
- */
-#ifndef DALVIK_JDWP_JDWPHANDLER_H_
-#define DALVIK_JDWP_JDWPHANDLER_H_
-
-#include "Common.h"
-#include "ExpandBuf.h"
-
-/*
- * JDWP message header for a request.
- */
-struct JdwpReqHeader {
- u4 length;
- u4 id;
- u1 cmdSet;
- u1 cmd;
-};
-
-/*
- * Process a request from the debugger.
- *
- * "buf" points past the header, to the content of the message. "dataLen"
- * can therefore be zero.
- */
-void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
- const u1* buf, int dataLen, ExpandBuf* pReply);
-
-/* helper function */
-void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc);
-
-#endif // DALVIK_JDWP_JDWPHANDLER_H_
diff --git a/vm/jdwp/JdwpMain.cpp b/vm/jdwp/JdwpMain.cpp
deleted file mode 100644
index 55e278daf..000000000
--- a/vm/jdwp/JdwpMain.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * JDWP initialization.
- */
-#include "jdwp/JdwpPriv.h"
-#include "Dalvik.h"
-#include "Atomic.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <time.h>
-#include <errno.h>
-
-
-static void* jdwpThreadStart(void* arg);
-
-/*
- * JdwpNetStateBase class implementation
- */
-JdwpNetStateBase::JdwpNetStateBase()
-{
- clientSock = -1;
- dvmDbgInitMutex(&socketLock);
-}
-
-/*
- * Write a packet. Grabs a mutex to assure atomicity.
- */
-ssize_t JdwpNetStateBase::writePacket(ExpandBuf* pReply)
-{
- dvmDbgLockMutex(&socketLock);
- ssize_t cc = TEMP_FAILURE_RETRY(write(clientSock, expandBufGetBuffer(pReply),
- expandBufGetLength(pReply)));
- dvmDbgUnlockMutex(&socketLock);
-
- return cc;
-}
-
-/*
- * Write a buffered packet. Grabs a mutex to assure atomicity.
- */
-ssize_t JdwpNetStateBase::writeBufferedPacket(const struct iovec* iov,
- int iovcnt)
-{
- dvmDbgLockMutex(&socketLock);
- ssize_t actual = TEMP_FAILURE_RETRY(writev(clientSock, iov, iovcnt));
- dvmDbgUnlockMutex(&socketLock);
-
- return actual;
-}
-
-/*
- * Initialize JDWP.
- *
- * Does not return until JDWP thread is running, but may return before
- * the thread is accepting network connections.
- */
-JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
-{
- JdwpState* state = NULL;
-
- /* comment this out when debugging JDWP itself */
- android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);
-
- state = (JdwpState*) calloc(1, sizeof(JdwpState));
-
- state->params = *pParams;
-
- state->requestSerial = 0x10000000;
- state->eventSerial = 0x20000000;
- dvmDbgInitMutex(&state->threadStartLock);
- dvmDbgInitMutex(&state->attachLock);
- dvmDbgInitMutex(&state->serialLock);
- dvmDbgInitMutex(&state->eventLock);
- state->eventThreadId = 0;
- dvmDbgInitMutex(&state->eventThreadLock);
- dvmDbgInitCond(&state->threadStartCond);
- dvmDbgInitCond(&state->attachCond);
- dvmDbgInitCond(&state->eventThreadCond);
-
- switch (pParams->transport) {
- case kJdwpTransportSocket:
- // ALOGD("prepping for JDWP over TCP");
- state->transport = dvmJdwpSocketTransport();
- break;
- case kJdwpTransportAndroidAdb:
- // ALOGD("prepping for JDWP over ADB");
- state->transport = dvmJdwpAndroidAdbTransport();
- /* TODO */
- break;
- default:
- ALOGE("Unknown transport %d", pParams->transport);
- assert(false);
- goto fail;
- }
-
- if (!dvmJdwpNetStartup(state, pParams))
- goto fail;
-
- /*
- * Grab a mutex or two before starting the thread. This ensures they
- * won't signal the cond var before we're waiting.
- */
- dvmDbgLockMutex(&state->threadStartLock);
- if (pParams->suspend)
- dvmDbgLockMutex(&state->attachLock);
-
- /*
- * We have bound to a port, or are trying to connect outbound to a
- * debugger. Create the JDWP thread and let it continue the mission.
- */
- if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
- jdwpThreadStart, state))
- {
- /* state is getting tossed, but unlock these anyway for cleanliness */
- dvmDbgUnlockMutex(&state->threadStartLock);
- if (pParams->suspend)
- dvmDbgUnlockMutex(&state->attachLock);
- goto fail;
- }
-
- /*
- * Wait until the thread finishes basic initialization.
- * TODO: cond vars should be waited upon in a loop
- */
- dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
- dvmDbgUnlockMutex(&state->threadStartLock);
-
-
- /*
- * For suspend=y, wait for the debugger to connect to us or for us to
- * connect to the debugger.
- *
- * The JDWP thread will signal us when it connects successfully or
- * times out (for timeout=xxx), so we have to check to see what happened
- * when we wake up.
- */
- if (pParams->suspend) {
- dvmChangeStatus(NULL, THREAD_VMWAIT);
- dvmDbgCondWait(&state->attachCond, &state->attachLock);
- dvmDbgUnlockMutex(&state->attachLock);
- dvmChangeStatus(NULL, THREAD_RUNNING);
-
- if (!dvmJdwpIsActive(state)) {
- ALOGE("JDWP connection failed");
- goto fail;
- }
-
- ALOGI("JDWP connected");
-
- /*
- * Ordinarily we would pause briefly to allow the debugger to set
- * breakpoints and so on, but for "suspend=y" the VM init code will
- * pause the VM when it sends the VM_START message.
- */
- }
-
- return state;
-
-fail:
- dvmJdwpShutdown(state); // frees state
- return NULL;
-}
-
-/*
- * Reset all session-related state. There should not be an active connection
- * to the client at this point. The rest of the VM still thinks there is
- * a debugger attached.
- *
- * This includes freeing up the debugger event list.
- */
-void dvmJdwpResetState(JdwpState* state)
-{
- /* could reset the serial numbers, but no need to */
-
- dvmJdwpUnregisterAll(state);
- assert(state->eventList == NULL);
-
- /*
- * Should not have one of these in progress. If the debugger went away
- * mid-request, though, we could see this.
- */
- if (state->eventThreadId != 0) {
- ALOGW("WARNING: resetting state while event in progress");
- assert(false);
- }
-}
-
-/*
- * Tell the JDWP thread to shut down. Frees "state".
- */
-void dvmJdwpShutdown(JdwpState* state)
-{
- void* threadReturn;
-
- if (state == NULL)
- return;
-
- if (dvmJdwpIsTransportDefined(state)) {
- if (dvmJdwpIsConnected(state))
- dvmJdwpPostVMDeath(state);
-
- /*
- * Close down the network to inspire the thread to halt.
- */
- if (gDvm.verboseShutdown)
- ALOGD("JDWP shutting down net...");
- dvmJdwpNetShutdown(state);
-
- if (state->debugThreadStarted) {
- state->run = false;
- if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
- ALOGW("JDWP thread join failed");
- }
- }
-
- if (gDvm.verboseShutdown)
- ALOGD("JDWP freeing netstate...");
- dvmJdwpNetFree(state);
- state->netState = NULL;
- }
- assert(state->netState == NULL);
-
- dvmJdwpResetState(state);
- free(state);
-}
-
-/*
- * Are we talking to a debugger?
- */
-bool dvmJdwpIsActive(JdwpState* state)
-{
- return dvmJdwpIsConnected(state);
-}
-
-/*
- * Entry point for JDWP thread. The thread was created through the VM
- * mechanisms, so there is a java/lang/Thread associated with us.
- */
-static void* jdwpThreadStart(void* arg)
-{
- JdwpState* state = (JdwpState*) arg;
-
- ALOGV("JDWP: thread running");
-
- /*
- * Finish initializing "state", then notify the creating thread that
- * we're running.
- */
- state->debugThreadHandle = dvmThreadSelf()->handle;
- state->run = true;
- android_atomic_release_store(true, &state->debugThreadStarted);
-
- dvmDbgLockMutex(&state->threadStartLock);
- dvmDbgCondBroadcast(&state->threadStartCond);
- dvmDbgUnlockMutex(&state->threadStartLock);
-
- /* set the thread state to VMWAIT so GCs don't wait for us */
- dvmDbgThreadWaiting();
-
- /*
- * Loop forever if we're in server mode, processing connections. In
- * non-server mode, we bail out of the thread when the debugger drops
- * us.
- *
- * We broadcast a notification when a debugger attaches, after we
- * successfully process the handshake.
- */
- while (state->run) {
- bool first;
-
- if (state->params.server) {
- /*
- * Block forever, waiting for a connection. To support the
- * "timeout=xxx" option we'll need to tweak this.
- */
- if (!dvmJdwpAcceptConnection(state))
- break;
- } else {
- /*
- * If we're not acting as a server, we need to connect out to the
- * debugger. To support the "timeout=xxx" option we need to
- * have a timeout if the handshake reply isn't received in a
- * reasonable amount of time.
- */
- if (!dvmJdwpEstablishConnection(state)) {
- /* wake anybody who was waiting for us to succeed */
- dvmDbgLockMutex(&state->attachLock);
- dvmDbgCondBroadcast(&state->attachCond);
- dvmDbgUnlockMutex(&state->attachLock);
- break;
- }
- }
-
- /* prep debug code to handle the new connection */
- dvmDbgConnected();
-
- /* process requests until the debugger drops */
- first = true;
- while (true) {
- // sanity check -- shouldn't happen?
- if (dvmThreadSelf()->status != THREAD_VMWAIT) {
- ALOGE("JDWP thread no longer in VMWAIT (now %d); resetting",
- dvmThreadSelf()->status);
- dvmDbgThreadWaiting();
- }
-
- if (!dvmJdwpProcessIncoming(state)) /* blocking read */
- break;
-
- if (first && !dvmJdwpAwaitingHandshake(state)) {
- /* handshake worked, tell the interpreter that we're active */
- first = false;
-
- /* set thread ID; requires object registry to be active */
- state->debugThreadId = dvmDbgGetThreadSelfId();
-
- /* wake anybody who's waiting for us */
- dvmDbgLockMutex(&state->attachLock);
- dvmDbgCondBroadcast(&state->attachCond);
- dvmDbgUnlockMutex(&state->attachLock);
- }
- }
-
- dvmJdwpCloseConnection(state);
-
- if (state->ddmActive) {
- state->ddmActive = false;
-
- /* broadcast the disconnect; must be in RUNNING state */
- dvmDbgThreadRunning();
- dvmDbgDdmDisconnected();
- dvmDbgThreadWaiting();
- }
-
- /* release session state, e.g. remove breakpoint instructions */
- dvmJdwpResetState(state);
-
- /* tell the interpreter that the debugger is no longer around */
- dvmDbgDisconnected();
-
- /* if we had threads suspended, resume them now */
- dvmUndoDebuggerSuspensions();
-
- /* if we connected out, this was a one-shot deal */
- if (!state->params.server)
- state->run = false;
- }
-
- /* back to running, for thread shutdown */
- dvmDbgThreadRunning();
-
- ALOGV("JDWP: thread exiting");
- return NULL;
-}
-
-
-/*
- * Return the thread handle, or (pthread_t)0 if the debugger isn't running.
- */
-pthread_t dvmJdwpGetDebugThread(JdwpState* state)
-{
- if (state == NULL)
- return 0;
-
- return state->debugThreadHandle;
-}
-
-
-/*
- * Support routines for waitForDebugger().
- *
- * We can't have a trivial "waitForDebugger" function that returns the
- * instant the debugger connects, because we run the risk of executing code
- * before the debugger has had a chance to configure breakpoints or issue
- * suspend calls. It would be nice to just sit in the suspended state, but
- * most debuggers don't expect any threads to be suspended when they attach.
- *
- * There's no JDWP event we can post to tell the debugger, "we've stopped,
- * and we like it that way". We could send a fake breakpoint, which should
- * cause the debugger to immediately send a resume, but the debugger might
- * send the resume immediately or might throw an exception of its own upon
- * receiving a breakpoint event that it didn't ask for.
- *
- * What we really want is a "wait until the debugger is done configuring
- * stuff" event. We can approximate this with a "wait until the debugger
- * has been idle for a brief period".
- */
-
-/*
- * Get a notion of the current time, in milliseconds.
- */
-s8 dvmJdwpGetNowMsec()
-{
-#ifdef HAVE_POSIX_CLOCKS
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- return now.tv_sec * 1000LL + now.tv_nsec / 1000000LL;
-#else
- struct timeval now;
- gettimeofday(&now, NULL);
- return now.tv_sec * 1000LL + now.tv_usec / 1000LL;
-#endif
-}
-
-/*
- * Return the time, in milliseconds, since the last debugger activity.
- *
- * Returns -1 if no debugger is attached, or 0 if we're in the middle of
- * processing a debugger request.
- */
-s8 dvmJdwpLastDebuggerActivity(JdwpState* state)
-{
- if (!gDvm.debuggerActive) {
- ALOGD("dvmJdwpLastDebuggerActivity: no active debugger");
- return -1;
- }
-
- s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
-
- /* initializing or in the middle of something? */
- if (last == 0) {
- ALOGV("+++ last=busy");
- return 0;
- }
-
- /* now get the current time */
- s8 now = dvmJdwpGetNowMsec();
- assert(now >= last);
-
- ALOGV("+++ debugger interval=%lld", now - last);
- return now - last;
-}
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
deleted file mode 100644
index 67a953f2b..000000000
--- a/vm/jdwp/JdwpPriv.h
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * JDWP internal interfaces.
- */
-#ifndef DALVIK_JDWP_JDWPPRIV_H_
-#define DALVIK_JDWP_JDWPPRIV_H_
-
-#define LOG_TAG "jdwp"
-
-#include "jdwp/Jdwp.h"
-#include "jdwp/JdwpEvent.h"
-#include "Debugger.h"
-
-#include <pthread.h>
-#include <sys/uio.h>
-
-/*
- * JDWP constants.
- */
-#define kJDWPHeaderLen 11
-#define kJDWPFlagReply 0x80
-
-/* DDM support */
-#define kJDWPDdmCmdSet 199 /* 0xc7, or 'G'+128 */
-#define kJDWPDdmCmd 1
-
-
-/*
- * Transport-specific network status.
- */
-struct JdwpNetState;
-struct JdwpState;
-
-/*
- * Transport functions.
- */
-struct JdwpTransport {
- bool (*startup)(struct JdwpState* state, const JdwpStartupParams* pParams);
- bool (*accept)(struct JdwpState* state);
- bool (*establish)(struct JdwpState* state);
- void (*close)(struct JdwpState* state);
- void (*shutdown)(struct JdwpState* state);
- void (*free)(struct JdwpState* state);
- bool (*isConnected)(struct JdwpState* state);
- bool (*awaitingHandshake)(struct JdwpState* state);
- bool (*processIncoming)(struct JdwpState* state);
- bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
- bool (*sendBufferedRequest)(struct JdwpState* state,
- const struct iovec* iov, int iovcnt);
-};
-
-const JdwpTransport* dvmJdwpSocketTransport();
-const JdwpTransport* dvmJdwpAndroidAdbTransport();
-
-
-/*
- * State for JDWP functions.
- */
-struct JdwpState {
- JdwpStartupParams params;
-
- /* wait for creation of the JDWP thread */
- pthread_mutex_t threadStartLock;
- pthread_cond_t threadStartCond;
-
- int debugThreadStarted;
- pthread_t debugThreadHandle;
- ObjectId debugThreadId;
- bool run;
-
- const JdwpTransport* transport;
- JdwpNetState* netState;
-
- /* for wait-for-debugger */
- pthread_mutex_t attachLock;
- pthread_cond_t attachCond;
-
- /* time of last debugger activity, in milliseconds */
- s8 lastActivityWhen;
-
- /* global counters and a mutex to protect them */
- u4 requestSerial;
- u4 eventSerial;
- pthread_mutex_t serialLock;
-
- /*
- * Events requested by the debugger (breakpoints, class prep, etc).
- */
- int numEvents; /* #of elements in eventList */
- JdwpEvent* eventList; /* linked list of events */
- pthread_mutex_t eventLock; /* guards numEvents/eventList */
-
- /*
- * Synchronize suspension of event thread (to avoid receiving "resume"
- * events before the thread has finished suspending itself).
- */
- pthread_mutex_t eventThreadLock;
- pthread_cond_t eventThreadCond;
- ObjectId eventThreadId;
-
- /*
- * DDM support.
- */
- bool ddmActive;
-};
-
-/*
- * Base class for JdwpNetState
- */
-class JdwpNetStateBase {
-public:
- int clientSock; /* active connection to debugger */
-
- JdwpNetStateBase();
- ssize_t writePacket(ExpandBuf* pReply);
- ssize_t writeBufferedPacket(const struct iovec* iov, int iovcnt);
-
-private:
- pthread_mutex_t socketLock; /* socket synchronization */
-};
-
-
-/* reset all session-specific data */
-void dvmJdwpResetState(JdwpState* state);
-
-/* atomic ops to get next serial number */
-u4 dvmJdwpNextRequestSerial(JdwpState* state);
-u4 dvmJdwpNextEventSerial(JdwpState* state);
-
-/* get current time, in msec */
-s8 dvmJdwpGetNowMsec(void);
-
-
-/*
- * Transport functions.
- */
-INLINE bool dvmJdwpNetStartup(JdwpState* state,
- const JdwpStartupParams* pParams)
-{
- return (*state->transport->startup)(state, pParams);
-}
-INLINE bool dvmJdwpAcceptConnection(JdwpState* state) {
- return (*state->transport->accept)(state);
-}
-INLINE bool dvmJdwpEstablishConnection(JdwpState* state) {
- return (*state->transport->establish)(state);
-}
-INLINE void dvmJdwpCloseConnection(JdwpState* state) {
- (*state->transport->close)(state);
-}
-INLINE void dvmJdwpNetShutdown(JdwpState* state) {
- (*state->transport->shutdown)(state);
-}
-INLINE void dvmJdwpNetFree(JdwpState* state) {
- (*state->transport->free)(state);
-}
-INLINE bool dvmJdwpIsTransportDefined(JdwpState* state) {
- return state != NULL && state->transport != NULL;
-}
-INLINE bool dvmJdwpIsConnected(JdwpState* state) {
- return state != NULL && (*state->transport->isConnected)(state);
-}
-INLINE bool dvmJdwpAwaitingHandshake(JdwpState* state) {
- return (*state->transport->awaitingHandshake)(state);
-}
-INLINE bool dvmJdwpProcessIncoming(JdwpState* state) {
- return (*state->transport->processIncoming)(state);
-}
-INLINE bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq) {
- return (*state->transport->sendRequest)(state, pReq);
-}
-INLINE bool dvmJdwpSendBufferedRequest(JdwpState* state,
- const struct iovec* iov, int iovcnt)
-{
- return (*state->transport->sendBufferedRequest)(state, iov, iovcnt);
-}
-
-#endif // DALVIK_JDWP_JDWPPRIV_H_
diff --git a/vm/jdwp/JdwpSocket.cpp b/vm/jdwp/JdwpSocket.cpp
deleted file mode 100644
index eaea607be..000000000
--- a/vm/jdwp/JdwpSocket.cpp
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * JDWP TCP socket network code.
- */
-#include "jdwp/JdwpPriv.h"
-#include "jdwp/JdwpHandler.h"
-#include "Bits.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#define kBasePort 8000
-#define kMaxPort 8040
-
-#define kInputBufferSize 8192
-
-#define kMagicHandshake "JDWP-Handshake"
-#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1)
-
-// fwd
-static void netShutdown(JdwpNetState* state);
-static void netFree(JdwpNetState* state);
-
-
-/*
- * JDWP network state.
- *
- * We only talk to one debugger at a time.
- */
-struct JdwpNetState : public JdwpNetStateBase {
- short listenPort;
- int listenSock; /* listen for connection from debugger */
- int wakePipe[2]; /* break out of select */
-
- struct in_addr remoteAddr;
- unsigned short remotePort;
-
- bool awaitingHandshake; /* waiting for "JDWP-Handshake" */
-
- /* pending data from the network; would be more efficient as circular buf */
- unsigned char inputBuffer[kInputBufferSize];
- int inputCount;
-
- JdwpNetState()
- {
- listenPort = 0;
- listenSock = -1;
- wakePipe[0] = -1;
- wakePipe[1] = -1;
-
- awaitingHandshake = false;
-
- inputCount = 0;
- }
-};
-
-static JdwpNetState* netStartup(short port);
-
-/*
- * Set up some stuff for transport=dt_socket.
- */
-static bool prepareSocket(JdwpState* state, const JdwpStartupParams* pParams)
-{
- unsigned short port;
-
- if (pParams->server) {
- if (pParams->port != 0) {
- /* try only the specified port */
- port = pParams->port;
- state->netState = netStartup(port);
- } else {
- /* scan through a range of ports, binding to the first available */
- for (port = kBasePort; port <= kMaxPort; port++) {
- state->netState = netStartup(port);
- if (state->netState != NULL)
- break;
- }
- }
- if (state->netState == NULL) {
- ALOGE("JDWP net startup failed (req port=%d)", pParams->port);
- return false;
- }
- } else {
- port = pParams->port; // used in a debug msg later
- state->netState = netStartup(-1);
- }
-
- if (pParams->suspend)
- ALOGI("JDWP will wait for debugger on port %d", port);
- else
- ALOGD("JDWP will %s on port %d",
- pParams->server ? "listen" : "connect", port);
-
- return true;
-}
-
-
-/*
- * Are we still waiting for the handshake string?
- */
-static bool awaitingHandshake(JdwpState* state)
-{
- return state->netState->awaitingHandshake;
-}
-
-/*
- * Initialize JDWP stuff.
- *
- * Allocates a new state structure. If "port" is non-negative, this also
- * tries to bind to a listen port. If "port" is less than zero, we assume
- * we're preparing for an outbound connection, and return without binding
- * to anything.
- *
- * This may be called several times if we're probing for a port.
- *
- * Returns 0 on success.
- */
-static JdwpNetState* netStartup(short port)
-{
- int one = 1;
- JdwpNetState* netState = new JdwpNetState;
-
- if (port < 0)
- return netState;
-
- assert(port != 0);
-
- netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (netState->listenSock < 0) {
- ALOGE("Socket create failed: %s", strerror(errno));
- goto fail;
- }
-
- /* allow immediate re-use */
- if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
- sizeof(one)) < 0)
- {
- ALOGE("setsockopt(SO_REUSEADDR) failed: %s", strerror(errno));
- goto fail;
- }
-
- union {
- struct sockaddr_in addrInet;
- struct sockaddr addrPlain;
- } addr;
- addr.addrInet.sin_family = AF_INET;
- addr.addrInet.sin_port = htons(port);
- inet_aton("127.0.0.1", &addr.addrInet.sin_addr);
-
- if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) {
- ALOGV("attempt to bind to port %u failed: %s", port, strerror(errno));
- goto fail;
- }
-
- netState->listenPort = port;
- LOGVV("+++ bound to port %d", netState->listenPort);
-
- if (listen(netState->listenSock, 5) != 0) {
- ALOGE("Listen failed: %s", strerror(errno));
- goto fail;
- }
-
- return netState;
-
-fail:
- netShutdown(netState);
- netFree(netState);
- return NULL;
-}
-
-/*
- * Shut down JDWP listener. Don't free state.
- *
- * Note that "netState" may be partially initialized if "startup" failed.
- *
- * This may be called from a non-JDWP thread as part of shutting the
- * JDWP thread down.
- *
- * (This is currently called several times during startup as we probe
- * for an open port.)
- */
-static void netShutdown(JdwpNetState* netState)
-{
- if (netState == NULL)
- return;
-
- int listenSock = netState->listenSock;
- int clientSock = netState->clientSock;
-
- /* clear these out so it doesn't wake up and try to reuse them */
- netState->listenSock = netState->clientSock = -1;
-
- /* "shutdown" dislodges blocking read() and accept() calls */
- if (listenSock >= 0) {
- shutdown(listenSock, SHUT_RDWR);
- close(listenSock);
- }
- if (clientSock >= 0) {
- shutdown(clientSock, SHUT_RDWR);
- close(clientSock);
- }
-
- /* if we might be sitting in select, kick us loose */
- if (netState->wakePipe[1] >= 0) {
- ALOGV("+++ writing to wakePipe");
- TEMP_FAILURE_RETRY(write(netState->wakePipe[1], "", 1));
- }
-}
-static void netShutdownExtern(JdwpState* state)
-{
- netShutdown(state->netState);
-}
-
-/*
- * Free JDWP state.
- *
- * Call this after shutting the network down with netShutdown().
- */
-static void netFree(JdwpNetState* netState)
-{
- if (netState == NULL)
- return;
- assert(netState->listenSock == -1);
- assert(netState->clientSock == -1);
-
- if (netState->wakePipe[0] >= 0) {
- close(netState->wakePipe[0]);
- netState->wakePipe[0] = -1;
- }
- if (netState->wakePipe[1] >= 0) {
- close(netState->wakePipe[1]);
- netState->wakePipe[1] = -1;
- }
-
- delete netState;
-}
-static void netFreeExtern(JdwpState* state)
-{
- netFree(state->netState);
-}
-
-/*
- * Returns "true" if we're connected to a debugger.
- */
-static bool isConnected(JdwpState* state)
-{
- return (state->netState != NULL &&
- state->netState->clientSock >= 0);
-}
-
-/*
- * Returns "true" if the fd is ready, "false" if not.
- */
-#if 0
-static bool isFdReadable(int sock)
-{
- fd_set readfds;
- struct timeval tv;
- int count;
-
- FD_ZERO(&readfds);
- FD_SET(sock, &readfds);
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- count = select(sock+1, &readfds, NULL, NULL, &tv);
- if (count <= 0)
- return false;
-
- if (FD_ISSET(sock, &readfds)) /* make sure it's our fd */
- return true;
-
- ALOGE("WEIRD: odd behavior in select (count=%d)", count);
- return false;
-}
-#endif
-
-#if 0
-/*
- * Check to see if we have a pending connection from the debugger.
- *
- * Returns true on success (meaning a connection is available).
- */
-static bool checkConnection(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
-
- assert(netState->listenSock >= 0);
- /* not expecting to be called when debugger is actively connected */
- assert(netState->clientSock < 0);
-
- if (!isFdReadable(netState->listenSock))
- return false;
- return true;
-}
-#endif
-
-/*
- * 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.
- * If that's not desirable, use checkConnection() to make sure something
- * is pending.
- */
-static bool acceptConnection(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- union {
- struct sockaddr_in addrInet;
- struct sockaddr addrPlain;
- } addr;
- socklen_t addrlen;
- int sock;
-
- if (netState->listenSock < 0)
- return false; /* you're not listening! */
-
- assert(netState->clientSock < 0); /* must not already be talking */
-
- addrlen = sizeof(addr);
- do {
- sock = accept(netState->listenSock, &addr.addrPlain, &addrlen);
- if (sock < 0 && errno != EINTR) {
- // When we call shutdown() on the socket, accept() returns with
- // EINVAL. Don't gripe about it.
- if (errno == EINVAL)
- LOGVV("accept failed: %s", strerror(errno));
- else
- ALOGE("accept failed: %s", strerror(errno));
- return false;
- }
- } while (sock < 0);
-
- netState->remoteAddr = addr.addrInet.sin_addr;
- netState->remotePort = ntohs(addr.addrInet.sin_port);
- ALOGV("+++ accepted connection from %s:%u",
- inet_ntoa(netState->remoteAddr), netState->remotePort);
-
- netState->clientSock = sock;
- netState->awaitingHandshake = true;
- netState->inputCount = 0;
-
- ALOGV("Setting TCP_NODELAY on accepted socket");
- setNoDelay(netState->clientSock);
-
- if (pipe(netState->wakePipe) < 0) {
- ALOGE("pipe failed");
- return false;
- }
-
- return true;
-}
-
-/*
- * Create a connection to a waiting debugger.
- */
-static bool establishConnection(JdwpState* state)
-{
- union {
- struct sockaddr_in addrInet;
- struct sockaddr addrPlain;
- } addr;
- struct hostent* pEntry;
- int h_errno;
-
- assert(state != NULL && state->netState != NULL);
- assert(!state->params.server);
- assert(state->params.host[0] != '\0');
- assert(state->params.port != 0);
-
- /*
- * Start by resolving the host name.
- */
-//#undef HAVE_GETHOSTBYNAME_R
-//#warning "forcing non-R"
-#ifdef HAVE_GETHOSTBYNAME_R
- struct hostent he;
- char auxBuf[128];
- int cc = gethostbyname_r(state->params.host, &he, auxBuf, sizeof(auxBuf),
- &pEntry, &h_errno);
- if (cc != 0) {
- ALOGW("gethostbyname_r('%s') failed: %s",
- state->params.host, strerror(errno));
- return false;
- }
-
-#else
- h_errno = 0;
- pEntry = gethostbyname(state->params.host);
- if (pEntry == NULL) {
- ALOGW("gethostbyname('%s') failed: %s",
- state->params.host, strerror(h_errno));
- return false;
- }
-#endif
-
- /* copy it out ASAP to minimize risk of multithreaded annoyances */
- memcpy(&addr.addrInet.sin_addr, pEntry->h_addr, pEntry->h_length);
- addr.addrInet.sin_family = pEntry->h_addrtype;
-
- addr.addrInet.sin_port = htons(state->params.port);
-
- ALOGI("Connecting out to '%s' %d",
- inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port));
-
- /*
- * Create a socket.
- */
- JdwpNetState* netState;
- netState = state->netState;
- netState->clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (netState->clientSock < 0) {
- ALOGE("Unable to create socket: %s", strerror(errno));
- return false;
- }
-
- /*
- * Try to connect.
- */
- if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) {
- ALOGE("Unable to connect to %s:%d: %s",
- inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port),
- strerror(errno));
- close(netState->clientSock);
- netState->clientSock = -1;
- return false;
- }
-
- ALOGI("Connection established to %s (%s:%d)",
- state->params.host, inet_ntoa(addr.addrInet.sin_addr),
- ntohs(addr.addrInet.sin_port));
- netState->awaitingHandshake = true;
- netState->inputCount = 0;
-
- setNoDelay(netState->clientSock);
-
- if (pipe(netState->wakePipe) < 0) {
- ALOGE("pipe failed");
- return false;
- }
-
- return true;
-}
-
-/*
- * Close the connection to the debugger.
- *
- * Reset the state so we're ready to receive a new connection.
- */
-static void closeConnection(JdwpState* state)
-{
- JdwpNetState* netState;
-
- assert(state != NULL && state->netState != NULL);
-
- netState = state->netState;
- if (netState->clientSock < 0)
- return;
-
- ALOGV("+++ closed connection to %s:%u",
- inet_ntoa(netState->remoteAddr), netState->remotePort);
-
- close(netState->clientSock);
- netState->clientSock = -1;
-
- return;
-}
-
-/*
- * Figure out if we have a full packet in the buffer.
- */
-static bool haveFullPacket(JdwpNetState* netState)
-{
- long length;
-
- if (netState->awaitingHandshake)
- return (netState->inputCount >= (int) kMagicHandshakeLen);
-
- if (netState->inputCount < 4)
- return false;
-
- length = get4BE(netState->inputBuffer);
- return (netState->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(JdwpNetState* netState, int count)
-{
- assert(count > 0);
- assert(count <= netState->inputCount);
-
- if (count == netState->inputCount) {
- netState->inputCount = 0;
- return;
- }
-
- memmove(netState->inputBuffer, netState->inputBuffer + count,
- netState->inputCount - count);
- netState->inputCount -= count;
-}
-
-/*
- * Dump the contents of a packet to stdout.
- */
-#if 0
-static void dumpPacket(const unsigned char* packetBuf)
-{
- const unsigned char* buf = packetBuf;
- u4 length, id;
- u1 flags, cmdSet, cmd;
- u2 error;
- bool reply;
- int dataLen;
-
- cmd = cmdSet = 0xcc;
-
- length = read4BE(&buf);
- id = read4BE(&buf);
- flags = read1(&buf);
- if ((flags & kJDWPFlagReply) != 0) {
- reply = true;
- error = read2BE(&buf);
- } else {
- reply = false;
- cmdSet = read1(&buf);
- cmd = read1(&buf);
- }
-
- dataLen = length - (buf - packetBuf);
-
- ALOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d",
- reply ? "reply" : "req",
- dataLen, id, flags, cmdSet, cmd);
- if (dataLen > 0)
- dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
-}
-#endif
-
-/*
- * Handle a packet. Returns "false" if we encounter a connection-fatal error.
- */
-static bool handlePacket(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- const unsigned char* buf = netState->inputBuffer;
- JdwpReqHeader hdr;
- u4 length, id;
- u1 flags, cmdSet, cmd;
- u2 error;
- bool reply;
- int dataLen;
-
- cmd = cmdSet = 0; // shut up gcc
-
- /*dumpPacket(netState->inputBuffer);*/
-
- length = read4BE(&buf);
- id = read4BE(&buf);
- flags = read1(&buf);
- if ((flags & kJDWPFlagReply) != 0) {
- reply = true;
- error = read2BE(&buf);
- } else {
- reply = false;
- cmdSet = read1(&buf);
- cmd = read1(&buf);
- }
-
- assert((int) length <= netState->inputCount);
- dataLen = length - (buf - netState->inputBuffer);
-
- if (!reply) {
- ExpandBuf* pReply = expandBufAlloc();
-
- hdr.length = length;
- hdr.id = id;
- hdr.cmdSet = cmdSet;
- hdr.cmd = cmd;
- dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
- if (expandBufGetLength(pReply) > 0) {
- ssize_t cc = netState->writePacket(pReply);
-
- if (cc != (ssize_t) expandBufGetLength(pReply)) {
- ALOGE("Failed sending reply to debugger: %s", strerror(errno));
- expandBufFree(pReply);
- return false;
- }
- } else {
- ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
- }
- expandBufFree(pReply);
- } else {
- ALOGV("reply?!");
- assert(false);
- }
-
- ALOGV("----------");
-
- consumeBytes(netState, length);
- return true;
-}
-
-/*
- * Process incoming data. If no data is available, this will block until
- * some arrives.
- *
- * If we get a full packet, handle it.
- *
- * To take some of the mystery out of life, we want to reject incoming
- * connections if we already have a debugger attached. If we don't, the
- * debugger will just mysteriously hang until it times out. We could just
- * close the listen socket, but there's a good chance we won't be able to
- * bind to the same port again, which would confuse utilities.
- *
- * Returns "false" on error (indicating that the connection has been severed),
- * "true" if things are still okay.
- */
-static bool processIncoming(JdwpState* state)
-{
- JdwpNetState* netState = state->netState;
- int readCount;
-
- assert(netState->clientSock >= 0);
-
- if (!haveFullPacket(netState)) {
- /* read some more, looping until we have data */
- errno = 0;
- while (1) {
- int selCount;
- fd_set readfds;
- int maxfd;
- int fd;
-
- maxfd = netState->listenSock;
- if (netState->clientSock > maxfd)
- maxfd = netState->clientSock;
- if (netState->wakePipe[0] > maxfd)
- maxfd = netState->wakePipe[0];
-
- if (maxfd < 0) {
- ALOGV("+++ all fds are closed");
- return false;
- }
-
- FD_ZERO(&readfds);
-
- /* configure fds; note these may get zapped by another thread */
- fd = netState->listenSock;
- if (fd >= 0)
- FD_SET(fd, &readfds);
- fd = netState->clientSock;
- if (fd >= 0)
- FD_SET(fd, &readfds);
- fd = netState->wakePipe[0];
- if (fd >= 0) {
- FD_SET(fd, &readfds);
- } else {
- ALOGI("NOTE: entering select w/o wakepipe");
- }
-
- /*
- * Select blocks until it sees activity on the file descriptors.
- * Closing the local file descriptor does not count as activity,
- * so we can't rely on that to wake us up (it works for read()
- * and accept(), but not select()).
- *
- * We can do one of three things: (1) send a signal and catch
- * EINTR, (2) open an additional fd ("wakePipe") and write to
- * it when it's time to exit, or (3) time out periodically and
- * re-issue the select. We're currently using #2, as it's more
- * reliable than #1 and generally better than #3. Wastes two fds.
- */
- selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
- if (selCount < 0) {
- if (errno == EINTR)
- continue;
- ALOGE("select failed: %s", strerror(errno));
- goto fail;
- }
-
- if (netState->wakePipe[0] >= 0 &&
- FD_ISSET(netState->wakePipe[0], &readfds))
- {
- if (netState->listenSock >= 0)
- ALOGE("Exit wake set, but not exiting?");
- else
- ALOGD("Got wake-up signal, bailing out of select");
- goto fail;
- }
- if (netState->listenSock >= 0 &&
- FD_ISSET(netState->listenSock, &readfds))
- {
- ALOGI("Ignoring second debugger -- accepting and dropping");
- union {
- struct sockaddr_in addrInet;
- struct sockaddr addrPlain;
- } addr;
- socklen_t addrlen;
- int tmpSock;
- tmpSock = accept(netState->listenSock, &addr.addrPlain,
- &addrlen);
- if (tmpSock < 0)
- ALOGI("Weird -- accept failed");
- else
- close(tmpSock);
- }
- if (netState->clientSock >= 0 &&
- FD_ISSET(netState->clientSock, &readfds))
- {
- readCount = read(netState->clientSock,
- netState->inputBuffer + netState->inputCount,
- sizeof(netState->inputBuffer) - netState->inputCount);
- if (readCount < 0) {
- /* read failed */
- if (errno != EINTR)
- goto fail;
- ALOGD("+++ EINTR hit");
- return true;
- } else if (readCount == 0) {
- /* EOF hit -- far end went away */
- ALOGD("+++ peer disconnected");
- goto fail;
- } else
- break;
- }
- }
-
- netState->inputCount += readCount;
- if (!haveFullPacket(netState))
- return true; /* still not there yet */
- }
-
- /*
- * Special-case the initial handshake. For some bizarre reason we're
- * expected to emulate bad tty settings by echoing the request back
- * exactly as it was sent. Note the handshake is always initiated by
- * the debugger, no matter who connects to whom.
- *
- * Other than this one case, the protocol [claims to be] stateless.
- */
- if (netState->awaitingHandshake) {
- int cc;
-
- if (memcmp(netState->inputBuffer,
- kMagicHandshake, kMagicHandshakeLen) != 0)
- {
- ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
- goto fail;
- }
-
- errno = 0;
- cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer,
- kMagicHandshakeLen));
- if (cc != kMagicHandshakeLen) {
- ALOGE("Failed writing handshake bytes: %s (%d of %d)",
- strerror(errno), cc, (int) kMagicHandshakeLen);
- goto fail;
- }
-
- consumeBytes(netState, kMagicHandshakeLen);
- netState->awaitingHandshake = false;
- ALOGV("+++ handshake complete");
- return true;
- }
-
- /*
- * Handle this packet.
- */
- return handlePacket(state);
-
-fail:
- closeConnection(state);
- return false;
-}
-
-/*
- * Send a request.
- *
- * The entire packet must be sent with a single write() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
-{
- JdwpNetState* netState = state->netState;
-
- /*dumpPacket(expandBufGetBuffer(pReq));*/
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- errno = 0;
- ssize_t cc = netState->writePacket(pReq);
-
- if (cc != (ssize_t) expandBufGetLength(pReq)) {
- ALOGE("Failed sending req to debugger: %s (%d of %d)",
- strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
- return false;
- }
-
- return true;
-}
-
-/*
- * Send a request that was split into multiple buffers.
- *
- * The entire packet must be sent with a single writev() call to avoid
- * threading issues.
- *
- * Returns "true" if it was sent successfully.
- */
-static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
- int iovcnt)
-{
- JdwpNetState* netState = state->netState;
-
- if (netState->clientSock < 0) {
- /* can happen with some DDMS events */
- ALOGV("NOT sending request -- no debugger is attached");
- return false;
- }
-
- size_t expected = 0;
- int i;
- for (i = 0; i < iovcnt; i++)
- expected += iov[i].iov_len;
-
- ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
-
- if ((size_t)actual != expected) {
- ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
- strerror(errno), (int) actual, expected);
- return false;
- }
-
- return true;
-}
-
-
-/*
- * Our functions.
- *
- * We can't generally share the implementations with other transports,
- * even if they're also socket-based, because our JdwpNetState will be
- * different from theirs.
- */
-static const JdwpTransport socketTransport = {
- prepareSocket,
- acceptConnection,
- establishConnection,
- closeConnection,
- netShutdownExtern,
- netFreeExtern,
- isConnected,
- awaitingHandshake,
- processIncoming,
- sendRequest,
- sendBufferedRequest,
-};
-
-/*
- * Return our set.
- */
-const JdwpTransport* dvmJdwpSocketTransport()
-{
- return &socketTransport;
-}
diff --git a/vm/jdwp/README.txt b/vm/jdwp/README.txt
deleted file mode 100644
index c97a0699b..000000000
--- a/vm/jdwp/README.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Java Debug Wire Protocol support
-
-This is a reasonably complete implementation, but only messages that are
-actually generated by debuggers have been implemented. The reasoning
-behind this is that it's better to leave a call unimplemented than have
-something that appears implemented but has never been tested.
-
-An attempt has been made to keep the implementation distinct from the VM,
-with Debugger.c acting as a sort of portability layer, so that the code
-might be useful in other projects. Once you get multiple simultaneous
-events and debugger requests with thread suspension bouncing around,
-though, it's difficult to keep things "generic".