blob: b0300de999f6c41f90f68e29f16e8b4eb78ac632 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/files/scoped_file.h"
#include "base/memory/madv_free_discardable_memory_allocator_posix.h"
#include "base/memory/madv_free_discardable_memory_posix.h"
#include "base/memory/page_size.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#define SUCCEED_IF_MADV_FREE_UNSUPPORTED() \
do { \
if (GetMadvFreeSupport() != base::MadvFreeSupport::kSupported) { \
SUCCEED() \
<< "MADV_FREE is not supported (Linux 4.5+ required), vacuously " \
"passing test"; \
return; \
} \
} while (0)
namespace base {
std::atomic<size_t> allocator_byte_count;
class MadvFreeDiscardableMemoryPosixTester
: public MadvFreeDiscardableMemoryPosix {
public:
MadvFreeDiscardableMemoryPosixTester(size_t size_in_bytes)
: MadvFreeDiscardableMemoryPosix(size_in_bytes, &allocator_byte_count) {}
using MadvFreeDiscardableMemoryPosix::DiscardPage;
using MadvFreeDiscardableMemoryPosix::GetPageCount;
using MadvFreeDiscardableMemoryPosix::IsLockedForTesting;
using MadvFreeDiscardableMemoryPosix::IsValid;
using MadvFreeDiscardableMemoryPosix::SetKeepMemoryForTesting;
};
class MadvFreeDiscardableMemoryTest : public ::testing::Test {
protected:
MadvFreeDiscardableMemoryTest() {}
~MadvFreeDiscardableMemoryTest() override {}
const size_t kPageSize = base::GetPageSize();
std::unique_ptr<MadvFreeDiscardableMemoryPosixTester>
AllocateLockedDiscardableMemoryPagesForTest(size_t size_in_pages) {
return std::make_unique<MadvFreeDiscardableMemoryPosixTester>(
size_in_pages * kPageSize);
}
};
using MadvFreeDiscardableMemoryDeathTest = MadvFreeDiscardableMemoryTest;
constexpr char kTestPattern[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
TEST_F(MadvFreeDiscardableMemoryTest, AllocateAndUse) {
SUCCEED_IF_MADV_FREE_UNSUPPORTED();
std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
AllocateLockedDiscardableMemoryPagesForTest(1);
mem->SetKeepMemoryForTesting(true);
ASSERT_TRUE(mem->IsValid());
ASSERT_TRUE(mem->IsLockedForTesting());
char buffer[sizeof(kTestPattern)];
// Write test pattern to block
uint8_t* data = mem->data_as<uint8_t>();
memcpy(data, kTestPattern, sizeof(kTestPattern));
// Read test pattern from block
data = mem->data_as<uint8_t>();
memcpy(buffer, data, sizeof(kTestPattern));
EXPECT_EQ(memcmp(kTestPattern, buffer, sizeof(kTestPattern)), 0);
// Memory contents should not change after successful unlock and lock.
mem->Unlock();
ASSERT_TRUE(mem->Lock());
EXPECT_EQ(memcmp(kTestPattern, buffer, sizeof(kTestPattern)), 0);
}
TEST_F(MadvFreeDiscardableMemoryTest, LockAndUnlock) {
SUCCEED_IF_MADV_FREE_UNSUPPORTED();
const size_t kPageCount = 10;
std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
AllocateLockedDiscardableMemoryPagesForTest(kPageCount);
ASSERT_TRUE(mem->IsValid());
ASSERT_TRUE(mem->IsLockedForTesting());
memset(mem->data(), 0xE7, kPageSize * kPageCount);
mem->Unlock();
ASSERT_FALSE(mem->IsLockedForTesting());
bool result = mem->Lock();
// If Lock() succeeded, the memory region should be valid. If Lock() failed,
// the memory region should be invalid.
ASSERT_EQ(result, mem->IsValid());
}
TEST_F(MadvFreeDiscardableMemoryTest, LockShouldFailAfterDiscard) {
SUCCEED_IF_MADV_FREE_UNSUPPORTED();
constexpr size_t kPageCount = 10;
std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
AllocateLockedDiscardableMemoryPagesForTest(kPageCount);
uint8_t* data = mem->data_as<uint8_t>();
ASSERT_TRUE(mem->IsValid());
ASSERT_TRUE(mem->IsLockedForTesting());
// Modify block data such that at least one page is non-zero.
memset(data, 0xff, kPageSize * kPageCount);
mem->Unlock();
ASSERT_FALSE(mem->IsLockedForTesting());
// Forcefully discard at least one non-zero page.
mem->DiscardPage(5);
// Locking when a page has been discarded should fail.
ASSERT_FALSE(mem->Lock());
// Locking after memory is deallocated should fail.
ASSERT_FALSE(mem->Lock());
// Check that memory has been deallocated.
ASSERT_FALSE(mem->IsValid());
}
} // namespace base