/src/mozilla-central/xpcom/tests/gtest/TestArenaAllocator.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/ArenaAllocator.h" |
8 | | #include "mozilla/ArenaAllocatorExtensions.h" |
9 | | #include "nsIMemoryReporter.h" // MOZ_MALLOC_SIZE_OF |
10 | | |
11 | | #include "gtest/gtest.h" |
12 | | |
13 | | using mozilla::ArenaAllocator; |
14 | | |
15 | | TEST(ArenaAllocator, Constructor) |
16 | 0 | { |
17 | 0 | ArenaAllocator<4096, 4> a; |
18 | 0 | } |
19 | | |
20 | | TEST(ArenaAllocator, DefaultAllocate) |
21 | 0 | { |
22 | 0 | // Test default 1-byte alignment. |
23 | 0 | ArenaAllocator<1024> a; |
24 | 0 | void* x = a.Allocate(101); |
25 | 0 | void* y = a.Allocate(101); |
26 | 0 |
|
27 | 0 | // Given 1-byte aligment, we expect the allocations to follow |
28 | 0 | // each other exactly. |
29 | 0 | EXPECT_EQ(uintptr_t(x) + 101, uintptr_t(y)); |
30 | 0 | } |
31 | | |
32 | | TEST(ArenaAllocator, AllocateAlignment) |
33 | 0 | { |
34 | 0 | // Test non-default 8-byte alignment. |
35 | 0 | static const size_t kAlignment = 8; |
36 | 0 | ArenaAllocator<1024, kAlignment> a; |
37 | 0 |
|
38 | 0 | // Make sure aligment is correct for 1-8. |
39 | 0 | for (size_t i = 1; i <= kAlignment; i++) { |
40 | 0 | // All of these should be 8 bytes |
41 | 0 | void* x = a.Allocate(i); |
42 | 0 | void* y = a.Allocate(i); |
43 | 0 | EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y)); |
44 | 0 | } |
45 | 0 |
|
46 | 0 | // Test with slightly larger than specified alignment. |
47 | 0 | void* x = a.Allocate(kAlignment + 1); |
48 | 0 | void* y = a.Allocate(kAlignment + 1); |
49 | 0 |
|
50 | 0 | // Given 8-byte aligment, and a non-8-byte aligned request we expect the |
51 | 0 | // allocations to be padded. |
52 | 0 | EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y)); |
53 | 0 |
|
54 | 0 | // We expect 7 bytes of padding to have been added. |
55 | 0 | EXPECT_EQ(uintptr_t(x) + kAlignment * 2, uintptr_t(y)); |
56 | 0 | } |
57 | | |
58 | | #if 0 |
59 | | TEST(ArenaAllocator, AllocateZeroBytes) |
60 | | { |
61 | | // This would have to be a death test. Since we chose to provide an |
62 | | // infallible allocator we can't just return nullptr in the 0 case as |
63 | | // there's no way to differentiate that from the OOM case. |
64 | | ArenaAllocator<1024> a; |
65 | | void* x = a.Allocate(0); |
66 | | EXPECT_FALSE(x); |
67 | | } |
68 | | |
69 | | TEST(ArenaAllocator, BadAlignment) |
70 | | { |
71 | | // This test causes build failures by triggering the static assert enforcing |
72 | | // a power-of-two alignment. |
73 | | ArenaAllocator<256, 3> a; |
74 | | ArenaAllocator<256, 7> b; |
75 | | ArenaAllocator<256, 17> c; |
76 | | } |
77 | | #endif |
78 | | |
79 | | TEST(ArenaAllocator, AllocateMultipleSizes) |
80 | 0 | { |
81 | 0 | // Test non-default 4-byte alignment. |
82 | 0 | ArenaAllocator<4096,4> a; |
83 | 0 |
|
84 | 0 | for (int i = 1; i < 50; i++) { |
85 | 0 | void* x = a.Allocate(i); |
86 | 0 | // All the allocations should be aligned properly. |
87 | 0 | EXPECT_EQ(uintptr_t(x) % 4, uintptr_t(0)); |
88 | 0 | } |
89 | 0 |
|
90 | 0 | // Test a large 64-byte alignment |
91 | 0 | ArenaAllocator<8192, 64> b; |
92 | 0 | for (int i = 1; i < 100; i++) { |
93 | 0 | void* x = b.Allocate(i); |
94 | 0 | // All the allocations should be aligned properly. |
95 | 0 | EXPECT_EQ(uintptr_t(x) % 64, uintptr_t(0)); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | TEST(ArenaAllocator, AllocateInDifferentChunks) |
100 | 0 | { |
101 | 0 | // Test default 1-byte alignment. |
102 | 0 | ArenaAllocator<4096> a; |
103 | 0 | void* x = a.Allocate(4000); |
104 | 0 | void* y = a.Allocate(4000); |
105 | 0 | EXPECT_NE(uintptr_t(x) + 4000, uintptr_t(y)); |
106 | 0 | } |
107 | | |
108 | | TEST(ArenaAllocator, AllocateLargerThanArenaSize) |
109 | 0 | { |
110 | 0 | // Test default 1-byte alignment. |
111 | 0 | ArenaAllocator<256> a; |
112 | 0 | void* x = a.Allocate(4000); |
113 | 0 | void* y = a.Allocate(4000); |
114 | 0 | EXPECT_TRUE(x); |
115 | 0 | EXPECT_TRUE(y); |
116 | 0 |
|
117 | 0 | // Now try a normal allocation, it should behave as expected. |
118 | 0 | x = a.Allocate(8); |
119 | 0 | y = a.Allocate(8); |
120 | 0 | EXPECT_EQ(uintptr_t(x) + 8, uintptr_t(y)); |
121 | 0 | } |
122 | | |
123 | | #ifndef MOZ_CODE_COVERAGE |
124 | | TEST(ArenaAllocator, AllocationsPerChunk) |
125 | 0 | { |
126 | 0 | // Test that expected number of allocations fit in one chunk. |
127 | 0 | // We use an alignment of 64-bytes to avoid worrying about differences in |
128 | 0 | // the header size on 32 and 64-bit platforms. |
129 | 0 | const size_t kArenaSize = 1024; |
130 | 0 | const size_t kAlignment = 64; |
131 | 0 | ArenaAllocator<kArenaSize, kAlignment> a; |
132 | 0 |
|
133 | 0 | // With an alignment of 64 bytes we expect the header to take up the first |
134 | 0 | // alignment sized slot leaving bytes leaving the rest available for |
135 | 0 | // allocation. |
136 | 0 | const size_t kAllocationsPerChunk = (kArenaSize / kAlignment) - 1; |
137 | 0 | void* x = nullptr; |
138 | 0 | void* y = a.Allocate(kAlignment); |
139 | 0 | EXPECT_TRUE(y); |
140 | 0 | for (size_t i = 1; i < kAllocationsPerChunk; i++) { |
141 | 0 | x = y; |
142 | 0 | y = a.Allocate(kAlignment); |
143 | 0 | EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y)); |
144 | 0 | } |
145 | 0 |
|
146 | 0 | // The next allocation should be in a different chunk. |
147 | 0 | x = y; |
148 | 0 | y = a.Allocate(kAlignment); |
149 | 0 | EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y)); |
150 | 0 | } |
151 | | |
152 | | TEST(ArenaAllocator, MemoryIsValid) |
153 | 0 | { |
154 | 0 | // Make multiple allocations and actually access the memory. This is |
155 | 0 | // expected to trip up ASAN or valgrind if out of bounds memory is |
156 | 0 | // accessed. |
157 | 0 | static const size_t kArenaSize = 1024; |
158 | 0 | static const size_t kAlignment = 64; |
159 | 0 | static const char kMark = char(0xBC); |
160 | 0 | ArenaAllocator<kArenaSize, kAlignment> a; |
161 | 0 |
|
162 | 0 | // Single allocation that should fill the arena. |
163 | 0 | size_t sz = kArenaSize - kAlignment; |
164 | 0 | char* x = (char*)a.Allocate(sz); |
165 | 0 | EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); |
166 | 0 | memset(x, kMark, sz); |
167 | 0 | for (size_t i = 0; i < sz; i++) { |
168 | 0 | EXPECT_EQ(x[i], kMark); |
169 | 0 | } |
170 | 0 |
|
171 | 0 | // Allocation over arena size. |
172 | 0 | sz = kArenaSize * 2; |
173 | 0 | x = (char*)a.Allocate(sz); |
174 | 0 | EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); |
175 | 0 | memset(x, kMark, sz); |
176 | 0 | for (size_t i = 0; i < sz; i++) { |
177 | 0 | EXPECT_EQ(x[i], kMark); |
178 | 0 | } |
179 | 0 |
|
180 | 0 | // Allocation half the arena size. |
181 | 0 | sz = kArenaSize / 2; |
182 | 0 | x = (char*)a.Allocate(sz); |
183 | 0 | EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); |
184 | 0 | memset(x, kMark, sz); |
185 | 0 | for (size_t i = 0; i < sz; i++) { |
186 | 0 | EXPECT_EQ(x[i], kMark); |
187 | 0 | } |
188 | 0 |
|
189 | 0 | // Repeat, this should actually end up in a new chunk. |
190 | 0 | x = (char*)a.Allocate(sz); |
191 | 0 | EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0)); |
192 | 0 | memset(x, kMark, sz); |
193 | 0 | for (size_t i = 0; i < sz; i++) { |
194 | 0 | EXPECT_EQ(x[i], kMark); |
195 | 0 | } |
196 | 0 | } |
197 | | #endif |
198 | | |
199 | | MOZ_DEFINE_MALLOC_SIZE_OF(TestSizeOf); |
200 | | |
201 | | TEST(ArenaAllocator, SizeOf) |
202 | 0 | { |
203 | 0 | // This tests the sizeof functionality. We can't test for equality as we |
204 | 0 | // can't reliably guarantee what sizes the underlying allocator is going to |
205 | 0 | // choose, so we just test that things grow (or not) as expected. |
206 | 0 | static const size_t kArenaSize = 4096; |
207 | 0 | ArenaAllocator<kArenaSize> a; |
208 | 0 |
|
209 | 0 | // Excluding *this we expect an empty arena allocator to have no overhead. |
210 | 0 | size_t sz = a.SizeOfExcludingThis(TestSizeOf); |
211 | 0 | EXPECT_EQ(sz, size_t(0)); |
212 | 0 |
|
213 | 0 | // Cause one chunk to be allocated. |
214 | 0 | (void)a.Allocate(kArenaSize / 2); |
215 | 0 | size_t prev_sz = sz; |
216 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
217 | 0 | EXPECT_GT(sz, prev_sz); |
218 | 0 |
|
219 | 0 | // Allocate within the current chunk. |
220 | 0 | (void)a.Allocate(kArenaSize / 4); |
221 | 0 | prev_sz = sz; |
222 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
223 | 0 | EXPECT_EQ(sz, prev_sz); |
224 | 0 |
|
225 | 0 | // Overflow to a new chunk. |
226 | 0 | (void)a.Allocate(kArenaSize / 2); |
227 | 0 | prev_sz = sz; |
228 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
229 | 0 | EXPECT_GT(sz, prev_sz); |
230 | 0 |
|
231 | 0 | // Allocate an oversized chunk with enough room for a header to fit in page |
232 | 0 | // size. We expect the underlying allocator to round up to page alignment. |
233 | 0 | (void)a.Allocate((kArenaSize * 2) - 64); |
234 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
235 | 0 | EXPECT_GT(sz, prev_sz); |
236 | 0 | } |
237 | | |
238 | | TEST(ArenaAllocator, Clear) |
239 | 0 | { |
240 | 0 | // Tests that the Clear function works as expected. The best proxy for |
241 | 0 | // checking if a clear is successful is to measure the size. If it's empty we |
242 | 0 | // expect the size to be 0. |
243 | 0 | static const size_t kArenaSize = 128; |
244 | 0 | ArenaAllocator<kArenaSize> a; |
245 | 0 |
|
246 | 0 | // Clearing an empty arena should work. |
247 | 0 | a.Clear(); |
248 | 0 |
|
249 | 0 | size_t sz = a.SizeOfExcludingThis(TestSizeOf); |
250 | 0 | EXPECT_EQ(sz, size_t(0)); |
251 | 0 |
|
252 | 0 | // Allocating should work after clearing an empty arena. |
253 | 0 | void* x = a.Allocate(10); |
254 | 0 | EXPECT_TRUE(x); |
255 | 0 |
|
256 | 0 | size_t prev_sz = sz; |
257 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
258 | 0 | EXPECT_GT(sz, prev_sz); |
259 | 0 |
|
260 | 0 | // Allocate enough for a few arena chunks to be necessary. |
261 | 0 | for (size_t i = 0; i < kArenaSize * 2; i++) { |
262 | 0 | x = a.Allocate(1); |
263 | 0 | EXPECT_TRUE(x); |
264 | 0 | } |
265 | 0 |
|
266 | 0 | prev_sz = sz; |
267 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
268 | 0 | EXPECT_GT(sz, prev_sz); |
269 | 0 |
|
270 | 0 | // Clearing should reduce the size back to zero. |
271 | 0 | a.Clear(); |
272 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
273 | 0 | EXPECT_EQ(sz, size_t(0)); |
274 | 0 |
|
275 | 0 | // Allocating should work after clearing an arena with allocations. |
276 | 0 | x = a.Allocate(10); |
277 | 0 | EXPECT_TRUE(x); |
278 | 0 |
|
279 | 0 | prev_sz = sz; |
280 | 0 | sz = a.SizeOfExcludingThis(TestSizeOf); |
281 | 0 | EXPECT_GT(sz, prev_sz); |
282 | 0 | } |
283 | | |
284 | | TEST(ArenaAllocator, Extensions) |
285 | 0 | { |
286 | 0 | ArenaAllocator<4096, 8> a; |
287 | 0 |
|
288 | 0 | // Test with raw strings. |
289 | 0 | const char* const kTestCStr = "This is a test string."; |
290 | 0 | char* c_dup = mozilla::ArenaStrdup(kTestCStr, a); |
291 | 0 | EXPECT_STREQ(c_dup, kTestCStr); |
292 | 0 |
|
293 | 0 | const char16_t* const kTestStr = u"This is a wide test string."; |
294 | 0 | char16_t* dup = mozilla::ArenaStrdup(kTestStr, a); |
295 | 0 | EXPECT_TRUE(nsString(dup).Equals(kTestStr)); |
296 | 0 |
|
297 | 0 | // Make sure it works with literal strings. |
298 | 0 | NS_NAMED_LITERAL_STRING(wideStr, "A wide string."); |
299 | 0 | nsLiteralString::char_type* wide = mozilla::ArenaStrdup(wideStr, a); |
300 | 0 | EXPECT_TRUE(wideStr.Equals(wide)); |
301 | 0 |
|
302 | 0 | NS_NAMED_LITERAL_CSTRING(cStr, "A c-string."); |
303 | 0 | nsLiteralCString::char_type* cstr = mozilla::ArenaStrdup(cStr, a); |
304 | 0 | EXPECT_TRUE(cStr.Equals(cstr)); |
305 | 0 |
|
306 | 0 | // Make sure it works with normal strings. |
307 | 0 | nsAutoString x(u"testing wide"); |
308 | 0 | nsAutoString::char_type* x_copy = mozilla::ArenaStrdup(x, a); |
309 | 0 | EXPECT_TRUE(x.Equals(x_copy)); |
310 | 0 |
|
311 | 0 | nsAutoCString y("testing c-string"); |
312 | 0 | nsAutoCString::char_type* y_copy = mozilla::ArenaStrdup(y, a); |
313 | 0 | EXPECT_TRUE(y.Equals(y_copy)); |
314 | 0 | } |