/* * Copyright (C) 2015 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. */ #if !ADB_HOST #if !defined(__ANDROID_RECOVERY__) #define TRACE_TAG JDWP #include "sysdeps.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "adb.h" #include "adb_io.h" #include "adb_unique_fd.h" #include "adb_utils.h" #include "app_processes.pb.h" using android::base::borrowed_fd; using android::base::unique_fd; /* here's how these things work. when adbd starts, it creates a unix server socket named @jdwp-control (@ is a shortcut for "first byte is zero" to use the private namespace instead of the file system) when a new JDWP daemon thread starts in a new VM process, it creates a connection to @jdwp-control to announce its availability. JDWP thread @jdwp-control | | |-------------------------------> | | hello I'm in process | | | | | the connection is kept alive. it will be closed automatically if the JDWP process terminates (this allows adbd to detect dead processes). adbd thus maintains a list of "active" JDWP processes. it can send its content to clients through the "device:debug-ports" service, or even updates through the "device:track-debug-ports" service. when a debugger wants to connect, it simply runs the command equivalent to "adb forward tcp: jdwp:" "jdwp:" is a new forward destination format used to target a given JDWP process on the device. when sutch a request arrives, adbd does the following: - first, it calls socketpair() to create a pair of equivalent sockets. - it attaches the first socket in the pair to a local socket which is itself attached to the transport's remote socket: - it sends the file descriptor of the second socket directly to the JDWP process with the help of sendmsg() JDWP thread @jdwp-control | | | <----------------------| | OK, try this file descriptor | | | | | then, the JDWP thread uses this new socket descriptor as its pass-through connection to the debugger (and receives the JDWP-Handshake message, answers to it, etc...) this gives the following graphics: ____________________________________ | | | ADB Server (host) | | | Debugger <---> LocalSocket <----> RemoteSocket | | ^^ | |___________________________||_______| || Transport || (TCP for emulator - USB for device) || || ___________________________||_______ | || | | ADBD (device) || | | VV | JDWP <======> LocalSocket <----> RemoteSocket | | | |____________________________________| due to the way adb works, this doesn't need a special socket type or fancy handling of socket termination if either the debugger or the JDWP process closes the connection. THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN TO HAVE A BETTER IDEA, LET ME KNOW - Digit **********************************************************************/ /** JDWP PID List Support Code ** for each JDWP process, we record its pid and its connected socket **/ enum class TrackerKind { kJdwp, kApp, }; static void jdwp_process_event(int socket, unsigned events, void* _proc); static void jdwp_process_list_updated(void); static void app_process_list_updated(void); struct JdwpProcess; static auto& _jdwp_list = *new std::list>(); struct JdwpProcess { JdwpProcess(unique_fd socket, ProcessInfo process) { CHECK(process.pid != 0); this->socket = socket; this->process = process; this->fde = fdevent_create(socket.release(), jdwp_process_event, this); if (!this->fde) { LOG(FATAL) << "could not create fdevent for new JDWP process"; } } ~JdwpProcess() { if (this->socket >= 0) { adb_shutdown(this->socket); this->socket = -1; } if (this->fde) { fdevent_destroy(this->fde); this->fde = nullptr; } out_fds.clear(); } void RemoveFromList() { auto pred = [this](const auto& proc) { return proc.get() == this; }; _jdwp_list.remove_if(pred); } borrowed_fd socket = -1; ProcessInfo process; fdevent* fde = nullptr; std::vector out_fds; }; // Populate the list of processes for "track-jdwp" service. static size_t jdwp_process_list(char* buffer, size_t bufferlen) { std::string temp; for (auto& proc : _jdwp_list) { if (!proc->process.debuggable) continue; std::string next = std::to_string(proc->process.pid) + "\n"; if (temp.length() + next.length() > bufferlen) { D("truncating JDWP process list (max len = %zu)", bufferlen); break; } temp.append(next); } memcpy(buffer, temp.data(), temp.length()); return temp.length(); } // Populate the list of processes for "track-app" service. // The list is a protobuf message in the binary format for efficiency. static size_t app_process_list(char* buffer, size_t bufferlen) { adb::proto::AppProcesses output; // result that's guaranteed to fit in the given buffer adb::proto::AppProcesses temp; // temporary result that may be longer than the given buffer std::string serialized_message; for (auto& proc : _jdwp_list) { if (!proc->process.debuggable && !proc->process.profileable) continue; auto* entry = temp.add_process(); entry->set_pid(proc->process.pid); entry->set_debuggable(proc->process.debuggable); entry->set_profileable(proc->process.profileable); entry->set_architecture(proc->process.arch_name, proc->process.arch_name_length); temp.SerializeToString(&serialized_message); if (serialized_message.size() > bufferlen) { D("truncating app process list (max len = %zu)", bufferlen); break; } output = temp; } output.SerializeToString(&serialized_message); memcpy(buffer, serialized_message.data(), serialized_message.length()); return serialized_message.length(); } // Populate the list of processes for either "track-jdwp" or "track-app" services, // depending on the given kind. static size_t process_list(TrackerKind kind, char* buffer, size_t bufferlen) { switch (kind) { case TrackerKind::kJdwp: return jdwp_process_list(buffer, bufferlen); case TrackerKind::kApp: return app_process_list(buffer, bufferlen); } } static size_t process_list_msg(TrackerKind kind, char* buffer, size_t bufferlen) { // Message is length-prefixed with 4 hex digits in ASCII. static constexpr size_t header_len = 4; if (bufferlen < header_len) { LOG(FATAL) << "invalid JDWP process list buffer size: " << bufferlen; } char head[header_len + 1]; size_t len = process_list(kind, buffer + header_len, bufferlen - header_len); snprintf(head, sizeof head, "%04zx", len); memcpy(buffer, head, header_len); return len + header_len; } static void jdwp_process_event(int socket, unsigned events, void* _proc) { JdwpProcess* proc = reinterpret_cast(_proc); CHECK_EQ(socket, proc->socket.get()); if (events & FDE_READ) { // We already have the PID, if we can read from the socket, we've probably hit EOF. D("terminating JDWP connection %" PRId64, proc->process.pid); goto CloseProcess; } if (events & FDE_WRITE) { D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size()); CHECK(!proc->out_fds.empty()); int fd = proc->out_fds.back().get(); if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) { D("sending new file descriptor to JDWP %" PRId64 " failed: %s", proc->process.pid, strerror(errno)); goto CloseProcess; } D("sent file descriptor %d to JDWP process %" PRId64, fd, proc->process.pid); proc->out_fds.pop_back(); if (proc->out_fds.empty()) { fdevent_del(proc->fde, FDE_WRITE); } } return; CloseProcess: bool debuggable = proc->process.debuggable; bool profileable = proc->process.profileable; proc->RemoveFromList(); if (debuggable) jdwp_process_list_updated(); if (debuggable || profileable) app_process_list_updated(); } unique_fd create_jdwp_connection_fd(int pid) { D("looking for pid %d in JDWP process list", pid); for (auto& proc : _jdwp_list) { // Don't allow JDWP connection to a non-debuggable process. if (!proc->process.debuggable) continue; if (proc->process.pid == static_cast(pid)) { int fds[2]; if (adb_socketpair(fds) < 0) { D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno)); return unique_fd{}; } D("socketpair: (%d,%d)", fds[0], fds[1]); proc->out_fds.emplace_back(fds[1]); if (proc->out_fds.size() == 1) { fdevent_add(proc->fde, FDE_WRITE); } return unique_fd{fds[0]}; } } D("search failed !!"); return unique_fd{}; } /** "jdwp" local service implementation ** this simply returns the list of known JDWP process pids **/ struct JdwpSocket : public asocket { bool pass = false; }; static void jdwp_socket_close(asocket* s) { D("LS(%d): closing jdwp socket", s->id); if (s->peer) { D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd); s->peer->peer = nullptr; s->peer->close(s->peer); s->peer = nullptr; } remove_socket(s); delete s; } static int jdwp_socket_enqueue(asocket* s, apacket::payload_type) { /* you can't write to this asocket */ D("LS(%d): JDWP socket received data?", s->id); s->peer->close(s->peer); return -1; } static void jdwp_socket_ready(asocket* s) { JdwpSocket* jdwp = (JdwpSocket*)s; asocket* peer = jdwp->peer; /* on the first call, send the list of pids, * on the second one, close the connection */ if (!jdwp->pass) { apacket::payload_type data; data.resize(s->get_max_payload()); size_t len = jdwp_process_list(&data[0], data.size()); data.resize(len); peer->enqueue(peer, std::move(data)); jdwp->pass = true; } else { peer->close(peer); } } asocket* create_jdwp_service_socket(void) { JdwpSocket* s = new JdwpSocket(); if (!s) { LOG(FATAL) << "failed to allocate JdwpSocket"; } install_local_socket(s); s->ready = jdwp_socket_ready; s->enqueue = jdwp_socket_enqueue; s->close = jdwp_socket_close; s->pass = false; return s; } /** "track-jdwp" local service implementation ** this periodically sends the list of known JDWP process pids ** to the client... **/ struct JdwpTracker : public asocket { TrackerKind kind; bool need_initial; explicit JdwpTracker(TrackerKind k, bool initial) : kind(k), need_initial(initial) {} }; static auto& _jdwp_trackers = *new std::vector>(); static void process_list_updated(TrackerKind kind) { std::string data; const int kMaxLength = kind == TrackerKind::kJdwp ? 1024 : 2048; data.resize(kMaxLength); data.resize(process_list_msg(kind, &data[0], data.size())); for (auto& t : _jdwp_trackers) { if (t->kind == kind && t->peer) { // The tracker might not have been connected yet. apacket::payload_type payload(data.begin(), data.end()); t->peer->enqueue(t->peer, std::move(payload)); } } } static void jdwp_process_list_updated(void) { process_list_updated(TrackerKind::kJdwp); } static void app_process_list_updated(void) { process_list_updated(TrackerKind::kApp); } static void jdwp_tracker_close(asocket* s) { D("LS(%d): destroying jdwp tracker service", s->id); if (s->peer) { D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd); s->peer->peer = nullptr; s->peer->close(s->peer); s->peer = nullptr; } remove_socket(s); auto pred = [s](const auto& tracker) { return tracker.get() == s; }; _jdwp_trackers.erase(std::remove_if(_jdwp_trackers.begin(), _jdwp_trackers.end(), pred), _jdwp_trackers.end()); } static void jdwp_tracker_ready(asocket* s) { JdwpTracker* t = (JdwpTracker*)s; if (t->need_initial) { apacket::payload_type data; data.resize(s->get_max_payload()); data.resize(process_list_msg(t->kind, &data[0], data.size())); t->need_initial = false; s->peer->enqueue(s->peer, std::move(data)); } } static int jdwp_tracker_enqueue(asocket* s, apacket::payload_type) { /* you can't write to this socket */ D("LS(%d): JDWP tracker received data?", s->id); s->peer->close(s->peer); return -1; } static asocket* create_process_tracker_service_socket(TrackerKind kind) { auto t = std::make_unique(kind, true); if (!t) { LOG(FATAL) << "failed to allocate JdwpTracker"; } memset(t.get(), 0, sizeof(asocket)); install_local_socket(t.get()); D("LS(%d): created new jdwp tracker service", t->id); t->ready = jdwp_tracker_ready; t->enqueue = jdwp_tracker_enqueue; t->close = jdwp_tracker_close; asocket* result = t.get(); _jdwp_trackers.emplace_back(std::move(t)); return result; } asocket* create_jdwp_tracker_service_socket() { return create_process_tracker_service_socket(TrackerKind::kJdwp); } asocket* create_app_tracker_service_socket() { return create_process_tracker_service_socket(TrackerKind::kApp); } int init_jdwp(void) { std::thread([]() { adb_thread_setname("jdwp control"); adbconnection_listen([](int fd, ProcessInfo process) { LOG(INFO) << "jdwp connection from " << process.pid; fdevent_run_on_main_thread([fd, process] { unique_fd ufd(fd); auto proc = std::make_unique(std::move(ufd), process); if (!proc) { LOG(FATAL) << "failed to allocate JdwpProcess"; } _jdwp_list.emplace_back(std::move(proc)); if (process.debuggable) jdwp_process_list_updated(); if (process.debuggable || process.profileable) app_process_list_updated(); }); }); }).detach(); return 0; } #else // !defined(__ANDROID_RECOVERY) #include "adb.h" asocket* create_jdwp_service_socket(void) { return nullptr; } unique_fd create_jdwp_connection_fd(int pid) { return {}; } asocket* create_app_tracker_service_socket() { return nullptr; } asocket* create_jdwp_tracker_service_socket() { return nullptr; } int init_jdwp() { return 0; } #endif /* defined(__ANDROID_RECOVERY__) */ #endif /* !ADB_HOST */