Skip to content

Commit c347614

Browse files
committed
[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.
1 parent dec8f13 commit c347614

File tree

8 files changed

+119
-12
lines changed

8 files changed

+119
-12
lines changed

compiler-rt/lib/scudo/standalone/combined.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,14 +638,8 @@ class Allocator {
638638
// sizing purposes.
639639
uptr getStats(char *Buffer, uptr Size) {
640640
ScopedString Str;
641-
const uptr Length = getStats(&Str) + 1;
642-
if (Length < Size)
643-
Size = Length;
644-
if (Buffer && Size) {
645-
memcpy(Buffer, Str.data(), Size);
646-
Buffer[Size - 1] = '\0';
647-
}
648-
return Length;
641+
getStats(&Str);
642+
return CopyToBuffer(Str, Buffer, Size);
649643
}
650644

651645
void printStats() {
@@ -654,6 +648,12 @@ class Allocator {
654648
Str.output();
655649
}
656650

651+
uptr getFragmentationInfo(char *Buffer, uptr Size) {
652+
ScopedString Str;
653+
Primary.getFragmentationInfo(&Str);
654+
return CopyToBuffer(Str, Buffer, Size);
655+
}
656+
657657
void printFragmentationInfo() {
658658
ScopedString Str;
659659
Primary.getFragmentationInfo(&Str);
@@ -1639,12 +1639,11 @@ class Allocator {
16391639
}
16401640
}
16411641

1642-
uptr getStats(ScopedString *Str) {
1642+
void getStats(ScopedString *Str) {
16431643
Primary.getStats(Str);
16441644
Secondary.getStats(Str);
16451645
Quarantine.getStats(Str);
16461646
TSDRegistry.getStats(Str);
1647-
return Str->length();
16481647
}
16491648

16501649
static typename AllocationRingBuffer::Entry *

compiler-rt/lib/scudo/standalone/include/scudo/interface.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ __attribute__((weak)) void __scudo_realloc_deallocate_hook(void *old_ptr);
3535

3636
void __scudo_print_stats(void);
3737

38+
// Reports all allocators configuration and general statistics as a null
39+
// terminated text string.
40+
#ifndef M_INFO_TOPIC_STATS
41+
#define M_INFO_TOPIC_STATS 1
42+
#endif
43+
44+
// Reports fragmentation statistics of the primary allocation as a null
45+
// terminated text string.
46+
#ifndef M_INFO_TOPIC_FRAGMENTATION
47+
#define M_INFO_TOPIC_FRAGMENTATION 2
48+
#endif
49+
50+
// Writes allocator statistics to the buffer, truncating to the specified size
51+
// if necessary. Returns the full report size (before truncation) for buffer
52+
// sizing purpose, or zero if the topic is not supported.
53+
size_t __scudo_get_info(uint32_t topic, void *buffer, size_t size);
54+
3855
typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);
3956

4057
// Determine the likely cause of a tag check fault or other memory protection

compiler-rt/lib/scudo/standalone/string_utils.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,14 @@ void Printf(const char *Format, ...) {
238238
va_end(Args);
239239
}
240240

241+
size_t CopyToBuffer(const ScopedString &input, char *output_base,
242+
size_t output_length) {
243+
if (output_base && output_length) {
244+
const size_t written = Min(input.length(), output_length - 1);
245+
memcpy(output_base, input.data(), written);
246+
output_base[written] = '\0';
247+
}
248+
return input.length() + 1;
249+
}
250+
241251
} // namespace scudo

compiler-rt/lib/scudo/standalone/string_utils.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ namespace scudo {
1919
class ScopedString {
2020
public:
2121
explicit ScopedString() { String.push_back('\0'); }
22-
uptr length() { return String.size() - 1; }
23-
const char *data() { return String.data(); }
22+
uptr length() const { return String.size() - 1; }
23+
const char *data() const { return String.data(); }
2424
void clear() {
2525
String.clear();
2626
String.push_back('\0');
@@ -45,6 +45,14 @@ class ScopedString {
4545

4646
void Printf(const char *Format, ...) FORMAT(1, 2);
4747

48+
// Copies the string to the buffer, truncating if necessary.
49+
// Null-terminates the output if output_length is greater than zero.
50+
// Returns the original string's size (including null).
51+
// This can be called with a null buffer or zero size for buffer
52+
// sizing purposes.
53+
size_t CopyToBuffer(const ScopedString &input, char *output_base,
54+
size_t output_length);
55+
4856
} // namespace scudo
4957

5058
#endif // SCUDO_STRING_UTILS_H_

compiler-rt/lib/scudo/standalone/tests/combined_test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ void ScudoCombinedTest<Config>::BasicTest(scudo::uptr SizeLog) {
333333

334334
Allocator->printStats();
335335
Allocator->printFragmentationInfo();
336+
337+
{
338+
char buffer[256] = {0};
339+
EXPECT_GT(Allocator->getStats(buffer, sizeof(buffer)), 0);
340+
EXPECT_GT(Allocator->getFragmentationInfo(buffer, sizeof(buffer)), 0);
341+
}
336342
}
337343

338344
#define SCUDO_MAKE_BASIC_TEST(SizeLog) \

compiler-rt/lib/scudo/standalone/tests/strings_test.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,48 @@ TEST(ScudoStringsTest, Padding) {
128128
testAgainstLibc<int>("%03d - %03d", -12, -1234);
129129
}
130130

131+
TEST(ScudoStringsTest, CopyToBuffer) {
132+
{ // Call with null buffer.
133+
scudo::ScopedString Str;
134+
Str.append("abc");
135+
EXPECT_EQ(4U, scudo::CopyToBuffer(Str, nullptr, 0));
136+
}
137+
138+
{ // Empty string.
139+
scudo::ScopedString Str;
140+
char buf[256] = {'0', '1'};
141+
EXPECT_EQ(1U, scudo::CopyToBuffer(Str, buf, sizeof(buf)));
142+
EXPECT_STREQ("", buf);
143+
EXPECT_EQ(0, buf[0]); // Rest of the buffer remains unchanged.
144+
}
145+
146+
{ // Empty string in empty buffer.
147+
scudo::ScopedString Str;
148+
char buf[256] = {'0', '1'};
149+
EXPECT_EQ(1U, scudo::CopyToBuffer(Str, buf, 0));
150+
EXPECT_EQ('0', buf[0]); // Nothing changed because provided size is 0.
151+
}
152+
153+
{ // Some text fittinh in the buffer.
154+
scudo::ScopedString Str;
155+
Str.append("abc");
156+
char buf[256] = {'0', '1', '2', '3', '4', '5'};
157+
EXPECT_EQ(4U, scudo::CopyToBuffer(
158+
Str, buf, sizeof(buf))); // Size includes terminal null.
159+
EXPECT_STREQ("abc", buf);
160+
EXPECT_EQ(buf[4],'4');
161+
}
162+
163+
{ // Some text with overflows.
164+
scudo::ScopedString Str;
165+
Str.append("abc");
166+
char buf[256] = {'0', '1', '2', '3', '4', '5'};
167+
EXPECT_EQ(4U, scudo::CopyToBuffer(Str, buf, 3));
168+
EXPECT_STREQ("ab", buf);
169+
EXPECT_EQ(buf[3],'3');
170+
}
171+
}
172+
131173
#if defined(__linux__)
132174

133175
#include <sys/resource.h>

compiler-rt/lib/scudo/standalone/wrappers_c.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,17 @@ scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)> SCUDO_ALLOCATOR;
3737

3838
extern "C" INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
3939

40+
extern "C" INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer,
41+
size_t size) {
42+
switch (topic) {
43+
case M_INFO_TOPIC_STATS:
44+
return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
45+
case M_INFO_TOPIC_FRAGMENTATION:
46+
return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
47+
size);
48+
default:
49+
return 0;
50+
}
51+
}
52+
4053
#endif // !SCUDO_ANDROID || !_BIONIC

compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ static scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)>
3838
// TODO(kostyak): support both allocators.
3939
INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
4040

41+
INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer, size_t size) {
42+
switch (topic) {
43+
case M_INFO_TOPIC_STATS:
44+
return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
45+
case M_INFO_TOPIC_FRAGMENTATION:
46+
return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
47+
size);
48+
default:
49+
return 0;
50+
}
51+
}
52+
4153
INTERFACE void __scudo_get_error_info(
4254
struct scudo_error_info *error_info, uintptr_t fault_addr,
4355
const char *stack_depot, size_t stack_depot_size, const char *region_info,

0 commit comments

Comments
 (0)