Skip to content

Commit 7ce67d3

Browse files
committed
[scudo][tests] Store the allocator instance in a global rather than posix_memalign it
The combined scudo allocator object is over 4MB in size which gets created via the posix_memalign on every test run. If the tests are sanitized with asan, then the asan allocator will need to mmap this large object every single time a test is run. Depending on where this is mapped, we might not be able to find a large enough contiguous space for scudo's primary allocator to reserve an arena. Such a case is more likely to occur on 39-bit vma for RISCV where the arena size is roughly a quarter of the whole address space and fragmentation can be a big issue. This helps reduce fragmentation by instead placing the allocator instance in a global storage rather than doing an anonymous mmap. Differential Revision: https://reviews.llvm.org/D158767
1 parent 764287f commit 7ce67d3

File tree

1 file changed

+62
-4
lines changed

1 file changed

+62
-4
lines changed

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

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,72 @@ template <typename Config> struct TestAllocator : scudo::Allocator<Config> {
7878
}
7979
~TestAllocator() { this->unmapTestOnly(); }
8080

81-
void *operator new(size_t size) {
81+
void *operator new(size_t size);
82+
void operator delete(void *ptr);
83+
};
84+
85+
constexpr size_t kMaxAlign = std::max({
86+
alignof(scudo::Allocator<scudo::DefaultConfig>),
87+
#if SCUDO_CAN_USE_PRIMARY64
88+
alignof(scudo::Allocator<scudo::FuchsiaConfig>),
89+
#endif
90+
alignof(scudo::Allocator<scudo::AndroidSvelteConfig>),
91+
alignof(scudo::Allocator<scudo::AndroidConfig>)
92+
});
93+
94+
#if SCUDO_RISCV64
95+
// The allocator is over 4MB large. Rather than creating an instance of this on
96+
// the heap, keep it in a global storage to reduce fragmentation from having to
97+
// mmap this at the start of every test.
98+
struct TestAllocatorStorage {
99+
static constexpr size_t kMaxSize = std::max({
100+
sizeof(scudo::Allocator<scudo::DefaultConfig>),
101+
#if SCUDO_CAN_USE_PRIMARY64
102+
sizeof(scudo::Allocator<scudo::FuchsiaConfig>),
103+
#endif
104+
sizeof(scudo::Allocator<scudo::AndroidSvelteConfig>),
105+
sizeof(scudo::Allocator<scudo::AndroidConfig>)
106+
});
107+
108+
// To alleviate some problem, let's skip the thread safety analysis here.
109+
static void *get(size_t size) NO_THREAD_SAFETY_ANALYSIS {
110+
assert(size <= kMaxSize &&
111+
"Allocation size doesn't fit in the allocator storage");
112+
M.lock();
113+
return AllocatorStorage;
114+
}
115+
116+
static void release(void *ptr) NO_THREAD_SAFETY_ANALYSIS {
117+
M.assertHeld();
118+
M.unlock();
119+
ASSERT_EQ(ptr, AllocatorStorage);
120+
}
121+
122+
static scudo::HybridMutex M;
123+
static uint8_t AllocatorStorage[kMaxSize];
124+
};
125+
scudo::HybridMutex TestAllocatorStorage::M;
126+
alignas(kMaxAlign) uint8_t TestAllocatorStorage::AllocatorStorage[kMaxSize];
127+
#else
128+
struct TestAllocatorStorage {
129+
static void *get(size_t size) NO_THREAD_SAFETY_ANALYSIS {
82130
void *p = nullptr;
83-
EXPECT_EQ(0, posix_memalign(&p, alignof(TestAllocator), size));
131+
EXPECT_EQ(0, posix_memalign(&p, kMaxAlign, size));
84132
return p;
85133
}
86-
87-
void operator delete(void *ptr) { free(ptr); }
134+
static void release(void *ptr) NO_THREAD_SAFETY_ANALYSIS { free(ptr); }
88135
};
136+
#endif
137+
138+
template <typename Config>
139+
void *TestAllocator<Config>::operator new(size_t size) {
140+
return TestAllocatorStorage::get(size);
141+
}
142+
143+
template <typename Config>
144+
void TestAllocator<Config>::operator delete(void *ptr) {
145+
TestAllocatorStorage::release(ptr);
146+
}
89147

90148
template <class TypeParam> struct ScudoCombinedTest : public Test {
91149
ScudoCombinedTest() {

0 commit comments

Comments
 (0)