blob: c037f49571b9008a52b51fa2edfa015197a01bbd [file] [log] [blame]
//===-- common_test.cpp -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "internal_defs.h"
#include "tests/scudo_unit_test.h"
#include "common.h"
#include "mem_map.h"
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <algorithm>
#include <vector>
namespace scudo {
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 seems 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();
}
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());
EXPECT_EQ(0U, getResidentPages(MemMap.getBase(), SizeBytes));
// Make the entire map resident.
memset(P, 1, SizeBytes);
EXPECT_EQ(NumPages, getResidentPages(MemMap.getBase(), SizeBytes));
// Should release the memory to the kernel immediately.
MemMap.releasePagesToOS(MemMap.getBase(), SizeBytes);
EXPECT_EQ(0U, getResidentPages(MemMap.getBase(), SizeBytes));
// Make the entire map resident again.
memset(P, 1, SizeBytes);
EXPECT_EQ(NumPages, getResidentPages(MemMap.getBase(), SizeBytes));
MemMap.unmap();
}
TEST(ScudoCommonTest, Zeros) {
const uptr Size = 1ull << 20;
MemMapT MemMap;
ASSERT_TRUE(MemMap.map(/*Addr=*/0U, Size, "Zeros"));
ASSERT_NE(MemMap.getBase(), 0U);
uptr *P = reinterpret_cast<uptr *>(MemMap.getBase());
const ptrdiff_t N = Size / sizeof(uptr);
EXPECT_EQ(std::count(P, P + N, 0), N);
memset(P, 1, Size);
EXPECT_EQ(std::count(P, P + N, 0), 0);
MemMap.releasePagesToOS(MemMap.getBase(), Size);
EXPECT_EQ(std::count(P, P + N, 0), N);
MemMap.unmap();
}
} // namespace scudo