blob: 799608f17c362dd9113ea529e7cd3b4bc65494f8 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/341324165): Fix and remove.
#pragma allow_unsafe_buffers
#endif
#include "base/trace_event/memory_usage_estimator.h"
#include <stdlib.h>
#include <string>
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(ARCH_CPU_64_BITS)
#define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a)
#else
#define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a)
#endif
namespace base {
namespace trace_event {
namespace {
// Test class with predictable memory usage.
class Data {
public:
explicit Data(size_t size = 17): size_(size) {
}
size_t size() const { return size_; }
size_t EstimateMemoryUsage() const {
return size_;
}
bool operator < (const Data& other) const {
return size_ < other.size_;
}
bool operator == (const Data& other) const {
return size_ == other.size_;
}
struct Hasher {
size_t operator () (const Data& data) const {
return data.size();
}
};
private:
size_t size_;
};
} // namespace
namespace internal {
// This kills variance of bucket_count across STL implementations.
template <>
size_t HashMapBucketCountForTesting<Data>(size_t) {
return 10;
}
template <>
size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) {
return 10;
}
} // namespace internal
TEST(EstimateMemoryUsageTest, String) {
std::string string(777, 'a');
EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string));
}
TEST(EstimateMemoryUsageTest, String16) {
std::u16string string(777, 'a');
EXPECT_EQ(sizeof(char16_t) * (string.capacity() + 1),
EstimateMemoryUsage(string));
}
TEST(EstimateMemoryUsageTest, Arrays) {
// std::array
{
std::array<Data, 10> array;
EXPECT_EQ(170u, EstimateMemoryUsage(array));
}
// T[N]
{
Data array[10];
EXPECT_EQ(170u, EstimateMemoryUsage(array));
}
// C array
{
struct Item {
char payload[10];
};
Item* array = new Item[7];
EXPECT_EQ(70u, EstimateMemoryUsage(base::span<const Item>(array, 7u)));
delete[] array;
}
}
TEST(EstimateMemoryUsageTest, UniquePtr) {
// Empty
{
std::unique_ptr<Data> ptr;
EXPECT_EQ(0u, EstimateMemoryUsage(ptr));
}
// Not empty
{
std::unique_ptr<Data> ptr(new Data());
EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr));
}
// With a pointer
{
std::unique_ptr<Data*> ptr(new Data*());
EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr));
}
}
TEST(EstimateMemoryUsageTest, Vector) {
std::vector<Data> vector;
vector.reserve(1000);
// For an empty vector we should return memory usage of its buffer
size_t capacity = vector.capacity();
size_t expected_size = capacity * sizeof(Data);
EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
// If vector is not empty, its size should also include memory usages
// of all elements.
for (size_t i = 0; i != capacity / 2; ++i) {
vector.push_back(Data(i));
expected_size += EstimateMemoryUsage(vector.back());
}
EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
}
TEST(EstimateMemoryUsageTest, Vector_of_Pointers) {
{
std::unique_ptr<Data> u_ptr = std::make_unique<Data>(11);
std::vector<Data*> vector;
vector.reserve(1000);
vector.push_back(u_ptr.get());
size_t capacity = vector.capacity();
size_t expected_size = capacity * sizeof(Data*);
EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
}
{
std::unique_ptr<Data> u_ptr = std::make_unique<Data>(11);
std::vector<raw_ptr<Data>> vector;
vector.reserve(1000);
vector.push_back(u_ptr.get());
size_t capacity = vector.capacity();
size_t expected_size = capacity * sizeof(raw_ptr<Data>);
EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
}
}
TEST(EstimateMemoryUsageTest, List) {
struct POD {
short data;
};
std::list<POD> list;
for (int i = 0; i != 1000; ++i) {
list.push_back(POD());
}
EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list));
}
TEST(EstimateMemoryUsageTest, Set) {
std::set<std::pair<int, Data>> set;
for (int i = 0; i != 1000; ++i) {
set.insert({i, Data(i)});
}
EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set));
}
TEST(EstimateMemoryUsageTest, MultiSet) {
std::multiset<bool> set;
for (int i = 0; i != 1000; ++i) {
set.insert((i & 1) != 0);
}
EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set));
}
TEST(EstimateMemoryUsageTest, Map) {
std::map<Data, int> map;
for (int i = 0; i != 1000; ++i) {
map.insert({Data(i), i});
}
EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
}
TEST(EstimateMemoryUsageTest, MultiMap) {
std::multimap<char, Data> map;
for (int i = 0; i != 1000; ++i) {
map.insert({static_cast<char>(i), Data(i)});
}
EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
}
TEST(EstimateMemoryUsageTest, UnorderedSet) {
std::unordered_set<Data, Data::Hasher> set;
for (int i = 0; i != 1000; ++i) {
set.insert(Data(i));
}
EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set));
}
TEST(EstimateMemoryUsageTest, UnorderedMultiSet) {
std::unordered_multiset<Data, Data::Hasher> set;
for (int i = 0; i != 500; ++i) {
set.insert(Data(i));
set.insert(Data(i));
}
EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set));
}
TEST(EstimateMemoryUsageTest, UnorderedMap) {
std::unordered_map<Data, short, Data::Hasher> map;
for (int i = 0; i != 1000; ++i) {
map.insert({Data(i), static_cast<short>(i)});
}
EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
}
TEST(EstimateMemoryUsageTest, UnorderedMultiMap) {
std::unordered_multimap<Data, short, Data::Hasher> map;
for (int i = 0; i != 1000; ++i) {
map.insert({Data(i), static_cast<short>(i)});
}
EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
}
TEST(EstimateMemoryUsageTest, Deque) {
std::deque<Data> deque;
// Pick a large value so that platform-specific accounting
// for deque's blocks is small compared to usage of all items.
constexpr size_t kDataSize = 100000;
for (int i = 0; i != 1500; ++i) {
deque.push_back(Data(kDataSize));
}
// Compare against a reasonable minimum (i.e. no overhead).
size_t min_expected_usage = deque.size() * (sizeof(Data) + kDataSize);
EXPECT_LE(min_expected_usage, EstimateMemoryUsage(deque));
}
TEST(EstimateMemoryUsageTest, IsStandardContainerComplexIteratorTest) {
struct abstract {
virtual void method() = 0;
};
static_assert(
internal::IsIteratorOfStandardContainer<std::list<int>::iterator>, "");
static_assert(
internal::IsIteratorOfStandardContainer<std::list<int>::const_iterator>,
"");
static_assert(
internal::IsIteratorOfStandardContainer<std::list<int>::reverse_iterator>,
"");
static_assert(internal::IsIteratorOfStandardContainer<
std::list<int>::const_reverse_iterator>,
"");
static_assert(!internal::IsIteratorOfStandardContainer<int>, "");
static_assert(!internal::IsIteratorOfStandardContainer<abstract*>, "");
}
} // namespace trace_event
} // namespace base