1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/memory_usage_estimator.h"
6
7 #include <stdlib.h>
8
9 #include <string>
10
11 #include "base/memory/ptr_util.h"
12 #include "build/build_config.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 #if defined(ARCH_CPU_64_BITS)
16 #define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a)
17 #else
18 #define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a)
19 #endif
20
21 namespace base {
22 namespace trace_event {
23
24 namespace {
25
26 // Test class with predictable memory usage.
27 class Data {
28 public:
Data(size_t size=17)29 explicit Data(size_t size = 17): size_(size) {
30 }
31
size() const32 size_t size() const { return size_; }
33
EstimateMemoryUsage() const34 size_t EstimateMemoryUsage() const {
35 return size_;
36 }
37
operator <(const Data & other) const38 bool operator < (const Data& other) const {
39 return size_ < other.size_;
40 }
operator ==(const Data & other) const41 bool operator == (const Data& other) const {
42 return size_ == other.size_;
43 }
44
45 struct Hasher {
operator ()base::trace_event::__anonf48f24f00111::Data::Hasher46 size_t operator () (const Data& data) const {
47 return data.size();
48 }
49 };
50
51 private:
52 size_t size_;
53 };
54
55 } // namespace
56
57 namespace internal {
58
59 // This kills variance of bucket_count across STL implementations.
60 template <>
HashMapBucketCountForTesting(size_t)61 size_t HashMapBucketCountForTesting<Data>(size_t) {
62 return 10;
63 }
64 template <>
HashMapBucketCountForTesting(size_t)65 size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) {
66 return 10;
67 }
68
69 } // namespace internal
70
TEST(EstimateMemoryUsageTest,String)71 TEST(EstimateMemoryUsageTest, String) {
72 std::string string(777, 'a');
73 EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string));
74 }
75
TEST(EstimateMemoryUsageTest,String16)76 TEST(EstimateMemoryUsageTest, String16) {
77 std::u16string string(777, 'a');
78 EXPECT_EQ(sizeof(char16_t) * (string.capacity() + 1),
79 EstimateMemoryUsage(string));
80 }
81
TEST(EstimateMemoryUsageTest,Arrays)82 TEST(EstimateMemoryUsageTest, Arrays) {
83 // std::array
84 {
85 std::array<Data, 10> array;
86 EXPECT_EQ(170u, EstimateMemoryUsage(array));
87 }
88
89 // T[N]
90 {
91 Data array[10];
92 EXPECT_EQ(170u, EstimateMemoryUsage(array));
93 }
94
95 // C array
96 {
97 struct Item {
98 char payload[10];
99 };
100 Item* array = new Item[7];
101 EXPECT_EQ(70u, EstimateMemoryUsage(array, 7));
102 delete[] array;
103 }
104 }
105
TEST(EstimateMemoryUsageTest,UniquePtr)106 TEST(EstimateMemoryUsageTest, UniquePtr) {
107 // Empty
108 {
109 std::unique_ptr<Data> ptr;
110 EXPECT_EQ(0u, EstimateMemoryUsage(ptr));
111 }
112
113 // Not empty
114 {
115 std::unique_ptr<Data> ptr(new Data());
116 EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr));
117 }
118
119 // With a pointer
120 {
121 std::unique_ptr<Data*> ptr(new Data*());
122 EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr));
123 }
124
125 // With an array
126 {
127 struct Item {
128 uint32_t payload[10];
129 };
130 std::unique_ptr<Item[]> ptr(new Item[7]);
131 EXPECT_EQ(280u, EstimateMemoryUsage(ptr, 7));
132 }
133 }
134
TEST(EstimateMemoryUsageTest,Vector)135 TEST(EstimateMemoryUsageTest, Vector) {
136 std::vector<Data> vector;
137 vector.reserve(1000);
138
139 // For an empty vector we should return memory usage of its buffer
140 size_t capacity = vector.capacity();
141 size_t expected_size = capacity * sizeof(Data);
142 EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
143
144 // If vector is not empty, its size should also include memory usages
145 // of all elements.
146 for (size_t i = 0; i != capacity / 2; ++i) {
147 vector.push_back(Data(i));
148 expected_size += EstimateMemoryUsage(vector.back());
149 }
150 EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
151 }
152
TEST(EstimateMemoryUsageTest,List)153 TEST(EstimateMemoryUsageTest, List) {
154 struct POD {
155 short data;
156 };
157 std::list<POD> list;
158 for (int i = 0; i != 1000; ++i) {
159 list.push_back(POD());
160 }
161 EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list));
162 }
163
TEST(EstimateMemoryUsageTest,Set)164 TEST(EstimateMemoryUsageTest, Set) {
165 std::set<std::pair<int, Data>> set;
166 for (int i = 0; i != 1000; ++i) {
167 set.insert({i, Data(i)});
168 }
169 EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set));
170 }
171
TEST(EstimateMemoryUsageTest,MultiSet)172 TEST(EstimateMemoryUsageTest, MultiSet) {
173 std::multiset<bool> set;
174 for (int i = 0; i != 1000; ++i) {
175 set.insert((i & 1) != 0);
176 }
177 EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set));
178 }
179
TEST(EstimateMemoryUsageTest,Map)180 TEST(EstimateMemoryUsageTest, Map) {
181 std::map<Data, int> map;
182 for (int i = 0; i != 1000; ++i) {
183 map.insert({Data(i), i});
184 }
185 EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
186 }
187
TEST(EstimateMemoryUsageTest,MultiMap)188 TEST(EstimateMemoryUsageTest, MultiMap) {
189 std::multimap<char, Data> map;
190 for (int i = 0; i != 1000; ++i) {
191 map.insert({static_cast<char>(i), Data(i)});
192 }
193 EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
194 }
195
TEST(EstimateMemoryUsageTest,UnorderedSet)196 TEST(EstimateMemoryUsageTest, UnorderedSet) {
197 std::unordered_set<Data, Data::Hasher> set;
198 for (int i = 0; i != 1000; ++i) {
199 set.insert(Data(i));
200 }
201 EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set));
202 }
203
TEST(EstimateMemoryUsageTest,UnorderedMultiSet)204 TEST(EstimateMemoryUsageTest, UnorderedMultiSet) {
205 std::unordered_multiset<Data, Data::Hasher> set;
206 for (int i = 0; i != 500; ++i) {
207 set.insert(Data(i));
208 set.insert(Data(i));
209 }
210 EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set));
211 }
212
TEST(EstimateMemoryUsageTest,UnorderedMap)213 TEST(EstimateMemoryUsageTest, UnorderedMap) {
214 std::unordered_map<Data, short, Data::Hasher> map;
215 for (int i = 0; i != 1000; ++i) {
216 map.insert({Data(i), static_cast<short>(i)});
217 }
218 EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
219 }
220
TEST(EstimateMemoryUsageTest,UnorderedMultiMap)221 TEST(EstimateMemoryUsageTest, UnorderedMultiMap) {
222 std::unordered_multimap<Data, short, Data::Hasher> map;
223 for (int i = 0; i != 1000; ++i) {
224 map.insert({Data(i), static_cast<short>(i)});
225 }
226 EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
227 }
228
TEST(EstimateMemoryUsageTest,Deque)229 TEST(EstimateMemoryUsageTest, Deque) {
230 std::deque<Data> deque;
231
232 // Pick a large value so that platform-specific accounting
233 // for deque's blocks is small compared to usage of all items.
234 constexpr size_t kDataSize = 100000;
235 for (int i = 0; i != 1500; ++i) {
236 deque.push_back(Data(kDataSize));
237 }
238
239 // Compare against a reasonable minimum (i.e. no overhead).
240 size_t min_expected_usage = deque.size() * (sizeof(Data) + kDataSize);
241 EXPECT_LE(min_expected_usage, EstimateMemoryUsage(deque));
242 }
243
TEST(EstimateMemoryUsageTest,IsStandardContainerComplexIteratorTest)244 TEST(EstimateMemoryUsageTest, IsStandardContainerComplexIteratorTest) {
245 struct abstract {
246 virtual void method() = 0;
247 };
248
249 static_assert(
250 internal::IsStandardContainerComplexIterator<std::list<int>::iterator>(),
251 "");
252 static_assert(internal::IsStandardContainerComplexIterator<
253 std::list<int>::const_iterator>(),
254 "");
255 static_assert(internal::IsStandardContainerComplexIterator<
256 std::list<int>::reverse_iterator>(),
257 "");
258 static_assert(internal::IsStandardContainerComplexIterator<
259 std::list<int>::const_reverse_iterator>(),
260 "");
261 static_assert(!internal::IsStandardContainerComplexIterator<int>(), "");
262 static_assert(!internal::IsStandardContainerComplexIterator<abstract*>(), "");
263 }
264
265 } // namespace trace_event
266 } // namespace base
267