1 // Copyright 2014 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 <fcntl.h>
6 #include <stdint.h>
7
8 #include "base/files/scoped_file.h"
9 #include "base/memory/discardable_shared_memory.h"
10 #include "base/memory/shared_memory_tracker.h"
11 #include "base/process/process_metrics.h"
12 #include "base/trace_event/memory_allocator_dump.h"
13 #include "base/trace_event/process_memory_dump.h"
14 #include "build/build_config.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace base {
18
19 class TestDiscardableSharedMemory : public DiscardableSharedMemory {
20 public:
21 TestDiscardableSharedMemory() = default;
22
TestDiscardableSharedMemory(UnsafeSharedMemoryRegion region)23 explicit TestDiscardableSharedMemory(UnsafeSharedMemoryRegion region)
24 : DiscardableSharedMemory(std::move(region)) {}
25
SetNow(Time now)26 void SetNow(Time now) { now_ = now; }
27
28 private:
29 // Overriden from DiscardableSharedMemory:
Now() const30 Time Now() const override { return now_; }
31
32 Time now_;
33 };
34
TEST(DiscardableSharedMemoryTest,CreateAndMap)35 TEST(DiscardableSharedMemoryTest, CreateAndMap) {
36 const uint32_t kDataSize = 1024;
37
38 TestDiscardableSharedMemory memory;
39 bool rv = memory.CreateAndMap(kDataSize);
40 ASSERT_TRUE(rv);
41 EXPECT_GE(memory.mapped_size(), kDataSize);
42 EXPECT_TRUE(memory.IsMemoryLocked());
43 }
44
TEST(DiscardableSharedMemoryTest,CreateFromHandle)45 TEST(DiscardableSharedMemoryTest, CreateFromHandle) {
46 const uint32_t kDataSize = 1024;
47
48 TestDiscardableSharedMemory memory1;
49 bool rv = memory1.CreateAndMap(kDataSize);
50 ASSERT_TRUE(rv);
51
52 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
53 ASSERT_TRUE(shared_region.IsValid());
54
55 TestDiscardableSharedMemory memory2(std::move(shared_region));
56 rv = memory2.Map(kDataSize);
57 ASSERT_TRUE(rv);
58 EXPECT_TRUE(memory2.IsMemoryLocked());
59 }
60
TEST(DiscardableSharedMemoryTest,LockAndUnlock)61 TEST(DiscardableSharedMemoryTest, LockAndUnlock) {
62 const uint32_t kDataSize = 1024;
63
64 TestDiscardableSharedMemory memory1;
65 bool rv = memory1.CreateAndMap(kDataSize);
66 ASSERT_TRUE(rv);
67
68 // Memory is initially locked. Unlock it.
69 memory1.SetNow(Time::FromDoubleT(1));
70 memory1.Unlock(0, 0);
71 EXPECT_FALSE(memory1.IsMemoryLocked());
72
73 // Lock and unlock memory.
74 DiscardableSharedMemory::LockResult lock_rv = memory1.Lock(0, 0);
75 EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
76 memory1.SetNow(Time::FromDoubleT(2));
77 memory1.Unlock(0, 0);
78
79 // Lock again before duplicating and passing ownership to new instance.
80 lock_rv = memory1.Lock(0, 0);
81 EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
82 EXPECT_TRUE(memory1.IsMemoryLocked());
83
84 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
85 ASSERT_TRUE(shared_region.IsValid());
86
87 TestDiscardableSharedMemory memory2(std::move(shared_region));
88 rv = memory2.Map(kDataSize);
89 ASSERT_TRUE(rv);
90
91 // Unlock second instance.
92 memory2.SetNow(Time::FromDoubleT(3));
93 memory2.Unlock(0, 0);
94
95 // Both memory instances should be unlocked now.
96 EXPECT_FALSE(memory2.IsMemoryLocked());
97 EXPECT_FALSE(memory1.IsMemoryLocked());
98
99 // Lock second instance before passing ownership back to first instance.
100 lock_rv = memory2.Lock(0, 0);
101 EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
102
103 // Memory should still be resident and locked.
104 rv = memory1.IsMemoryResident();
105 EXPECT_TRUE(rv);
106 EXPECT_TRUE(memory1.IsMemoryLocked());
107
108 // Unlock first instance.
109 memory1.SetNow(Time::FromDoubleT(4));
110 memory1.Unlock(0, 0);
111 }
112
TEST(DiscardableSharedMemoryTest,Purge)113 TEST(DiscardableSharedMemoryTest, Purge) {
114 const uint32_t kDataSize = 1024;
115
116 TestDiscardableSharedMemory memory1;
117 bool rv = memory1.CreateAndMap(kDataSize);
118 ASSERT_TRUE(rv);
119
120 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
121 ASSERT_TRUE(shared_region.IsValid());
122
123 TestDiscardableSharedMemory memory2(std::move(shared_region));
124 rv = memory2.Map(kDataSize);
125 ASSERT_TRUE(rv);
126
127 // This should fail as memory is locked.
128 rv = memory1.Purge(Time::FromDoubleT(1));
129 EXPECT_FALSE(rv);
130
131 memory2.SetNow(Time::FromDoubleT(2));
132 memory2.Unlock(0, 0);
133
134 ASSERT_TRUE(memory2.IsMemoryResident());
135
136 // Memory is unlocked, but our usage timestamp is incorrect.
137 rv = memory1.Purge(Time::FromDoubleT(3));
138 EXPECT_FALSE(rv);
139
140 ASSERT_TRUE(memory2.IsMemoryResident());
141
142 // Memory is unlocked and our usage timestamp should be correct.
143 rv = memory1.Purge(Time::FromDoubleT(4));
144 EXPECT_TRUE(rv);
145
146 // Lock should fail as memory has been purged.
147 DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
148 EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
149
150 ASSERT_FALSE(memory2.IsMemoryResident());
151 }
152
TEST(DiscardableSharedMemoryTest,LastUsed)153 TEST(DiscardableSharedMemoryTest, LastUsed) {
154 const uint32_t kDataSize = 1024;
155
156 TestDiscardableSharedMemory memory1;
157 bool rv = memory1.CreateAndMap(kDataSize);
158 ASSERT_TRUE(rv);
159
160 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
161 ASSERT_TRUE(shared_region.IsValid());
162
163 TestDiscardableSharedMemory memory2(std::move(shared_region));
164 rv = memory2.Map(kDataSize);
165 ASSERT_TRUE(rv);
166
167 memory2.SetNow(Time::FromDoubleT(1));
168 memory2.Unlock(0, 0);
169
170 EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(1));
171
172 DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
173 EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
174
175 // This should fail as memory is locked.
176 rv = memory1.Purge(Time::FromDoubleT(2));
177 ASSERT_FALSE(rv);
178
179 // Last usage should have been updated to timestamp passed to Purge above.
180 EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2));
181
182 memory2.SetNow(Time::FromDoubleT(3));
183 memory2.Unlock(0, 0);
184
185 // Usage time should be correct for |memory2| instance.
186 EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(3));
187
188 // However, usage time has not changed as far as |memory1| instance knows.
189 EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2));
190
191 // Memory is unlocked, but our usage timestamp is incorrect.
192 rv = memory1.Purge(Time::FromDoubleT(4));
193 EXPECT_FALSE(rv);
194
195 // The failed purge attempt should have updated usage time to the correct
196 // value.
197 EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(3));
198
199 // Purge memory through |memory2| instance. The last usage time should be
200 // set to 0 as a result of this.
201 rv = memory2.Purge(Time::FromDoubleT(5));
202 EXPECT_TRUE(rv);
203 EXPECT_TRUE(memory2.last_known_usage().is_null());
204
205 // This should fail as memory has already been purged and |memory1|'s usage
206 // time is incorrect as a result.
207 rv = memory1.Purge(Time::FromDoubleT(6));
208 EXPECT_FALSE(rv);
209
210 // The failed purge attempt should have updated usage time to the correct
211 // value.
212 EXPECT_TRUE(memory1.last_known_usage().is_null());
213
214 // Purge should succeed now that usage time is correct.
215 rv = memory1.Purge(Time::FromDoubleT(7));
216 EXPECT_TRUE(rv);
217 }
218
TEST(DiscardableSharedMemoryTest,LockShouldAlwaysFailAfterSuccessfulPurge)219 TEST(DiscardableSharedMemoryTest, LockShouldAlwaysFailAfterSuccessfulPurge) {
220 const uint32_t kDataSize = 1024;
221
222 TestDiscardableSharedMemory memory1;
223 bool rv = memory1.CreateAndMap(kDataSize);
224 ASSERT_TRUE(rv);
225
226 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
227 ASSERT_TRUE(shared_region.IsValid());
228
229 TestDiscardableSharedMemory memory2(std::move(shared_region));
230 rv = memory2.Map(kDataSize);
231 ASSERT_TRUE(rv);
232
233 memory2.SetNow(Time::FromDoubleT(1));
234 memory2.Unlock(0, 0);
235
236 rv = memory2.Purge(Time::FromDoubleT(2));
237 EXPECT_TRUE(rv);
238
239 // Lock should fail as memory has been purged.
240 DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
241 EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
242 }
243
244 #if defined(OS_ANDROID)
TEST(DiscardableSharedMemoryTest,LockShouldFailIfPlatformLockPagesFails)245 TEST(DiscardableSharedMemoryTest, LockShouldFailIfPlatformLockPagesFails) {
246 const uint32_t kDataSize = 1024;
247
248 DiscardableSharedMemory memory1;
249 bool rv1 = memory1.CreateAndMap(kDataSize);
250 ASSERT_TRUE(rv1);
251
252 base::UnsafeSharedMemoryRegion region = memory1.DuplicateRegion();
253 int fd = region.GetPlatformHandle();
254 DiscardableSharedMemory memory2(std::move(region));
255 bool rv2 = memory2.Map(kDataSize);
256 ASSERT_TRUE(rv2);
257
258 // Unlock() the first page of memory, so we can test Lock()ing it.
259 memory2.Unlock(0, base::GetPageSize());
260 // To cause ashmem_pin_region() to fail, we arrange for it to be called with
261 // an invalid file-descriptor, which requires a valid-looking fd (i.e. we
262 // can't just Close() |memory|), but one on which the operation is invalid.
263 // We can overwrite the |memory| fd with a handle to a different file using
264 // dup2(), which has the nice properties that |memory| still has a valid fd
265 // that it can close, etc without errors, but on which ashmem_pin_region()
266 // will fail.
267 base::ScopedFD null(open("/dev/null", O_RDONLY));
268 ASSERT_EQ(fd, dup2(null.get(), fd));
269
270 // Now re-Lock()ing the first page should fail.
271 DiscardableSharedMemory::LockResult lock_rv =
272 memory2.Lock(0, base::GetPageSize());
273 EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
274 }
275 #endif // defined(OS_ANDROID)
276
TEST(DiscardableSharedMemoryTest,LockAndUnlockRange)277 TEST(DiscardableSharedMemoryTest, LockAndUnlockRange) {
278 const uint32_t kDataSize = 32;
279
280 uint32_t data_size_in_bytes = kDataSize * base::GetPageSize();
281
282 TestDiscardableSharedMemory memory1;
283 bool rv = memory1.CreateAndMap(data_size_in_bytes);
284 ASSERT_TRUE(rv);
285
286 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
287 ASSERT_TRUE(shared_region.IsValid());
288
289 TestDiscardableSharedMemory memory2(std::move(shared_region));
290 rv = memory2.Map(data_size_in_bytes);
291 ASSERT_TRUE(rv);
292
293 // Unlock first page.
294 memory2.SetNow(Time::FromDoubleT(1));
295 memory2.Unlock(0, base::GetPageSize());
296
297 rv = memory1.Purge(Time::FromDoubleT(2));
298 EXPECT_FALSE(rv);
299
300 // Lock first page again.
301 memory2.SetNow(Time::FromDoubleT(3));
302 DiscardableSharedMemory::LockResult lock_rv =
303 memory2.Lock(0, base::GetPageSize());
304 EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv);
305
306 // Unlock first page.
307 memory2.SetNow(Time::FromDoubleT(4));
308 memory2.Unlock(0, base::GetPageSize());
309
310 rv = memory1.Purge(Time::FromDoubleT(5));
311 EXPECT_FALSE(rv);
312
313 // Unlock second page.
314 memory2.SetNow(Time::FromDoubleT(6));
315 memory2.Unlock(base::GetPageSize(), base::GetPageSize());
316
317 rv = memory1.Purge(Time::FromDoubleT(7));
318 EXPECT_FALSE(rv);
319
320 // Unlock anything onwards.
321 memory2.SetNow(Time::FromDoubleT(8));
322 memory2.Unlock(2 * base::GetPageSize(), 0);
323
324 // Memory is unlocked, but our usage timestamp is incorrect.
325 rv = memory1.Purge(Time::FromDoubleT(9));
326 EXPECT_FALSE(rv);
327
328 // The failed purge attempt should have updated usage time to the correct
329 // value.
330 EXPECT_EQ(Time::FromDoubleT(8), memory1.last_known_usage());
331
332 // Purge should now succeed.
333 rv = memory1.Purge(Time::FromDoubleT(10));
334 EXPECT_TRUE(rv);
335 }
336
TEST(DiscardableSharedMemoryTest,MappedSize)337 TEST(DiscardableSharedMemoryTest, MappedSize) {
338 const uint32_t kDataSize = 1024;
339
340 TestDiscardableSharedMemory memory;
341 bool rv = memory.CreateAndMap(kDataSize);
342 ASSERT_TRUE(rv);
343
344 EXPECT_LE(kDataSize, memory.mapped_size());
345
346 // Mapped size should be 0 after memory segment has been unmapped.
347 rv = memory.Unmap();
348 EXPECT_TRUE(rv);
349 EXPECT_EQ(0u, memory.mapped_size());
350 }
351
TEST(DiscardableSharedMemoryTest,Close)352 TEST(DiscardableSharedMemoryTest, Close) {
353 const uint32_t kDataSize = 1024;
354
355 TestDiscardableSharedMemory memory;
356 bool rv = memory.CreateAndMap(kDataSize);
357 ASSERT_TRUE(rv);
358
359 // Mapped size should be unchanged after memory segment has been closed.
360 memory.Close();
361 EXPECT_LE(kDataSize, memory.mapped_size());
362
363 // Memory is initially locked. Unlock it.
364 memory.SetNow(Time::FromDoubleT(1));
365 memory.Unlock(0, 0);
366
367 // Lock and unlock memory.
368 DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0);
369 EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
370 memory.SetNow(Time::FromDoubleT(2));
371 memory.Unlock(0, 0);
372 }
373
TEST(DiscardableSharedMemoryTest,ZeroSize)374 TEST(DiscardableSharedMemoryTest, ZeroSize) {
375 TestDiscardableSharedMemory memory;
376 bool rv = memory.CreateAndMap(0);
377 ASSERT_TRUE(rv);
378
379 EXPECT_LE(0u, memory.mapped_size());
380
381 // Memory is initially locked. Unlock it.
382 memory.SetNow(Time::FromDoubleT(1));
383 memory.Unlock(0, 0);
384
385 // Lock and unlock memory.
386 DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0);
387 EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv);
388 memory.SetNow(Time::FromDoubleT(2));
389 memory.Unlock(0, 0);
390 }
391
392 // This test checks that zero-filled pages are returned after purging a segment
393 // when DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE is
394 // defined and MADV_REMOVE is supported.
395 #if defined(DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE)
TEST(DiscardableSharedMemoryTest,ZeroFilledPagesAfterPurge)396 TEST(DiscardableSharedMemoryTest, ZeroFilledPagesAfterPurge) {
397 const uint32_t kDataSize = 1024;
398
399 TestDiscardableSharedMemory memory1;
400 bool rv = memory1.CreateAndMap(kDataSize);
401 ASSERT_TRUE(rv);
402
403 UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
404 ASSERT_TRUE(shared_region.IsValid());
405
406 TestDiscardableSharedMemory memory2(std::move(shared_region));
407 rv = memory2.Map(kDataSize);
408 ASSERT_TRUE(rv);
409
410 // Initialize all memory to '0xaa'.
411 memset(memory2.memory(), 0xaa, kDataSize);
412
413 // Unlock memory.
414 memory2.SetNow(Time::FromDoubleT(1));
415 memory2.Unlock(0, 0);
416 EXPECT_FALSE(memory1.IsMemoryLocked());
417
418 // Memory is unlocked, but our usage timestamp is incorrect.
419 rv = memory1.Purge(Time::FromDoubleT(2));
420 EXPECT_FALSE(rv);
421 rv = memory1.Purge(Time::FromDoubleT(3));
422 EXPECT_TRUE(rv);
423
424 // Check that reading memory after it has been purged is returning
425 // zero-filled pages.
426 uint8_t expected_data[kDataSize] = {};
427 EXPECT_EQ(memcmp(memory2.memory(), expected_data, kDataSize), 0);
428 }
429 #endif
430
TEST(DiscardableSharedMemoryTest,TracingOwnershipEdges)431 TEST(DiscardableSharedMemoryTest, TracingOwnershipEdges) {
432 const uint32_t kDataSize = 1024;
433 TestDiscardableSharedMemory memory1;
434 bool rv = memory1.CreateAndMap(kDataSize);
435 ASSERT_TRUE(rv);
436
437 base::trace_event::MemoryDumpArgs args = {
438 base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
439 trace_event::ProcessMemoryDump pmd(args);
440 trace_event::MemoryAllocatorDump* client_dump =
441 pmd.CreateAllocatorDump("discardable_manager/map1");
442 const bool is_owned = false;
443 memory1.CreateSharedMemoryOwnershipEdge(client_dump, &pmd, is_owned);
444 const auto* shm_dump = pmd.GetAllocatorDump(
445 SharedMemoryTracker::GetDumpNameForTracing(memory1.mapped_id()));
446 EXPECT_TRUE(shm_dump);
447 EXPECT_EQ(shm_dump->GetSizeInternal(), client_dump->GetSizeInternal());
448 const auto edges = pmd.allocator_dumps_edges();
449 EXPECT_EQ(2u, edges.size());
450 EXPECT_NE(edges.end(), edges.find(shm_dump->guid()));
451 EXPECT_NE(edges.end(), edges.find(client_dump->guid()));
452 // TODO(ssid): test for weak global dump once the
453 // CreateWeakSharedMemoryOwnershipEdge() is fixed, crbug.com/661257.
454 }
455
456 } // namespace base
457