Add a tool to track down fd leaks.

Add a hook that's called upon file descriptor creation to libc, and a
library that uses it to capture backtraces for file descriptor creation,
to make it easier to hunt down file descriptor leaks.

Currently, this doesn't capture all of the ways of creating a file
descriptor, but completeness isn't required for this to be useful as
long as leaked file descriptors are created with a function that is
tracked. The primary unhandled case is binder, which receives file
descriptors as a payload in a not-trivially-parsable byte blob, but
there's a chance that the leak we're currently trying to track down
isn't of a file descriptor received over binder, so leave that for
later.

Bug: http://b/140703823
Test: manual
Change-Id: I308a14c2e234cdba4207157b634ab6b8bc539dd9
diff --git a/libc/Android.bp b/libc/Android.bp
index f1cca49..1cfcf21 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1103,7 +1103,7 @@
         "bionic/clone.cpp",
         "bionic/ctype.cpp",
         "bionic/dirent.cpp",
-        "bionic/dup2.cpp",
+        "bionic/dup.cpp",
         "bionic/environ.cpp",
         "bionic/error.cpp",
         "bionic/eventfd_read.cpp",
@@ -1112,7 +1112,9 @@
         "bionic/faccessat.cpp",
         "bionic/fchmod.cpp",
         "bionic/fchmodat.cpp",
+        "bionic/fcntl.cpp",
         "bionic/fdsan.cpp",
+        "bionic/fdtrack.cpp",
         "bionic/ffs.cpp",
         "bionic/fgetxattr.cpp",
         "bionic/flistxattr.cpp",
@@ -1181,6 +1183,7 @@
         "bionic/realpath.cpp",
         "bionic/reboot.cpp",
         "bionic/recv.cpp",
+        "bionic/recvmsg.cpp",
         "bionic/rename.cpp",
         "bionic/rmdir.cpp",
         "bionic/scandir.cpp",
