diff options
-rw-r--r-- | benchmarks/Android.bp | 1 | ||||
-rw-r--r-- | benchmarks/ScopedDecayTimeRestorer.h | 40 | ||||
-rw-r--r-- | benchmarks/malloc_benchmark.cpp | 4 | ||||
-rw-r--r-- | benchmarks/malloc_sql_benchmark.cpp | 7 | ||||
-rw-r--r-- | benchmarks/stdlib_benchmark.cpp | 22 | ||||
-rw-r--r-- | libc/bionic/malloc_common.cpp | 27 | ||||
-rw-r--r-- | libc/bionic/malloc_common_dynamic.cpp | 8 | ||||
-rw-r--r-- | libc/platform/bionic/malloc.h | 7 | ||||
-rw-r--r-- | libc/private/bionic_globals.h | 1 | ||||
-rw-r--r-- | tests/malloc_test.cpp | 33 |
10 files changed, 138 insertions, 12 deletions
diff --git a/benchmarks/Android.bp b/benchmarks/Android.bp index ffb592108..75e607cd1 100644 --- a/benchmarks/Android.bp +++ b/benchmarks/Android.bp @@ -72,6 +72,7 @@ cc_defaults { target: { android: { + header_libs: ["bionic_libc_platform_headers"], static_libs: [ "libmeminfo", "libprocinfo", diff --git a/benchmarks/ScopedDecayTimeRestorer.h b/benchmarks/ScopedDecayTimeRestorer.h new file mode 100644 index 000000000..5835b4316 --- /dev/null +++ b/benchmarks/ScopedDecayTimeRestorer.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <malloc.h> + +#if defined(__BIONIC__) + +#include "platform/bionic/malloc.h" + +class ScopedDecayTimeRestorer { + public: + ScopedDecayTimeRestorer() { + bool value; + if (android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value))) { + saved_value_ = value ? 1 : 0; + } + } + + virtual ~ScopedDecayTimeRestorer() { mallopt(M_DECAY_TIME, saved_value_); } + + private: + int saved_value_ = 0; +}; + +#endif diff --git a/benchmarks/malloc_benchmark.cpp b/benchmarks/malloc_benchmark.cpp index 258343fca..8f467d2ab 100644 --- a/benchmarks/malloc_benchmark.cpp +++ b/benchmarks/malloc_benchmark.cpp @@ -36,11 +36,14 @@ #include <vector> #include <benchmark/benchmark.h> +#include "ScopedDecayTimeRestorer.h" #include "util.h" #if defined(__BIONIC__) static void RunMalloptPurge(benchmark::State& state, int purge_value) { + ScopedDecayTimeRestorer restorer; + static size_t sizes[] = {8, 16, 32, 64, 128, 1024, 4096, 16384, 65536, 131072, 1048576}; static int pagesize = getpagesize(); mallopt(M_DECAY_TIME, 1); @@ -69,7 +72,6 @@ static void RunMalloptPurge(benchmark::State& state, int purge_value) { mallopt(purge_value, 0); } - mallopt(M_DECAY_TIME, 0); } static void RunThreadsThroughput(benchmark::State& state, size_t size, size_t num_threads) { diff --git a/benchmarks/malloc_sql_benchmark.cpp b/benchmarks/malloc_sql_benchmark.cpp index 383325ccb..d5b17f677 100644 --- a/benchmarks/malloc_sql_benchmark.cpp +++ b/benchmarks/malloc_sql_benchmark.cpp @@ -31,6 +31,7 @@ #include <unistd.h> #include <benchmark/benchmark.h> +#include "ScopedDecayTimeRestorer.h" #include "util.h" #if defined(__BIONIC__) @@ -104,6 +105,8 @@ void BenchmarkMalloc(MallocEntry entries[], size_t total_entries, size_t max_all #include "malloc_sql.h" static void BM_malloc_sql_trace_default(benchmark::State& state) { + ScopedDecayTimeRestorer restorer; + // The default is expected to be a zero decay time. mallopt(M_DECAY_TIME, 0); @@ -115,14 +118,14 @@ static void BM_malloc_sql_trace_default(benchmark::State& state) { BIONIC_BENCHMARK(BM_malloc_sql_trace_default); static void BM_malloc_sql_trace_decay1(benchmark::State& state) { + ScopedDecayTimeRestorer restorer; + mallopt(M_DECAY_TIME, 1); for (auto _ : state) { BenchmarkMalloc(g_sql_entries, sizeof(g_sql_entries) / sizeof(MallocEntry), kMaxSqlAllocSlots); } - - mallopt(M_DECAY_TIME, 0); } BIONIC_BENCHMARK(BM_malloc_sql_trace_decay1); diff --git a/benchmarks/stdlib_benchmark.cpp b/benchmarks/stdlib_benchmark.cpp index 14b380a45..9be72e7aa 100644 --- a/benchmarks/stdlib_benchmark.cpp +++ b/benchmarks/stdlib_benchmark.cpp @@ -22,6 +22,7 @@ #include <unistd.h> #include <benchmark/benchmark.h> +#include "ScopedDecayTimeRestorer.h" #include "util.h" static void MallocFree(benchmark::State& state) { @@ -40,6 +41,8 @@ static void MallocFree(benchmark::State& state) { static void BM_stdlib_malloc_free_default(benchmark::State& state) { #if defined(__BIONIC__) + ScopedDecayTimeRestorer restorer; + // The default is expected to be a zero decay time. mallopt(M_DECAY_TIME, 0); #endif @@ -50,11 +53,11 @@ BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_free_default, "AT_COMMON_SIZES"); #if defined(__BIONIC__) static void BM_stdlib_malloc_free_decay1(benchmark::State& state) { + ScopedDecayTimeRestorer restorer; + mallopt(M_DECAY_TIME, 1); MallocFree(state); - - mallopt(M_DECAY_TIME, 0); } BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_free_decay1, "AT_COMMON_SIZES"); #endif @@ -75,6 +78,8 @@ static void CallocFree(benchmark::State& state) { static void BM_stdlib_calloc_free_default(benchmark::State& state) { #if defined(__BIONIC__) + ScopedDecayTimeRestorer restorer; + // The default is expected to be a zero decay time. mallopt(M_DECAY_TIME, 0); #endif @@ -113,8 +118,9 @@ static void MallocMultiple(benchmark::State& state, size_t nbytes, size_t numAll } void BM_stdlib_malloc_forty_default(benchmark::State& state) { - #if defined(__BIONIC__) + ScopedDecayTimeRestorer restorer; + // The default is expected to be a zero decay time. mallopt(M_DECAY_TIME, 0); #endif @@ -125,17 +131,19 @@ BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_forty_default, "AT_COMMON_SIZES"); #if defined(__BIONIC__) void BM_stdlib_malloc_forty_decay1(benchmark::State& state) { + ScopedDecayTimeRestorer restorer; + mallopt(M_DECAY_TIME, 1); MallocMultiple(state, state.range(0), 40); - - mallopt(M_DECAY_TIME, 0); } BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_forty_decay1, "AT_COMMON_SIZES"); #endif void BM_stdlib_malloc_multiple_8192_allocs_default(benchmark::State& state) { #if defined(__BIONIC__) + ScopedDecayTimeRestorer restorer; + // The default is expected to be a zero decay time. mallopt(M_DECAY_TIME, 0); #endif @@ -146,11 +154,11 @@ BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_multiple_8192_allocs_default, "AT_SMA #if defined(__BIONIC__) void BM_stdlib_malloc_multiple_8192_allocs_decay1(benchmark::State& state) { + ScopedDecayTimeRestorer restorer; + mallopt(M_DECAY_TIME, 1); MallocMultiple(state, 8192, state.range(0)); - - mallopt(M_DECAY_TIME, 0); } BIONIC_BENCHMARK_WITH_ARG(BM_stdlib_malloc_multiple_8192_allocs_decay1, "AT_SMALL_SIZES"); #endif diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index e159fdcde..3c4884b2c 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -110,12 +110,27 @@ extern "C" int mallopt(int param, int value) { if (param == M_BIONIC_ZERO_INIT) { return SetHeapZeroInitialize(value); } + // The rest we pass on... + int retval; auto dispatch_table = GetDispatchTable(); if (__predict_false(dispatch_table != nullptr)) { - return dispatch_table->mallopt(param, value); + retval = dispatch_table->mallopt(param, value); + } else { + retval = Malloc(mallopt)(param, value); } - return Malloc(mallopt)(param, value); + + // Track the M_DECAY_TIME mallopt calls. + if (param == M_DECAY_TIME && retval == 1) { + __libc_globals.mutate([value](libc_globals* globals) { + if (value == 0) { + atomic_store(&globals->decay_time_enabled, false); + } else { + atomic_store(&globals->decay_time_enabled, true); + } + }); + } + return retval; } extern "C" void* malloc(size_t bytes) { @@ -341,6 +356,14 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) { *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->memtag_stack); return true; } + if (opcode == M_GET_DECAY_TIME_ENABLED) { + if (arg == nullptr || arg_size != sizeof(bool)) { + errno = EINVAL; + return false; + } + *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->decay_time_enabled); + return true; + } errno = ENOTSUP; return false; } diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp index 802a94fed..792a11428 100644 --- a/libc/bionic/malloc_common_dynamic.cpp +++ b/libc/bionic/malloc_common_dynamic.cpp @@ -543,6 +543,14 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) { *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->memtag_stack); return true; } + if (opcode == M_GET_DECAY_TIME_ENABLED) { + if (arg == nullptr || arg_size != sizeof(bool)) { + errno = EINVAL; + return false; + } + *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->decay_time_enabled); + return true; + } // Try heapprofd's mallopt, as it handles options not covered here. return HeapprofdMallopt(opcode, arg, arg_size); } diff --git a/libc/platform/bionic/malloc.h b/libc/platform/bionic/malloc.h index 0a6546ec9..a06b8ee30 100644 --- a/libc/platform/bionic/malloc.h +++ b/libc/platform/bionic/malloc.h @@ -104,6 +104,13 @@ enum { // Query whether memtag stack is enabled for this process. M_MEMTAG_STACK_IS_ON = 11, #define M_MEMTAG_STACK_IS_ON M_MEMTAG_STACK_IS_ON + // Query whether the current process has the decay time enabled so that + // the memory from allocations are not immediately released to the OS. + // Result is assigned to the arg pointer's destination. + // arg = bool* + // arg_size = sizeof(bool) + M_GET_DECAY_TIME_ENABLED = 12, +#define M_GET_DECAY_TIME_ENABLED M_GET_DECAY_TIME_ENABLED }; #pragma clang diagnostic push diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h index d9c42349f..15b570de2 100644 --- a/libc/private/bionic_globals.h +++ b/libc/private/bionic_globals.h @@ -49,6 +49,7 @@ struct libc_globals { long setjmp_cookie; uintptr_t heap_pointer_tag; _Atomic(bool) memtag_stack; + _Atomic(bool) decay_time_enabled; // In order to allow a complete switch between dispatch tables without // the need for copying each function by function in the structure, diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp index 2411753b4..14a426ff8 100644 --- a/tests/malloc_test.cpp +++ b/tests/malloc_test.cpp @@ -1734,3 +1734,36 @@ TEST(malloc, zeroed_allocations_realloc) { } } } + +TEST(android_mallopt, get_decay_time_enabled_errors) { +#if defined(__BIONIC__) + errno = 0; + EXPECT_FALSE(android_mallopt(M_GET_DECAY_TIME_ENABLED, nullptr, sizeof(bool))); + EXPECT_ERRNO(EINVAL); + + errno = 0; + int value; + EXPECT_FALSE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value))); + EXPECT_ERRNO(EINVAL); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(android_mallopt, get_decay_time_enabled) { +#if defined(__BIONIC__) + SKIP_WITH_HWASAN << "hwasan does not implement mallopt"; + + EXPECT_EQ(1, mallopt(M_DECAY_TIME, 0)); + + bool value; + EXPECT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value))); + EXPECT_FALSE(value); + + EXPECT_EQ(1, mallopt(M_DECAY_TIME, 1)); + EXPECT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value))); + EXPECT_TRUE(value); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} |