Skip to content

Conversation

@cferris1000
Copy link
Contributor

Move the test utility to get the resident pages into linux.cpp.

Add some specific tests for this function.

Add displaying of resident pages from in the secondary cache when calling getStats.

Move the test utility to get the resident pages into linux.cpp.

Add some specific tests for this function.

Add displaying of resident pages from in the secondary cache when
calling getStats.
@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Christopher Ferris (cferris1000)

Changes

Move the test utility to get the resident pages into linux.cpp.

Add some specific tests for this function.

Add displaying of resident pages from in the secondary cache when calling getStats.


Full diff: https://github.com/llvm/llvm-project/pull/170568.diff

6 Files Affected:

  • (modified) compiler-rt/lib/scudo/standalone/common.h (+5)
  • (modified) compiler-rt/lib/scudo/standalone/fuchsia.cpp (+2)
  • (modified) compiler-rt/lib/scudo/standalone/linux.cpp (+28)
  • (modified) compiler-rt/lib/scudo/standalone/secondary.h (+16-2)
  • (modified) compiler-rt/lib/scudo/standalone/tests/common_test.cpp (+51-27)
  • (modified) compiler-rt/lib/scudo/standalone/trusty.cpp (+2)
diff --git a/compiler-rt/lib/scudo/standalone/common.h b/compiler-rt/lib/scudo/standalone/common.h
index 8adcebd55698d..9097532f750d9 100644
--- a/compiler-rt/lib/scudo/standalone/common.h
+++ b/compiler-rt/lib/scudo/standalone/common.h
@@ -188,6 +188,11 @@ u32 getThreadID();
 constexpr uptr MaxRandomLength = 256U;
 bool getRandom(void *Buffer, uptr Length, bool Blocking = false);
 
+// Get the total number of resident pages for BaseAddress to BaseAddress + Size.
+// This function can run slowly, and is only expected to be called
+// from getStats functions where performance does not matter.
+u64 getResidentPages(uptr BaseAddress, uptr Size);
+
 // Platform memory mapping functions.
 
 #define MAP_ALLOWNOMEM (1U << 0)
diff --git a/compiler-rt/lib/scudo/standalone/fuchsia.cpp b/compiler-rt/lib/scudo/standalone/fuchsia.cpp
index 2144f1b63f894..f5a2e4d601361 100644
--- a/compiler-rt/lib/scudo/standalone/fuchsia.cpp
+++ b/compiler-rt/lib/scudo/standalone/fuchsia.cpp
@@ -231,6 +231,8 @@ void outputRaw(const char *Buffer) {
 
 void setAbortMessage(const char *Message) {}
 
+u64 getResidentPages(uptr BaseAddress, uptr Size) { return 0; }
+
 } // namespace scudo
 
 #endif // SCUDO_FUCHSIA
diff --git a/compiler-rt/lib/scudo/standalone/linux.cpp b/compiler-rt/lib/scudo/standalone/linux.cpp
index 57171edac1e9e..3ef857f76b702 100644
--- a/compiler-rt/lib/scudo/standalone/linux.cpp
+++ b/compiler-rt/lib/scudo/standalone/linux.cpp
@@ -246,6 +246,34 @@ void setAbortMessage(const char *Message) {
     android_set_abort_message(Message);
 }
 
+u64 getResidentPages(uptr BaseAddress, uptr Size) {
+  unsigned char PageData[256];
+
+  uptr PageSize = getPageSizeCached();
+  uptr PageSizeLog = getPageSizeLogCached();
+
+  // Make sure the address is page aligned.
+  uptr CurrentAddress = BaseAddress & ~(PageSize - 1);
+  uptr LastAddress = roundUp(BaseAddress + Size, PageSize);
+  u64 ResidentPages = 0;
+  while (CurrentAddress < LastAddress) {
+    uptr Length = LastAddress - CurrentAddress;
+    if ((Length >> PageSizeLog) > sizeof(PageData)) {
+      Length = sizeof(PageData) << PageSizeLog;
+    }
+    if (mincore(reinterpret_cast<void *>(CurrentAddress), Length, PageData) ==
+        -1)
+      break;
+    for (size_t I = 0; I < Length >> PageSizeLog; I++) {
+      if (PageData[I])
+        ResidentPages++;
+    }
+    CurrentAddress += Length;
+  }
+
+  return ResidentPages;
+}
+
 } // namespace scudo
 
 #endif // SCUDO_LINUX
diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h
index 2509db26e6c82..04e33c04baa34 100644
--- a/compiler-rt/lib/scudo/standalone/secondary.h
+++ b/compiler-rt/lib/scudo/standalone/secondary.h
@@ -9,6 +9,12 @@
 #ifndef SCUDO_SECONDARY_H_
 #define SCUDO_SECONDARY_H_
 
+#ifndef __STDC_FORMAT_MACROS
+// Ensure PRId64 macro is available
+#define __STDC_FORMAT_MACROS 1
+#endif
+#include <inttypes.h>
+
 #include "chunk.h"
 #include "common.h"
 #include "list.h"
@@ -221,9 +227,17 @@ class MapAllocatorCache {
 
     for (CachedBlock &Entry : LRUEntries) {
       Str->append("  StartBlockAddress: 0x%zx, EndBlockAddress: 0x%zx, "
-                  "BlockSize: %zu %s\n",
+                  "BlockSize: %zu%s",
                   Entry.CommitBase, Entry.CommitBase + Entry.CommitSize,
-                  Entry.CommitSize, Entry.Time == 0 ? "[R]" : "");
+                  Entry.CommitSize, Entry.Time == 0 ? " [R]" : "");
+#if SCUDO_LINUX
+      // getResidentPages only works on linux systems currently.
+      Str->append(", Resident Pages: %" PRId64 "/%zu\n",
+                  getResidentPages(Entry.CommitBase, Entry.CommitSize),
+                  Entry.CommitSize / getPageSizeCached());
+#else
+      Str->append("\n");
+#endif
     }
   }
 