@@ -1814,7 +1817,7 @@
     name: "bionic_libc_platform_headers",
     visibility: [
         "//art:__subpackages__",
-        "//bionic/libc:__subpackages__",
+        "//bionic:__subpackages__",
         "//frameworks:__subpackages__",
         "//external/perfetto:__subpackages__",
         "//external/scudo:__subpackages__",
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 517d5f9..1343e4e 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -123,12 +123,12 @@
 ssize_t     readv(int, const struct iovec*, int)   all
 ssize_t     writev(int, const struct iovec*, int)  all
 int         __fcntl64:fcntl64(int, int, void*)  lp32
-int         fcntl(int, int, void*)  lp64
+int         __fcntl:fcntl(int, int, void*)  lp64
 int         flock(int, int)   all
 int         __fchmod:fchmod(int, mode_t)  all
-int         dup(int)  all
 int         pipe2(int*, int) all
-int         dup3(int, int, int)   all
+int         __dup:dup(int)  all
+int         __dup3:dup3(int, int, int)   all
 int         fsync(int)  all
 int         fdatasync(int) all
 int         fchown:fchown32(int, uid_t, gid_t)  arm,x86
@@ -254,9 +254,9 @@
 int           shutdown(int, int)  arm,arm64,mips,mips64,x86_64
 int           setsockopt(int, int, int, const void*, socklen_t)  arm,arm64,mips,mips64,x86_64
 int           getsockopt(int, int, int, void*, socklen_t*)    arm,arm64,mips,mips64,x86_64
-ssize_t       recvmsg(int, struct msghdr*, unsigned int)   arm,arm64,mips,mips64,x86_64
+ssize_t       __recvmsg:recvmsg(int, struct msghdr*, unsigned int)   arm,arm64,mips,mips64,x86_64
 ssize_t       __sendmsg:sendmsg(int, const struct msghdr*, unsigned int)  arm,arm64,mips,mips64,x86_64
-int           recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   arm,arm64,mips,mips64,x86_64
+int           __recvmmsg:recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   arm,arm64,mips,mips64,x86_64
 int           __sendmmsg:sendmmsg(int, struct mmsghdr*, unsigned int, int)   arm,arm64,mips,mips64,x86_64
 
 # sockets for x86. These are done as an "indexed" call to socketcall syscall.
@@ -273,9 +273,9 @@
 int           setsockopt:socketcall:14(int, int, int, const void*, socklen_t)  x86
 int           getsockopt:socketcall:15(int, int, int, void*, socklen_t*)    x86
 int           __sendmsg:socketcall:16(int, const struct msghdr*, unsigned int)  x86
-int           recvmsg:socketcall:17(int, struct msghdr*, unsigned int)   x86
+int           __recvmsg:socketcall:17(int, struct msghdr*, unsigned int)   x86
 int           __accept4:socketcall:18(int, struct sockaddr*, socklen_t*, int)  x86
-int           recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   x86
+int           __recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   x86
 int           __sendmmsg:socketcall:20(int, struct mmsghdr*, unsigned int, int)   x86
 
 # scheduler & real-time
diff --git a/libc/bionic/NetdClientDispatch.cpp b/libc/bionic/NetdClientDispatch.cpp
index 463ef36..e6f4a97 100644
--- a/libc/bionic/NetdClientDispatch.cpp
+++ b/libc/bionic/NetdClientDispatch.cpp
@@ -18,6 +18,8 @@
 
 #include <sys/socket.h>
 
+#include "private/bionic_fdtrack.h"
+
 #ifdef __i386__
 #define __socketcall __attribute__((__cdecl__))
 #else
@@ -53,7 +55,7 @@
 };
 
 int accept4(int fd, sockaddr* addr, socklen_t* addr_length, int flags) {
-    return __netdClientDispatch.accept4(fd, addr, addr_length, flags);
+  return FDTRACK_CREATE(__netdClientDispatch.accept4(fd, addr, addr_length, flags));
 }
 
 int connect(int fd, const sockaddr* addr, socklen_t addr_length) {
@@ -74,5 +76,5 @@
 }
 
 int socket(int domain, int type, int protocol) {
-    return __netdClientDispatch.socket(domain, type, protocol);
+  return FDTRACK_CREATE(__netdClientDispatch.socket(domain, type, protocol));
 }
diff --git a/libc/bionic/dup2.cpp b/libc/bionic/dup.cpp
similarity index 83%
rename from libc/bionic/dup2.cpp
rename to libc/bionic/dup.cpp
index 98c5646..d9e89a5 100644
--- a/libc/bionic/dup2.cpp
+++ b/libc/bionic/dup.cpp
@@ -29,6 +29,15 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include "private/bionic_fdtrack.h"
+
+extern "C" int __dup(int old_fd);
+extern "C" int __dup3(int old_fd, int new_fd, int flags);
+
+int dup(int old_fd) {
+  return FDTRACK_CREATE(__dup(old_fd));
+}
+
 int dup2(int old_fd, int new_fd) {
   // If old_fd is equal to new_fd and a valid file descriptor, dup2 returns
   // old_fd without closing it. This is not true of dup3, so we have to
@@ -40,5 +49,9 @@
     return old_fd;
   }
 
-  return dup3(old_fd, new_fd, 0);
+  return FDTRACK_CREATE(__dup3(old_fd, new_fd, 0));
+}
+
+int dup3(int old_fd, int new_fd, int flags) {
+  return FDTRACK_CREATE(__dup3(old_fd, new_fd, flags));
 }
