diff options
Diffstat (limited to 'xpcom/tests/gtest/TestArenaAllocator.cpp')
-rw-r--r-- | xpcom/tests/gtest/TestArenaAllocator.cpp | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestArenaAllocator.cpp b/xpcom/tests/gtest/TestArenaAllocator.cpp new file mode 100644 index 0000000000..fb11952927 --- /dev/null +++ b/xpcom/tests/gtest/TestArenaAllocator.cpp @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArenaAllocator.h" +#include "mozilla/ArenaAllocatorExtensions.h" +#include "nsIMemoryReporter.h" // MOZ_MALLOC_SIZE_OF + +#include "gtest/gtest.h" + +using mozilla::ArenaAllocator; + +TEST(ArenaAllocator, Constructor) +{ ArenaAllocator<4096, 4> a; } + +TEST(ArenaAllocator, DefaultAllocate) +{ + // Test default 1-byte alignment. + ArenaAllocator<1024> a; + void* x = a.Allocate(101); + void* y = a.Allocate(101); + + // Given 1-byte aligment, we expect the allocations to follow + // each other exactly. + EXPECT_EQ(uintptr_t(x) + 101, uintptr_t(y)); +} + +TEST(ArenaAllocator, AllocateAlignment) +{ + // Test non-default 8-byte alignment. + static const size_t kAlignment = 8; + ArenaAllocator<1024, kAlignment> a; + + // Make sure aligment is correct for 1-8. + for (size_t i = 1; i <= kAlignment; i++) { + // All of these should be 8 bytes + void* x = a.Allocate(i); + void* y = a.Allocate(i); + EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y)); + } + + // Test with slightly larger than specified alignment. + void* x = a.Allocate(kAlignment + 1); + void* y = a.Allocate(kAlignment + 1); + + // Given 8-byte aligment, and a non-8-byte aligned request we expect the + // allocations to be padded. + EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y)); + + // We expect 7 bytes of padding to have been added. + EXPECT_EQ(uintptr_t(x) + kAlignment * 2, uintptr_t(y)); +} + +#if 0 +TEST(ArenaAllocator, AllocateZeroBytes) +{ + // This would have to be a death test. Since we chose to provide an + // infallible allocator we can't just return nullptr in the 0 case as + // there's no way to differentiate that from the OOM case. + ArenaAllocator<1024> a; + void* x = a.Allocate(0); + EXPECT_FALSE(x); +} + +TEST(ArenaAllocator, BadAlignment) +{ + // This test causes build failures by triggering the static assert enforcing + // a power-of-two alignment. + ArenaAllocator<256, 3> a; + ArenaAllocator<256, 7> b; + ArenaAllocator<256, 17> c; +} +#endif + +TEST(ArenaAllocator, AllocateMultipleSizes) +{ + // Test non-default 4-byte alignment. + ArenaAllocator<4096, 4> a; + + for (int i = 1; i < 50; i++) { + void* x = a.Allocate(i); + // All the allocations should be aligned properly. + EXPECT_EQ(uintptr_t(x) % 4, uintptr_t(0)); + } + + // Test a large 64-byte alignment + ArenaAllocator<8192, 64> b; + for (int i = 1; i < 100; i++) { + void* x = b.Allocate(i); + // All the allocations should be aligned properly. + EXPECT_EQ(uintptr_t(x) % 64, uintptr_t(0)); + } +} + +TEST(ArenaAllocator, AllocateInDifferentChunks) +{ + // Test default 1-byte alignment. + ArenaAllocator<4096> a; + void* x = a.Allocate(4000); + void* y = a.Allocate(4000); + EXPECT_NE(uintptr_t(x) + 4000, uintptr_t(y)); +} + +TEST(ArenaAllocator, AllocateLargerThanArenaSize) +{ + // Test default 1-byte alignment. + ArenaAllocator<256> a; + void* x = a.Allocate(4000); + void* y = a.Allocate(4000); + EXPECT_TRUE(x); + EXPECT_TRUE(y); + + // Now try a normal allocation, it should behave as expected. + x = a.Allocate(8); + y = a.Allocate(8); + EXPECT_EQ(uintptr_t(x) + 8, uintptr_t(y)); +} + +TEST(ArenaAllocator, AllocationsPerChunk) +{ + // Test that expected number of allocations fit in one chunk. + // We use an alignment of 64-bytes to avoid worrying about differences in + // the header size on 32 and 64-bit platforms. + const size_t kArenaSize = 1024; + const size_t kAlignment = 64; + ArenaAllocator<kArenaSize, kAlignment> a; + + // With an alignment of 64 bytes we expect the header to take up the first + // alignment sized slot leaving bytes leaving the rest available for + // allocation. + const size_t kAllocationsPerChunk = (kArenaSize / kAlignment) - 1; + void* x = nullptr; + void* y = a.Allocate(kAlignment); + EXPECT_TRUE(y); + for (size_t i = 1; i < kAllocationsPerChunk; i++) { + x = y; + y = a.Allocate(kAlignment); + EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y)); + } + + // The next allocation should be in a different chunk. + x = y; + y = a.Allocate(kAlignment); + EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y)); +} + +TEST(ArenaAllocator, MemoryIsValid) +{ + // Make multiple allocations and actually access the memory. This is + // expected to trip up ASAN or valgrind if out of bounds memory is + // accessed. + static const size_t kArenaSize = 1024; + static const size_t kAlignment = 64; + static const char kMark = char(0xBC); + ArenaAllocator<kArenaSize, kAlignment> a; + + // Single allocation that should fill the arena. + size_t sz = kArenaSize - kAlignment; + char* x = (char*)a.Allocate(sz); + EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); + memset(x, kMark, sz); + for (size_t i = 0; i < sz; i++) { + EXPECT_EQ(x[i], kMark); + } + + // Allocation over arena size. + sz = kArenaSize * 2; + x = (char*)a.Allocate(sz); + EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); + memset(x, kMark, sz); + for (size_t i = 0; i < sz; i++) { + EXPECT_EQ(x[i], kMark); + } + + // Allocation half the arena size. + sz = kArenaSize / 2; + x = (char*)a.Allocate(sz); + EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); + memset(x, kMark, sz); + for (size_t i = 0; i < sz; i++) { + EXPECT_EQ(x[i], kMark); + } + + // Repeat, this should actually end up in a new chunk. + x = (char*)a.Allocate(sz); + EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); + memset(x, kMark, sz); + for (size_t i = 0; i < sz; i++) { + EXPECT_EQ(x[i], kMark); + } +} + +MOZ_DEFINE_MALLOC_SIZE_OF(TestSizeOf); + +TEST(ArenaAllocator, SizeOf) +{ + // This tests the sizeof functionality. We can't test for equality as we + // can't reliably guarantee what sizes the underlying allocator is going to + // choose, so we just test that things grow (or not) as expected. + static const size_t kArenaSize = 4096; + ArenaAllocator<kArenaSize> a; + + // Excluding *this we expect an empty arena allocator to have no overhead. + size_t sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_EQ(sz, size_t(0)); + + // Cause one chunk to be allocated. + (void)a.Allocate(kArenaSize / 2); + size_t prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); + + // Allocate within the current chunk. + (void)a.Allocate(kArenaSize / 4); + prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_EQ(sz, prev_sz); + + // Overflow to a new chunk. + (void)a.Allocate(kArenaSize / 2); + prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); + + // Allocate an oversized chunk with enough room for a header to fit in page + // size. We expect the underlying allocator to round up to page alignment. + (void)a.Allocate((kArenaSize * 2) - 64); + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); +} + +TEST(ArenaAllocator, Clear) +{ + // Tests that the Clear function works as expected. The best proxy for + // checking if a clear is successful is to measure the size. If it's empty we + // expect the size to be 0. + static const size_t kArenaSize = 128; + ArenaAllocator<kArenaSize> a; + + // Clearing an empty arena should work. + a.Clear(); + + size_t sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_EQ(sz, size_t(0)); + + // Allocating should work after clearing an empty arena. + void* x = a.Allocate(10); + EXPECT_TRUE(x); + + size_t prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); + + // Allocate enough for a few arena chunks to be necessary. + for (size_t i = 0; i < kArenaSize * 2; i++) { + x = a.Allocate(1); + EXPECT_TRUE(x); + } + + prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); + + // Clearing should reduce the size back to zero. + a.Clear(); + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_EQ(sz, size_t(0)); + + // Allocating should work after clearing an arena with allocations. + x = a.Allocate(10); + EXPECT_TRUE(x); + + prev_sz = sz; + sz = a.SizeOfExcludingThis(TestSizeOf); + EXPECT_GT(sz, prev_sz); +} + +TEST(ArenaAllocator, Extensions) +{ + ArenaAllocator<4096, 8> a; + + // Test with raw strings. + const char* const kTestCStr = "This is a test string."; + char* c_dup = mozilla::ArenaStrdup(kTestCStr, a); + EXPECT_STREQ(c_dup, kTestCStr); + + const char16_t* const kTestStr = u"This is a wide test string."; + char16_t* dup = mozilla::ArenaStrdup(kTestStr, a); + EXPECT_TRUE(nsString(dup).Equals(kTestStr)); + + // Make sure it works with literal strings. + constexpr auto wideStr = u"A wide string."_ns; + nsLiteralString::char_type* wide = mozilla::ArenaStrdup(wideStr, a); + EXPECT_TRUE(wideStr.Equals(wide)); + + constexpr auto cStr = "A c-string."_ns; + nsLiteralCString::char_type* cstr = mozilla::ArenaStrdup(cStr, a); + EXPECT_TRUE(cStr.Equals(cstr)); + + // Make sure it works with normal strings. + nsAutoString x(u"testing wide"); + nsAutoString::char_type* x_copy = mozilla::ArenaStrdup(x, a); + EXPECT_TRUE(x.Equals(x_copy)); + + nsAutoCString y("testing c-string"); + nsAutoCString::char_type* y_copy = mozilla::ArenaStrdup(y, a); + EXPECT_TRUE(y.Equals(y_copy)); +} |