aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmarks/Android.bp1
-rw-r--r--benchmarks/ScopedDecayTimeRestorer.h40
-rw-r--r--benchmarks/malloc_benchmark.cpp4
-rw-r--r--benchmarks/malloc_sql_benchmark.cpp7
-rw-r--r--benchmarks/stdlib_benchmark.cpp22
-rw-r--r--libc/bionic/malloc_common.cpp27
-rw-r--r--libc/bionic/malloc_common_dynamic.cpp8
-rw-r--r--libc/platform/bionic/malloc.h7
-rw-r--r--libc/private/bionic_globals.h1
-rw-r--r--tests/malloc_test.cpp33
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
+}