[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 Mar 7 03:28:26 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: None (piwicode)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/130273.diff
8 Files Affected:
- (modified) compiler-rt/lib/scudo/standalone/combined.h (+22-8)
- (modified) compiler-rt/lib/scudo/standalone/include/scudo/interface.h (+17)
- (modified) compiler-rt/lib/scudo/standalone/string_utils.cpp (+12)
- (modified) compiler-rt/lib/scudo/standalone/string_utils.h (+8-2)
- (modified) compiler-rt/lib/scudo/standalone/tests/combined_test.cpp (+6)
- (modified) compiler-rt/lib/scudo/standalone/tests/strings_test.cpp (+40)
- (modified) compiler-rt/lib/scudo/standalone/wrappers_c.cpp (+11)
- (modified) compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp (+10)
``````````diff
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 5deb8c97f1c86..7ab17c5d204c0 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -633,14 +633,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() {
@@ -649,6 +643,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);
@@ -1739,6 +1739,20 @@ class Allocator {
return (Bytes - sizeof(AllocationRingBuffer)) /
sizeof(typename AllocationRingBuffer::Entry);
}
+
+ // Writes the string content to the specified buffer as a null terminated
+ // string, and returns the input string size (including terminal '\0') for
+ // buffer sizing purpose.
+ size_t copyToBuffer(const ScopedString &input, char *output_base,
+ size_t output_length) {
+ if (!output_base)
+ return 0;
+ // Assume that ScopedString internal vector terminal null exists and is not
+ // reported by length.
+ const size_t written = Min(input.length() + 1, output_length);
+ memcpy(output_base, input.data(), written);
+ return written;
+ }
};
} // namespace scudo
diff --git a/compiler-rt/lib/scudo/standalone/include/scudo/interface.h b/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
index a2dedea910cc0..6845b40c8b5cc 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, char *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..e60ae991352cf 100644
--- a/compiler-rt/lib/scudo/standalone/string_utils.cpp
+++ b/compiler-rt/lib/scudo/standalone/string_utils.cpp
@@ -238,4 +238,16 @@ 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) {
+ // Assume that ScopedString internal vector ends with '\0' and that the
+ // terminal null is not reported by `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..ccb3cf516d8fe 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,12 @@ 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).
+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 9d665ef88c402..30f54ae4d0db8 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -291,6 +291,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..71968ad308675 100644
--- a/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/strings_test.cpp
@@ -128,6 +128,46 @@ 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);
+ }
+
+ { // 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);
+ }
+}
+
#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..725e3f3e25996 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c.cpp
@@ -37,4 +37,15 @@ 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, char *buffer,
+ size_t size) {
+ switch (topic) {
+ case M_INFO_TOPIC_STATS:
+ return Allocator.getStats(buffer, size);
+ case M_INFO_TOPIC_FRAGMENTATION:
+ return Allocator.getFragmentationInfo(buffer, size);
+ }
+ 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..f649059b9c878 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
@@ -38,6 +38,16 @@ 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, char *buffer, size_t size) {
+ switch (topic) {
+ case M_INFO_TOPIC_STATS:
+ return Allocator.getStats(buffer, size);
+ case M_INFO_TOPIC_FRAGMENTATION:
+ return Allocator.getFragmentationInfo(buffer, size);
+ }
+ 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,
``````````
</details>
https://github.com/llvm/llvm-project/pull/130273
More information about the llvm-commits
mailing list