@samitouri / QOSAMI-WSL / commits / 88f76dd7

Address memory reclaim follow-ups from #41096 (#41171)

* Address follow-ups from PR #41096 - Parse the /proc/stat cpu line with std::regex instead of a manual strtoull cursor loop, while retaining the O_CLOEXEC bounded read. - Use drop_caches=3 in DropCache mode to also drop reclaimable slab (dentries/inodes), matching the SReclaimable slab counted by GetReclaimableCacheBytes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e37f3d5-68e7-4973-8c27-bf4329c8fd9b * Restrict /proc/stat field separators to spaces/tabs ECMAScript \s matches newlines, so on a truncated aggregate cpu line the optional irq/softirq/steal groups could consume digits from the next line in the read buffer. Use [ \t] so the match cannot span lines. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e37f3d5-68e7-4973-8c27-bf4329c8fd9b --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e37f3d5-68e7-4973-8c27-bf4329c8fd9b

Ben Hillis committed Jul 27, 2026 at 12:26 UTC 88f76dd7dc2412af452834d753a0c6ce4af2a3ac
1 file changed +17 -28
src/linux/init/util.cpp
+17 -28
@@ -3633,46 +3633,30 @@ Return Value:
3633 buffer[result] = '\0';
3634
3635 //
3636 - // Format: "cpu user nice system idle iowait irq softirq steal ...". Fields after steal are ignored.
3636 + // Format: "cpu user nice system idle iowait irq softirq steal ...". The user, nice, system, idle,
3637 + // and iowait fields are required; irq, softirq, and steal are optional and any fields after steal
3638 + // are ignored.
3639 //
3640
3639 - if (strncmp(buffer, "cpu ", 4) != 0)
3641 + static const std::regex cpuLine{R"(^cpu[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)(?:[ \t]+(\d+))?(?:[ \t]+(\d+))?(?:[ \t]+(\d+))?)"};
3642 + std::cmatch match;
3643 + if (!std::regex_search(buffer, match, cpuLine))
3644 {
3641 - LOG_ERROR("/proc/stat first line missing cpu label");
3645 + LOG_ERROR("failed to parse /proc/stat cpu line");
3646 return false;
3647 }
3648
3649 unsigned long long fields[8] = {};
3646 - const char* cursor = buffer + 3;
3647 - int parsed = 0;
3648 - for (; parsed < static_cast<int>(COUNT_OF(fields)); parsed += 1)
3650 + for (size_t index = 0; index < COUNT_OF(fields); index += 1)
3651 {
3650 - char* end = nullptr;
3651 - const unsigned long long value = strtoull(cursor, &end, 10);
3652 - if (end == cursor)
3652 + if (match[index + 1].matched)
3653 {
3654 - break;
3654 + fields[index] = strtoull(match[index + 1].first, nullptr, 10);
3655 }
3656 -
3657 - fields[parsed] = value;
3658 - cursor = end;
3659 - }
3660 -
3661 - if (parsed < 5)
3662 - {
3663 - LOG_ERROR("failed to parse /proc/stat cpu line (parsed {})", parsed);
3664 - return false;
3656 }
3657
3658 Idle = fields[3] + fields[4];
3668 - Busy = 0;
3669 - for (int index = 0; index < parsed; index += 1)
3670 - {
3671 - if (index != 3 && index != 4)
3672 - {
3673 - Busy += fields[index];
3674 - }
3675 - }
3659 + Busy = fields[0] + fields[1] + fields[2] + fields[5] + fields[6] + fields[7];
3660
3661 return true;
3662 }
@@ -3957,7 +3941,12 @@ try
3941 }
3942 else if (!droppedThisIdlePeriod)
3943 {
3960 - if (WriteToFile("/proc/sys/vm/drop_caches", "1\n") == 0)
3944 + //
3945 + // drop_caches=3 frees the page cache along with reclaimable slab (dentries and
3946 + // inodes), matching the SReclaimable slab counted by GetReclaimableCacheBytes.
3947 + //
3948 +
3949 + if (WriteToFile("/proc/sys/vm/drop_caches", "3\n") == 0)
3950 {
3951 droppedThisIdlePeriod = true;
3952 reclaimed = true;