Skip to content

Commit 8bc45f1

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 399865c commit 8bc45f1

File tree

8 files changed

+121
-2
lines changed

8 files changed

+121
-2
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ class Allocator {
638638
// sizing purposes.
639639
uptr getStats(char *Buffer, uptr Size) {
640640
ScopedString Str;
641+
// TODO: Use Str.copyToBuffer and fail when Buffer is NULL.
641642
const uptr Length = getStats(&Str) + 1;
642643
if (Length < Size)
643644
Size = Length;
@@ -654,6 +655,12 @@ class Allocator {
654655
Str.output();
655656
}
656657

658+
uptr getFragmentationInfo(char *Buffer, uptr Size) {
659+
ScopedString Str;
660+
Primary.getFragmentationInfo(&Str);
661+
return Str.copyToBuffer(Buffer, Size);
662+
}
663+
657664
void printFragmentationInfo() {
658665
ScopedString Str;
659666
Primary.getFragmentationInfo(&Str);

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
@@ -229,6 +229,16 @@ void ScopedString::append(const char *Format, ...) {
229229
va_end(Args);
230230
}
231231

232+
size_t ScopedString::copyToBuffer(char *OutputBase, size_t OutputLength) {
233+
DCHECK(OutputBase);
234+
if (OutputLength) {
235+
const size_t Written = Min(length(), OutputLength - 1);
236+
memcpy(OutputBase, data(), Written);
237+
OutputBase[Written] = '\0';
238+
}
239+
return length() + 1;
240+
}
241+
232242
void Printf(const char *Format, ...) {
233243
va_list Args;
234244
va_start(Args, Format);

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

Lines changed: 7 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');
@@ -31,6 +31,11 @@ class ScopedString {
3131
void reserve(size_t Size) { String.reserve(Size + 1); }
3232
uptr capacity() { return String.capacity() - 1; }
3333

34+
// Copies the string to the buffer, truncating if necessary.
35+
// Null-terminates the output if output_length is greater than zero.
36+
// Returns the original string's size (including null).
37+
size_t copyToBuffer(char *OutputBase, size_t OutputLength);
38+
3439
private:
3540
void appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
3641
bool PadWithZero, bool Negative, bool Upper);

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_LE(0, Allocator->getStats(buffer, sizeof(buffer)));
340+
EXPECT_LE(0, Allocator->getFragmentationInfo(buffer, sizeof(buffer)));
341+
}
336342
}
337343

338344
#define SCUDO_MAKE_BASIC_TEST(SizeLog) \

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

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

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

133176
#include <sys/resource.h>

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "internal_defs.h"
1616
#include "platform.h"
1717
#include "scudo/interface.h"
18+
#include "string_utils.h"
1819
#include "wrappers_c.h"
1920
#include "wrappers_c_checks.h"
2021

@@ -37,4 +38,19 @@ scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)> SCUDO_ALLOCATOR;
3738

3839
extern "C" INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
3940

41+
extern "C" INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer,
42+
size_t size) {
43+
switch (topic) {
44+
case M_INFO_TOPIC_STATS:
45+
return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
46+
case M_INFO_TOPIC_FRAGMENTATION:
47+
return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
48+
size);
49+
default:
50+
scudo::Printf("Scudo WARNING: unrecognized __scudo_get_info topic: %d\n",
51+
topic);
52+
return 0;
53+
}
54+
}
55+
4056
#endif // !SCUDO_ANDROID || !_BIONIC

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "internal_defs.h"
1616
#include "platform.h"
1717
#include "scudo/interface.h"
18+
#include "string_utils.h"
1819
#include "wrappers_c.h"
1920
#include "wrappers_c_checks.h"
2021

@@ -38,6 +39,20 @@ static scudo::Allocator<scudo::Config, SCUDO_PREFIX(malloc_postinit)>
3839
// TODO(kostyak): support both allocators.
3940
INTERFACE void __scudo_print_stats(void) { Allocator.printStats(); }
4041

42+
INTERFACE size_t __scudo_get_info(uint32_t topic, void *buffer, size_t size) {
43+
switch (topic) {
44+
case M_INFO_TOPIC_STATS:
45+
return Allocator.getStats(reinterpret_cast<char *>(buffer), size);
46+
case M_INFO_TOPIC_FRAGMENTATION:
47+
return Allocator.getFragmentationInfo(reinterpret_cast<char *>(buffer),
48+
size);
49+
default:
50+
scudo::Printf("Scudo WARNING: unrecognized __scudo_get_info topic: %d\n",
51+
topic);
52+
return 0;
53+
}
54+
}
55+
4156
INTERFACE void __scudo_get_error_info(
4257
struct scudo_error_info *error_info, uintptr_t fault_addr,
4358
const char *stack_depot, size_t stack_depot_size, const char *region_info,

0 commit comments

Comments
 (0)