summaryrefslogtreecommitdiffstats
path: root/adb
diff options
context:
space:
mode:
authorJosh Gao <jmgao@google.com>2018-06-18 16:37:01 -0700
committerJosh Gao <jmgao@google.com>2018-06-18 17:46:34 -0700
commit3bbc8164b12f06c819a5b2acdc525199a9ff6f3e (patch)
treea2215059e8060795a9b0af7c85d115c893898060 /adb
parentded557fa58ec2c7c2f2bea8b71d75afe21c37168 (diff)
downloadsystem_core-3bbc8164b12f06c819a5b2acdc525199a9ff6f3e.tar.gz
system_core-3bbc8164b12f06c819a5b2acdc525199a9ff6f3e.tar.bz2
system_core-3bbc8164b12f06c819a5b2acdc525199a9ff6f3e.zip
adb: detect some spin loops and abort.
Track pending fdevents, and abort if we see an fdevent ready for read/write for 5 minutes continuously. Also, add a service that intentionally starts spinning, to test this. Test: manually changed timeout to a minute, `adb raw spin` Change-Id: Ibfa5e7e654996587f745887cb04987b982d79bed
Diffstat (limited to 'adb')
-rw-r--r--adb/fdevent.cpp49
-rw-r--r--adb/services.cpp25
2 files changed, 74 insertions, 0 deletions
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 098a39dc1..f98c11a2f 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -35,6 +35,7 @@
#include <unordered_map>
#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
@@ -357,10 +358,56 @@ 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;
+ std::chrono::steady_clock::time_point timestamp;
+ uint64_t cycle;
+ };
+ static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+
+ auto now = std::chrono::steady_clock::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 > std::chrono::minutes(5)) {
+ 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;
@@ -370,6 +417,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();
diff --git a/adb/services.cpp b/adb/services.cpp
index a757d9066..b613d8312 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -181,6 +181,29 @@ static void reconnect_service(int fd, void* arg) {
kick_transport(t);
}
+static void spin_service(int fd, void*) {
+ unique_fd sfd(fd);
+
+ if (!__android_log_is_debuggable()) {
+ WriteFdExactly(sfd.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(sfd.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(sfd.get(), "spinning\n");
+}
+
int reverse_service(const char* command, atransport* transport) {
int s[2];
if (adb_socketpair(s)) {
@@ -328,6 +351,8 @@ int service_to_fd(const char* name, atransport* transport) {
reinterpret_cast<void*>(1));
} else if (!strcmp(name, "reconnect")) {
ret = create_service_thread("reconnect", reconnect_service, transport);
+ } else if (!strcmp(name, "spin")) {
+ ret = create_service_thread("spin", spin_service, nullptr);
#endif
}
if (ret >= 0) {