diff options
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | Android.mk | 2 | ||||
| -rw-r--r-- | adbconnection/Android.bp | 77 | ||||
| -rw-r--r-- | adbconnection/adbconnection.cc | 639 | ||||
| -rw-r--r-- | adbconnection/adbconnection.h | 155 | ||||
| -rw-r--r-- | cmdline/cmdline_parser_test.cc | 5 | ||||
| -rw-r--r-- | cmdline/cmdline_types.h | 3 | ||||
| -rw-r--r-- | runtime/jdwp_provider.h | 1 | ||||
| -rw-r--r-- | runtime/runtime.cc | 5 |
9 files changed, 888 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp index 2400115663..197860694b 100644 --- a/Android.bp +++ b/Android.bp @@ -20,6 +20,7 @@ art_static_dependencies = [ ] subdirs = [ + "adbconnection", "benchmark", "build", "cmdline", diff --git a/Android.mk b/Android.mk index 174cde36ef..cb0b709107 100644 --- a/Android.mk +++ b/Android.mk @@ -370,6 +370,7 @@ LOCAL_REQUIRED_MODULES := \ libopenjdkjvmti \ patchoat \ profman \ + libadbconnection \ # For nosy apps, we provide a fake library that avoids namespace issues and gives some warnings. LOCAL_REQUIRED_MODULES += libart_fake @@ -395,6 +396,7 @@ LOCAL_REQUIRED_MODULES += \ libopenjdkjvmtid \ patchoatd \ profmand \ + libadbconnectiond \ endif endif diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp new file mode 100644 index 0000000000..b2f82dd97b --- /dev/null +++ b/adbconnection/Android.bp @@ -0,0 +1,77 @@ +// +// Copyright (C) 2017 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} + +cc_defaults { + name: "adbconnection-defaults", + host_supported: true, + srcs: ["adbconnection.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it requires + // to be same ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + target: { + android: { + shared_libs: [ + "libcutils", + ], + }, + host: { + }, + }, + header_libs: [ + "libnativehelper_header_only", + "dt_fd_forward_export", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, + required: [ + "libjdwp", + "libdt_fd_forward", + ], +} + +art_cc_library { + name: "libadbconnection", + defaults: ["adbconnection-defaults"], + shared_libs: [ + "libart", + ], +} + +art_cc_library { + name: "libadbconnectiond", + defaults: [ + "art_debug_defaults", + "adbconnection-defaults", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc new file mode 100644 index 0000000000..2a9982a6e4 --- /dev/null +++ b/adbconnection/adbconnection.cc @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2017 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 <array> + +#include "adbconnection.h" + +#include "android-base/endian.h" +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" +#include "runtime-inl.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "well_known_classes.h" + +#include "jdwp/jdwp_priv.h" + +#include "fd_transport.h" + +#include "poll.h" + +#ifdef ART_TARGET_ANDROID +#include "cutils/sockets.h" +#endif + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/eventfd.h> +#include <jni.h> + +namespace adbconnection { + +using dt_fd_forward::kListenStartMessage; +using dt_fd_forward::kListenEndMessage; +using dt_fd_forward::kAcceptMessage; +using dt_fd_forward::kCloseMessage; + +using android::base::StringPrintf; + +static constexpr int kEventfdLocked = 0; +static constexpr int kEventfdUnlocked = 1; +static constexpr int kControlSockSendTimeout = 10; + +static AdbConnectionState* gState; + +static bool IsDebuggingPossible() { + // TODO We need to do this on IsJdwpAllowed not IsDebuggable in order to support userdebug + // workloads. For now we will only allow it when we are debuggable so that testing is easier. + return art::Runtime::Current()->IsJavaDebuggable() && art::Dbg::IsJdwpAllowed(); +} + +// Begin running the debugger. +void AdbConnectionDebuggerController::StartDebugger() { + if (IsDebuggingPossible()) { + connection_->StartDebuggerThreads(); + } else { + LOG(ERROR) << "Not starting debugger since process cannot load the jdwp agent."; + } +} + +// The debugger should begin shutting down since the runtime is ending. We don't actually do +// anything here. The real shutdown has already happened as far as the agent is concerned. +void AdbConnectionDebuggerController::StopDebugger() { } + +bool AdbConnectionDebuggerController::IsDebuggerConfigured() { + return IsDebuggingPossible() && !art::Runtime::Current()->GetJdwpOptions().empty(); +} + +void AdbConnectionDdmCallback::DdmPublishChunk(uint32_t type, + const art::ArrayRef<const uint8_t>& data) { + connection_->PublishDdmData(type, data); +} + +class ScopedEventFdLock { + public: + explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) { + TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_))); + } + + ~ScopedEventFdLock() { + TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); + } + + private: + int fd_; + uint64_t data_; +}; + +AdbConnectionState::AdbConnectionState(const std::string& agent_name) + : agent_name_(agent_name), + controller_(this), + ddm_callback_(this), + sleep_event_fd_(-1), + control_sock_(-1), + local_agent_control_sock_(-1), + remote_agent_control_sock_(-1), + adb_connection_socket_(-1), + adb_write_event_fd_(-1), + shutting_down_(false), + agent_loaded_(false), + agent_listening_(false), + next_ddm_id_(1) { + // Setup the addr. + control_addr_.controlAddrUn.sun_family = AF_UNIX; + control_addr_len_ = sizeof(control_addr_.controlAddrUn.sun_family) + sizeof(kJdwpControlName) - 1; + memcpy(control_addr_.controlAddrUn.sun_path, kJdwpControlName, sizeof(kJdwpControlName) - 1); + + // Add the startup callback. + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&controller_); +} + +static jobject CreateAdbConnectionThread(art::Thread* thr) { + JNIEnv* env = thr->GetJniEnv(); + // Move to native state to talk with the jnienv api. + art::ScopedThreadStateChange stsc(thr, art::kNative); + ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName)); + ScopedLocalRef<jobject> thr_group( + env, + env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup, + art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup)); + return env->NewObject(art::WellKnownClasses::java_lang_Thread, + art::WellKnownClasses::java_lang_Thread_init, + thr_group.get(), + thr_name.get(), + /*Priority*/ 0, + /*Daemon*/ true); +} + +struct CallbackData { + AdbConnectionState* this_; + jobject thr_; +}; + +static void* CallbackFunction(void* vdata) { + std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata)); + art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName, + true, + data->thr_); + CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached."; + // The name in Attach() is only for logging. Set the thread name. This is important so + // that the thread is no longer seen as starting up. + { + art::ScopedObjectAccess soa(self); + self->SetThreadName(kAdbConnectionThreadName); + } + + // Release the peer. + JNIEnv* env = self->GetJniEnv(); + env->DeleteGlobalRef(data->thr_); + data->thr_ = nullptr; + { + // The StartThreadBirth was called in the parent thread. We let the runtime know we are up + // before going into the provided code. + art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); + art::Runtime::Current()->EndThreadBirth(); + } + data->this_->RunPollLoop(self); + int detach_result = art::Runtime::Current()->GetJavaVM()->DetachCurrentThread(); + CHECK_EQ(detach_result, 0); + + return nullptr; +} + +void AdbConnectionState::StartDebuggerThreads() { + // First do all the final setup we need. + CHECK_EQ(adb_write_event_fd_.get(), -1); + CHECK_EQ(sleep_event_fd_.get(), -1); + CHECK_EQ(local_agent_control_sock_.get(), -1); + CHECK_EQ(remote_agent_control_sock_.get(), -1); + + sleep_event_fd_.reset(eventfd(kEventfdLocked, EFD_CLOEXEC)); + CHECK_NE(sleep_event_fd_.get(), -1) << "Unable to create wakeup eventfd."; + adb_write_event_fd_.reset(eventfd(kEventfdUnlocked, EFD_CLOEXEC)); + CHECK_NE(adb_write_event_fd_.get(), -1) << "Unable to create write-lock eventfd."; + + { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&ddm_callback_); + } + // Setup the socketpair we use to talk to the agent. + bool has_sockets; + do { + has_sockets = android::base::Socketpair(AF_UNIX, + SOCK_SEQPACKET | SOCK_CLOEXEC, + 0, + &local_agent_control_sock_, + &remote_agent_control_sock_); + } while (!has_sockets && errno == EINTR); + if (!has_sockets) { + PLOG(FATAL) << "Unable to create socketpair for agent control!"; + } + + // Next start the threads. + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); + { + art::Runtime* runtime = art::Runtime::Current(); + art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); + if (runtime->IsShuttingDownLocked()) { + // The runtime is shutting down so we cannot create new threads. This shouldn't really happen. + LOG(ERROR) << "The runtime is shutting down when we are trying to start up the debugger!"; + return; + } + runtime->StartThreadBirth(); + } + ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self())); + pthread_t pthread; + std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) }); + int pthread_create_result = pthread_create(&pthread, + nullptr, + &CallbackFunction, + data.get()); + if (pthread_create_result != 0) { + // If the create succeeded the other thread will call EndThreadBirth. + art::Runtime* runtime = art::Runtime::Current(); + soa.Env()->DeleteGlobalRef(data->thr_); + LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!"; + art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_); + runtime->EndThreadBirth(); + return; + } + data.release(); +} + +static bool FlagsSet(int16_t data, int16_t flags) { + return (data & flags) == flags; +} + +void AdbConnectionState::CloseFds() { + // Lock the write_event_fd so that concurrent PublishDdms will see that the connection is closed. + ScopedEventFdLock lk(adb_write_event_fd_); + // shutdown(adb_connection_socket_, SHUT_RDWR); + adb_connection_socket_.reset(); +} + +uint32_t AdbConnectionState::NextDdmId() { + // Just have a normal counter but always set the sign bit. + return (next_ddm_id_++) | 0x80000000; +} + +void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data) { + // Get the write_event early to fail fast. + ScopedEventFdLock lk(adb_write_event_fd_); + if (adb_connection_socket_ == -1) { + LOG(WARNING) << "Not sending ddms data of type " + << StringPrintf("%c%c%c%c", + static_cast<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type)) << " due to no connection!"; + // Adb is not connected. + return; + } + + // the adb_write_event_fd_ will ensure that the adb_connection_socket_ will not go away until + // after we have sent our data. + static constexpr uint32_t kDdmPacketHeaderSize = + kJDWPHeaderLen // jdwp command packet size + + sizeof(uint32_t) // Type + + sizeof(uint32_t); // length + std::array<uint8_t, kDdmPacketHeaderSize> pkt; + uint8_t* pkt_data = pkt.data(); + + // Write the length first. + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(kDdmPacketHeaderSize + data.size()); + pkt_data += sizeof(uint32_t); + + // Write the id next; + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(NextDdmId()); + pkt_data += sizeof(uint32_t); + + // next the flags. (0 for cmd packet because DDMS). + *(pkt_data++) = 0; + // Now the cmd-set + *(pkt_data++) = kJDWPDdmCmdSet; + // Now the command + *(pkt_data++) = kJDWPDdmCmd; + + // now the type. + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(type); + pkt_data += sizeof(uint32_t); + + // Now the data.size() + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(data.size()); + pkt_data += sizeof(uint32_t); + + static uint32_t constexpr kIovSize = 2; + struct iovec iovs[kIovSize] = { + { pkt.data(), pkt.size() }, + { const_cast<uint8_t*>(data.data()), data.size() }, + }; + // now pkt_header has the header. + // use writev to send the actual data. + ssize_t res = TEMP_FAILURE_RETRY(writev(adb_connection_socket_, iovs, kIovSize)); + if (static_cast<size_t>(res) != (kDdmPacketHeaderSize + data.size())) { + PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)", + static_cast<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type), + res, data.size() + kDdmPacketHeaderSize); + } else { + VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu", + static_cast<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type), + data.size() + kDdmPacketHeaderSize); + } +} + +void AdbConnectionState::SendAgentFds() { + // TODO + DCHECK(!sent_agent_fds_); + char dummy = '!'; + union { + cmsghdr cm; + char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)]; + } cm_un; + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = 1; + + msghdr msg; + msg.msg_name = nullptr; + 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); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(dt_fd_forward::FdSet::kDataLength); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + // Duplicate the fds before sending them. + android::base::unique_fd read_fd(dup(adb_connection_socket_)); + CHECK_NE(read_fd.get(), -1) << "Failed to dup read_fd_: " << strerror(errno); + android::base::unique_fd write_fd(dup(adb_connection_socket_)); + CHECK_NE(write_fd.get(), -1) << "Failed to dup write_fd: " << strerror(errno); + android::base::unique_fd write_lock_fd(dup(adb_write_event_fd_)); + CHECK_NE(write_lock_fd.get(), -1) << "Failed to dup write_lock_fd: " << strerror(errno); + + dt_fd_forward::FdSet { + read_fd.get(), write_fd.get(), write_lock_fd.get() + }.WriteData(CMSG_DATA(cmsg)); + + int res = TEMP_FAILURE_RETRY(sendmsg(local_agent_control_sock_, &msg, MSG_EOR)); + if (res < 0) { + PLOG(ERROR) << "Failed to send agent adb connection fds."; + } else { + sent_agent_fds_ = true; + VLOG(jdwp) << "Fds have been sent to jdwp agent!"; + } +} + +android::base::unique_fd AdbConnectionState::ReadFdFromAdb() { + // We don't actually care about the data that is sent. We do need to receive something though. + char dummy = '!'; + union { + cmsghdr cm; + char buffer[CMSG_SPACE(sizeof(int))]; + } cm_un; + + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = 1; + + msghdr msg; + msg.msg_name = nullptr; + 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); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + (reinterpret_cast<int*>(CMSG_DATA(cmsg)))[0] = -1; + + int rc = TEMP_FAILURE_RETRY(recvmsg(control_sock_, &msg, 0)); + + if (rc <= 0) { + PLOG(WARNING) << "Receiving file descriptor from ADB failed (socket " << control_sock_ << ")"; + return android::base::unique_fd(-1); + } else { + VLOG(jdwp) << "Fds have been received from ADB!"; + } + + return android::base::unique_fd((reinterpret_cast<int*>(CMSG_DATA(cmsg)))[0]); +} + +bool AdbConnectionState::SetupAdbConnection() { + int sleep_ms = 500; + const int sleep_max_ms = 2*1000; + char buff[sizeof(pid_t) + 1]; + + android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET, 0)); + if (sock < 0) { + PLOG(ERROR) << "Could not create ADB control socket"; + return false; + } + struct timeval timeout; + timeout.tv_sec = kControlSockSendTimeout; + timeout.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + snprintf(buff, sizeof(buff), "%04x", getpid()); + buff[sizeof(pid_t)] = 0; + + while (!shutting_down_) { + // 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(sock, &control_addr_.controlAddrPlain, control_addr_len_); + if (ret == 0) { + bool trusted = sock >= 0; +#ifdef ART_TARGET_ANDROID + // Needed for socket_peer_is_trusted. + trusted = trusted && socket_peer_is_trusted(sock); +#endif + if (!trusted) { + LOG(ERROR) << "adb socket is not trusted. Aborting connection."; + if (sock >= 0 && shutdown(sock, SHUT_RDWR)) { + PLOG(ERROR) << "trouble shutting down socket"; + } + return false; + } + /* now try to send our pid to the ADB daemon */ + ret = TEMP_FAILURE_RETRY(send(sock, buff, sizeof(pid_t), 0)); + if (ret == sizeof(pid_t)) { + LOG(INFO) << "PID " << getpid() << " send to adb"; + control_sock_ = std::move(sock); + return true; + } else { + PLOG(ERROR) << "Weird, can't send JDWP process pid to ADB. Aborting connection."; + return false; + } + } else { + PLOG(ERROR) << "Can't connect to ADB control socket. Will retry."; + + usleep(sleep_ms * 1000); + + sleep_ms += (sleep_ms >> 1); + if (sleep_ms > sleep_max_ms) { + sleep_ms = sleep_max_ms; + } + } + } + return false; +} + +void AdbConnectionState::RunPollLoop(art::Thread* self) { + CHECK_EQ(self->GetState(), art::kNative); + art::Locks::mutator_lock_->AssertNotHeld(self); + self->SetState(art::kWaitingInMainDebuggerLoop); + // shutting_down_ set by StopDebuggerThreads + while (!shutting_down_) { + // First get the control_sock_ from adb if we don't have one. We only need to do this once. + if (control_sock_ == -1 && !SetupAdbConnection()) { + LOG(ERROR) << "Failed to setup adb connection."; + return; + } + while (!shutting_down_ && control_sock_ != -1) { + struct pollfd pollfds[4] = { + { sleep_event_fd_, POLLIN, 0 }, + // -1 as an fd causes it to be ignored by poll + { (agent_loaded_ ? local_agent_control_sock_ : -1), POLLIN, 0 }, + // Check for the control_sock_ actually going away. Only do this if we don't have an active + // connection. + { (adb_connection_socket_ == -1 ? control_sock_ : -1), POLLIN | POLLRDHUP, 0 }, + // if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we don't + // have a real connection yet or the socket through adb needs to be listened to for incoming + // data that the agent can handle. + { ((!agent_has_socket_ && !sent_agent_fds_) ? adb_connection_socket_ : -1), POLLIN, 0 } + }; + int res = TEMP_FAILURE_RETRY(poll(pollfds, 4, -1)); + if (res < 0) { + PLOG(ERROR) << "Failed to poll!"; + return; + } + // We don't actually care about doing this we just use it to wake us up. + // const struct pollfd& sleep_event_poll = pollfds[0]; + const struct pollfd& agent_control_sock_poll = pollfds[1]; + const struct pollfd& control_sock_poll = pollfds[2]; + const struct pollfd& adb_socket_poll = pollfds[3]; + if (FlagsSet(agent_control_sock_poll.revents, POLLIN)) { + DCHECK(agent_loaded_); + char buf[257]; + res = TEMP_FAILURE_RETRY(recv(local_agent_control_sock_, buf, sizeof(buf) - 1, 0)); + if (res < 0) { + PLOG(ERROR) << "Failed to read message from agent control socket! Retrying"; + continue; + } else { + buf[res + 1] = '\0'; + VLOG(jdwp) << "Local agent control sock has data: " << static_cast<const char*>(buf); + } + if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) { + agent_listening_ = true; + if (adb_connection_socket_ != -1) { + SendAgentFds(); + } + } else if (memcmp(kListenEndMessage, buf, sizeof(kListenEndMessage)) == 0) { + agent_listening_ = false; + } else if (memcmp(kCloseMessage, buf, sizeof(kCloseMessage)) == 0) { + CloseFds(); + agent_has_socket_ = false; + } else if (memcmp(kAcceptMessage, buf, sizeof(kAcceptMessage)) == 0) { + agent_has_socket_ = true; + sent_agent_fds_ = false; + } else { + LOG(ERROR) << "Unknown message received from debugger! '" << std::string(buf) << "'"; + } + } else if (FlagsSet(control_sock_poll.revents, POLLIN)) { + bool maybe_send_fds = false; + { + // Hold onto this lock so that concurrent ddm publishes don't try to use an illegal fd. + ScopedEventFdLock sefdl(adb_write_event_fd_); + android::base::unique_fd new_fd(ReadFdFromAdb()); + if (new_fd == -1) { + // Something went wrong. We need to retry getting the control socket. + PLOG(ERROR) << "Something went wrong getting fds from adb. Retry!"; + control_sock_.reset(); + break; + } else if (adb_connection_socket_ != -1) { + // We already have a connection. + VLOG(jdwp) << "Ignoring second debugger. Accept then drop!"; + if (new_fd >= 0) { + new_fd.reset(); + } + } else { + VLOG(jdwp) << "Adb connection established with fd " << new_fd; + adb_connection_socket_ = std::move(new_fd); + maybe_send_fds = true; + } + } + if (maybe_send_fds && agent_loaded_ && agent_listening_) { + VLOG(jdwp) << "Sending fds as soon as we received them."; + SendAgentFds(); + } + } else if (FlagsSet(control_sock_poll.revents, POLLRDHUP)) { + // The other end of the adb connection just dropped it. + // Reset the connection since we don't have an active socket through the adb server. + DCHECK(!agent_has_socket_) << "We shouldn't be doing anything if there is already a " + << "connection active"; + control_sock_.reset(); + break; + } else if (FlagsSet(adb_socket_poll.revents, POLLIN)) { + DCHECK(!agent_has_socket_); + if (!agent_loaded_) { + DCHECK(!agent_listening_); + // Load the agent now! + self->AssertNoPendingException(); + art::Runtime::Current()->AttachAgent(MakeAgentArg()); + if (self->IsExceptionPending()) { + LOG(ERROR) << "Failed to load agent " << agent_name_; + art::ScopedObjectAccess soa(self); + self->GetException()->Dump(); + self->ClearException(); + return; + } + agent_loaded_ = true; + } else if (agent_listening_ && !sent_agent_fds_) { + VLOG(jdwp) << "Sending agent fds again on data."; + SendAgentFds(); + } + } else { + VLOG(jdwp) << "Woke up poll without anything to do!"; + } + } + } +} + +std::string AdbConnectionState::MakeAgentArg() { + // TODO Get this from something user settable? + const std::string& opts = art::Runtime::Current()->GetJdwpOptions(); + return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") + + "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_); +} + +void AdbConnectionState::StopDebuggerThreads() { + // The regular agent system will take care of unloading the agent (if needed). + shutting_down_ = true; + // Wakeup the poll loop. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data))); +} + +// The plugin initialization function. +extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection); + // TODO Provide some way for apps to set this maybe? + gState = new AdbConnectionState(kDefaultJdwpAgentName); + CHECK(gState != nullptr); + return true; +} + +extern "C" bool ArtPlugin_Deinitialize() { + CHECK(gState != nullptr); + // Just do this a second time? + // TODO I don't think this should be needed. + gState->StopDebuggerThreads(); + delete gState; + return true; +} + +} // namespace adbconnection diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h new file mode 100644 index 0000000000..28a5a05af3 --- /dev/null +++ b/adbconnection/adbconnection.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef ART_ADBCONNECTION_ADBCONNECTION_H_ +#define ART_ADBCONNECTION_ADBCONNECTION_H_ + +#include <stdint.h> +#include <vector> +#include <limits> + +#include "android-base/unique_fd.h" + +#include "base/mutex.h" +#include "runtime_callbacks.h" + +#include <sys/socket.h> +#include <sys/un.h> +#include <jni.h> + +namespace adbconnection { + +static constexpr char kJdwpControlName[] = "\0jdwp-control"; +static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread"; + +// The default jdwp agent name. +static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so"; + +class AdbConnectionState; + +struct AdbConnectionDebuggerController : public art::DebuggerControlCallback { + explicit AdbConnectionDebuggerController(AdbConnectionState* connection) + : connection_(connection) {} + + // Begin running the debugger. + void StartDebugger() OVERRIDE; + + // The debugger should begin shutting down since the runtime is ending. + void StopDebugger() OVERRIDE; + + bool IsDebuggerConfigured() OVERRIDE; + + private: + AdbConnectionState* connection_; +}; + +struct AdbConnectionDdmCallback : public art::DdmCallback { + explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {} + + void DdmPublishChunk(uint32_t type, + const art::ArrayRef<const uint8_t>& data) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + AdbConnectionState* connection_; +}; + +class AdbConnectionState { + public: + explicit AdbConnectionState(const std::string& agent_name); + + // Called on the listening thread to start dealing with new input. thr is used to attach the new + // thread to the runtime. + void RunPollLoop(art::Thread* self); + + // Sends ddms data over the socket, if there is one. This data is sent even if we haven't finished + // hand-shaking yet. + void PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data); + + // Stops debugger threads during shutdown. + void StopDebuggerThreads(); + + private: + uint32_t NextDdmId(); + + void StartDebuggerThreads(); + + // Tell adbd about the new runtime. + bool SetupAdbConnection(); + + std::string MakeAgentArg(); + + android::base::unique_fd ReadFdFromAdb(); + + void SendAgentFds(); + + void CloseFds(); + + std::string agent_name_; + + AdbConnectionDebuggerController controller_; + AdbConnectionDdmCallback ddm_callback_; + + // Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads + android::base::unique_fd sleep_event_fd_; + + // Socket that we use to talk to adbd. + android::base::unique_fd control_sock_; + + // Socket that we use to talk to the agent (if it's loaded). + android::base::unique_fd local_agent_control_sock_; + + // The fd of the socket the agent uses to talk to us. We need to keep it around in order to clean + // it up when the runtime goes away. + android::base::unique_fd remote_agent_control_sock_; + + // The fd that is forwarded through adb to the client. This is guarded by the + // adb_write_event_fd_. + android::base::unique_fd adb_connection_socket_; + + // The fd we send to the agent to let us synchronize access to the shared adb_connection_socket_. + // This is also used as a general lock for the adb_connection_socket_ on any threads other than + // the poll thread. + android::base::unique_fd adb_write_event_fd_; + + std::atomic<bool> shutting_down_; + + // True if we have loaded the agent library. + std::atomic<bool> agent_loaded_; + + // True if the dt_fd_forward transport is listening for a new communication channel. + std::atomic<bool> agent_listening_; + + // True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or + // the adb connection socket until connection goes away. + std::atomic<bool> agent_has_socket_; + + std::atomic<bool> sent_agent_fds_; + + std::atomic<uint32_t> next_ddm_id_; + + socklen_t control_addr_len_; + union { + sockaddr_un controlAddrUn; + sockaddr controlAddrPlain; + } control_addr_; + + friend struct AdbConnectionDebuggerController; +}; + +} // namespace adbconnection + +#endif // ART_ADBCONNECTION_ADBCONNECTION_H_ diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 802200013d..5d672061df 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -388,6 +388,11 @@ TEST_F(CmdlineParserTest, TestJdwpProviderNone) { EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kNone, opt_args, M::JdwpProvider); } // TEST_F +TEST_F(CmdlineParserTest, TestJdwpProviderAdbconnection) { + const char* opt_args = "-XjdwpProvider:adbconnection"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kAdbConnection, opt_args, M::JdwpProvider); +} // TEST_F + TEST_F(CmdlineParserTest, TestJdwpProviderHelp) { EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:help", CmdlineResult::kUsage); } // TEST_F diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 5c887f8a73..529fe2bcdb 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -75,10 +75,13 @@ struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> { return Result::Usage( "Example: -XjdwpProvider:none to disable JDWP\n" "Example: -XjdwpProvider:internal for internal jdwp implementation\n" + "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n" "Example: -XjdwpProvider:default for the default jdwp implementation" " (currently internal)\n"); } else if (option == "internal" || option == "default") { return Result::Success(JdwpProvider::kInternal); + } else if (option == "adbconnection") { + return Result::Success(JdwpProvider::kAdbConnection); } else if (option == "none") { return Result::Success(JdwpProvider::kNone); } else { diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 849ba211fe..b62e10b4f8 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -27,6 +27,7 @@ namespace art { enum class JdwpProvider { kNone, kInternal, + kAdbConnection, }; std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9c527e7fcd..a172392c51 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1241,6 +1241,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } break; } + case JdwpProvider::kAdbConnection: { + constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" + : "libadbconnection.so"; + plugins_.push_back(Plugin::Create(plugin_name)); + } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback()); |