[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
Fri Jun 6 08:50:53 PDT 2025


https://github.com/piwicode updated https://github.com/llvm/llvm-project/pull/130273

>From 3a4578ec083e455d03e66c6d9e430760923a6208 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   | 19 ++++-----
 .../standalone/include/scudo/interface.h      | 17 ++++++++
 .../lib/scudo/standalone/string_utils.cpp     | 10 +++++
 .../lib/scudo/standalone/string_utils.h       | 12 +++++-
 .../scudo/standalone/tests/combined_test.cpp  |  6 +++
 .../scudo/standalone/tests/strings_test.cpp   | 42 +++++++++++++++++++
 .../lib/scudo/standalone/wrappers_c.cpp       | 13 ++++++
 .../scudo/standalone/wrappers_c_bionic.cpp    | 12 ++++++
 8 files changed, 119 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 43655642843cb..66a9476f30dba 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -638,14 +638,8 @@ class Allocator {
   // sizing purposes.
   uptr getStats(char *Buffer, uptr Size) {
     ScopedString Str;
-    const uptr Length = getStats(&Str) + 1;
-    if (Length < Size)
-      Size = Length;
-    if (Buffer && Size) {
-      memcpy(Buffer, Str.data(), Size);
-      Buffer[Size - 1] = '\0';
-    }
-    return Length;
+    getStats(&Str);
+    return CopyToBuffer(Str, Buffer, Size);
   }
 
   void printStats() {
@@ -654,6 +648,12 @@ class Allocator {
     Str.output();
   }
 
+  uptr getFragmentationInfo(char *Buffer, uptr Size) {
+    ScopedString Str;
+    Primary.getFragmentationInfo(&Str);
+    return CopyToBuffer(Str, Buffer, Size);
+  }
+
   void printFragmentationInfo() {
     ScopedString Str;
     Primary.getFragmentationInfo(&Str);
@@ -1639,12 +1639,11 @@ class Allocator {
     }
   }
 
-  uptr getStats(ScopedString *Str) {
+  void getStats(ScopedString *Str) {
     Primary.getStats(Str);
     Secondary.getStats(Str);
     Quarantine.getStats(Str);
     TSDRegistry.getStats(Str);
-    return Str->length();
   }
 
   static typename AllocationRingBuffer::Entry *
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..062a4e6546a35 100644
--- a/compiler-rt/lib/scudo/standalone/string_utils.cpp
+++ b/compiler-rt/lib/scudo/standalone/string_utils.cpp
@@ -238,4 +238,14 @@ void Printf(const char *Format, ...) {
   va_end(Args);
 }
 
+size_t CopyToBuffer(const ScopedString &input, char *output_base,
+                    size_t output_length) {
+  if (output_base && output_length) {
+    const size_t written = Min(input.length(), output_length - 1);
+    memcpy(output_base, input.data(), written);
+    output_base[written] = '\0';
+  }
+  return input.length() + 1;
+}
+
 } // namespace scudo
diff --git a/compiler-rt/lib/scudo/standalone/string_utils.h b/compiler-rt/lib/scudo/standalone/string_utils.h
index cf61e150f20e5..f50a495bee1ee 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');
@@ -45,6 +45,14 @@ class ScopedString {
 
 void Printf(const char *Format, ...) FORMAT(1, 2);
 
+// 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).
+// This can be called with a null buffer or zero size for buffer
+// sizing purposes.
+size_t CopyToBuffer(const ScopedString &input, char *output_base,
+                    size_t output_length);
+
 } // namespace scudo
 
 #endif // SCUDO_STRING_UTILS_H_
diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 7e8d5b4396d2e..4b47869db41a9 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_GT(Allocator->getStats(buffer, sizeof(buffer)), 0);
+    EXPECT_GT(Allocator->getFragmentationInfo(buffer, sizeof(buffer)), 0);
+  }
 }
 
 #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..f0b6e9db5e9b1 100644
--- a/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
@@ -128,6 +128,48 @@ TEST(ScudoStringsTest, Padding) {
   testAgainstLibc<int>("%03d - %03d", -12, -1234);
 }
 
+TEST(ScudoStringsTest, CopyToBuffer) {
+  { // Call with null buffer.
+    scudo::ScopedString Str;
+    Str.append("abc");
+    EXPECT_EQ(4U, scudo::CopyToBuffer(Str, nullptr, 0));
+  }
+
+  { // Empty string.
+    scudo::ScopedString Str;
+    char buf[256] = {'0', '1'};
+    EXPECT_EQ(1U, scudo::CopyToBuffer(Str, buf, sizeof(buf)));
+    EXPECT_STREQ("", buf);
+    EXPECT_EQ(0, buf[0]); // Rest of the buffer remains unchanged.
+  }
+
+  { // Empty string in empty buffer.
+    scudo::ScopedString Str;
+    char buf[256] = {'0', '1'};
+    EXPECT_EQ(1U, scudo::CopyToBuffer(Str, buf, 0));
+    EXPECT_EQ('0', buf[0]); // Nothing changed because provided size is 0.
+  }
+
+  { // Some text fittinh in the buffer.
+    scudo::ScopedString Str;
+    Str.append("abc");
+    char buf[256] = {'0', '1', '2', '3', '4', '5'};
+    EXPECT_EQ(4U, scudo::CopyToBuffer(
+                      Str, buf, sizeof(buf))); // Size includes terminal null.
+    EXPECT_STREQ("abc", buf);
+    EXPECT_EQ(buf[4],'4');
+  }
+
+  { // Some text with overflows.
+    scudo::ScopedString Str;
+    Str.append("abc");
+    char buf[256] = {'0', '1', '2', '3', '4', '5'};
+    EXPECT_EQ(4U, scudo::CopyToBuffer(Str, buf, 3));
+    EXPECT_STREQ("ab", buf);
+    EXPECT_EQ(buf[3],'3');
+  }
+}
+
 #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..319cae9027f54 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
@@ -37,4 +37,17 @@ 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:
+    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..83d0396512659 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
@@ -38,6 +38,18 @@ 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:
+    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