Merge changes Ia4244757,Ibcdf69d9
* changes: adbd: clean up jdwp service a bit. Revert "Revert "adb: detect some spin loops and abort.""
This commit is contained in:
commit
f3186de123
3 changed files with 121 additions and 65 deletions
|
@ -212,6 +212,7 @@ static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
|
|||
|
||||
static void jdwp_process_event(int socket, unsigned events, void* _proc) {
|
||||
JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
|
||||
CHECK_EQ(socket, proc->socket);
|
||||
|
||||
if (events & FDE_READ) {
|
||||
if (proc->pid < 0) {
|
||||
|
@ -225,82 +226,50 @@ static void jdwp_process_event(int socket, unsigned events, void* _proc) {
|
|||
D("Adding pid %d to jdwp process list", proc->pid);
|
||||
jdwp_process_list_updated();
|
||||
} else {
|
||||
/* the pid was read, if we get there it's probably because the connection
|
||||
* was closed (e.g. the JDWP process exited or crashed) */
|
||||
char buf[32];
|
||||
|
||||
while (true) {
|
||||
int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
|
||||
|
||||
if (len == 0) {
|
||||
D("terminating JDWP %d connection: EOF", proc->pid);
|
||||
break;
|
||||
} else if (len < 0) {
|
||||
if (len < 0 && errno == EAGAIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
D("terminating JDWP %d connection: EOF", proc->pid);
|
||||
break;
|
||||
} else {
|
||||
D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
|
||||
len);
|
||||
}
|
||||
}
|
||||
|
||||
// We already have the PID, if we can read from the socket, we've probably hit EOF.
|
||||
D("terminating JDWP connection %d", proc->pid);
|
||||
goto CloseProcess;
|
||||
}
|
||||
}
|
||||
|
||||
if (events & FDE_WRITE) {
|
||||
D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
|
||||
if (!proc->out_fds.empty()) {
|
||||
int fd = proc->out_fds.back().get();
|
||||
struct cmsghdr* cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
|
||||
CHECK(!proc->out_fds.empty());
|
||||
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
int fd = proc->out_fds.back().get();
|
||||
struct cmsghdr* cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
|
||||
if (!set_file_block_mode(proc->socket, true)) {
|
||||
VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
|
||||
goto CloseProcess;
|
||||
}
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
|
||||
int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
|
||||
if (ret < 0) {
|
||||
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
|
||||
if (ret < 0) {
|
||||
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
|
||||
D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
|
||||
|
||||
proc->out_fds.pop_back();
|
||||
|
||||
if (!set_file_block_mode(proc->socket, false)) {
|
||||
VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
if (proc->out_fds.empty()) {
|
||||
fdevent_del(proc->fde, FDE_WRITE);
|
||||
}
|
||||
proc->out_fds.pop_back();
|
||||
if (proc->out_fds.empty()) {
|
||||
fdevent_del(proc->fde, FDE_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,9 +375,10 @@ static int jdwp_control_init(JdwpControl* control, const char* sockname, int soc
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void jdwp_control_event(int s, unsigned events, void* _control) {
|
||||
static void jdwp_control_event(int fd, unsigned events, void* _control) {
|
||||
JdwpControl* control = (JdwpControl*)_control;
|
||||
|
||||
CHECK_EQ(fd, control->listen_socket);
|
||||
if (events & FDE_READ) {
|
||||
int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
|
||||
if (s < 0) {
|
||||
|
|
|
@ -202,6 +202,27 @@ unique_fd ShellService(const std::string& args, const atransport* transport) {
|
|||
return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
|
||||
}
|
||||
|
||||
static void spin_service(unique_fd fd) {
|
||||
if (!__android_log_is_debuggable()) {
|
||||
WriteFdExactly(fd.get(), "refusing to spin on non-debuggable build\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// A service that creates an fdevent that's always pending, and then ignores it.
|
||||
unique_fd pipe_read, pipe_write;
|
||||
if (!Pipe(&pipe_read, &pipe_write)) {
|
||||
WriteFdExactly(fd.get(), "failed to create pipe\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fdevent_run_on_main_thread([fd = pipe_read.release()]() {
|
||||
fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
|
||||
fdevent_add(fde, FDE_READ);
|
||||
});
|
||||
|
||||
WriteFdExactly(fd.get(), "spinning\n");
|
||||
}
|
||||
|
||||
unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
|
||||
if (!strncmp("dev:", name, 4)) {
|
||||
return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
|
||||
|
@ -254,6 +275,9 @@ unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
|
|||
} else if (!strcmp(name, "reconnect")) {
|
||||
return create_service_thread(
|
||||
"reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
|
||||
} else if (!strcmp(name, "spin")) {
|
||||
return create_service_thread("spin", spin_service);
|
||||
}
|
||||
|
||||
return unique_fd{};
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
|
@ -44,6 +46,7 @@
|
|||
#include "adb_trace.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
|
||||
#define FDE_EVENTMASK 0x00ff
|
||||
#define FDE_STATEMASK 0xff00
|
||||
|
@ -247,6 +250,7 @@ static void fdevent_process() {
|
|||
}
|
||||
CHECK_GT(pollfds.size(), 0u);
|
||||
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
|
||||
|
||||
int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
|
||||
if (ret == -1) {
|
||||
PLOG(ERROR) << "poll(), ret = " << ret;
|
||||
|
@ -367,10 +371,66 @@ void fdevent_run_on_main_thread(std::function<void()> fn) {
|
|||
}
|
||||
}
|
||||
|
||||
static void fdevent_check_spin(uint64_t cycle) {
|
||||
// Check to see if we're spinning because we forgot about an fdevent
|
||||
// by keeping track of how long fdevents have been continuously pending.
|
||||
struct SpinCheck {
|
||||
fdevent* fde;
|
||||
android::base::boot_clock::time_point timestamp;
|
||||
uint64_t cycle;
|
||||
};
|
||||
static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
|
||||
static auto last_cycle = android::base::boot_clock::now();
|
||||
|
||||
auto now = android::base::boot_clock::now();
|
||||
if (now - last_cycle > 10ms) {
|
||||
// We're not spinning.
|
||||
g_continuously_pending.clear();
|
||||
last_cycle = now;
|
||||
return;
|
||||
}
|
||||
last_cycle = now;
|
||||
|
||||
for (auto* fde : g_pending_list) {
|
||||
auto it = g_continuously_pending.find(fde->id);
|
||||
if (it == g_continuously_pending.end()) {
|
||||
g_continuously_pending[fde->id] =
|
||||
SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
|
||||
} else {
|
||||
it->second.cycle = cycle;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
|
||||
if (it->second.cycle != cycle) {
|
||||
it = g_continuously_pending.erase(it);
|
||||
} else {
|
||||
// Use an absurdly long window, since all we really care about is
|
||||
// getting a bugreport eventually.
|
||||
if (now - it->second.timestamp > 300s) {
|
||||
LOG(FATAL_WITHOUT_ABORT)
|
||||
<< "detected spin in fdevent: " << dump_fde(it->second.fde);
|
||||
#if defined(__linux__)
|
||||
int fd = it->second.fde->fd.get();
|
||||
std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
|
||||
std::string path;
|
||||
if (!android::base::Readlink(fd_path, &path)) {
|
||||
PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
|
||||
}
|
||||
LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fdevent_loop() {
|
||||
set_main_thread();
|
||||
fdevent_run_setup();
|
||||
|
||||
uint64_t cycle = 0;
|
||||
while (true) {
|
||||
if (terminate_loop) {
|
||||
return;
|
||||
|
@ -380,6 +440,8 @@ void fdevent_loop() {
|
|||
|
||||
fdevent_process();
|
||||
|
||||
fdevent_check_spin(cycle++);
|
||||
|
||||
while (!g_pending_list.empty()) {
|
||||
fdevent* fde = g_pending_list.front();
|
||||
g_pending_list.pop_front();
|
||||
|
|
Loading…
Reference in a new issue