diff --git a/libc/bionic/fcntl.cpp b/libc/bionic/fcntl.cpp
new file mode 100644
index 0000000..c508131
--- /dev/null
+++ b/libc/bionic/fcntl.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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 <stdarg.h>
+#include <fcntl.h>
+
+#include "private/bionic_fdtrack.h"
+
+#if defined(__LP64__)
+
+extern "C" int __fcntl(int fd, int cmd, ...);
+
+int fcntl(int fd, int cmd, ...) {
+  va_list args;
+  va_start(args, cmd);
+
+  // This is a bit sketchy, especially because arg can be an int, but all of our
+  // supported 64-bit ABIs pass arg in a register.
+  void* arg = va_arg(args, void*);
+  va_end(args);
+
+  int rc = __fcntl(fd, cmd, arg);
+  if (cmd == F_DUPFD) {
+    return FDTRACK_CREATE_NAME("F_DUPFD", rc);
+  } else if (cmd == F_DUPFD_CLOEXEC) {
+    return FDTRACK_CREATE_NAME("F_DUPFD_CLOEXEC", rc);
+  }
+  return rc;
+}
+
+#else
+
+extern "C" int __fcntl64(int, int, ...);
+
+// For fcntl we use the fcntl64 system call to signal that we're using struct flock64.
+int fcntl(int fd, int cmd, ...) {
+  va_list ap;
+
+  va_start(ap, cmd);
+  void* arg = va_arg(ap, void*);
+  va_end(ap);
+
+  if (cmd == F_DUPFD) {
+    return FDTRACK_CREATE_NAME("F_DUPFD", __fcntl64(fd, cmd, arg));
+  } else if (cmd == F_DUPFD_CLOEXEC) {
+    return FDTRACK_CREATE_NAME("F_DUPFD_CLOEXEC", __fcntl64(fd, cmd, arg));
+  }
+  return __fcntl64(fd, cmd, arg);
+}
+
+#endif
diff --git a/libc/bionic/fdsan.cpp b/libc/bionic/fdsan.cpp
index d4ac71c..ebc680f 100644
--- a/libc/bionic/fdsan.cpp
+++ b/libc/bionic/fdsan.cpp
@@ -43,6 +43,7 @@
 #include <platform/bionic/reserved_signals.h>
 #include <sys/system_properties.h>
 
+#include "private/bionic_fdtrack.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_inline_raise.h"
 #include "pthread_internal.h"
@@ -245,6 +246,7 @@
 }
 
 int android_fdsan_close_with_tag(int fd, uint64_t expected_tag) {
+  FDTRACK_CLOSE(fd);
   FdEntry* fde = GetFdEntry(fd);
   if (!fde) {
     return __close(fd);
diff --git a/libc/bionic/fdtrack.cpp b/libc/bionic/fdtrack.cpp
new file mode 100644
index 0000000..38eb219
--- /dev/null
+++ b/libc/bionic/fdtrack.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 <stdatomic.h>
+
+#include <android/fdtrack.h>
+
+#include <platform/bionic/reserved_signals.h>
+
+#include "private/bionic_fdtrack.h"
+#include "private/bionic_tls.h"
+#include "private/bionic_globals.h"
+
+_Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
+
+bool android_fdtrack_get_enabled() {
+  return !__get_bionic_tls().fdtrack_disabled;
+}
+
+bool android_fdtrack_set_enabled(bool new_value) {
+  auto& tls = __get_bionic_tls();
+  bool prev = !tls.fdtrack_disabled;
+  tls.fdtrack_disabled = !new_value;
+  return prev;
+}
+
+bool android_fdtrack_compare_exchange_hook(android_fdtrack_hook_t* expected,
+                                           android_fdtrack_hook_t value) {
+  return atomic_compare_exchange_strong(&__android_fdtrack_hook, expected, value);
+}
+
+void __libc_init_fdtrack() {
+  // Register a no-op signal handler.
+  signal(BIONIC_SIGNAL_FDTRACK, [](int) {});
+}
diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index 2de1bc7..f08e582 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -37,27 +37,17 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include "private/bionic_fdtrack.h"
+
 #if defined(__LP64__)
 #error This code is only needed on 32-bit systems!
 #endif
 
 // System calls we need.
-extern "C" int __fcntl64(int, int, void*);
 extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int);
 extern "C" int __preadv64(int, const struct iovec*, int, long, long);
 extern "C" int __pwritev64(int, const struct iovec*, int, long, long);
 
-// For fcntl we use the fcntl64 system call to signal that we're using struct flock64.
-int fcntl(int fd, int cmd, ...) {
-  va_list ap;
-
-  va_start(ap, cmd);
-  void* arg = va_arg(ap, void*);
-  va_end(ap);
-
-  return __fcntl64(fd, cmd, arg);
-}
-
 // For lseek64 we need to use the llseek system call which splits the off64_t in two and
 // returns the off64_t result via a pointer because 32-bit kernels can't return 64-bit results.
 off64_t lseek64(int fd, off64_t off, int whence) {
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index e4106e9..12628f7 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -104,6 +104,7 @@
 
   __system_properties_init(); // Requires 'environ'.
   __libc_init_fdsan(); // Requires system properties (for debug.fdsan).
+  __libc_init_fdtrack();
 
   SetDefaultHeapTaggingLevel();
 }
diff --git a/libc/bionic/open.cpp b/libc/bionic/open.cpp
index 222e5d3..bd8685a 100644
--- a/libc/bionic/open.cpp
+++ b/libc/bionic/open.cpp
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include "private/bionic_fdtrack.h"
 #include "private/bionic_fortify.h"
 
 extern "C" int __openat(int, const char*, int, int);
@@ -62,13 +63,13 @@
     va_end(args);
   }
 
