Merge "Add android_mallopt M_GET_DECAY_TIME_ENABLED." into main
diff --git a/benchmarks/Android.bp b/benchmarks/Android.bp
index ffb5921..75e607c 100644
--- a/benchmarks/Android.bp
+++ b/benchmarks/Android.bp
@@ -72,6 +72,7 @@
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 0000000..5835b43
--- /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 258343f..8f467d2 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 @@
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 383325c..d5b17f6 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 @@
#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 @@
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 14b380a..9be72e7 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 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 @@
#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 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 @@
}
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 @@
#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 @@
#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 e159fdc..3c4884b 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -110,12 +110,27 @@
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 @@
*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 802a94f..792a114 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -543,6 +543,14 @@
*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 0a6546e..a06b8ee 100644
--- a/libc/platform/bionic/malloc.h
+++ b/libc/platform/bionic/malloc.h
@@ -104,6 +104,13 @@
// 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 d9c4234..15b570d 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -49,6 +49,7 @@
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 2411753..14a426f 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -1734,3 +1734,36 @@
}
}
}
+
+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
+}