blob: 1ffefef6ac8b78b8edb54dded352612f0efdcd65 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "memory_type_table.h"
#include <limits>
#include <gtest/gtest.h>
namespace art {
TEST(memory_type_range, range) {
MemoryTypeRange<int> r(0x1000u, 0x2000u, 42);
EXPECT_EQ(r.Start(), 0x1000u);
EXPECT_EQ(r.Limit(), 0x2000u);
EXPECT_EQ(r.Type(), 42);
}
TEST(memory_type_range, range_contains) {
MemoryTypeRange<int> r(0x1000u, 0x2000u, 42);
EXPECT_FALSE(r.Contains(0x0fffu));
EXPECT_TRUE(r.Contains(0x1000u));
EXPECT_TRUE(r.Contains(0x1fffu));
EXPECT_FALSE(r.Contains(0x2000u));
}
TEST(memory_type_range, range_overlaps) {
static const int kMemoryType = 42;
MemoryTypeRange<int> a(0x1000u, 0x2000u, kMemoryType);
{
// |<----- a ----->|<----- b ----->|
MemoryTypeRange<int> b(a.Limit(), a.Limit() + a.Size(), kMemoryType);
EXPECT_FALSE(a.Overlaps(b));
EXPECT_FALSE(b.Overlaps(a));
}
{
// |<----- a ----->| |<----- c ----->|
MemoryTypeRange<int> c(a.Limit() + a.Size(), a.Limit() + 2 * a.Size(), kMemoryType);
EXPECT_FALSE(a.Overlaps(c));
EXPECT_FALSE(c.Overlaps(a));
}
{
// |<----- a ----->|
// |<- d ->|
MemoryTypeRange<int> d(a.Start() + a.Size() / 4, a.Limit() - a.Size() / 4, kMemoryType);
EXPECT_TRUE(a.Overlaps(d));
EXPECT_TRUE(d.Overlaps(a));
}
{
// |<----- a ----->|
// |<- e ->|
MemoryTypeRange<int> e(a.Start(), a.Start() + a.Size() / 2, kMemoryType);
EXPECT_TRUE(a.Overlaps(e));
EXPECT_TRUE(e.Overlaps(a));
}
{
// |<----- a ----->|
// |<- f ->|
MemoryTypeRange<int> f(a.Start() + a.Size() / 2, a.Limit(), kMemoryType);
EXPECT_TRUE(a.Overlaps(f));
EXPECT_TRUE(f.Overlaps(a));
}
{
// |<----- a ----->|
// |<----- g ----->|
MemoryTypeRange<int> g(a.Start() + a.Size() / 2, a.Limit() + a.Size() / 2, kMemoryType);
EXPECT_TRUE(a.Overlaps(g));
EXPECT_TRUE(g.Overlaps(a));
}
}
TEST(memory_type_range, range_adjoins) {
static const int kMemoryType = 42;
MemoryTypeRange<int> a(0x1000u, 0x2000u, kMemoryType);
{
// |<--- a --->|<--- b --->|
MemoryTypeRange<int> b(a.Limit(), a.Limit() + a.Size(), kMemoryType);
EXPECT_TRUE(a.Adjoins(b));
EXPECT_TRUE(b.Adjoins(a));
}
{
// |<--- a --->| |<--- c --->|
MemoryTypeRange<int> c(a.Limit() + a.Size(), a.Limit() + 2 * a.Size(), kMemoryType);
EXPECT_FALSE(a.Adjoins(c));
EXPECT_FALSE(c.Adjoins(a));
}
{
// |<--- a --->|
// |<--- d --->|
MemoryTypeRange<int> d(a.Start() + a.Size() / 2, a.Limit() + a.Size() / 2, kMemoryType);
EXPECT_FALSE(a.Adjoins(d));
EXPECT_FALSE(d.Adjoins(a));
}
}
TEST(memory_type_range, combinable_with) {
// Adjoining ranges of same type.
EXPECT_TRUE(MemoryTypeRange<int>(0x1000, 0x2000, 0)
.CombinableWith(MemoryTypeRange<int>(0x800, 0x1000, 0)));
EXPECT_TRUE(MemoryTypeRange<int>(0x800, 0x1000, 0)
.CombinableWith(MemoryTypeRange<int>(0x1000, 0x2000, 0)));
// Adjoining ranges of different types.
EXPECT_FALSE(MemoryTypeRange<int>(0x1000, 0x2000, 0)
.CombinableWith(MemoryTypeRange<int>(0x800, 0x1000, 1)));
EXPECT_FALSE(MemoryTypeRange<int>(0x800, 0x1000, 1)
.CombinableWith(MemoryTypeRange<int>(0x1000, 0x2000, 0)));
// Disjoint ranges.
EXPECT_FALSE(MemoryTypeRange<int>(0x0800, 0x1000, 0)
.CombinableWith(MemoryTypeRange<int>(0x1f00, 0x2000, 0)));
EXPECT_FALSE(MemoryTypeRange<int>(0x1f00, 0x2000, 0)
.CombinableWith(MemoryTypeRange<int>(0x800, 0x1000, 0)));
// Overlapping ranges.
EXPECT_FALSE(MemoryTypeRange<int>(0x0800, 0x2000, 0)
.CombinableWith(MemoryTypeRange<int>(0x1f00, 0x2000, 0)));
}
TEST(memory_type_range, is_valid) {
EXPECT_TRUE(MemoryTypeRange<int>(std::numeric_limits<uintptr_t>::min(),
std::numeric_limits<uintptr_t>::max(),
0).IsValid());
EXPECT_TRUE(MemoryTypeRange<int>(1u, 2u, 0).IsValid());
EXPECT_TRUE(MemoryTypeRange<int>(0u, 0u, 0).IsValid());
EXPECT_FALSE(MemoryTypeRange<int>(2u, 1u, 0).IsValid());
EXPECT_FALSE(MemoryTypeRange<int>(std::numeric_limits<uintptr_t>::max(),
std::numeric_limits<uintptr_t>::min(),
0).IsValid());
}
TEST(memory_type_range, range_equality) {
static const int kMemoryType = 42;
MemoryTypeRange<int> a(0x1000u, 0x2000u, kMemoryType);
MemoryTypeRange<int> b(a.Start(), a.Limit(), a.Type());
EXPECT_TRUE(a == b);
EXPECT_FALSE(a != b);
MemoryTypeRange<int> c(a.Start() + 1, a.Limit(), a.Type());
EXPECT_FALSE(a == c);
EXPECT_TRUE(a != c);
MemoryTypeRange<int> d(a.Start(), a.Limit() + 1, a.Type());
EXPECT_FALSE(a == d);
EXPECT_TRUE(a != d);
MemoryTypeRange<int> e(a.Start(), a.Limit(), a.Type() + 1);
EXPECT_FALSE(a == e);
EXPECT_TRUE(a != e);
}
TEST(memory_type_table_builder, add_lookup) {
MemoryTypeTable<int>::Builder builder;
MemoryTypeRange<int> range(0x1000u, 0x2000u, 0);
EXPECT_EQ(builder.Size(), 0u);
EXPECT_EQ(builder.Add(range), true);
EXPECT_EQ(builder.Lookup(range.Start() - 1u), nullptr);
EXPECT_EQ(builder.Size(), 1u);
const MemoryTypeRange<int>* first = builder.Lookup(range.Start());
ASSERT_TRUE(first != nullptr);
EXPECT_EQ(range, *first);
const MemoryTypeRange<int>* last = builder.Lookup(range.Limit() - 1u);
ASSERT_TRUE(last != nullptr);
EXPECT_EQ(range, *last);
EXPECT_EQ(builder.Lookup(range.Limit()), nullptr);
}
TEST(memory_type_table_builder, add_lookup_multi) {
MemoryTypeTable<char>::Builder builder;
MemoryTypeRange<char> ranges[3] = {
MemoryTypeRange<char>(0x1, 0x2, 'a'),
MemoryTypeRange<char>(0x2, 0x4, 'b'),
MemoryTypeRange<char>(0x4, 0x8, 'c'),
};
for (const auto& range : ranges) {
builder.Add(range);
}
ASSERT_EQ(builder.Size(), sizeof(ranges) / sizeof(ranges[0]));
ASSERT_TRUE(builder.Lookup(0x0) == nullptr);
ASSERT_TRUE(builder.Lookup(0x8) == nullptr);
for (const auto& range : ranges) {
auto first = builder.Lookup(range.Start());
ASSERT_TRUE(first != nullptr);
EXPECT_EQ(*first, range);
auto last = builder.Lookup(range.Limit() - 1);
ASSERT_TRUE(last != nullptr);
EXPECT_EQ(*last, range);
}
}
TEST(memory_type_table_builder, add_overlapping) {
MemoryTypeTable<int>::Builder builder;
MemoryTypeRange<int> range(0x1000u, 0x2000u, 0);
builder.Add(range);
EXPECT_EQ(builder.Size(), 1u);
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x0800u, 0x2800u, 0)));
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x0800u, 0x1800u, 0)));
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x1800u, 0x2800u, 0)));
EXPECT_EQ(builder.Size(), 1u);
}
TEST(memory_type_table_builder, add_zero_size) {
MemoryTypeTable<int>::Builder builder;
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x1000u, 0)));
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x1001u, 0)));
// Checking adjoining zero length don't get included
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x1000u, 0)));
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x1001u, 0x1001u, 0)));
// Check around extremes
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x0u, 0x0u, 0)));
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(~0u, ~0u, 0)));
}
TEST(memory_type_table_builder, add_invalid_range) {
MemoryTypeTable<int>::Builder builder;
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x1000u, 0)));
EXPECT_FALSE(builder.Add(MemoryTypeRange<int>(0x2000u, 0x1000u, 0)));
}
TEST(memory_type_table_builder, add_adjoining) {
MemoryTypeTable<int>::Builder builder;
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x2000u, 0)));
EXPECT_EQ(builder.Size(), 1u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x0800u, 0x1000u, 0)));
EXPECT_EQ(builder.Size(), 1u);
ASSERT_NE(builder.Lookup(0x0900u), nullptr);
EXPECT_EQ(builder.Lookup(0x0900u)->Start(), 0x0800u);
EXPECT_EQ(builder.Lookup(0x0900u)->Limit(), 0x2000u);
EXPECT_EQ(builder.Lookup(0x0900u)->Type(), 0);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x2000u, 0x2100u, 0)));
EXPECT_EQ(builder.Size(), 1u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x3000u, 0x3100u, 0)));
EXPECT_EQ(builder.Size(), 2u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x2100u, 0x3000u, 0)));
ASSERT_NE(builder.Lookup(0x2000u), nullptr);
EXPECT_EQ(builder.Lookup(0x2000u)->Start(), 0x0800u);
EXPECT_EQ(builder.Lookup(0x2000u)->Limit(), 0x3100u);
EXPECT_EQ(builder.Lookup(0x2000u)->Type(), 0);
EXPECT_EQ(builder.Size(), 1u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x4000u, 0x4100u, 0)));
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x4f00u, 0x5000u, 0)));
EXPECT_EQ(builder.Size(), 3u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x4100u, 0x4f00u, 0)));
ASSERT_NE(builder.Lookup(0x4f00u), nullptr);
ASSERT_EQ(builder.Lookup(0x4f00u)->Start(), 0x4000u);
ASSERT_EQ(builder.Lookup(0x4f00u)->Limit(), 0x5000u);
ASSERT_EQ(builder.Lookup(0x4f00u)->Type(), 0);
EXPECT_EQ(builder.Size(), 2u);
ASSERT_NE(builder.Lookup(0x4f00u), nullptr);
}
TEST(memory_type_table_builder, add_adjoining_different_type) {
MemoryTypeTable<int>::Builder builder;
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x0000u, 0x1000u, 1)));
EXPECT_EQ(builder.Size(), 1u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x1000u, 0x2000u, 2)));
EXPECT_EQ(builder.Size(), 2u);
EXPECT_TRUE(builder.Add(MemoryTypeRange<int>(0x2000u, 0x3000u, 3)));
EXPECT_EQ(builder.Size(), 3u);
}
TEST(memory_type_table, create) {
MemoryTypeTable<int>::Builder builder;
builder.Add(MemoryTypeRange<int>(0x1000u, 0x2000u, 0));
builder.Add(MemoryTypeRange<int>(0x2000u, 0x3000u, 1));
builder.Add(MemoryTypeRange<int>(0x4000u, 0x5000u, 2));
MemoryTypeTable<int> table = builder.Build();
EXPECT_TRUE(table.Lookup(0x0000u) == nullptr);
EXPECT_TRUE(table.Lookup(0x0800u) == nullptr);
EXPECT_TRUE(table.Lookup(0x3000u) == nullptr);
EXPECT_TRUE(table.Lookup(0x3fffu) == nullptr);
EXPECT_TRUE(table.Lookup(0x5000u) == nullptr);
EXPECT_TRUE(table.Lookup(~0u) == nullptr);
ASSERT_TRUE(table.Lookup(0x1000u) != nullptr);
ASSERT_TRUE(table.Lookup(0x1fffu) != nullptr);
EXPECT_EQ(*table.Lookup(0x1000u), MemoryTypeRange<int>(0x1000u, 0x2000u, 0));
EXPECT_EQ(*table.Lookup(0x1fffu), MemoryTypeRange<int>(0x1000u, 0x2000u, 0));
ASSERT_TRUE(table.Lookup(0x2000u) != nullptr);
ASSERT_TRUE(table.Lookup(0x2fffu) != nullptr);
EXPECT_EQ(*table.Lookup(0x2000u), MemoryTypeRange<int>(0x2000u, 0x3000u, 1));
EXPECT_EQ(*table.Lookup(0x2fffu), MemoryTypeRange<int>(0x2000u, 0x3000u, 1));
ASSERT_TRUE(table.Lookup(0x4000u) != nullptr);
ASSERT_TRUE(table.Lookup(0x4fffu) != nullptr);
EXPECT_EQ(*table.Lookup(0x4000u), MemoryTypeRange<int>(0x4000u, 0x5000u, 2));
EXPECT_EQ(*table.Lookup(0x4fffu), MemoryTypeRange<int>(0x4000u, 0x5000u, 2));
}
TEST(memory_type_table, find_all) {
static constexpr size_t kRangeCount = 64;
static constexpr uintptr_t kRangeSize = 1024;
MemoryTypeTable<int>::Builder builder;
for (size_t i = 0; i < kRangeCount; i++) {
const uintptr_t start = i * kRangeSize;
builder.Add(MemoryTypeRange<int>(start, start + kRangeSize, static_cast<int>(i)));
}
for (size_t delta = 0; delta < kRangeSize; delta += kRangeSize / 2) {
for (size_t i = 0; i < kRangeCount; i++) {
const uintptr_t start = i * kRangeSize;
const MemoryTypeRange<int> expected(start, start + kRangeSize, static_cast<int>(i));
const uintptr_t address = i * kRangeSize + delta;
const MemoryTypeRange<int>* actual = builder.Lookup(address);
ASSERT_TRUE(actual != nullptr) << reinterpret_cast<void*>(address);
EXPECT_EQ(expected, *actual) << reinterpret_cast<void*>(address);
}
}
MemoryTypeTable<int> table = builder.Build();
for (size_t delta = 0; delta < kRangeSize; delta += kRangeSize / 2) {
for (size_t i = 0; i < kRangeCount; i++) {
const uintptr_t start = i * kRangeSize;
const MemoryTypeRange<int> expected(start, start + kRangeSize, static_cast<int>(i));
const uintptr_t address = i * kRangeSize + delta;
const MemoryTypeRange<int>* actual = table.Lookup(address);
ASSERT_TRUE(actual != nullptr) << reinterpret_cast<void*>(address);
EXPECT_EQ(expected, *actual) << reinterpret_cast<void*>(address);
}
}
}
} // namespace art