-  return __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode);
+  return FDTRACK_CREATE(__openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode));
 }
 __strong_alias(open64, open);
 
 int __open_2(const char* pathname, int flags) {
   if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
-  return __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), 0);
+  return FDTRACK_CREATE_NAME("open", __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), 0));
 }
 
 int openat(int fd, const char *pathname, int flags, ...) {
@@ -81,11 +82,11 @@
     va_end(args);
   }
 
-  return __openat(fd, pathname, force_O_LARGEFILE(flags), mode);
+  return FDTRACK_CREATE_NAME("openat", __openat(fd, pathname, force_O_LARGEFILE(flags), mode));
 }
 __strong_alias(openat64, openat);
 
 int __openat_2(int fd, const char* pathname, int flags) {
   if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
-  return __openat(fd, pathname, force_O_LARGEFILE(flags), 0);
+  return FDTRACK_CREATE_NAME("openat", __openat(fd, pathname, force_O_LARGEFILE(flags), 0));
 }
diff --git a/libc/bionic/recvmsg.cpp b/libc/bionic/recvmsg.cpp
new file mode 100644
index 0000000..003f43d
--- /dev/null
+++ b/libc/bionic/recvmsg.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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 <string.h>
+#include <sys/socket.h>
+
+#include <async_safe/log.h>
+
+#include "private/bionic_fdtrack.h"
+
+extern "C" ssize_t __recvmsg(int __fd, struct msghdr* __msg, int __flags);
+extern "C" int __recvmmsg(int __fd, struct mmsghdr* __msgs, unsigned int __msg_count, int __flags,
+                          const struct timespec* __timeout);
+
+static inline __attribute__((artificial)) __attribute__((always_inline)) void track_fds(
+    struct msghdr* msg, const char* function_name) {
+  if (!__android_fdtrack_hook) {
+    return;
+  }
+
+  for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+    if (cmsg->cmsg_type != SCM_RIGHTS) {
+      continue;
+    }
+
+    if (cmsg->cmsg_len <= sizeof(struct cmsghdr)) {
+      continue;
+    }
+
+    size_t data_length = cmsg->cmsg_len - sizeof(struct cmsghdr);
+    if (data_length % sizeof(int) != 0) {
+      async_safe_fatal("invalid cmsg length: %zu", data_length);
+    }
+
+    for (size_t offset = 0; offset < data_length; offset += sizeof(int)) {
+      int fd;
+      memcpy(&fd, CMSG_DATA(cmsg) + offset, sizeof(int));
+      FDTRACK_CREATE_NAME(function_name, fd);
+    }
+  }
+}
+
+ssize_t recvmsg(int __fd, struct msghdr* __msg, int __flags) {
+  ssize_t rc = __recvmsg(__fd, __msg, __flags);
+  if (rc == -1) {
+    return -1;
+  }
+  track_fds(__msg, "recvmsg");
+  return rc;
+}
+
+int recvmmsg(int __fd, struct mmsghdr* __msgs, unsigned int __msg_count, int __flags,
+             const struct timespec* __timeout) {
+  int rc = __recvmmsg(__fd, __msgs, __msg_count, __flags, __timeout);
+  if (rc == -1) {
+    return -1;
+  }
+  for (int i = 0; i < rc; ++i) {
+    track_fds(&__msgs[i].msg_hdr, "recvmmsg");
+  }
+  return rc;
+}
diff --git a/libc/include/android/fdtrack.h b/libc/include/android/fdtrack.h
new file mode 100644
index 0000000..6eb379b
--- /dev/null
+++ b/libc/include/android/fdtrack.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+// Types of an android_fdtrack_event.
+enum android_fdtrack_event_type {
+  // File descriptor creation: create is the active member of android_fdtrack_event::data.
+  ANDROID_FDTRACK_EVENT_TYPE_CREATE,
+
+  // File descriptor closed.
+  ANDROID_FDTRACK_EVENT_TYPE_CLOSE,
+};
+
+struct android_fdtrack_event {
+  // File descriptor for which this event occurred.
+  int fd;
+
+  // Type of event: this is one of the enumerators of android_fdtrack_event_type.
+  uint8_t type;
+
+  // Data for the event.
+  union {
+    struct {
+      const char* function_name;
+    } create;
+  } data;
+};
+
+// Callback invoked upon file descriptor creation/closure.
+typedef void (*android_fdtrack_hook_t)(struct android_fdtrack_event*);
+
+// Register a hook which is called to track fd lifecycle events.
+bool android_fdtrack_compare_exchange_hook(android_fdtrack_hook_t* expected, android_fdtrack_hook_t value) __INTRODUCED_IN(30);
+
+// Enable/disable fdtrack *on the current thread*.
+// This is primarily useful when performing operations which you don't want to track
+// (e.g. when emitting already-recorded information).
+bool android_fdtrack_get_enabled() __INTRODUCED_IN(30);
+bool android_fdtrack_set_enabled(bool new_value) __INTRODUCED_IN(30);
+
+__END_DECLS
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 2710fb2..dddd84c 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1484,6 +1484,9 @@
   global:
     __mempcpy_chk;
     __tls_get_addr; # arm64
+    android_fdtrack_compare_exchange_hook;
+    android_fdtrack_get_enabled;
+    android_fdtrack_set_enabled;
     call_once;
     cnd_broadcast;
     cnd_destroy;
diff --git a/libc/platform/bionic/reserved_signals.h b/libc/platform/bionic/reserved_signals.h
index 7634b27..4ac6455 100644
--- a/libc/platform/bionic/reserved_signals.h
+++ b/libc/platform/bionic/reserved_signals.h
@@ -42,14 +42,16 @@
 //   36 (__SIGRTMIN + 4)        platform profilers (heapprofd, traced_perf)
 //   37 (__SIGRTMIN + 5)        coverage (libprofile-extras)
 //   38 (__SIGRTMIN + 6)        heapprofd ART managed heap dumps
+//   39 (__SIGRTMIN + 7)        fdtrack
 //
 // If you change this, also change __ndk_legacy___libc_current_sigrtmin
 // in <android/legacy_signal_inlines.h> to match.
 
 #define BIONIC_SIGNAL_DEBUGGER __SIGRTMIN + 3
 #define BIONIC_SIGNAL_PROFILER __SIGRTMIN + 4
+#define BIONIC_SIGNAL_FDTRACK __SIGRTMIN + 7
 
-#define __SIGRT_RESERVED 7
+#define __SIGRT_RESERVED 8
 static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset, int how) {
   int (*block)(sigset64_t*, int);
   int (*unblock)(sigset64_t*, int);
@@ -77,5 +79,6 @@
   unblock(&sigset, __SIGRTMIN + 4);
   unblock(&sigset, __SIGRTMIN + 5);
   unblock(&sigset, __SIGRTMIN + 6);
+  unblock(&sigset, __SIGRTMIN + 7);
   return sigset;
 }
