Skip to content

Commit d9a9960

Browse files
authored
[scudo] Add fragmentation info for each memory group (#107475)
This information helps with tuning the heuristic of selecting memory groups to release the unused pages.
1 parent 6f8d278 commit d9a9960

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@ template <typename Config> class SizeClassAllocator32 {
332332
}
333333
}
334334

335+
void getMemoryGroupFragmentationInfo(ScopedString *Str) {
336+
// Each region is also a memory group because region size is the same as
337+
// group size.
338+
getFragmentationInfo(Str);
339+
}
340+
335341
bool setOption(Option O, sptr Value) {
336342
if (O == Option::ReleaseInterval) {
337343
const s32 Interval = Max(

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,18 @@ template <typename Config> class SizeClassAllocator64 {
395395
}
396396
}
397397

398+
void getMemoryGroupFragmentationInfo(ScopedString *Str) {
399+
Str->append(
400+
"Fragmentation Stats: SizeClassAllocator64: page size = %zu bytes\n",
401+
getPageSizeCached());
402+
403+
for (uptr I = 1; I < NumClasses; I++) {
404+
RegionInfo *Region = getRegionInfo(I);
405+
ScopedLock L(Region->MMLock);
406+
getMemoryGroupFragmentationInfoInRegion(Region, I, Str);
407+
}
408+
}
409+
398410
bool setOption(Option O, sptr Value) {
399411
if (O == Option::ReleaseInterval) {
400412
const s32 Interval = Max(
@@ -1193,6 +1205,50 @@ template <typename Config> class SizeClassAllocator64 {
11931205
AllocatedPagesCount, InUseBytes >> 10, Integral, Fractional);
11941206
}
11951207

1208+
void getMemoryGroupFragmentationInfoInRegion(RegionInfo *Region, uptr ClassId,
1209+
ScopedString *Str)
1210+
REQUIRES(Region->MMLock) {
1211+
const uptr BlockSize = getSizeByClassId(ClassId);
1212+
const uptr AllocatedUserEnd =
1213+
Region->MemMapInfo.AllocatedUser + Region->RegionBeg;
1214+
1215+
SinglyLinkedList<BatchGroupT> GroupsToRelease;
1216+
{
1217+
ScopedLock L(Region->FLLock);
1218+
GroupsToRelease = Region->FreeListInfo.BlockList;
1219+
Region->FreeListInfo.BlockList.clear();
1220+
}
1221+
1222+
constexpr uptr GroupSize = (1UL << GroupSizeLog);
1223+
constexpr uptr MaxNumGroups = RegionSize / GroupSize;
1224+
1225+
MemoryGroupFragmentationRecorder<GroupSize, MaxNumGroups> Recorder;
1226+
if (!GroupsToRelease.empty()) {
1227+
PageReleaseContext Context =
1228+
markFreeBlocks(Region, BlockSize, AllocatedUserEnd,
1229+
getCompactPtrBaseByClassId(ClassId), GroupsToRelease);
1230+
auto SkipRegion = [](UNUSED uptr RegionIndex) { return false; };
1231+
releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
1232+
1233+
mergeGroupsToReleaseBack(Region, GroupsToRelease);
1234+
}
1235+
1236+
Str->append("MemoryGroupFragmentationInfo in Region %zu (%zu)\n", ClassId,
1237+
BlockSize);
1238+
1239+
const uptr MaxNumGroupsInUse =
1240+
roundUp(Region->MemMapInfo.AllocatedUser, GroupSize) / GroupSize;
1241+
for (uptr I = 0; I < MaxNumGroupsInUse; ++I) {
1242+
uptr Integral;
1243+
uptr Fractional;
1244+
computePercentage(Recorder.NumPagesInOneGroup -
1245+
Recorder.getNumFreePages(I),
1246+
Recorder.NumPagesInOneGroup, &Integral, &Fractional);
1247+
Str->append("MemoryGroup #%zu (0x%zx): util: %3zu.%02zu%%\n", I,
1248+
Region->RegionBeg + I * GroupSize, Integral, Fractional);
1249+
}
1250+
}
1251+
11961252
NOINLINE uptr releaseToOSMaybe(RegionInfo *Region, uptr ClassId,
11971253
ReleaseToOS ReleaseType = ReleaseToOS::Normal)
11981254
REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,22 @@ class FragmentationRecorder {
9595
uptr ReleasedPagesCount = 0;
9696
};
9797

98+
template <uptr GroupSize, uptr NumGroups>
99+
class MemoryGroupFragmentationRecorder {
100+
public:
101+
const uptr NumPagesInOneGroup = GroupSize / getPageSizeCached();
102+
103+
void releasePageRangeToOS(uptr From, uptr To) {
104+
for (uptr I = From / getPageSizeCached(); I < To / getPageSizeCached(); ++I)
105+
++FreePagesCount[I / NumPagesInOneGroup];
106+
}
107+
108+
uptr getNumFreePages(uptr GroupId) { return FreePagesCount[GroupId]; }
109+
110+
private:
111+
uptr FreePagesCount[NumGroups] = {};
112+
};
113+
98114
// A buffer pool which holds a fixed number of static buffers of `uptr` elements
99115
// for fast buffer allocation. If the request size is greater than
100116
// `StaticBufferNumElements` or if all the static buffers are in use, it'll

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ SCUDO_TYPED_TEST(ScudoPrimaryTest, PrimaryThreaded) {
386386
scudo::ScopedString Str;
387387
Allocator->getStats(&Str);
388388
Allocator->getFragmentationInfo(&Str);
389+
Allocator->getMemoryGroupFragmentationInfo(&Str);
389390
Str.output();
390391
}
391392

0 commit comments

Comments
 (0)