blob: 33c1a45d0d4fd3a658da225ba7c9a399a55ad9a7 [file] [log] [blame]
// Copyright (C) 2018 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 "aemu/base/SubAllocator.h"
#include "aemu/base/ArraySize.h"
#include "aemu/base/files/MemStream.h"
#include <gtest/gtest.h>
#include <random>
#include <string>
namespace android {
namespace base {
// Test: can allocate/free memory of various sizes,
// and if the allocation reasonably cannot be satisfied,
// the allocation fails.
TEST(SubAllocator, Basic) {
const size_t pageSizesToTest[] = {
1, 2, 4, 8, 16, 32, 64, 128,
256, 512, 1024, 2048, 4096,
8192, 16384, 32768, 65536,
};
const size_t sizesToTest[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 32, 33, 64, 65,
127, 129, 1023, 1024,
2047, 2048, 2049,
4096, 16384, 65535
};
const size_t allocCounts[] = {
1, 2, 3, 4,
};
const size_t bufferSize = 65536;
std::vector<uint8_t> buffer(bufferSize);
for (size_t pageSize : pageSizesToTest) {
SubAllocator subAlloc(
buffer.data(),
(uint64_t)bufferSize,
pageSize);
EXPECT_TRUE(subAlloc.empty());
for (size_t allocCount : allocCounts) {
std::vector<void*> ptrs;
for (size_t allocSize : sizesToTest) {
size_t trySize = allocSize / allocCount;
size_t atomSize =
pageSize *
((trySize + pageSize - 1) / pageSize);
size_t total = 0;
for (size_t i = 0; i < allocCount; ++i) {
void* ptr = subAlloc.alloc(trySize);
if (ptr) {
total += atomSize;
} else {
EXPECT_TRUE(
(trySize == 0) ||
(total + atomSize > bufferSize)) <<
"pageSize " << pageSize <<
"allocCount " << allocCount <<
"trySize " << trySize <<
"try# " << i <<
"allocedSoFar " << total;
}
ptrs.push_back(ptr);
}
for (auto ptr : ptrs) {
subAlloc.free(ptr);
}
EXPECT_TRUE(subAlloc.empty());
}
}
}
}
// Test: freeAll resets the state and allows more allocation
// without individual freeing.
TEST(SubAllocator, FreeAll) {
const size_t pageSize = 64;
const size_t bufferSize = 65536;
std::vector<uint8_t> buffer(bufferSize);
SubAllocator subAlloc(
buffer.data(),
(uint64_t)bufferSize,
(uint64_t)pageSize);
const size_t fillCount = bufferSize / pageSize;
const size_t numTrials = 10;
for (size_t i = 0; i < numTrials; ++i) {
for (size_t j = 0; j < fillCount; ++j) {
void* ptr = subAlloc.alloc(pageSize);
EXPECT_NE(nullptr, ptr);
}
subAlloc.freeAll();
EXPECT_TRUE(subAlloc.empty());
}
}
// Test: Random testing
TEST(SubAllocator, Random) {
// Given the buffer size, any combination of
// page size, alloc size, and count should work
// over a single run.
const size_t pageSizesToTest[] = {
1, 2, 4, 8, 16, 32, 64, 128,
256, 512, 1024, 2048, 4096,
};
const size_t sizesToTest[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 32, 33, 64, 65,
127, 129, 1023, 1024,
2047, 2048, 2049,
4096,
};
const size_t allocCounts[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
};
const size_t numSizes = arraySize(sizesToTest);
const size_t numCounts = arraySize(allocCounts);
const size_t bufferSize = 65536;
std::vector<uint8_t> buffer(bufferSize);
const size_t numTrials = 1000;
std::default_random_engine generator;
generator.seed(0);
std::uniform_int_distribution<int> sizeDistribution(0, numSizes - 1);
std::uniform_int_distribution<int> countDistribution(0, numCounts - 1);
for (auto pageSize : pageSizesToTest) {
SubAllocator subAlloc(
buffer.data(),
(uint64_t)bufferSize,
pageSize);
for (size_t i = 0; i < numTrials; ++i) {
size_t count =
allocCounts[countDistribution(generator)];
std::vector<void*> ptrs;
size_t total = 0;
for (size_t j = 0; j < count; ++j) {
size_t allocSize =
sizesToTest[sizeDistribution(generator)];
void* ptr = subAlloc.alloc(allocSize);
if (ptr) {
total += allocSize;
}
ptrs.push_back(ptr);
EXPECT_NE(nullptr, ptr) <<
"pageSize " << pageSize <<
"runCount " << count <<
"alloc# " << j <<
"size " << allocSize <<
"total " << total;
}
for (auto ptr : ptrs) {
subAlloc.free(ptr);
}
EXPECT_TRUE(subAlloc.empty());
}
}
}
// Test save/load of suballocator snapshot
TEST(SubAllocator, Snapshot) {
MemStream snapshotStream;
std::vector<uint8_t*> storage(4096, 0);
SubAllocator subAlloc(
storage.data(), (uint64_t)storage.size(), 8);
const size_t numBufs = 6;
// Alloc/free a few things.
uint8_t* bufs[numBufs] = {
(uint8_t*)subAlloc.alloc(16),
(uint8_t*)subAlloc.alloc(8),
(uint8_t*)subAlloc.alloc(32),
(uint8_t*)subAlloc.alloc(64),
(uint8_t*)subAlloc.alloc(8),
(uint8_t*)subAlloc.alloc(128),
};
subAlloc.free(bufs[3]);
bufs[3] = (uint8_t*)subAlloc.alloc(24);
subAlloc.free(bufs[4]);
bufs[4] = (uint8_t*)subAlloc.alloc(64);
subAlloc.free(bufs[0]);
bufs[0] = (uint8_t*)subAlloc.alloc(8);
for (uint32_t i = 0; i < numBufs; ++i) {
EXPECT_NE(nullptr, bufs[i]);
}
// Record offsets
uint64_t offs[numBufs];
for (uint32_t i = 0; i < numBufs; ++i) {
offs[i] = subAlloc.getOffset(bufs[i]);
}
// Write special values
for (uint32_t i = 0; i < numBufs; ++i) {
*(bufs[i]) = (uint8_t)i;
}
// Save/load the snapshot
subAlloc.save(&snapshotStream);
subAlloc.load(&snapshotStream);
EXPECT_FALSE(subAlloc.empty());
// Set the post load buffer to our original one
EXPECT_TRUE(subAlloc.postLoad(storage.data()));
// Do some allocations. None should intersect with any
// previously allocated buffer.
for (uint32_t i = 0; i < 10; ++i) {
uint8_t* buf = (uint8_t*)subAlloc.alloc(8);
if (!buf) continue;
for (uint32_t j = 0; j < numBufs; ++j) {
EXPECT_NE(bufs[j], buf);
}
}
// Expect all data to remain unchanged
for (uint32_t i = 0; i < numBufs; ++i) {
EXPECT_EQ((uint8_t)i, *(bufs[i]));
}
// Expect all offsets to be the same
for (uint32_t i = 0; i < numBufs; ++i) {
EXPECT_EQ(offs[i], subAlloc.getOffset(bufs[i]));
}
// Freeing everything should still work
for (uint32_t i = 0; i < numBufs; ++i) {
EXPECT_TRUE(subAlloc.free(bufs[i]));
}
// Allocate all buffers again
for (uint32_t i = 0; i < numBufs; ++i) {
bufs[i] = (uint8_t*)subAlloc.alloc(8);
EXPECT_NE(nullptr, bufs[i]);
}
}
} // namespace base
} // namespace android