diff options
| author | Josh Gao <jmgao@google.com> | 2020-01-28 13:54:00 -0800 |
|---|---|---|
| committer | Josh Gao <jmgao@google.com> | 2020-02-03 14:29:52 -0800 |
| commit | ad8f02d780da7949b9bfc7d9890f4f7f98216acb (patch) | |
| tree | 81f5e483439c773b32fa85f7b09fa233f9f06124 /libfdtrack | |
| parent | 24e7ebb0c0063867e763c72dac2377e149837a4d (diff) | |
fdtrack: add a test.
Test: logcat -c; fdtrack_test; logcat -d | grep fdtrack
Change-Id: Ie1101c9cf968299ec1f03f20097a2ed27f6e15bd
Diffstat (limited to 'libfdtrack')
| -rw-r--r-- | libfdtrack/Android.bp | 8 | ||||
| -rw-r--r-- | libfdtrack/fdtrack.cpp | 79 | ||||
| -rw-r--r-- | libfdtrack/fdtrack_test.cpp | 111 | ||||
| -rw-r--r-- | libfdtrack/libfdtrack.map.txt | 1 |
4 files changed, 180 insertions, 19 deletions
diff --git a/libfdtrack/Android.bp b/libfdtrack/Android.bp index fd1351263..a907d9ae0 100644 --- a/libfdtrack/Android.bp +++ b/libfdtrack/Android.bp @@ -16,3 +16,11 @@ cc_library_shared { allow_undefined_symbols: true, recovery_available: true, } + +cc_test { + name: "fdtrack_test", + srcs: ["fdtrack_test.cpp"], + whole_static_libs: ["libBionicCtsGtestMain"], + static_libs: ["liblog"], + test_suites: ["device-tests"], +} diff --git a/libfdtrack/fdtrack.cpp b/libfdtrack/fdtrack.cpp index 831a50d1b..b20e65ea1 100644 --- a/libfdtrack/fdtrack.cpp +++ b/libfdtrack/fdtrack.cpp @@ -27,6 +27,7 @@ */ #include <inttypes.h> +#include <stdint.h> #include <array> #include <mutex> @@ -47,6 +48,11 @@ struct FdEntry { }; extern "C" void fdtrack_dump(); + +using fdtrack_callback_t = bool (*)(int fd, const char* const* function_names, + const uint64_t* function_offsets, size_t count, void* arg); +extern "C" void fdtrack_iterate(fdtrack_callback_t callback, void* arg); + static void fd_hook(android_fdtrack_event* event); // Backtraces for the first 4k file descriptors ought to be enough to diagnose an fd leak. @@ -61,6 +67,10 @@ static unwindstack::LocalUnwinder& Unwinder() { } __attribute__((constructor)) static void ctor() { + for (auto& entry : stack_traces) { + entry.backtrace.reserve(kStackDepth); + } + signal(BIONIC_SIGNAL_FDTRACK, [](int) { fdtrack_dump(); }); if (Unwinder().Init()) { android_fdtrack_hook_t expected = nullptr; @@ -97,15 +107,12 @@ static void fd_hook(android_fdtrack_event* event) { } } -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..."); - } - +void fdtrack_iterate(fdtrack_callback_t callback, void* arg) { bool prev = android_fdtrack_set_enabled(false); + for (int fd = 0; fd < static_cast<int>(stack_traces.size()); ++fd) { + const char* function_names[kStackDepth]; + uint64_t function_offsets[kStackDepth]; FdEntry* entry = GetFdEntry(fd); if (!entry) { continue; @@ -117,26 +124,60 @@ void fdtrack_dump() { } if (entry->backtrace.empty()) { + entry->mutex.unlock(); continue; - } - - uint64_t fdsan_owner = android_fdsan_get_owner_tag(fd); + } else if (entry->backtrace.size() < 2) { + async_safe_format_log(ANDROID_LOG_WARN, "fdtrack", "fd %d missing frames: size = %zu", fd, + entry->backtrace.size()); - if (fdsan_owner != 0) { - async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (owner = %#" PRIx64 ")", fd, - fdsan_owner); - } else { - async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (unowned)", fd); + entry->mutex.unlock(); + continue; } - const size_t frame_skip = 2; + constexpr size_t frame_skip = 2; for (size_t i = frame_skip; i < entry->backtrace.size(); ++i) { - auto& frame = entry->backtrace[i]; - async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", " %zu: %s+%" PRIu64, i - frame_skip, - frame.function_name.c_str(), frame.function_offset); + 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() - frame_skip, arg); + entry->mutex.unlock(); + + if (!should_continue) { + break; + } } + android_fdtrack_set_enabled(prev); } + +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..."); + } + + fdtrack_iterate( + [](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 = %#" PRIx64 ")", fd, + fdsan_owner); + } else { + async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (unowned)", fd); + } + + 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]); + } + + return true; + }, + nullptr); +} diff --git a/libfdtrack/fdtrack_test.cpp b/libfdtrack/fdtrack_test.cpp new file mode 100644 index 000000000..0edf0b81e --- /dev/null +++ b/libfdtrack/fdtrack_test.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dlfcn.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +#include <map> +#include <vector> + +#include <gtest/gtest.h> + +struct FdtrackFrame { + const char* function_name; + uint64_t function_offset; +}; + +std::map<int, std::vector<FdtrackFrame>> RunFdtrack(std::function<void()> f) { + void* libfdtrack = dlopen("libfdtrack.so", RTLD_NOW); + if (!libfdtrack) { + errx(1, "failed to dlopen libfdtrack.so: %s", dlerror()); + } + + using fdtrack_callback_t = bool (*)(int fd, const char* const* function_names, + const uint64_t* function_offsets, size_t count, void* arg); + auto fdtrack_iterate = reinterpret_cast<void (*)(fdtrack_callback_t, void* arg)>( + dlsym(libfdtrack, "fdtrack_iterate")); + if (!fdtrack_iterate) { + errx(1, "failed to dlsym fdtrack_iterate"); + } + + f(); + + std::map<int, std::vector<FdtrackFrame>> result; + fdtrack_iterate( + [](int fd, const char* const* function_names, const uint64_t* function_offsets, size_t count, + void* arg) { + auto& map = *static_cast<decltype(result)*>(arg); + for (size_t i = 0; i < count; ++i) { + map[fd].push_back(FdtrackFrame{ + .function_name = function_names[i], + .function_offset = function_offsets[i], + }); + } + + return true; + }, + &result); + + return result; +} + +TEST(fdtrack, open) { + static int fd = -1; + auto result = RunFdtrack([]() { fd = open("/dev/null", O_RDONLY | O_CLOEXEC); }); + + ASSERT_NE(-1, fd); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(fd, result.begin()->first); + ASSERT_NE(nullptr, strstr(result.begin()->second.at(0).function_name, "open")); +} + +TEST(fdtrack, close) { + static int fd1 = -1; + static int fd2 = -1; + static int fd3 = -1; + auto result = RunFdtrack([]() { + fd1 = open("/dev/null", O_RDONLY | O_CLOEXEC); + fd2 = open("/dev/null", O_RDONLY | O_CLOEXEC); + fd3 = open("/dev/null", O_RDONLY | O_CLOEXEC); + close(fd2); + }); + + ASSERT_NE(-1, fd1); + ASSERT_NE(-1, fd2); + ASSERT_NE(-1, fd3); + + ASSERT_EQ(2, result.size()); + ASSERT_EQ(1, result.count(fd1)); + ASSERT_EQ(1, result.count(fd3)); + + ASSERT_NE(nullptr, strstr(result[fd1].at(0).function_name, "open")); + ASSERT_NE(nullptr, strstr(result[fd3].at(0).function_name, "open")); +} diff --git a/libfdtrack/libfdtrack.map.txt b/libfdtrack/libfdtrack.map.txt index 7a54c35b3..7a23954d8 100644 --- a/libfdtrack/libfdtrack.map.txt +++ b/libfdtrack/libfdtrack.map.txt @@ -1,6 +1,7 @@ LIBFDTRACK { global: fdtrack_dump; + fdtrack_iterate; local: *; }; |
