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(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 + #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 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(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(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(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