allow for heapprofd's signal to be multiplexed
This patch refactors heapprofd_malloc to make it easier to reuse the
reserved signal for multiple purposes. We define a new generic signal
handler for profilers, which dispatches to more specific logic based on
the signal's payload (si_value).
The profiler signal handler is installed during libc preinit, after
malloc initialization (so races against synchronous heapprofd
initialization need not be considered). In terms of code organization, I
copied the existing approach with a loosely referenced function in
bionic_globals.h. Do tell if you'd rather a different approach here.
The profileability of a process is quite tied to the malloc
files/interfaces in bionic - in particular, it's set through
android_mallopt. I do not change that, but instead introduce a new
android_mallopt option to be able to query profileability of the
process (which is now used by the new profiler signal handler). As part
of that, gZygoteChildProfileable is moved from heapprofd_malloc to
common (alongside gZygoteChild).
I've removed the masking and reraising of the heapprofd signal when
racing against malloc_limit init. We're ok with taking a simpler
approach and dropping the heapprofd signal in such an unlikely race.
Note: this requires a corresponding change in heapprofd to use sigqueue()
instead of kill(), as the latter leaves the si_value uninitialized(?) on
the receiving side.
Bug: 144281346
Change-Id: I93bb2e82cff5870e5ca499cf86439860aca9dfa5
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 8068f4e..2cb1e8a 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -75,9 +75,19 @@
// =============================================================================
pthread_mutex_t gGlobalsMutateLock = PTHREAD_MUTEX_INITIALIZER;
-bool gZygoteChild = false;
-
_Atomic bool gGlobalsMutating = false;
+
+static bool gZygoteChild = false;
+
+// In a Zygote child process, this is set to true if profiling of this process
+// is allowed. Note that this is set at a later time than gZygoteChild. The
+// latter is set during the fork (while still in zygote's SELinux domain). While
+// this bit is set after the child is specialized (and has transferred SELinux
+// domains if applicable). These two flags are read by the
+// BIONIC_SIGNAL_PROFILER handler, which does nothing if the process is not
+// profileable.
+static _Atomic bool gZygoteChildProfileable = false;
+
// =============================================================================
static constexpr MallocDispatch __libc_malloc_default_dispatch
@@ -391,13 +401,10 @@
if (HeapprofdShouldLoad()) {
HeapprofdInstallHooksAtInit(globals);
}
-
- // Install this last to avoid as many race conditions as possible.
- HeapprofdInstallSignalHandler();
} else {
- // Install a signal handler that prints an error since we don't support
- // heapprofd and any other hook to be installed at the same time.
- HeapprofdInstallErrorSignalHandler();
+ // Record the fact that incompatible hooks are active, to skip any later
+ // heapprofd signal handler invocations.
+ HeapprofdRememberHookConflict();
}
}
@@ -476,6 +483,27 @@
gZygoteChild = true;
return true;
}
+ if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
+ if (arg != nullptr || arg_size != 0) {
+ errno = EINVAL;
+ return false;
+ }
+ atomic_store_explicit(&gZygoteChildProfileable, true, memory_order_release);
+ // Also check if heapprofd should start profiling from app startup.
+ HeapprofdInitZygoteChildProfiling();
+ return true;
+ }
+ if (opcode == M_GET_PROCESS_PROFILEABLE) {
+ if (arg == nullptr || arg_size != sizeof(bool)) {
+ errno = EINVAL;
+ return false;
+ }
+ // Native processes are considered profileable. Zygote children are considered
+ // profileable only when appropriately tagged.
+ *reinterpret_cast<bool*>(arg) =
+ !gZygoteChild || atomic_load_explicit(&gZygoteChildProfileable, memory_order_acquire);
+ return true;
+ }
if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) {
return LimitEnable(arg, arg_size);
}
@@ -503,6 +531,7 @@
if (opcode == M_SET_HEAP_TAGGING_LEVEL) {
return SetHeapTaggingLevel(arg, arg_size);
}
+ // Try heapprofd's mallopt, as it handles options not covered here.
return HeapprofdMallopt(opcode, arg, arg_size);
}
// =============================================================================