diff options
| author | Josh Gao <jmgao@google.com> | 2018-06-18 16:37:01 -0700 |
|---|---|---|
| committer | Josh Gao <jmgao@google.com> | 2018-06-18 17:46:34 -0700 |
| commit | 3bbc8164b12f06c819a5b2acdc525199a9ff6f3e (patch) | |
| tree | a2215059e8060795a9b0af7c85d115c893898060 /adb | |
| parent | ded557fa58ec2c7c2f2bea8b71d75afe21c37168 (diff) | |
| download | system_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.cpp | 49 | ||||
| -rw-r--r-- | adb/services.cpp | 25 |
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) { |
