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
This commit is contained in:
parent
a04764bd28
commit
175c8867b0
|
@ -1613,6 +1613,7 @@ filegroup {
|
|||
"bionic/icu.cpp",
|
||||
"bionic/malloc_common.cpp",
|
||||
"bionic/malloc_common_dynamic.cpp",
|
||||
"bionic/android_profiling_dynamic.cpp",
|
||||
"bionic/malloc_heapprofd.cpp",
|
||||
"bionic/malloc_limit.cpp",
|
||||
"bionic/ndk_cruft.cpp",
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_STATIC)
|
||||
#error This file should not be compiled for static targets.
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <async_safe/log.h>
|
||||
#include <platform/bionic/malloc.h>
|
||||
#include <platform/bionic/reserved_signals.h>
|
||||
|
||||
#include "malloc_heapprofd.h"
|
||||
|
||||
// This file defines the handler for the reserved signal sent by the Android
|
||||
// platform's profilers. The accompanying signal value discriminates between
|
||||
// specific requestors:
|
||||
// 0: heapprofd heap profiler.
|
||||
static constexpr int kHeapprofdSignalValue = 0;
|
||||
|
||||
static void HandleProfilingSignal(int, siginfo_t*, void*);
|
||||
|
||||
// Called during dynamic libc preinit.
|
||||
__LIBC_HIDDEN__ void __libc_init_profiling_handlers() {
|
||||
struct sigaction action = {};
|
||||
action.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
action.sa_sigaction = HandleProfilingSignal;
|
||||
sigaction(BIONIC_SIGNAL_PROFILER, &action, nullptr);
|
||||
}
|
||||
|
||||
static void HandleProfilingSignal(int /*signal_number*/, siginfo_t* info, void* /*ucontext*/) {
|
||||
if (info->si_code != SI_QUEUE)
|
||||
return;
|
||||
|
||||
int signal_value = info->si_value.sival_int;
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc", "%s: received profiling signal with si_value: %d",
|
||||
getprogname(), signal_value);
|
||||
|
||||
// Proceed only if the process is considered profileable.
|
||||
bool profileable = false;
|
||||
android_mallopt(M_GET_PROCESS_PROFILEABLE, &profileable, sizeof(profileable));
|
||||
if (!profileable) {
|
||||
async_safe_write_log(ANDROID_LOG_ERROR, "libc", "profiling signal rejected (not profileable)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (signal_value == kHeapprofdSignalValue) {
|
||||
HandleHeapprofdSignal();
|
||||
} else {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "unrecognized profiling signal si_value: %d",
|
||||
signal_value);
|
||||
}
|
||||
}
|
|
@ -99,6 +99,9 @@ static void __libc_preinit_impl() {
|
|||
// Hooks for various libraries to let them know that we're starting up.
|
||||
__libc_globals.mutate(__libc_init_malloc);
|
||||
|
||||
// Install reserved signal handlers for assisting the platform's profilers.
|
||||
__libc_init_profiling_handlers();
|
||||
|
||||
__libc_init_fork_handler();
|
||||
|
||||
#if __has_feature(hwaddress_sanitizer)
|
||||
|
|
|
@ -74,8 +74,6 @@ __END_DECLS
|
|||
|
||||
#endif
|
||||
|
||||
extern bool gZygoteChild;
|
||||
|
||||
static inline const MallocDispatch* GetDispatchTable() {
|
||||
return atomic_load_explicit(&__libc_globals->current_dispatch_table, memory_order_acquire);
|
||||
}
|
||||
|
|
|
@ -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 @@ static void MallocInitImpl(libc_globals* globals) {
|
|||
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 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
|
|||
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 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
|
|||
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);
|
||||
}
|
||||
// =============================================================================
|
||||
|
|
|
@ -49,18 +49,18 @@
|
|||
static constexpr char kHeapprofdSharedLib[] = "heapprofd_client.so";
|
||||
static constexpr char kHeapprofdPrefix[] = "heapprofd";
|
||||
static constexpr char kHeapprofdPropertyEnable[] = "heapprofd.enable";
|
||||
static constexpr int kHeapprofdSignal = __SIGRTMIN + 4;
|
||||
|
||||
// The logic for triggering heapprofd (at runtime) is as follows:
|
||||
// 1. HEAPPROFD_SIGNAL is received by the process, entering the
|
||||
// MaybeInstallInitHeapprofdHook signal handler.
|
||||
// 1. A reserved profiling signal is received by the process, its si_value
|
||||
// discriminating between different handlers. For the case of heapprofd,
|
||||
// HandleHeapprofdSignal is called.
|
||||
// 2. If the initialization is not already in flight
|
||||
// (gHeapprofdInitInProgress is false), the malloc hook is set to
|
||||
// point at InitHeapprofdHook, and gHeapprofdInitInProgress is set to
|
||||
// true.
|
||||
// 3. The next malloc call enters InitHeapprofdHook, which removes the malloc
|
||||
// hook, and spawns a detached pthread to run the InitHeapprofd task.
|
||||
// (gHeapprofdInitHook_installed atomic is used to perform this once.)
|
||||
// (gHeapprofdInitHookInstalled atomic is used to perform this once.)
|
||||
// 4. InitHeapprofd, on a dedicated pthread, loads the heapprofd client library,
|
||||
// installs the full set of heapprofd hooks, and invokes the client's
|
||||
// initializer. The dedicated pthread then terminates.
|
||||
|
@ -79,12 +79,9 @@ static _Atomic (void*) gHeapprofdHandle = nullptr;
|
|||
static _Atomic bool gHeapprofdInitInProgress = false;
|
||||
static _Atomic bool gHeapprofdInitHookInstalled = 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 the global
|
||||
// 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).
|
||||
static _Atomic bool gZygoteChildProfileable = false;
|
||||
// Set to true if the process has enabled malloc_debug or malloc_hooks, which
|
||||
// are incompatible (and take precedence over) heapprofd.
|
||||
static _Atomic bool gHeapprofdIncompatibleHooks = false;
|
||||
|
||||
extern "C" void* MallocInitHeapprofdHook(size_t);
|
||||
|
||||
|
@ -93,7 +90,7 @@ static constexpr MallocDispatch __heapprofd_init_dispatch
|
|||
Malloc(calloc),
|
||||
Malloc(free),
|
||||
Malloc(mallinfo),
|
||||
MallocInitHeapprofdHook,
|
||||
MallocInitHeapprofdHook, // malloc replacement
|
||||
Malloc(malloc_usable_size),
|
||||
Malloc(memalign),
|
||||
Malloc(posix_memalign),
|
||||
|
@ -112,38 +109,6 @@ static constexpr MallocDispatch __heapprofd_init_dispatch
|
|||
Malloc(malloc_info),
|
||||
};
|
||||
|
||||
static void MaybeInstallInitHeapprofdHook(int) {
|
||||
// Zygote child processes must be marked profileable.
|
||||
if (gZygoteChild &&
|
||||
!atomic_load_explicit(&gZygoteChildProfileable, memory_order_acquire)) {
|
||||
error_log("%s: not enabling heapprofd, not marked profileable.", getprogname());
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking this variable is only necessary when this could conflict with
|
||||
// the change to enable the allocation limit. All other places will
|
||||
// not ever have a conflict modifying the globals.
|
||||
if (!atomic_exchange(&gGlobalsMutating, true)) {
|
||||
if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
|
||||
__libc_globals.mutate([](libc_globals* globals) {
|
||||
atomic_store(&globals->default_dispatch_table, &__heapprofd_init_dispatch);
|
||||
auto dispatch_table = GetDispatchTable();
|
||||
if (dispatch_table == nullptr || dispatch_table == &globals->malloc_dispatch_table) {
|
||||
atomic_store(&globals->current_dispatch_table, &__heapprofd_init_dispatch);
|
||||
}
|
||||
});
|
||||
}
|
||||
atomic_store(&gGlobalsMutating, false);
|
||||
} else {
|
||||
// The only way you can get to this point is if the signal has been
|
||||
// blocked by a call to HeapprofdMaskSignal. The raise below will
|
||||
// do nothing until a call to HeapprofdUnmaskSignal, which will cause
|
||||
// the signal to be resent. Using this avoids the need for a busy loop
|
||||
// waiting for gGlobalsMutating to change back to false.
|
||||
raise(kHeapprofdSignal);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr char kHeapprofdProgramPropertyPrefix[] = "heapprofd.enable.";
|
||||
constexpr size_t kHeapprofdProgramPropertyPrefixSize = sizeof(kHeapprofdProgramPropertyPrefix) - 1;
|
||||
constexpr size_t kMaxCmdlineSize = 512;
|
||||
|
@ -203,6 +168,39 @@ static bool GetHeapprofdProgramProperty(char* data, size_t size) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Runtime triggering entry-point. Two possible call sites:
|
||||
// * when receiving a profiling signal with a si_value indicating heapprofd.
|
||||
// * when a Zygote child is marking itself as profileable, and there's a
|
||||
// matching profiling request for this process (in which case heapprofd client
|
||||
// is loaded synchronously).
|
||||
// In both cases, the caller is responsible for verifying that the process is
|
||||
// considered profileable.
|
||||
void HandleHeapprofdSignal() {
|
||||
if (atomic_load_explicit(&gHeapprofdIncompatibleHooks, memory_order_acquire)) {
|
||||
error_log("%s: not enabling heapprofd, malloc_debug/malloc_hooks are enabled.", getprogname());
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking this variable is only necessary when this could conflict with
|
||||
// the change to enable the allocation limit. All other places will
|
||||
// not ever have a conflict modifying the globals.
|
||||
if (!atomic_exchange(&gGlobalsMutating, true)) {
|
||||
if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
|
||||
__libc_globals.mutate([](libc_globals* globals) {
|
||||
atomic_store(&globals->default_dispatch_table, &__heapprofd_init_dispatch);
|
||||
auto dispatch_table = GetDispatchTable();
|
||||
if (dispatch_table == nullptr || dispatch_table == &globals->malloc_dispatch_table) {
|
||||
atomic_store(&globals->current_dispatch_table, &__heapprofd_init_dispatch);
|
||||
}
|
||||
});
|
||||
}
|
||||
atomic_store(&gGlobalsMutating, false);
|
||||
}
|
||||
// Otherwise, we're racing against malloc_limit's enable logic (at most once
|
||||
// per process, and a niche feature). This is highly unlikely, so simply give
|
||||
// up if it does happen.
|
||||
}
|
||||
|
||||
bool HeapprofdShouldLoad() {
|
||||
// First check for heapprofd.enable. If it is set to "all", enable
|
||||
// heapprofd for all processes. Otherwise, check heapprofd.enable.${prog},
|
||||
|
@ -226,38 +224,8 @@ bool HeapprofdShouldLoad() {
|
|||
return property_value[0] != '\0';
|
||||
}
|
||||
|
||||
void HeapprofdInstallSignalHandler() {
|
||||
struct sigaction action = {};
|
||||
action.sa_handler = MaybeInstallInitHeapprofdHook;
|
||||
sigaction(kHeapprofdSignal, &action, nullptr);
|
||||
}
|
||||
|
||||
extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t);
|
||||
|
||||
void HeapprofdMaskSignal() {
|
||||
sigset64_t mask_set;
|
||||
// Need to use this function instead because sigprocmask64 filters
|
||||
// out this signal.
|
||||
__rt_sigprocmask(SIG_SETMASK, nullptr, &mask_set, sizeof(mask_set));
|
||||
sigaddset64(&mask_set, kHeapprofdSignal);
|
||||
__rt_sigprocmask(SIG_SETMASK, &mask_set, nullptr, sizeof(mask_set));
|
||||
}
|
||||
|
||||
void HeapprofdUnmaskSignal() {
|
||||
sigset64_t mask_set;
|
||||
__rt_sigprocmask(SIG_SETMASK, nullptr, &mask_set, sizeof(mask_set));
|
||||
sigdelset64(&mask_set, kHeapprofdSignal);
|
||||
__rt_sigprocmask(SIG_SETMASK, &mask_set, nullptr, sizeof(mask_set));
|
||||
}
|
||||
|
||||
static void DisplayError(int) {
|
||||
error_log("Cannot install heapprofd while malloc debug/malloc hooks are enabled.");
|
||||
}
|
||||
|
||||
void HeapprofdInstallErrorSignalHandler() {
|
||||
struct sigaction action = {};
|
||||
action.sa_handler = DisplayError;
|
||||
sigaction(kHeapprofdSignal, &action, nullptr);
|
||||
void HeapprofdRememberHookConflict() {
|
||||
atomic_store_explicit(&gHeapprofdIncompatibleHooks, true, memory_order_release);
|
||||
}
|
||||
|
||||
static void CommonInstallHooks(libc_globals* globals) {
|
||||
|
@ -326,15 +294,12 @@ extern "C" void* MallocInitHeapprofdHook(size_t bytes) {
|
|||
return Malloc(malloc)(bytes);
|
||||
}
|
||||
|
||||
// Marks this process as a profileable zygote child.
|
||||
static bool HandleInitZygoteChildProfiling() {
|
||||
atomic_store_explicit(&gZygoteChildProfileable, true, memory_order_release);
|
||||
|
||||
bool HeapprofdInitZygoteChildProfiling() {
|
||||
// Conditionally start "from startup" profiling.
|
||||
if (HeapprofdShouldLoad()) {
|
||||
// Directly call the signal handler (will correctly guard against
|
||||
// concurrent signal delivery).
|
||||
MaybeInstallInitHeapprofdHook(kHeapprofdSignal);
|
||||
// Directly call the signal handler codepath (properly protects against
|
||||
// concurrent invocations).
|
||||
HandleHeapprofdSignal();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -358,13 +323,6 @@ static bool DispatchReset() {
|
|||
}
|
||||
|
||||
bool HeapprofdMallopt(int opcode, void* arg, size_t arg_size) {
|
||||
if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
|
||||
if (arg != nullptr || arg_size != 0) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
return HandleInitZygoteChildProfiling();
|
||||
}
|
||||
if (opcode == M_RESET_HOOKS) {
|
||||
if (arg != nullptr || arg_size != 0) {
|
||||
errno = EINVAL;
|
||||
|
|
|
@ -36,12 +36,11 @@ bool HeapprofdShouldLoad();
|
|||
|
||||
void HeapprofdInstallHooksAtInit(libc_globals* globals);
|
||||
|
||||
void HeapprofdInstallSignalHandler();
|
||||
void HeapprofdRememberHookConflict();
|
||||
|
||||
void HeapprofdInstallErrorSignalHandler();
|
||||
void HandleHeapprofdSignal();
|
||||
|
||||
void HeapprofdMaskSignal();
|
||||
|
||||
void HeapprofdUnmaskSignal();
|
||||
bool HeapprofdInitZygoteChildProfiling();
|
||||
|
||||
bool HeapprofdMallopt(int optcode, void* arg, size_t arg_size);
|
||||
|
||||
|
|
|
@ -264,7 +264,6 @@ static bool EnableLimitDispatchTable() {
|
|||
}
|
||||
#else
|
||||
static bool EnableLimitDispatchTable() {
|
||||
HeapprofdMaskSignal();
|
||||
pthread_mutex_lock(&gGlobalsMutateLock);
|
||||
// All other code that calls mutate will grab the gGlobalsMutateLock.
|
||||
// However, there is one case where the lock cannot be acquired, in the
|
||||
|
@ -272,7 +271,7 @@ static bool EnableLimitDispatchTable() {
|
|||
// threads calling mutate at the same time, use an atomic variable to
|
||||
// verify that only this function or the signal handler are calling mutate.
|
||||
// If this function is called at the same time as the signal handler is
|
||||
// being called, allow up to five ms for the signal handler to complete
|
||||
// being called, allow a short period for the signal handler to complete
|
||||
// before failing.
|
||||
bool enabled = false;
|
||||
size_t num_tries = 20;
|
||||
|
@ -291,7 +290,6 @@ static bool EnableLimitDispatchTable() {
|
|||
usleep(1000);
|
||||
}
|
||||
pthread_mutex_unlock(&gGlobalsMutateLock);
|
||||
HeapprofdUnmaskSignal();
|
||||
if (enabled) {
|
||||
info_log("malloc_limit: Allocation limit enabled, max size %" PRId64 " bytes\n", gAllocLimit);
|
||||
} else {
|
||||
|
|
|
@ -91,6 +91,12 @@ enum {
|
|||
// arg_size = sizeof(HeapTaggingLevel)
|
||||
M_SET_HEAP_TAGGING_LEVEL = 8,
|
||||
#define M_SET_HEAP_TAGGING_LEVEL M_SET_HEAP_TAGGING_LEVEL
|
||||
// Query whether the current process is considered to be profileable by the
|
||||
// Android platform. Result is assigned to the arg pointer's destination.
|
||||
// arg = bool*
|
||||
// arg_size = sizeof(bool)
|
||||
M_GET_PROCESS_PROFILEABLE = 9,
|
||||
#define M_GET_PROCESS_PROFILEABLE M_GET_PROCESS_PROFILEABLE
|
||||
};
|
||||
|
||||
enum HeapTaggingLevel {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
// 33 (__SIGRTMIN + 1) libbacktrace
|
||||
// 34 (__SIGRTMIN + 2) libcore
|
||||
// 35 (__SIGRTMIN + 3) debuggerd
|
||||
// 36 (__SIGRTMIN + 4) heapprofd native dumps
|
||||
// 36 (__SIGRTMIN + 4) platform profiling (heapprofd)
|
||||
// 37 (__SIGRTMIN + 5) coverage (libprofile-extras)
|
||||
// 38 (__SIGRTMIN + 6) heapprofd ART managed heap dumps
|
||||
//
|
||||
|
@ -47,6 +47,7 @@
|
|||
// in <android/legacy_signal_inlines.h> to match.
|
||||
|
||||
#define BIONIC_SIGNAL_DEBUGGER __SIGRTMIN + 3
|
||||
#define BIONIC_SIGNAL_PROFILER __SIGRTMIN + 4
|
||||
|
||||
#define __SIGRT_RESERVED 7
|
||||
static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset, int how) {
|
||||
|
|
|
@ -99,6 +99,7 @@ struct libc_shared_globals {
|
|||
|
||||
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();
|
||||
__LIBC_HIDDEN__ void __libc_init_fdsan();
|
||||
__LIBC_HIDDEN__ void __libc_init_profiling_handlers();
|
||||
|
||||
__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals);
|
||||
__LIBC_HIDDEN__ void __libc_init_setjmp_cookie(libc_globals* globals);
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
|
||||
#include <elf.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
|
@ -962,7 +963,11 @@ static void SetAllocationLimitMultipleThreads() {
|
|||
|
||||
// Let them go all at once.
|
||||
go = true;
|
||||
ASSERT_EQ(0, kill(getpid(), __SIGRTMIN + 4));
|
||||
// Send hardcoded signal (BIONIC_SIGNAL_PROFILER with value 0) to trigger
|
||||
// heapprofd handler.
|
||||
union sigval signal_value;
|
||||
signal_value.sival_int = 0;
|
||||
ASSERT_EQ(0, sigqueue(getpid(), __SIGRTMIN + 4, signal_value));
|
||||
|
||||
size_t num_successful = 0;
|
||||
for (size_t i = 0; i < kNumThreads; i++) {
|
||||
|
|
Loading…
Reference in New Issue