Coverage Report

Created: 2018-09-25 14:53

/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
}