diff --git a/compiler-rt/lib/scudo/standalone/tests/common_test.cpp b/compiler-rt/lib/scudo/standalone/tests/common_test.cpp
index 71f810e9d9724..9a7687356fced 100644
--- a/compiler-rt/lib/scudo/standalone/tests/common_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/common_test.cpp
@@ -21,50 +21,74 @@
 
 namespace scudo {
 
-static void getResidentPages(void *BaseAddress, size_t TotalPages,
-                             size_t *ResidentPages) {
-  std::vector<unsigned char> Pages(TotalPages, 0);
-  ASSERT_EQ(
-      0, mincore(BaseAddress, TotalPages * getPageSizeCached(), Pages.data()))
-      << strerror(errno);
-  *ResidentPages = 0;
-  for (unsigned char Value : Pages) {
-    if (Value & 1) {
-      ++*ResidentPages;
-    }
-  }
+TEST(ScudoCommonTest, VerifyGetResidentPages) {
+  if (!SCUDO_LINUX)
+    GTEST_SKIP() << "Only valid on linux systems.";
+
+  constexpr uptr NumPages = 512;
+  const uptr SizeBytes = NumPages * getPageSizeCached();
+
+  MemMapT MemMap;
+  ASSERT_TRUE(MemMap.map(/*Addr=*/0U, SizeBytes, "ResidentMemorySize"));
+  ASSERT_NE(MemMap.getBase(), 0U);
+
+  // Only android seem to properly detect when single pages are touched.
+#if SCUDO_ANDROID
+  // Verify nothing should be mapped in right after the map is created.
+  EXPECT_EQ(0U, getResidentPages(MemMap.getBase(), SizeBytes));
+
+  // Touch a page.
+  u8 *Data = reinterpret_cast<u8 *>(MemMap.getBase());
+  Data[0] = 1;
+  EXPECT_EQ(1U, getResidentPages(MemMap.getBase(), SizeBytes));
+
+  // Touch a non-consective page.
+  Data[getPageSizeCached() * 2] = 1;
+  EXPECT_EQ(2U, getResidentPages(MemMap.getBase(), SizeBytes));
+
+  // Touch a page far enough that the function has to make multiple calls
+  // to mincore.
+  Data[getPageSizeCached() * 300] = 1;
+  EXPECT_EQ(3U, getResidentPages(MemMap.getBase(), SizeBytes));
+
+  // Touch another page in the same range to make sure the second
+  // read is working.
+  Data[getPageSizeCached() * 400] = 1;
+  EXPECT_EQ(4U, getResidentPages(MemMap.getBase(), SizeBytes));
+#endif
+
+  // Now write the whole thing.
+  memset(reinterpret_cast<void *>(MemMap.getBase()), 1, SizeBytes);
+  EXPECT_EQ(NumPages, getResidentPages(MemMap.getBase(), SizeBytes));
+
+  MemMap.unmap();
 }
 
-// Fuchsia needs getResidentPages implementation.
-TEST(ScudoCommonTest, SKIP_ON_FUCHSIA(ResidentMemorySize)) {
-  // Make sure to have the size of the map on a page boundary.
-  const uptr PageSize = getPageSizeCached();
-  const size_t NumPages = 1000;
-  const uptr SizeBytes = NumPages * PageSize;
+TEST(ScudoCommonTest, VerifyReleasePagesToOS) {
+  if (!SCUDO_LINUX)
+    GTEST_SKIP() << "Only valid on linux systems.";
+
+  constexpr uptr NumPages = 1000;
+  const uptr SizeBytes = NumPages * getPageSizeCached();
 
   MemMapT MemMap;
   ASSERT_TRUE(MemMap.map(/*Addr=*/0U, SizeBytes, "ResidentMemorySize"));
   ASSERT_NE(MemMap.getBase(), 0U);
 
   void *P = reinterpret_cast<void *>(MemMap.getBase());
-  size_t ResidentPages;
-  getResidentPages(P, NumPages, &ResidentPages);
-  EXPECT_EQ(0U, ResidentPages);
+  EXPECT_EQ(0U, getResidentPages(MemMap.getBase(), SizeBytes));
 
   // Make the entire map resident.
   memset(P, 1, SizeBytes);
-  getResidentPages(P, NumPages, &ResidentPages);
-  EXPECT_EQ(NumPages, ResidentPages);
+  EXPECT_EQ(NumPages, getResidentPages(MemMap.getBase(), SizeBytes));
 
   // Should release the memory to the kernel immediately.
   MemMap.releasePagesToOS(MemMap.getBase(), SizeBytes);
-  getResidentPages(P, NumPages, &ResidentPages);
-  EXPECT_EQ(0U, ResidentPages);
+  EXPECT_EQ(0U, getResidentPages(MemMap.getBase(), SizeBytes));
 
   // Make the entire map resident again.
   memset(P, 1, SizeBytes);
-  getResidentPages(P, NumPages, &ResidentPages);
-  EXPECT_EQ(NumPages, ResidentPages);
+  EXPECT_EQ(NumPages, getResidentPages(MemMap.getBase(), SizeBytes));
 
   MemMap.unmap();
 }
diff --git a/compiler-rt/lib/scudo/standalone/trusty.cpp b/compiler-rt/lib/scudo/standalone/trusty.cpp
index 26b349c6e506e..d4b5b6bf1c3bc 100644
--- a/compiler-rt/lib/scudo/standalone/trusty.cpp
+++ b/compiler-rt/lib/scudo/standalone/trusty.cpp
@@ -113,6 +113,8 @@ void outputRaw(const char *Buffer) { printf("%s", Buffer); }
 
 void setAbortMessage(UNUSED const char *Message) {}
 
+u64 getResidentPages(UNUSED uptr BaseAddress, UNUSED uptr Size) { return 0; }
+
 } // namespace scudo
 
 #endif // SCUDO_TRUSTY

@pirama-arumuga-nainar
Copy link
Collaborator

@sadafebrahimi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants