xref: /aosp_15_r20/bionic/docs/fdtrack.md (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1## fdtrack
2
3[TOC]
4
5fdtrack is a file descriptor leak checker added to Android in API level 30.
6
7fdtrack consists of several parts: a set of hooks in bionic to register a
8callback that's invoked on file descriptor operations, a library that implements
9a hook to perform and store backtraces for file descriptor creation, and
10code in frameworks to automatically enable it (and deliberately crash a process
11that's leaking).
12
13### bionic hooks
14bionic provides a header in the `bionic_libc_platform_headers` header_lib at <[bionic/fdtrack.h](https://android.googlesource.com/platform/bionic/+/refs/heads/main/libc/platform/bionic/fdtrack.h)>.
15Register a callback with `android_fdtrack_compare_exchange_hook` to receive
16callbacks upon file descriptor creation and destruction. This function can be
17called at any point in order to start capturing events, but be sure to properly
18handle unbalanced closes. This callback may be called from an async signal safe
19context, but not vfork (bionic tracks whether a thread is vforked, and chooses
20not to call callbacks when this is the case).
21
22### libfdtrack
23[libfdtrack](https://android.googlesource.com/platform/bionic/+/refs/heads/main/libfdtrack)
24implements a library that uses libunwindstack to unwind and store fd creation backtraces.
25
26### frameworks
27As the name implies, `spawnFdLeakCheckThread` in SystemServer spawns a thread
28to monitor the number of open file descriptors every so often.
29If that passes a certain threshold, fdtrack is enabled.
30If it passes another threshold, the process is aborted.
31These thresholds are configurable via system properties:
32```
33    // Number of open file descriptors before fdtrack starts; default 1600.
34    private static final String SYSPROP_FDTRACK_ENABLE_THRESHOLD =
35            "persist.sys.debug.fdtrack_enable_threshold";
36
37    // Number of open file descriptors before aborting; default 3000.
38    private static final String SYSPROP_FDTRACK_ABORT_THRESHOLD =
39            "persist.sys.debug.fdtrack_abort_threshold";
40
41    // Number of seconds between open fd count checks; default 120s.
42    private static final String SYSPROP_FDTRACK_INTERVAL =
43            "persist.sys.debug.fdtrack_interval";
44```
45Note that it's also possible to monitor the number of open file descriptors for
46a given process from the shell. `adb shell watch ls -l /proc/<pid>/fd` will show
47them (and you can choose your own update rate as an argument to `watch`).
48
49#### Using libfdtrack
50libfdtrack registers its hook upon being loaded, so to start capturing
51backtraces, `dlopen("libfdtrack.so", RTLD_GLOBAL)` is all that's needed. To dump
52its output to logcat, either use `fdtrack_dump`, or send the signal
53`BIONIC_SIGNAL_FDTRACK` (available from `<bionic/reserved_signals.h>`) to the
54process. If you wish to iterate through the results programmatically,
55`fdtrack_iterate` can be used (warning: this interface is currently unstable,
56don't use it in code that can be used on multiple platform versions.)
57
58libfdtrack adds a significant amount of overhead, so for processes that are
59latency-critical like system_server, it's not feasible to always capture
60backtraces. Instead, if you can detect that an fd leak is ongoing, turning on
61backtraces for a while and then triggering a dump can be sufficient.
62system_server [implements this approach](https://android.googlesource.com/platform/frameworks/base/+/679f3e4242b8e018eb7df90ef433f81088a64fff%5E%21/),
63spawning a thread that regularly checks the count of fds in the process, turns
64on fdtrack when it hits a threshold, and then aborts after another threshold.
65This dumps the output to logcat, which will be available in both the tombstone
66and logcat from bugreports.
67
68#### Implementation details
69There are multiple methods to unwind in Android:
70
71 * libunwindstack
72   * Pros
73     * Primary method on the platform
74     * Able to unwind through ART
75   * Cons
76     * Uses malloc internally: unsafe unless a separate allocator is
77       statically-linked and steps are taken to prevent the unwind from being
78       interrupted by a signal
79     * Slow - infeasible to be used always in latency-sensitive processes
80 * `android_unsafe_frame_pointer_chase`
81   * Pros
82     * Definitely async signal safe
83     * Fast
84   * Cons
85     * Unable to unwind through ART because it doesn't maintain the frame pointer
86     * Requires -fno-omit-frame-pointer to be used on all code being unwound
87       through, which currently isn't the case on Android
88     * Frame layout is a mess on 32-bit ARM: the ARM standard, clang, and GCC
89       [all disagree](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92172)
90     * Chasing the frame pointer will often result in multiple frames inside the
91       same function
92
93libfdtrack chooses to use libunwindstack for now, since unwinding through ART
94is critical to being useful for the initial user, system_server.
95