diff --git a/libc/private/bionic_fdtrack.h b/libc/private/bionic_fdtrack.h
new file mode 100644
index 0000000..ddf7249
--- /dev/null
+++ b/libc/private/bionic_fdtrack.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <stdatomic.h>
+
+#include <android/fdtrack.h>
+
+#include "bionic/pthread_internal.h"
+#include "private/bionic_tls.h"
+#include "private/ErrnoRestorer.h"
+
+extern "C" _Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
+
+// Macro to record file descriptor creation.
+// e.g.:
+//   int socket(int domain, int type, int protocol) {
+//     return FDTRACK_CREATE_NAME("socket", __socket(domain, type, protocol));
+//   }
+#define FDTRACK_CREATE_NAME(name, fd_value)                       \
+  ({                                                              \
+    int __fd = (fd_value);                                        \
+    if (__fd != -1 && __predict_false(__android_fdtrack_hook)) {  \
+      bionic_tls& tls = __get_bionic_tls();                       \
+      /* fdtrack_disabled is only true during reentrant calls. */ \
+      if (!__predict_false(tls.fdtrack_disabled)) {               \
+        ErrnoRestorer r;                                          \
+        tls.fdtrack_disabled = true;                              \
+        android_fdtrack_event event;                              \
+        event.fd = __fd;                                          \
+        event.type = ANDROID_FDTRACK_EVENT_TYPE_CREATE;           \
+        event.data.create.function_name = name;                   \
+        __android_fdtrack_hook(&event);                           \
+        tls.fdtrack_disabled = false;                             \
+      }                                                           \
+    }                                                             \
+    __fd;                                                         \
+  })
+
+// Macro to record file descriptor creation, with the current function's name.
+// e.g.:
+//   int socket(int domain, int type, int protocol) {
+//     return FDTRACK_CREATE_NAME(__socket(domain, type, protocol));
+//   }
+#define FDTRACK_CREATE(fd_value) FDTRACK_CREATE_NAME(__func__, (fd_value))
+
+// Macro to record file descriptor closure.
+// Note that this does not actually close the file descriptor.
+#define FDTRACK_CLOSE(fd_value)                                  \
+  ({                                                             \
+    int __fd = (fd_value);                                       \
+    if (__fd != -1 && __predict_false(__android_fdtrack_hook)) { \
+      bionic_tls& tls = __get_bionic_tls();                      \
+      if (!__predict_false(tls.fdtrack_disabled)) {              \
+        int saved_errno = errno;                                 \
+        tls.fdtrack_disabled = true;                             \
+        android_fdtrack_event event;                             \
+        event.fd = __fd;                                         \
+        event.type = ANDROID_FDTRACK_EVENT_TYPE_CLOSE;           \
+        __android_fdtrack_hook(&event);                          \
+        tls.fdtrack_disabled = false;                            \
+        errno = saved_errno;                                     \
+      }                                                          \
+    }                                                            \
+    __fd;                                                        \
+  })
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 8997ceb..4cc9bbe 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -100,6 +100,7 @@
 
 __LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();
 __LIBC_HIDDEN__ void __libc_init_fdsan();
+__LIBC_HIDDEN__ void __libc_init_fdtrack();
 __LIBC_HIDDEN__ void __libc_init_profiling_handlers();
 
 __LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals);
diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h
index bb5c67b..80d645a 100644
--- a/libc/private/bionic_tls.h
+++ b/libc/private/bionic_tls.h
@@ -128,6 +128,9 @@
   group_state_t group;
   passwd_state_t passwd;
 
+  char fdtrack_disabled;
+  char padding[3];
+
   // Initialize the main thread's final object using its bootstrap object.
   void copy_from_bootstrap(const bionic_tls* boot __attribute__((unused))) {
     // Nothing in bionic_tls needs to be preserved in the transition to the