diff options
Diffstat (limited to 'libfdtrack/fdtrack.cpp')
-rw-r--r-- | libfdtrack/fdtrack.cpp | 142 |
1 files changed, 12 insertions, 130 deletions
diff --git a/libfdtrack/fdtrack.cpp b/libfdtrack/fdtrack.cpp index 2e9cfbcd0..d37157799 100644 --- a/libfdtrack/fdtrack.cpp +++ b/libfdtrack/fdtrack.cpp @@ -31,12 +31,9 @@ #include <array> #include <mutex> -#include <thread> -#include <utility> #include <vector> #include <android/fdsan.h> -#include <android/set_abort_message.h> #include <bionic/fdtrack.h> #include <android-base/no_destructor.h> @@ -51,7 +48,6 @@ struct FdEntry { }; extern "C" void fdtrack_dump(); -extern "C" void fdtrack_dump_fatal(); using fdtrack_callback_t = bool (*)(int fd, const char* const* function_names, const uint64_t* function_offsets, size_t count, void* arg); @@ -61,10 +57,7 @@ static void fd_hook(android_fdtrack_event* event); // Backtraces for the first 4k file descriptors ought to be enough to diagnose an fd leak. static constexpr size_t kFdTableSize = 4096; - -// 32 frames, plus two to skip from fdtrack itself. -static constexpr size_t kStackDepth = 34; -static constexpr size_t kStackFrameSkip = 2; +static constexpr size_t kStackDepth = 10; static bool installed = false; static std::array<FdEntry, kFdTableSize> stack_traces [[clang::no_destroy]]; @@ -78,23 +71,11 @@ __attribute__((constructor)) static void ctor() { entry.backtrace.reserve(kStackDepth); } - struct sigaction sa = {}; - sa.sa_sigaction = [](int, siginfo_t* siginfo, void*) { - if (siginfo->si_code == SI_QUEUE && siginfo->si_int == 1) { - fdtrack_dump_fatal(); - } else { - fdtrack_dump(); - } - }; - sa.sa_flags = SA_SIGINFO | SA_ONSTACK; - sigaction(BIONIC_SIGNAL_FDTRACK, &sa, nullptr); - + signal(BIONIC_SIGNAL_FDTRACK, [](int) { fdtrack_dump(); }); if (Unwinder().Init()) { android_fdtrack_hook_t expected = nullptr; installed = android_fdtrack_compare_exchange_hook(&expected, &fd_hook); } - - android_fdtrack_set_globally_enabled(true); } __attribute__((destructor)) static void dtor() { @@ -153,14 +134,15 @@ void fdtrack_iterate(fdtrack_callback_t callback, void* arg) { continue; } - for (size_t i = kStackFrameSkip; i < entry->backtrace.size(); ++i) { - size_t j = i - kStackFrameSkip; + constexpr size_t frame_skip = 2; + for (size_t i = frame_skip; i < entry->backtrace.size(); ++i) { + size_t j = i - frame_skip; function_names[j] = entry->backtrace[i].function_name.c_str(); function_offsets[j] = entry->backtrace[i].function_offset; } - bool should_continue = callback(fd, function_names, function_offsets, - entry->backtrace.size() - kStackFrameSkip, arg); + bool should_continue = + callback(fd, function_names, function_offsets, entry->backtrace.size() - frame_skip, arg); entry->mutex.unlock(); @@ -172,47 +154,16 @@ void fdtrack_iterate(fdtrack_callback_t callback, void* arg) { android_fdtrack_set_enabled(prev); } -static size_t hash_stack(const char* const* function_names, const uint64_t* function_offsets, - size_t stack_depth) { - size_t hash = 0; - for (size_t i = 0; i < stack_depth; ++i) { - // To future maintainers: if a libc++ update ever makes this invalid, replace this with +. - hash = std::__hash_combine(hash, std::hash<std::string_view>()(function_names[i])); - hash = std::__hash_combine(hash, std::hash<uint64_t>()(function_offsets[i])); - } - return hash; -} - -static void fdtrack_dump_impl(bool fatal) { +void fdtrack_dump() { if (!installed) { async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack not installed"); } else { async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack dumping..."); } - // If we're aborting, identify the most common stack in the hopes that it's the culprit, - // and emit that in the abort message so crash reporting can separate different fd leaks out. - // This is horrible and quadratic, but we need to avoid allocation since this can happen in - // response to a signal generated asynchronously. We're only going to dump 1k fds by default, - // and we're about to blow up the entire system, so this isn't too expensive. - struct StackInfo { - size_t hash = 0; - size_t count = 0; - - size_t stack_depth = 0; - const char* function_names[kStackDepth - kStackFrameSkip]; - uint64_t function_offsets[kStackDepth - kStackFrameSkip]; - }; - struct StackList { - size_t count = 0; - std::array<StackInfo, 128> data; - }; - static StackList stacks; - fdtrack_iterate( - [](int fd, const char* const* function_names, const uint64_t* function_offsets, - size_t stack_depth, void* stacks_ptr) { - auto stacks = static_cast<StackList*>(stacks_ptr); + [](int fd, const char* const* function_names, const uint64_t* function_offsets, size_t count, + void*) { uint64_t fdsan_owner = android_fdsan_get_owner_tag(fd); if (fdsan_owner != 0) { async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (owner = 0x%" PRIx64 ")", fd, @@ -221,81 +172,12 @@ static void fdtrack_dump_impl(bool fatal) { async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (unowned)", fd); } - for (size_t i = 0; i < stack_depth; ++i) { + for (size_t i = 0; i < count; ++i) { async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", " %zu: %s+%" PRIu64, i, function_names[i], function_offsets[i]); } - if (stacks) { - size_t hash = hash_stack(function_names, function_offsets, stack_depth); - bool found_stack = false; - for (size_t i = 0; i < stacks->count; ++i) { - if (stacks->data[i].hash == hash) { - ++stacks->data[i].count; - found_stack = true; - break; - } - } - - if (!found_stack) { - if (stacks->count < stacks->data.size()) { - auto& stack = stacks->data[stacks->count++]; - stack.hash = hash; - stack.count = 1; - stack.stack_depth = stack_depth; - for (size_t i = 0; i < stack_depth; ++i) { - stack.function_names[i] = function_names[i]; - stack.function_offsets[i] = function_offsets[i]; - } - } - } - } - return true; }, - fatal ? &stacks : nullptr); - - if (fatal) { - // Find the most common stack. - size_t max = 0; - StackInfo* stack = nullptr; - for (size_t i = 0; i < stacks.count; ++i) { - if (stacks.data[i].count > max) { - stack = &stacks.data[i]; - max = stack->count; - } - } - - static char buf[1024]; - - if (!stack) { - async_safe_format_buffer(buf, sizeof(buf), - "aborting due to fd leak: failed to find most common stack"); - } else { - char* p = buf; - p += async_safe_format_buffer(buf, sizeof(buf), - "aborting due to fd leak: most common stack =\n"); - - for (size_t i = 0; i < stack->stack_depth; ++i) { - ssize_t bytes_left = buf + sizeof(buf) - p; - if (bytes_left > 0) { - p += async_safe_format_buffer(p, buf + sizeof(buf) - p, " %zu: %s+%" PRIu64 "\n", i, - stack->function_names[i], stack->function_offsets[i]); - } - } - } - - android_set_abort_message(buf); - - // Abort on a different thread to avoid ART dumping runtime stacks. - std::thread([]() { abort(); }).join(); - } -} - -void fdtrack_dump() { - fdtrack_dump_impl(false); -} - -void fdtrack_dump_fatal() { - fdtrack_dump_impl(true); + nullptr); } |