[compiler-rt] [scudo] Add `__scudo_get_info` symbol to export stats to a buffer. (PR #130273)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 16 07:37:07 PDT 2025
https://github.com/piwicode updated https://github.com/llvm/llvm-project/pull/130273
>From 8bc45f1e5aa869cd48404ebe202dc90b5da38cfd Mon Sep 17 00:00:00 2001
From: Pierre Labatut <plabatut at google.com>
Date: Fri, 7 Mar 2025 11:55:40 +0100
Subject: [PATCH] [scudo] Add `__scudo_get_info` symbol to export stats to a
buffer.
Also make possible to get the fragmentation stats from the primary
allocator.
Currenty, `__scudo_print_stats` symbol writes the report to the provided
printer, which is not convenient for processing the result.
---
compiler-rt/lib/scudo/standalone/combined.h | 7 +++
.../standalone/include/scudo/interface.h | 17 ++++++++
.../lib/scudo/standalone/string_utils.cpp | 10 +++++
.../lib/scudo/standalone/string_utils.h | 9 +++-
.../scudo/standalone/tests/combined_test.cpp | 6 +++
.../scudo/standalone/tests/strings_test.cpp | 43 +++++++++++++++++++
.../lib/scudo/standalone/wrappers_c.cpp | 16 +++++++
.../scudo/standalone/wrappers_c_bionic.cpp | 15 +++++++
8 files changed, 121 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 43655642843cb..4a25508f2e381 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -638,6 +638,7 @@ class Allocator {
// sizing purposes.
uptr getStats(char *Buffer, uptr Size) {
ScopedString Str;
+ // TODO: Use Str.copyToBuffer and fail when Buffer is NULL.
const uptr Length = getStats(&Str) + 1;
if (Length < Size)
Size = Length;
@@ -654,6 +655,12 @@ class Allocator {
Str.output();
}
+ uptr getFragmentationInfo(char *Buffer, uptr Size) {
+ ScopedString Str;
+ Primary.getFragmentationInfo(&Str);
+ return Str.copyToBuffer(Buffer, Size);
+ }
+
void printFragmentationInfo() {
ScopedString Str;
Primary.getFragmentationInfo(&Str);
diff --git a/compiler-rt/lib/scudo/standalone/include/scudo/interface.h b/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
index a2dedea910cc0..4113ccd7bd84e 100644
--- a/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
+++ b/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
@@ -35,6 +35,23 @@ __attribute__((weak)) void __scudo_realloc_deallocate_hook(void *old_ptr);
void __scudo_print_stats(void);
+// Reports all allocators configuration and general statistics as a null
+// terminated text string.
+#ifndef M_INFO_TOPIC_STATS
+#define M_INFO_TOPIC_STATS 1
+#endif
+
+// Reports fragmentation statistics of the primary allocation as a null
+// terminated text string.
+#ifndef M_INFO_TOPIC_FRAGMENTATION
+#define M_INFO_TOPIC_FRAGMENTATION 2
+#endif
+
+// Writes allocator statistics to the buffer, truncating to the specified size
+// if necessary. Returns the full report size (before truncation) for buffer
+// sizing purpose, or zero if the topic is not supported.
+size_t __scudo_get_info(uint32_t topic, void *buffer, size_t size);
+
typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);
// Determine the likely cause of a tag check fault or other memory protection
diff --git a/compiler-rt/lib/scudo/standalone/string_utils.cpp b/compiler-rt/lib/scudo/standalone/string_utils.cpp
index e584bd806e579..5c3ce8ad03fea 100644
--- a/compiler-rt/lib/scudo/standalone/string_utils.cpp
+++ b/compiler-rt/lib/scudo/standalone/string_utils.cpp
@@ -229,6 +229,16 @@ void ScopedString::append(const char *Format, ...) {
va_end(Args);
}
+size_t ScopedString::copyToBuffer(char *OutputBase, size_t OutputLength) {
+ DCHECK(OutputBase);
+ if (OutputLength) {
+ const size_t Written = Min(length(), OutputLength - 1);
+ memcpy(OutputBase, data(), Written);
+ OutputBase[Written] = '\0';
+ }
+ return length() + 1;
+}
+
void Printf(const char *Format, ...) {
va_list Args;
va_start(Args, Format);
diff --git a/compiler-rt/lib/scudo/standalone/string_utils.h b/compiler-rt/lib/scudo/standalone/string_utils.h
index cf61e150f20e5..8d320d8854665 100644
--- a/compiler-rt/lib/scudo/standalone/string_utils.h
+++ b/compiler-rt/lib/scudo/standalone/string_utils.h
@@ -19,8 +19,8 @@ namespace scudo {
class ScopedString {
public:
explicit ScopedString() { String.push_back('\0'); }
- uptr length() { return String.size() - 1; }
- const char *data() { return String.data(); }
+ uptr length() const { return String.size() - 1; }
+ const char *data() const { return String.data(); }
void clear() {
String.clear();
String.push_back('\0');
@@ -31,6 +31,11 @@ class ScopedString {
void reserve(size_t Size) { String.reserve(Size + 1); }
uptr capacity() { return String.capacity() - 1; }
+ // Copies the string to the buffer, truncating if necessary.
+ // Null-terminates the output if output_length is greater than zero.
+ // Returns the original string's size (including null).
+ size_t copyToBuffer(char *OutputBase, size_t OutputLength);
+
private:
void appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
bool PadWithZero, bool Negative, bool Upper);
diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 7e8d5b4396d2e..9b34575f05a10 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -333,6 +333,12 @@ void ScudoCombinedTest<Config>::BasicTest(scudo::uptr SizeLog) {
Allocator->printStats();
Allocator->printFragmentationInfo();
+
+ {
+ char buffer[256] = {0};
+ EXPECT_LE(0, Allocator->getStats(buffer, sizeof(buffer)));
+ EXPECT_LE(0, Allocator->getFragmentationInfo(buffer, sizeof(buffer)));
+ }
}
#define SCUDO_MAKE_BASIC_TEST(SizeLog) \
diff --git a/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp b/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
index f81e5036e83b0..e5e5d63a385ac 100644
--- a/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
@@ -128,6 +128,49 @@ TEST(ScudoStringsTest, Padding) {
testAgainstLibc<int>("%03d - %03d", -12, -1234);
}
+TEST(ScudoStringsTest, CopyFromAnEmptyString) {
+ scudo::ScopedString Str;
+ char buf[256] = {'0', '1'};
+ EXPECT_EQ(1U, Str.copyToBuffer(buf, sizeof(buf)));
+ EXPECT_STREQ("", buf);
+ EXPECT_EQ(0, buf[0]); // Rest of the buffer remains unchanged.
+}
+
+TEST(ScudoStringsTest, CopyFromAnEmptyStringIntoZeroSizeBuffer) {
+ scudo::ScopedString Str;
+ char buf[256] = {'0', '1'};
+ EXPECT_EQ(1U, Str.copyToBuffer(buf, 0));
+ EXPECT_EQ('0', buf[0]); // Nothing changed because provided size is 0.
+}
+
+TEST(ScudoStringsTest, CopyIntoLargeEnoughBuffer) {
+ scudo::ScopedString Str;
+ Str.append("abc");
+ char buf[256] = {'0', '1', '2', '3', '4', '5'};
+ // Size includes terminal null.
+ EXPECT_EQ(4U, Str.copyToBuffer(buf, sizeof(buf)));
+ EXPECT_STREQ("abc", buf);
+ EXPECT_EQ(buf[4], '4');
+}
+
+TEST(ScudoStringsTest, CopyWithTextOverflow) {
+ scudo::ScopedString Str;
+ Str.append("abc");
+ char buf[256] = {'0', '1', '2', '3', '4', '5'};
+ EXPECT_EQ(4U, Str.copyToBuffer(buf, 3));
+ EXPECT_STREQ("ab", buf);
+ EXPECT_EQ(buf[3], '3');
+}
+
+TEST(ScudoStringsTest, CopyIntoExactFit) {
+ scudo::ScopedString Str;
+ Str.append("abc");
+ char buf[256] = {'0', '1', '2', '3', '4', '5'};
+ EXPECT_EQ(4U, Str.copyToBuffer(buf, 4));
+ EXPECT_STREQ("abc", buf);
+ EXPECT_EQ(buf[4], '4');
+}
+
#if defined(__linux__)
#include <sys/resource.h>
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.cpp b/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
index 60014a0f66bf5..64580fd6ba646 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
@@ -15,6 +15,7 @@
#include "internal_defs.h"
#include "platform.h"
#include "scudo/interface.h"
+#include "string_utils.h"
#include "wrappers_c.h"
#include "wrappers_c_checks.h"
@@ -37,4 +38,19 @@ scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)> SCUDO_ALLOCATOR;
extern "C" INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
+extern "C" INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer,
+ size_t size) {
+ switch (topic) {
+ case M_INFO_TOPIC_STATS:
+ return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
+ case M_INFO_TOPIC_FRAGMENTATION:
+ return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
+ size);
+ default:
+ scudo::Printf("Scudo WARNING: unrecognized __scudo_get_info topic: %d\n",
+ topic);
+ return 0;
+ }
+}
+
#endif // !SCUDO_ANDROID || !_BIONIC
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
index e9d8c1e8d3db2..7ba8fbba45715 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
@@ -15,6 +15,7 @@
#include "internal_defs.h"
#include "platform.h"
#include "scudo/interface.h"
+#include "string_utils.h"
#include "wrappers_c.h"
#include "wrappers_c_checks.h"
@@ -38,6 +39,20 @@ static scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)>
// TODO(kostyak): support both allocators.
INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
+INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer, size_t size) {
+ switch (topic) {
+ case M_INFO_TOPIC_STATS:
+ return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
+ case M_INFO_TOPIC_FRAGMENTATION:
+ return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
+ size);
+ default:
+ scudo::Printf("Scudo WARNING: unrecognized __scudo_get_info topic: %d\n",
+ topic);
+ return 0;
+ }
+}
+
INTERFACE void __scudo_get_error_info(
struct scudo_error_info *error_info, uintptr_t fault_addr,
const char *stack_depot, size_t stack_depot_size, const char *region_info,
More information about the llvm-commits
mailing list