67 lines
3.4 KiB
Markdown
67 lines
3.4 KiB
Markdown
|
## fdtrack
|
||
|
|
||
|
fdtrack consists of two parts: a set of hooks in bionic to register a callback
|
||
|
that's invoked on file descriptor operations, and a library that implements a
|
||
|
hook to perform and store backtraces for file descriptor creation.
|
||
|
|
||
|
### bionic hooks
|
||
|
bionic provides a header in the `bionic_libc_platform_headers` header_lib at <[bionic/fdtrack.h](https://android.googlesource.com/platform/bionic/+/refs/heads/master/libc/platform/bionic/fdtrack.h)>.
|
||
|
Register a callback with `android_fdtrack_compare_exchange_hook` to receive
|
||
|
callbacks upon file descriptor creation and destruction. This function can be
|
||
|
called at any point in order to start capturing events, but be sure to properly
|
||
|
handle unbalanced closes. This callback may be called from an async signal safe
|
||
|
context, but not vfork (bionic tracks whether a thread is vforked, and chooses
|
||
|
not to call callbacks when this is the case).
|
||
|
|
||
|
### libfdtrack
|
||
|
[libfdtrack](https://android.googlesource.com/platform/bionic/+/refs/heads/master/libfdtrack)
|
||
|
implements a library that uses libunwindstack to unwind and store fd creation backtraces.
|
||
|
|
||
|
|
||
|
#### Using libfdtrack
|
||
|
libfdtrack registers its hook upon being loaded, so to start capturing
|
||
|
backtraces, `dlopen("libfdtrack.so", RTLD_GLOBAL)` is all that's needed. To dump
|
||
|
its output to logcat, either use `fdtrack_dump`, or send the signal
|
||
|
`BIONIC_SIGNAL_FDTRACK` (available from `<bionic/reserved_signals.h>`) to the
|
||
|
process. If you wish to iterate through the results programmatically,
|
||
|
`fdtrack_iterate` can be used (warning: this interface is currently unstable,
|
||
|
don't use it in code that can be used on multiple platform versions.)
|
||
|
|
||
|
libfdtrack adds a significant amount of overhead, so for processes that are
|
||
|
latency-critical like system_server, it's not feasible to always capture
|
||
|
backtraces. Instead, if you can detect that an fd leak is ongoing, turning on
|
||
|
backtraces for a while and then triggering a dump can be sufficient.
|
||
|
system_server [implements this approach](https://android.googlesource.com/platform/frameworks/base/+/679f3e4242b8e018eb7df90ef433f81088a64fff%5E%21/),
|
||
|
spawning a thread that regularly checks the count of fds in the process, turns
|
||
|
on fdtrack when it hits a threshold, and then aborts after another threshold.
|
||
|
This dumps the output to logcat, which will be available in both the tombstone
|
||
|
and logcat from bugreports.
|
||
|
|
||
|
#### Implementation details
|
||
|
There are multiple methods to unwind in Android:
|
||
|
|
||
|
* libunwindstack
|
||
|
* Pros
|
||
|
* Primary method on the platform
|
||
|
* Able to unwind through ART
|
||
|
* Cons
|
||
|
* Uses malloc internally: unsafe unless a separate allocator is
|
||
|
statically-linked and steps are taken to prevent the unwind from being
|
||
|
interrupted by a signal
|
||
|
* Slow - infeasible to be used always in latency-sensitive processes
|
||
|
* `android_unsafe_frame_pointer_chase`
|
||
|
* Pros
|
||
|
* Definitely async signal safe
|
||
|
* Fast
|
||
|
* Cons
|
||
|
* Unable to unwind through ART because it doesn't maintain the frame pointer
|
||
|
* Requires -fno-omit-frame-pointer to be used on all code being unwound
|
||
|
through, which currently isn't the case on Android
|
||
|
* Frame layout is a mess on 32-bit ARM: the ARM standard, clang, and GCC
|
||
|
[all disagree](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92172)
|
||
|
* Chasing the frame pointer will often result in multiple frames inside the
|
||
|
same function
|
||
|
|
||
|
libfdtrack chooses to use libunwindstack for now, since unwinding through ART
|
||
|
is critical to being useful for the initial user, system_server.
|