discardable_shared_memory_unittest.cc 15 KB

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