diff options
| author | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:46:17 -0700 |
|---|---|---|
| committer | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:51:13 -0700 |
| commit | 870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch) | |
| tree | 7487dad3970556a040f88a49852a3dc9ed19bebd /vm/jdwp | |
| parent | 76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff) | |
| download | android_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.cpp | 175 | ||||
| -rw-r--r-- | vm/jdwp/ExpandBuf.h | 56 | ||||
| -rw-r--r-- | vm/jdwp/Jdwp.h | 236 | ||||
| -rw-r--r-- | vm/jdwp/JdwpAdb.cpp | 746 | ||||
| -rw-r--r-- | vm/jdwp/JdwpConstants.cpp | 260 | ||||
| -rw-r--r-- | vm/jdwp/JdwpConstants.h | 230 | ||||
| -rw-r--r-- | vm/jdwp/JdwpEvent.cpp | 1278 | ||||
| -rw-r--r-- | vm/jdwp/JdwpEvent.h | 129 | ||||
| -rw-r--r-- | vm/jdwp/JdwpHandler.cpp | 1973 | ||||
| -rw-r--r-- | vm/jdwp/JdwpHandler.h | 47 | ||||
| -rw-r--r-- | vm/jdwp/JdwpMain.cpp | 449 | ||||
| -rw-r--r-- | vm/jdwp/JdwpPriv.h | 192 | ||||
| -rw-r--r-- | vm/jdwp/JdwpSocket.cpp | 910 | ||||
| -rw-r--r-- | vm/jdwp/README.txt | 12 |
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". |
