Merge "Add a log_allocator_stats_on_exit option." into main
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 0d442b4..6be899d 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -212,6 +212,10 @@
"log_allocator_stats_on_signal",
{LOG_ALLOCATOR_STATS_ON_SIGNAL, &Config::VerifyValueEmpty},
},
+ {
+ "log_allocator_stats_on_exit",
+ {LOG_ALLOCATOR_STATS_ON_EXIT, &Config::VerifyValueEmpty},
+ },
};
bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 8551712..4840d43 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -49,6 +49,7 @@
constexpr uint64_t CHECK_UNREACHABLE_ON_SIGNAL = 0x2000;
constexpr uint64_t BACKTRACE_SPECIFIC_SIZES = 0x4000;
constexpr uint64_t LOG_ALLOCATOR_STATS_ON_SIGNAL = 0x8000;
+constexpr uint64_t LOG_ALLOCATOR_STATS_ON_EXIT = 0x10000;
// In order to guarantee posix compliance, set the minimum alignment
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
diff --git a/libc/malloc_debug/LogAllocatorStats.cpp b/libc/malloc_debug/LogAllocatorStats.cpp
index 6d1434e..ee6bfdf 100644
--- a/libc/malloc_debug/LogAllocatorStats.cpp
+++ b/libc/malloc_debug/LogAllocatorStats.cpp
@@ -43,13 +43,17 @@
g_call_mallopt = true;
}
+void Log() {
+ info_log("Logging allocator stats...");
+ if (mallopt(M_LOG_STATS, 0) == 0) {
+ error_log("mallopt(M_LOG_STATS, 0) call failed.");
+ }
+}
+
void CheckIfShouldLog() {
bool expected = true;
if (g_call_mallopt.compare_exchange_strong(expected, false)) {
- info_log("Logging allocator stats...");
- if (mallopt(M_LOG_STATS, 0) == 0) {
- error_log("mallopt(M_LOG_STATS, 0) call failed.");
- }
+ Log();
}
}
diff --git a/libc/malloc_debug/LogAllocatorStats.h b/libc/malloc_debug/LogAllocatorStats.h
index 99e0738..ded4f94 100644
--- a/libc/malloc_debug/LogAllocatorStats.h
+++ b/libc/malloc_debug/LogAllocatorStats.h
@@ -35,6 +35,8 @@
bool Initialize(const Config& config);
+void Log();
+
void CheckIfShouldLog();
} // namespace LogAllocatorStats
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 6d88092..3743852 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -461,6 +461,10 @@
getpid()).c_str());
}
+ if (g_debug->config().options() & LOG_ALLOCATOR_STATS_ON_EXIT) {
+ LogAllocatorStats::Log();
+ }
+
backtrace_shutdown();
// In order to prevent any issues of threads freeing previous pointers
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index c79d052..d33f9cd 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -861,6 +861,24 @@
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugConfigTest, log_allocator_stats_on_exit) {
+ ASSERT_TRUE(InitConfig("log_allocator_stats_on_exit")) << getFakeLogPrint();
+ ASSERT_EQ(LOG_ALLOCATOR_STATS_ON_EXIT, config->options());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, trigger_log_allocator_stats_on_exit_fail) {
+ ASSERT_FALSE(InitConfig("log_allocator_stats_on_exit=200")) << getFakeLogPrint();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'log_allocator_stats_on_exit' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugConfigTest, size) {
ASSERT_TRUE(InitConfig("backtrace_size=37")) << getFakeLogPrint();
ASSERT_EQ(BACKTRACE_SPECIFIC_SIZES, config->options());
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index ef8d235..c808dc0 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -426,7 +426,7 @@
Init(
"guard backtrace backtrace_enable_on_signal fill expand_alloc free_track leak_track "
"record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal "
- "log_allocator_stats_on_signal");
+ "log_allocator_stats_on_signal log_allocator_stats_on_exit");
VerifyAllocCalls(true);
}
@@ -2844,6 +2844,25 @@
}
}
+TEST_F(MallocDebugTest, log_allocator_stats_on_exit) {
+ Init("log_allocator_stats_on_exit");
+
+ void* pointer = debug_malloc(110);
+ ASSERT_TRUE(pointer != nullptr);
+ debug_free(pointer);
+
+ debug_finalize();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ if (!running_with_hwasan()) {
+ // Do an exact match because the mallopt should not fail in normal operation.
+ ASSERT_STREQ("4 malloc_debug Logging allocator stats...\n", getFakeLogPrint().c_str());
+ } else {
+ // mallopt fails with hwasan, so just verify that the message is present.
+ ASSERT_MATCH(getFakeLogPrint(), "4 malloc_debug Logging allocator stats...\\n");
+ }
+}
+
TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_size) {
Init("leak_track backtrace backtrace_size=120");