1 // Copyright 2015 The Chromium Authors. All rights reserved.
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/metrics/persistent_memory_allocator.h"
6
7 #include <memory>
8
9 #include "base/files/file.h"
10 #include "base/files/file_util.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/memory/shared_memory.h"
14 #include "base/metrics/histogram.h"
15 #include "base/rand_util.h"
16 #include "base/strings/safe_sprintf.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "base/synchronization/lock.h"
20 #include "base/threading/simple_thread.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22
23 namespace base {
24
25 namespace {
26
27 const uint32_t TEST_MEMORY_SIZE = 1 << 20; // 1 MiB
28 const uint32_t TEST_MEMORY_PAGE = 64 << 10; // 64 KiB
29 const uint32_t TEST_ID = 12345;
30 const char TEST_NAME[] = "TestAllocator";
31
SetFileLength(const base::FilePath & path,size_t length)32 void SetFileLength(const base::FilePath& path, size_t length) {
33 {
34 File file(path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
35 DCHECK(file.IsValid());
36 ASSERT_TRUE(file.SetLength(static_cast<int64_t>(length)));
37 }
38
39 int64_t actual_length;
40 DCHECK(GetFileSize(path, &actual_length));
41 DCHECK_EQ(length, static_cast<size_t>(actual_length));
42 }
43
44 } // namespace
45
46 typedef PersistentMemoryAllocator::Reference Reference;
47
48 class PersistentMemoryAllocatorTest : public testing::Test {
49 public:
50 // This can't be statically initialized because it's value isn't defined
51 // in the PersistentMemoryAllocator header file. Instead, it's simply set
52 // in the constructor.
53 uint32_t kAllocAlignment;
54
55 struct TestObject1 {
56 static constexpr uint32_t kPersistentTypeId = 1;
57 static constexpr size_t kExpectedInstanceSize = 4 + 1 + 3;
58 int32_t onething;
59 char oranother;
60 };
61
62 struct TestObject2 {
63 static constexpr uint32_t kPersistentTypeId = 2;
64 static constexpr size_t kExpectedInstanceSize = 8 + 4 + 4 + 8 + 8;
65 int64_t thiis;
66 int32_t that;
67 float andthe;
68 double other;
69 char thing[8];
70 };
71
PersistentMemoryAllocatorTest()72 PersistentMemoryAllocatorTest() {
73 kAllocAlignment = GetAllocAlignment();
74 mem_segment_.reset(new char[TEST_MEMORY_SIZE]);
75 }
76
SetUp()77 void SetUp() override {
78 allocator_.reset();
79 ::memset(mem_segment_.get(), 0, TEST_MEMORY_SIZE);
80 allocator_.reset(new PersistentMemoryAllocator(
81 mem_segment_.get(), TEST_MEMORY_SIZE, TEST_MEMORY_PAGE,
82 TEST_ID, TEST_NAME, false));
83 }
84
TearDown()85 void TearDown() override {
86 allocator_.reset();
87 }
88
CountIterables()89 unsigned CountIterables() {
90 PersistentMemoryAllocator::Iterator iter(allocator_.get());
91 uint32_t type;
92 unsigned count = 0;
93 while (iter.GetNext(&type) != 0) {
94 ++count;
95 }
96 return count;
97 }
98
GetAllocAlignment()99 static uint32_t GetAllocAlignment() {
100 return PersistentMemoryAllocator::kAllocAlignment;
101 }
102
103 protected:
104 std::unique_ptr<char[]> mem_segment_;
105 std::unique_ptr<PersistentMemoryAllocator> allocator_;
106 };
107
TEST_F(PersistentMemoryAllocatorTest,AllocateAndIterate)108 TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
109 allocator_->CreateTrackingHistograms(allocator_->Name());
110
111 std::string base_name(TEST_NAME);
112 EXPECT_EQ(TEST_ID, allocator_->Id());
113 EXPECT_TRUE(allocator_->used_histogram_);
114 EXPECT_EQ("UMA.PersistentAllocator." + base_name + ".UsedPct",
115 allocator_->used_histogram_->histogram_name());
116 EXPECT_EQ(PersistentMemoryAllocator::MEMORY_INITIALIZED,
117 allocator_->GetMemoryState());
118
119 // Get base memory info for later comparison.
120 PersistentMemoryAllocator::MemoryInfo meminfo0;
121 allocator_->GetMemoryInfo(&meminfo0);
122 EXPECT_EQ(TEST_MEMORY_SIZE, meminfo0.total);
123 EXPECT_GT(meminfo0.total, meminfo0.free);
124
125 // Validate allocation of test object and make sure it can be referenced
126 // and all metadata looks correct.
127 TestObject1* obj1 = allocator_->New<TestObject1>();
128 ASSERT_TRUE(obj1);
129 Reference block1 = allocator_->GetAsReference(obj1);
130 ASSERT_NE(0U, block1);
131 EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject1>(block1));
132 EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject2>(block1));
133 EXPECT_LE(sizeof(TestObject1), allocator_->GetAllocSize(block1));
134 EXPECT_GT(sizeof(TestObject1) + kAllocAlignment,
135 allocator_->GetAllocSize(block1));
136 PersistentMemoryAllocator::MemoryInfo meminfo1;
137 allocator_->GetMemoryInfo(&meminfo1);
138 EXPECT_EQ(meminfo0.total, meminfo1.total);
139 EXPECT_GT(meminfo0.free, meminfo1.free);
140
141 // Verify that pointers can be turned back into references and that invalid
142 // addresses return null.
143 char* memory1 = allocator_->GetAsArray<char>(block1, 1, 1);
144 ASSERT_TRUE(memory1);
145 EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 0));
146 EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 1));
147 EXPECT_EQ(0U, allocator_->GetAsReference(memory1, 2));
148 EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 1, 0));
149 EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 16, 0));
150 EXPECT_EQ(0U, allocator_->GetAsReference(nullptr, 0));
151 EXPECT_EQ(0U, allocator_->GetAsReference(&base_name, 0));
152
153 // Ensure that the test-object can be made iterable.
154 PersistentMemoryAllocator::Iterator iter1a(allocator_.get());
155 EXPECT_EQ(0U, iter1a.GetLast());
156 uint32_t type;
157 EXPECT_EQ(0U, iter1a.GetNext(&type));
158 allocator_->MakeIterable(block1);
159 EXPECT_EQ(block1, iter1a.GetNext(&type));
160 EXPECT_EQ(1U, type);
161 EXPECT_EQ(block1, iter1a.GetLast());
162 EXPECT_EQ(0U, iter1a.GetNext(&type));
163 EXPECT_EQ(block1, iter1a.GetLast());
164
165 // Create second test-object and ensure everything is good and it cannot
166 // be confused with test-object of another type.
167 TestObject2* obj2 = allocator_->New<TestObject2>();
168 ASSERT_TRUE(obj2);
169 Reference block2 = allocator_->GetAsReference(obj2);
170 ASSERT_NE(0U, block2);
171 EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject2>(block2));
172 EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject1>(block2));
173 EXPECT_LE(sizeof(TestObject2), allocator_->GetAllocSize(block2));
174 EXPECT_GT(sizeof(TestObject2) + kAllocAlignment,
175 allocator_->GetAllocSize(block2));
176 PersistentMemoryAllocator::MemoryInfo meminfo2;
177 allocator_->GetMemoryInfo(&meminfo2);
178 EXPECT_EQ(meminfo1.total, meminfo2.total);
179 EXPECT_GT(meminfo1.free, meminfo2.free);
180
181 // Ensure that second test-object can also be made iterable.
182 allocator_->MakeIterable(obj2);
183 EXPECT_EQ(block2, iter1a.GetNext(&type));
184 EXPECT_EQ(2U, type);
185 EXPECT_EQ(block2, iter1a.GetLast());
186 EXPECT_EQ(0U, iter1a.GetNext(&type));
187 EXPECT_EQ(block2, iter1a.GetLast());
188
189 // Check that the iterator can be reset to the beginning.
190 iter1a.Reset();
191 EXPECT_EQ(0U, iter1a.GetLast());
192 EXPECT_EQ(block1, iter1a.GetNext(&type));
193 EXPECT_EQ(block1, iter1a.GetLast());
194 EXPECT_EQ(block2, iter1a.GetNext(&type));
195 EXPECT_EQ(block2, iter1a.GetLast());
196 EXPECT_EQ(0U, iter1a.GetNext(&type));
197
198 // Check that the iterator can be reset to an arbitrary location.
199 iter1a.Reset(block1);
200 EXPECT_EQ(block1, iter1a.GetLast());
201 EXPECT_EQ(block2, iter1a.GetNext(&type));
202 EXPECT_EQ(block2, iter1a.GetLast());
203 EXPECT_EQ(0U, iter1a.GetNext(&type));
204
205 // Check that iteration can begin after an arbitrary location.
206 PersistentMemoryAllocator::Iterator iter1b(allocator_.get(), block1);
207 EXPECT_EQ(block2, iter1b.GetNext(&type));
208 EXPECT_EQ(0U, iter1b.GetNext(&type));
209
210 // Ensure nothing has gone noticably wrong.
211 EXPECT_FALSE(allocator_->IsFull());
212 EXPECT_FALSE(allocator_->IsCorrupt());
213
214 // Check the internal histogram record of used memory.
215 allocator_->UpdateTrackingHistograms();
216 std::unique_ptr<HistogramSamples> used_samples(
217 allocator_->used_histogram_->SnapshotSamples());
218 EXPECT_TRUE(used_samples);
219 EXPECT_EQ(1, used_samples->TotalCount());
220
221 // Check that an object's type can be changed.
222 EXPECT_EQ(2U, allocator_->GetType(block2));
223 allocator_->ChangeType(block2, 3, 2, false);
224 EXPECT_EQ(3U, allocator_->GetType(block2));
225 allocator_->New<TestObject2>(block2, 3, false);
226 EXPECT_EQ(2U, allocator_->GetType(block2));
227
228 // Create second allocator (read/write) using the same memory segment.
229 std::unique_ptr<PersistentMemoryAllocator> allocator2(
230 new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE,
231 TEST_MEMORY_PAGE, 0, "", false));
232 EXPECT_EQ(TEST_ID, allocator2->Id());
233 EXPECT_FALSE(allocator2->used_histogram_);
234
235 // Ensure that iteration and access through second allocator works.
236 PersistentMemoryAllocator::Iterator iter2(allocator2.get());
237 EXPECT_EQ(block1, iter2.GetNext(&type));
238 EXPECT_EQ(block2, iter2.GetNext(&type));
239 EXPECT_EQ(0U, iter2.GetNext(&type));
240 EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject1>(block1));
241 EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject2>(block2));
242
243 // Create a third allocator (read-only) using the same memory segment.
244 std::unique_ptr<const PersistentMemoryAllocator> allocator3(
245 new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE,
246 TEST_MEMORY_PAGE, 0, "", true));
247 EXPECT_EQ(TEST_ID, allocator3->Id());
248 EXPECT_FALSE(allocator3->used_histogram_);
249
250 // Ensure that iteration and access through third allocator works.
251 PersistentMemoryAllocator::Iterator iter3(allocator3.get());
252 EXPECT_EQ(block1, iter3.GetNext(&type));
253 EXPECT_EQ(block2, iter3.GetNext(&type));
254 EXPECT_EQ(0U, iter3.GetNext(&type));
255 EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject1>(block1));
256 EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject2>(block2));
257
258 // Ensure that GetNextOfType works.
259 PersistentMemoryAllocator::Iterator iter1c(allocator_.get());
260 EXPECT_EQ(block2, iter1c.GetNextOfType<TestObject2>());
261 EXPECT_EQ(0U, iter1c.GetNextOfType(2));
262
263 // Ensure that GetNextOfObject works.
264 PersistentMemoryAllocator::Iterator iter1d(allocator_.get());
265 EXPECT_EQ(obj2, iter1d.GetNextOfObject<TestObject2>());
266 EXPECT_EQ(nullptr, iter1d.GetNextOfObject<TestObject2>());
267
268 // Ensure that deleting an object works.
269 allocator_->Delete(obj2);
270 PersistentMemoryAllocator::Iterator iter1z(allocator_.get());
271 EXPECT_EQ(nullptr, iter1z.GetNextOfObject<TestObject2>());
272
273 // Ensure that the memory state can be set.
274 allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
275 EXPECT_EQ(PersistentMemoryAllocator::MEMORY_DELETED,
276 allocator_->GetMemoryState());
277 }
278
TEST_F(PersistentMemoryAllocatorTest,PageTest)279 TEST_F(PersistentMemoryAllocatorTest, PageTest) {
280 // This allocation will go into the first memory page.
281 Reference block1 = allocator_->Allocate(TEST_MEMORY_PAGE / 2, 1);
282 EXPECT_LT(0U, block1);
283 EXPECT_GT(TEST_MEMORY_PAGE, block1);
284
285 // This allocation won't fit in same page as previous block.
286 Reference block2 =
287 allocator_->Allocate(TEST_MEMORY_PAGE - 2 * kAllocAlignment, 2);
288 EXPECT_EQ(TEST_MEMORY_PAGE, block2);
289
290 // This allocation will also require a new page.
291 Reference block3 = allocator_->Allocate(2 * kAllocAlignment + 99, 3);
292 EXPECT_EQ(2U * TEST_MEMORY_PAGE, block3);
293 }
294
295 // A simple thread that takes an allocator and repeatedly allocates random-
296 // sized chunks from it until no more can be done.
297 class AllocatorThread : public SimpleThread {
298 public:
AllocatorThread(const std::string & name,void * base,uint32_t size,uint32_t page_size)299 AllocatorThread(const std::string& name,
300 void* base,
301 uint32_t size,
302 uint32_t page_size)
303 : SimpleThread(name, Options()),
304 count_(0),
305 iterable_(0),
306 allocator_(base, size, page_size, 0, std::string(), false) {}
307
Run()308 void Run() override {
309 for (;;) {
310 uint32_t size = RandInt(1, 99);
311 uint32_t type = RandInt(100, 999);
312 Reference block = allocator_.Allocate(size, type);
313 if (!block)
314 break;
315
316 count_++;
317 if (RandInt(0, 1)) {
318 allocator_.MakeIterable(block);
319 iterable_++;
320 }
321 }
322 }
323
iterable()324 unsigned iterable() { return iterable_; }
count()325 unsigned count() { return count_; }
326
327 private:
328 unsigned count_;
329 unsigned iterable_;
330 PersistentMemoryAllocator allocator_;
331 };
332
333 // Test parallel allocation/iteration and ensure consistency across all
334 // instances.
TEST_F(PersistentMemoryAllocatorTest,ParallelismTest)335 TEST_F(PersistentMemoryAllocatorTest, ParallelismTest) {
336 void* memory = mem_segment_.get();
337 AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
338 AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
339 AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
340 AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
341 AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
342
343 t1.Start();
344 t2.Start();
345 t3.Start();
346 t4.Start();
347 t5.Start();
348
349 unsigned last_count = 0;
350 do {
351 unsigned count = CountIterables();
352 EXPECT_LE(last_count, count);
353 } while (!allocator_->IsCorrupt() && !allocator_->IsFull());
354
355 t1.Join();
356 t2.Join();
357 t3.Join();
358 t4.Join();
359 t5.Join();
360
361 EXPECT_FALSE(allocator_->IsCorrupt());
362 EXPECT_TRUE(allocator_->IsFull());
363 EXPECT_EQ(CountIterables(),
364 t1.iterable() + t2.iterable() + t3.iterable() + t4.iterable() +
365 t5.iterable());
366 }
367
368 // A simple thread that counts objects by iterating through an allocator.
369 class CounterThread : public SimpleThread {
370 public:
CounterThread(const std::string & name,PersistentMemoryAllocator::Iterator * iterator,Lock * lock,ConditionVariable * condition,bool * wake_up)371 CounterThread(const std::string& name,
372 PersistentMemoryAllocator::Iterator* iterator,
373 Lock* lock,
374 ConditionVariable* condition,
375 bool* wake_up)
376 : SimpleThread(name, Options()),
377 iterator_(iterator),
378 lock_(lock),
379 condition_(condition),
380 count_(0),
381 wake_up_(wake_up) {}
382
Run()383 void Run() override {
384 // Wait so all threads can start at approximately the same time.
385 // Best performance comes from releasing a single worker which then
386 // releases the next, etc., etc.
387 {
388 AutoLock autolock(*lock_);
389
390 // Before calling Wait(), make sure that the wake up condition
391 // has not already passed. Also, since spurious signal events
392 // are possible, check the condition in a while loop to make
393 // sure that the wake up condition is met when this thread
394 // returns from the Wait().
395 // See usage comments in src/base/synchronization/condition_variable.h.
396 while (!*wake_up_) {
397 condition_->Wait();
398 condition_->Signal();
399 }
400 }
401
402 uint32_t type;
403 while (iterator_->GetNext(&type) != 0) {
404 ++count_;
405 }
406 }
407
count()408 unsigned count() { return count_; }
409
410 private:
411 PersistentMemoryAllocator::Iterator* iterator_;
412 Lock* lock_;
413 ConditionVariable* condition_;
414 unsigned count_;
415 bool* wake_up_;
416
417 DISALLOW_COPY_AND_ASSIGN(CounterThread);
418 };
419
420 // Ensure that parallel iteration returns the same number of objects as
421 // single-threaded iteration.
TEST_F(PersistentMemoryAllocatorTest,IteratorParallelismTest)422 TEST_F(PersistentMemoryAllocatorTest, IteratorParallelismTest) {
423 // Fill the memory segment with random allocations.
424 unsigned iterable_count = 0;
425 for (;;) {
426 uint32_t size = RandInt(1, 99);
427 uint32_t type = RandInt(100, 999);
428 Reference block = allocator_->Allocate(size, type);
429 if (!block)
430 break;
431 allocator_->MakeIterable(block);
432 ++iterable_count;
433 }
434 EXPECT_FALSE(allocator_->IsCorrupt());
435 EXPECT_TRUE(allocator_->IsFull());
436 EXPECT_EQ(iterable_count, CountIterables());
437
438 PersistentMemoryAllocator::Iterator iter(allocator_.get());
439 Lock lock;
440 ConditionVariable condition(&lock);
441 bool wake_up = false;
442
443 CounterThread t1("t1", &iter, &lock, &condition, &wake_up);
444 CounterThread t2("t2", &iter, &lock, &condition, &wake_up);
445 CounterThread t3("t3", &iter, &lock, &condition, &wake_up);
446 CounterThread t4("t4", &iter, &lock, &condition, &wake_up);
447 CounterThread t5("t5", &iter, &lock, &condition, &wake_up);
448
449 t1.Start();
450 t2.Start();
451 t3.Start();
452 t4.Start();
453 t5.Start();
454
455 // Take the lock and set the wake up condition to true. This helps to
456 // avoid a race condition where the Signal() event is called before
457 // all the threads have reached the Wait() and thus never get woken up.
458 {
459 AutoLock autolock(lock);
460 wake_up = true;
461 }
462
463 // This will release all the waiting threads.
464 condition.Signal();
465
466 t1.Join();
467 t2.Join();
468 t3.Join();
469 t4.Join();
470 t5.Join();
471
472 EXPECT_EQ(iterable_count,
473 t1.count() + t2.count() + t3.count() + t4.count() + t5.count());
474
475 #if 0
476 // These ensure that the threads don't run sequentially. It shouldn't be
477 // enabled in general because it could lead to a flaky test if it happens
478 // simply by chance but it is useful during development to ensure that the
479 // test is working correctly.
480 EXPECT_NE(iterable_count, t1.count());
481 EXPECT_NE(iterable_count, t2.count());
482 EXPECT_NE(iterable_count, t3.count());
483 EXPECT_NE(iterable_count, t4.count());
484 EXPECT_NE(iterable_count, t5.count());
485 #endif
486 }
487
TEST_F(PersistentMemoryAllocatorTest,DelayedAllocationTest)488 TEST_F(PersistentMemoryAllocatorTest, DelayedAllocationTest) {
489 std::atomic<Reference> ref1, ref2;
490 ref1.store(0, std::memory_order_relaxed);
491 ref2.store(0, std::memory_order_relaxed);
492 DelayedPersistentAllocation da1(allocator_.get(), &ref1, 1001, 100, true);
493 DelayedPersistentAllocation da2a(allocator_.get(), &ref2, 2002, 200, 0, true);
494 DelayedPersistentAllocation da2b(allocator_.get(), &ref2, 2002, 200, 5, true);
495
496 // Nothing should yet have been allocated.
497 uint32_t type;
498 PersistentMemoryAllocator::Iterator iter(allocator_.get());
499 EXPECT_EQ(0U, iter.GetNext(&type));
500
501 // Do first delayed allocation and check that a new persistent object exists.
502 EXPECT_EQ(0U, da1.reference());
503 void* mem1 = da1.Get();
504 ASSERT_TRUE(mem1);
505 EXPECT_NE(0U, da1.reference());
506 EXPECT_EQ(allocator_->GetAsReference(mem1, 1001),
507 ref1.load(std::memory_order_relaxed));
508 EXPECT_NE(0U, iter.GetNext(&type));
509 EXPECT_EQ(1001U, type);
510 EXPECT_EQ(0U, iter.GetNext(&type));
511
512 // Do second delayed allocation and check.
513 void* mem2a = da2a.Get();
514 ASSERT_TRUE(mem2a);
515 EXPECT_EQ(allocator_->GetAsReference(mem2a, 2002),
516 ref2.load(std::memory_order_relaxed));
517 EXPECT_NE(0U, iter.GetNext(&type));
518 EXPECT_EQ(2002U, type);
519 EXPECT_EQ(0U, iter.GetNext(&type));
520
521 // Third allocation should just return offset into second allocation.
522 void* mem2b = da2b.Get();
523 ASSERT_TRUE(mem2b);
524 EXPECT_EQ(0U, iter.GetNext(&type));
525 EXPECT_EQ(reinterpret_cast<uintptr_t>(mem2a) + 5,
526 reinterpret_cast<uintptr_t>(mem2b));
527 }
528
529 // This test doesn't verify anything other than it doesn't crash. Its goal
530 // is to find coding errors that aren't otherwise tested for, much like a
531 // "fuzzer" would.
532 // This test is suppsoed to fail on TSAN bot (crbug.com/579867).
533 #if defined(THREAD_SANITIZER)
534 #define MAYBE_CorruptionTest DISABLED_CorruptionTest
535 #else
536 #define MAYBE_CorruptionTest CorruptionTest
537 #endif
TEST_F(PersistentMemoryAllocatorTest,MAYBE_CorruptionTest)538 TEST_F(PersistentMemoryAllocatorTest, MAYBE_CorruptionTest) {
539 char* memory = mem_segment_.get();
540 AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
541 AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
542 AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
543 AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
544 AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
545
546 t1.Start();
547 t2.Start();
548 t3.Start();
549 t4.Start();
550 t5.Start();
551
552 do {
553 size_t offset = RandInt(0, TEST_MEMORY_SIZE - 1);
554 char value = RandInt(0, 255);
555 memory[offset] = value;
556 } while (!allocator_->IsCorrupt() && !allocator_->IsFull());
557
558 t1.Join();
559 t2.Join();
560 t3.Join();
561 t4.Join();
562 t5.Join();
563
564 CountIterables();
565 }
566
567 // Attempt to cause crashes or loops by expressly creating dangerous conditions.
TEST_F(PersistentMemoryAllocatorTest,MaliciousTest)568 TEST_F(PersistentMemoryAllocatorTest, MaliciousTest) {
569 Reference block1 = allocator_->Allocate(sizeof(TestObject1), 1);
570 Reference block2 = allocator_->Allocate(sizeof(TestObject1), 2);
571 Reference block3 = allocator_->Allocate(sizeof(TestObject1), 3);
572 Reference block4 = allocator_->Allocate(sizeof(TestObject1), 3);
573 Reference block5 = allocator_->Allocate(sizeof(TestObject1), 3);
574 allocator_->MakeIterable(block1);
575 allocator_->MakeIterable(block2);
576 allocator_->MakeIterable(block3);
577 allocator_->MakeIterable(block4);
578 allocator_->MakeIterable(block5);
579 EXPECT_EQ(5U, CountIterables());
580 EXPECT_FALSE(allocator_->IsCorrupt());
581
582 // Create loop in iterable list and ensure it doesn't hang. The return value
583 // from CountIterables() in these cases is unpredictable. If there is a
584 // failure, the call will hang and the test killed for taking too long.
585 uint32_t* header4 = (uint32_t*)(mem_segment_.get() + block4);
586 EXPECT_EQ(block5, header4[3]);
587 header4[3] = block4;
588 CountIterables(); // loop: 1-2-3-4-4
589 EXPECT_TRUE(allocator_->IsCorrupt());
590
591 // Test where loop goes back to previous block.
592 header4[3] = block3;
593 CountIterables(); // loop: 1-2-3-4-3
594
595 // Test where loop goes back to the beginning.
596 header4[3] = block1;
597 CountIterables(); // loop: 1-2-3-4-1
598 }
599
600
601 //----- LocalPersistentMemoryAllocator -----------------------------------------
602
TEST(LocalPersistentMemoryAllocatorTest,CreationTest)603 TEST(LocalPersistentMemoryAllocatorTest, CreationTest) {
604 LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, 42, "");
605 EXPECT_EQ(42U, allocator.Id());
606 EXPECT_NE(0U, allocator.Allocate(24, 1));
607 EXPECT_FALSE(allocator.IsFull());
608 EXPECT_FALSE(allocator.IsCorrupt());
609 }
610
611
612 //----- SharedPersistentMemoryAllocator ----------------------------------------
613
TEST(SharedPersistentMemoryAllocatorTest,CreationTest)614 TEST(SharedPersistentMemoryAllocatorTest, CreationTest) {
615 SharedMemoryHandle shared_handle_1;
616 SharedMemoryHandle shared_handle_2;
617
618 PersistentMemoryAllocator::MemoryInfo meminfo1;
619 Reference r123, r456, r789;
620 {
621 std::unique_ptr<SharedMemory> shmem1(new SharedMemory());
622 ASSERT_TRUE(shmem1->CreateAndMapAnonymous(TEST_MEMORY_SIZE));
623 SharedPersistentMemoryAllocator local(std::move(shmem1), TEST_ID, "",
624 false);
625 EXPECT_FALSE(local.IsReadonly());
626 r123 = local.Allocate(123, 123);
627 r456 = local.Allocate(456, 456);
628 r789 = local.Allocate(789, 789);
629 local.MakeIterable(r123);
630 local.ChangeType(r456, 654, 456, false);
631 local.MakeIterable(r789);
632 local.GetMemoryInfo(&meminfo1);
633 EXPECT_FALSE(local.IsFull());
634 EXPECT_FALSE(local.IsCorrupt());
635
636 shared_handle_1 = local.shared_memory()->handle().Duplicate();
637 ASSERT_TRUE(shared_handle_1.IsValid());
638 shared_handle_2 = local.shared_memory()->handle().Duplicate();
639 ASSERT_TRUE(shared_handle_2.IsValid());
640 }
641
642 // Read-only test.
643 std::unique_ptr<SharedMemory> shmem2(new SharedMemory(shared_handle_1,
644 /*readonly=*/true));
645 ASSERT_TRUE(shmem2->Map(TEST_MEMORY_SIZE));
646
647 SharedPersistentMemoryAllocator shalloc2(std::move(shmem2), 0, "", true);
648 EXPECT_TRUE(shalloc2.IsReadonly());
649 EXPECT_EQ(TEST_ID, shalloc2.Id());
650 EXPECT_FALSE(shalloc2.IsFull());
651 EXPECT_FALSE(shalloc2.IsCorrupt());
652
653 PersistentMemoryAllocator::Iterator iter2(&shalloc2);
654 uint32_t type;
655 EXPECT_EQ(r123, iter2.GetNext(&type));
656 EXPECT_EQ(r789, iter2.GetNext(&type));
657 EXPECT_EQ(0U, iter2.GetNext(&type));
658
659 EXPECT_EQ(123U, shalloc2.GetType(r123));
660 EXPECT_EQ(654U, shalloc2.GetType(r456));
661 EXPECT_EQ(789U, shalloc2.GetType(r789));
662
663 PersistentMemoryAllocator::MemoryInfo meminfo2;
664 shalloc2.GetMemoryInfo(&meminfo2);
665 EXPECT_EQ(meminfo1.total, meminfo2.total);
666 EXPECT_EQ(meminfo1.free, meminfo2.free);
667
668 // Read/write test.
669 std::unique_ptr<SharedMemory> shmem3(new SharedMemory(shared_handle_2,
670 /*readonly=*/false));
671 ASSERT_TRUE(shmem3->Map(TEST_MEMORY_SIZE));
672
673 SharedPersistentMemoryAllocator shalloc3(std::move(shmem3), 0, "", false);
674 EXPECT_FALSE(shalloc3.IsReadonly());
675 EXPECT_EQ(TEST_ID, shalloc3.Id());
676 EXPECT_FALSE(shalloc3.IsFull());
677 EXPECT_FALSE(shalloc3.IsCorrupt());
678
679 PersistentMemoryAllocator::Iterator iter3(&shalloc3);
680 EXPECT_EQ(r123, iter3.GetNext(&type));
681 EXPECT_EQ(r789, iter3.GetNext(&type));
682 EXPECT_EQ(0U, iter3.GetNext(&type));
683
684 EXPECT_EQ(123U, shalloc3.GetType(r123));
685 EXPECT_EQ(654U, shalloc3.GetType(r456));
686 EXPECT_EQ(789U, shalloc3.GetType(r789));
687
688 PersistentMemoryAllocator::MemoryInfo meminfo3;
689 shalloc3.GetMemoryInfo(&meminfo3);
690 EXPECT_EQ(meminfo1.total, meminfo3.total);
691 EXPECT_EQ(meminfo1.free, meminfo3.free);
692
693 // Interconnectivity test.
694 Reference obj = shalloc3.Allocate(42, 42);
695 ASSERT_TRUE(obj);
696 shalloc3.MakeIterable(obj);
697 EXPECT_EQ(obj, iter2.GetNext(&type));
698 EXPECT_EQ(42U, type);
699
700 // Clear-on-change test.
701 Reference data_ref = shalloc3.Allocate(sizeof(int) * 4, 911);
702 int* data = shalloc3.GetAsArray<int>(data_ref, 911, 4);
703 ASSERT_TRUE(data);
704 data[0] = 0;
705 data[1] = 1;
706 data[2] = 2;
707 data[3] = 3;
708 ASSERT_TRUE(shalloc3.ChangeType(data_ref, 119, 911, false));
709 EXPECT_EQ(0, data[0]);
710 EXPECT_EQ(1, data[1]);
711 EXPECT_EQ(2, data[2]);
712 EXPECT_EQ(3, data[3]);
713 ASSERT_TRUE(shalloc3.ChangeType(data_ref, 191, 119, true));
714 EXPECT_EQ(0, data[0]);
715 EXPECT_EQ(0, data[1]);
716 EXPECT_EQ(0, data[2]);
717 EXPECT_EQ(0, data[3]);
718 }
719
720
721 #if !defined(OS_NACL)
722 //----- FilePersistentMemoryAllocator ------------------------------------------
723
TEST(FilePersistentMemoryAllocatorTest,CreationTest)724 TEST(FilePersistentMemoryAllocatorTest, CreationTest) {
725 ScopedTempDir temp_dir;
726 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
727 FilePath file_path = temp_dir.GetPath().AppendASCII("persistent_memory");
728
729 PersistentMemoryAllocator::MemoryInfo meminfo1;
730 Reference r123, r456, r789;
731 {
732 LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
733 EXPECT_FALSE(local.IsReadonly());
734 r123 = local.Allocate(123, 123);
735 r456 = local.Allocate(456, 456);
736 r789 = local.Allocate(789, 789);
737 local.MakeIterable(r123);
738 local.ChangeType(r456, 654, 456, false);
739 local.MakeIterable(r789);
740 local.GetMemoryInfo(&meminfo1);
741 EXPECT_FALSE(local.IsFull());
742 EXPECT_FALSE(local.IsCorrupt());
743
744 File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
745 ASSERT_TRUE(writer.IsValid());
746 writer.Write(0, (const char*)local.data(), local.used());
747 }
748
749 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
750 mmfile->Initialize(file_path);
751 EXPECT_TRUE(mmfile->IsValid());
752 const size_t mmlength = mmfile->length();
753 EXPECT_GE(meminfo1.total, mmlength);
754
755 FilePersistentMemoryAllocator file(std::move(mmfile), 0, 0, "", false);
756 EXPECT_FALSE(file.IsReadonly());
757 EXPECT_EQ(TEST_ID, file.Id());
758 EXPECT_FALSE(file.IsFull());
759 EXPECT_FALSE(file.IsCorrupt());
760
761 PersistentMemoryAllocator::Iterator iter(&file);
762 uint32_t type;
763 EXPECT_EQ(r123, iter.GetNext(&type));
764 EXPECT_EQ(r789, iter.GetNext(&type));
765 EXPECT_EQ(0U, iter.GetNext(&type));
766
767 EXPECT_EQ(123U, file.GetType(r123));
768 EXPECT_EQ(654U, file.GetType(r456));
769 EXPECT_EQ(789U, file.GetType(r789));
770
771 PersistentMemoryAllocator::MemoryInfo meminfo2;
772 file.GetMemoryInfo(&meminfo2);
773 EXPECT_GE(meminfo1.total, meminfo2.total);
774 EXPECT_GE(meminfo1.free, meminfo2.free);
775 EXPECT_EQ(mmlength, meminfo2.total);
776 EXPECT_EQ(0U, meminfo2.free);
777
778 // There's no way of knowing if Flush actually does anything but at least
779 // verify that it runs without CHECK violations.
780 file.Flush(false);
781 file.Flush(true);
782 }
783
TEST(FilePersistentMemoryAllocatorTest,ExtendTest)784 TEST(FilePersistentMemoryAllocatorTest, ExtendTest) {
785 ScopedTempDir temp_dir;
786 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
787 FilePath file_path = temp_dir.GetPath().AppendASCII("extend_test");
788 MemoryMappedFile::Region region = {0, 16 << 10}; // 16KiB maximum size.
789
790 // Start with a small but valid file of persistent data.
791 ASSERT_FALSE(PathExists(file_path));
792 {
793 LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
794 local.Allocate(1, 1);
795 local.Allocate(11, 11);
796
797 File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
798 ASSERT_TRUE(writer.IsValid());
799 writer.Write(0, (const char*)local.data(), local.used());
800 }
801 ASSERT_TRUE(PathExists(file_path));
802 int64_t before_size;
803 ASSERT_TRUE(GetFileSize(file_path, &before_size));
804
805 // Map it as an extendable read/write file and append to it.
806 {
807 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
808 mmfile->Initialize(
809 File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE),
810 region, MemoryMappedFile::READ_WRITE_EXTEND);
811 FilePersistentMemoryAllocator allocator(std::move(mmfile), region.size, 0,
812 "", false);
813 EXPECT_EQ(static_cast<size_t>(before_size), allocator.used());
814
815 allocator.Allocate(111, 111);
816 EXPECT_LT(static_cast<size_t>(before_size), allocator.used());
817 }
818
819 // Validate that append worked.
820 int64_t after_size;
821 ASSERT_TRUE(GetFileSize(file_path, &after_size));
822 EXPECT_LT(before_size, after_size);
823
824 // Verify that it's still an acceptable file.
825 {
826 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
827 mmfile->Initialize(
828 File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE),
829 region, MemoryMappedFile::READ_WRITE_EXTEND);
830 EXPECT_TRUE(FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true));
831 EXPECT_TRUE(
832 FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, false));
833 }
834 }
835
TEST(FilePersistentMemoryAllocatorTest,AcceptableTest)836 TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) {
837 const uint32_t kAllocAlignment =
838 PersistentMemoryAllocatorTest::GetAllocAlignment();
839 ScopedTempDir temp_dir;
840 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
841
842 LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
843 local.MakeIterable(local.Allocate(1, 1));
844 local.MakeIterable(local.Allocate(11, 11));
845 const size_t minsize = local.used();
846 std::unique_ptr<char[]> garbage(new char[minsize]);
847 RandBytes(garbage.get(), minsize);
848
849 std::unique_ptr<MemoryMappedFile> mmfile;
850 char filename[100];
851 for (size_t filesize = minsize; filesize > 0; --filesize) {
852 strings::SafeSPrintf(filename, "memory_%d_A", filesize);
853 FilePath file_path = temp_dir.GetPath().AppendASCII(filename);
854 ASSERT_FALSE(PathExists(file_path));
855 {
856 File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
857 ASSERT_TRUE(writer.IsValid());
858 writer.Write(0, (const char*)local.data(), filesize);
859 }
860 ASSERT_TRUE(PathExists(file_path));
861
862 // Request read/write access for some sizes that are a multple of the
863 // allocator's alignment size. The allocator is strict about file size
864 // being a multiple of its internal alignment when doing read/write access.
865 const bool read_only = (filesize % (2 * kAllocAlignment)) != 0;
866 const uint32_t file_flags =
867 File::FLAG_OPEN | File::FLAG_READ | (read_only ? 0 : File::FLAG_WRITE);
868 const MemoryMappedFile::Access map_access =
869 read_only ? MemoryMappedFile::READ_ONLY : MemoryMappedFile::READ_WRITE;
870
871 mmfile.reset(new MemoryMappedFile());
872 mmfile->Initialize(File(file_path, file_flags), map_access);
873 EXPECT_EQ(filesize, mmfile->length());
874 if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) {
875 // Make sure construction doesn't crash. It will, however, cause
876 // error messages warning about about a corrupted memory segment.
877 FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
878 read_only);
879 // Also make sure that iteration doesn't crash.
880 PersistentMemoryAllocator::Iterator iter(&allocator);
881 uint32_t type_id;
882 Reference ref;
883 while ((ref = iter.GetNext(&type_id)) != 0) {
884 const char* data = allocator.GetAsArray<char>(
885 ref, 0, PersistentMemoryAllocator::kSizeAny);
886 uint32_t type = allocator.GetType(ref);
887 size_t size = allocator.GetAllocSize(ref);
888 // Ensure compiler can't optimize-out above variables.
889 (void)data;
890 (void)type;
891 (void)size;
892 }
893
894 // Ensure that short files are detected as corrupt and full files are not.
895 EXPECT_EQ(filesize != minsize, allocator.IsCorrupt());
896 } else {
897 // For filesize >= minsize, the file must be acceptable. This
898 // else clause (file-not-acceptable) should be reached only if
899 // filesize < minsize.
900 EXPECT_LT(filesize, minsize);
901 }
902
903 strings::SafeSPrintf(filename, "memory_%d_B", filesize);
904 file_path = temp_dir.GetPath().AppendASCII(filename);
905 ASSERT_FALSE(PathExists(file_path));
906 {
907 File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
908 ASSERT_TRUE(writer.IsValid());
909 writer.Write(0, (const char*)garbage.get(), filesize);
910 }
911 ASSERT_TRUE(PathExists(file_path));
912
913 mmfile.reset(new MemoryMappedFile());
914 mmfile->Initialize(File(file_path, file_flags), map_access);
915 EXPECT_EQ(filesize, mmfile->length());
916 if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) {
917 // Make sure construction doesn't crash. It will, however, cause
918 // error messages warning about about a corrupted memory segment.
919 FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
920 read_only);
921 EXPECT_TRUE(allocator.IsCorrupt()); // Garbage data so it should be.
922 } else {
923 // For filesize >= minsize, the file must be acceptable. This
924 // else clause (file-not-acceptable) should be reached only if
925 // filesize < minsize.
926 EXPECT_GT(minsize, filesize);
927 }
928 }
929 }
930
TEST_F(PersistentMemoryAllocatorTest,TruncateTest)931 TEST_F(PersistentMemoryAllocatorTest, TruncateTest) {
932 ScopedTempDir temp_dir;
933 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
934 FilePath file_path = temp_dir.GetPath().AppendASCII("truncate_test");
935
936 // Start with a small but valid file of persistent data. Keep the "used"
937 // amount for both allocations.
938 Reference a1_ref;
939 Reference a2_ref;
940 size_t a1_used;
941 size_t a2_used;
942 ASSERT_FALSE(PathExists(file_path));
943 {
944 LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, TEST_ID, "");
945 a1_ref = allocator.Allocate(100 << 10, 1);
946 allocator.MakeIterable(a1_ref);
947 a1_used = allocator.used();
948 a2_ref = allocator.Allocate(200 << 10, 11);
949 allocator.MakeIterable(a2_ref);
950 a2_used = allocator.used();
951
952 File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
953 ASSERT_TRUE(writer.IsValid());
954 writer.Write(0, static_cast<const char*>(allocator.data()),
955 allocator.size());
956 }
957 ASSERT_TRUE(PathExists(file_path));
958 EXPECT_LE(a1_used, a2_ref);
959
960 // Truncate the file to include everything and make sure it can be read, both
961 // with read-write and read-only access.
962 for (size_t file_length : {a2_used, a1_used, a1_used / 2}) {
963 SCOPED_TRACE(StringPrintf("file_length=%zu", file_length));
964 SetFileLength(file_path, file_length);
965
966 for (bool read_only : {false, true}) {
967 SCOPED_TRACE(StringPrintf("read_only=%s", read_only ? "true" : "false"));
968
969 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
970 mmfile->Initialize(
971 File(file_path, File::FLAG_OPEN |
972 (read_only ? File::FLAG_READ
973 : File::FLAG_READ | File::FLAG_WRITE)),
974 read_only ? MemoryMappedFile::READ_ONLY
975 : MemoryMappedFile::READ_WRITE);
976 ASSERT_TRUE(
977 FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only));
978
979 FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
980 read_only);
981
982 PersistentMemoryAllocator::Iterator iter(&allocator);
983 uint32_t type_id;
984 EXPECT_EQ(file_length >= a1_used ? a1_ref : 0U, iter.GetNext(&type_id));
985 EXPECT_EQ(file_length >= a2_used ? a2_ref : 0U, iter.GetNext(&type_id));
986 EXPECT_EQ(0U, iter.GetNext(&type_id));
987
988 // Ensure that short files are detected as corrupt and full files are not.
989 EXPECT_EQ(file_length < a2_used, allocator.IsCorrupt());
990 }
991
992 // Ensure that file length was not adjusted.
993 int64_t actual_length;
994 ASSERT_TRUE(GetFileSize(file_path, &actual_length));
995 EXPECT_EQ(file_length, static_cast<size_t>(actual_length));
996 }
997 }
998
999 #endif // !defined(OS_NACL)
1000
1001 } // namespace base
1002