• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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