memory_reclaimer_unittest.cc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright 2019 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 "base/allocator/partition_allocator/memory_reclaimer.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h"
  8. #include "base/allocator/partition_allocator/partition_alloc.h"
  9. #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
  10. #include "base/allocator/partition_allocator/partition_alloc_base/logging.h"
  11. #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
  12. #include "base/allocator/partition_allocator/partition_alloc_config.h"
  13. #include "build/build_config.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
  16. defined(PA_THREAD_CACHE_SUPPORTED)
  17. #include "base/allocator/partition_allocator/thread_cache.h"
  18. #endif
  19. // Otherwise, PartitionAlloc doesn't allocate any memory, and the tests are
  20. // meaningless.
  21. #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
  22. namespace partition_alloc {
  23. namespace {
  24. void HandleOOM(size_t unused_size) {
  25. PA_LOG(FATAL) << "Out of memory";
  26. }
  27. } // namespace
  28. class MemoryReclaimerTest : public ::testing::Test {
  29. public:
  30. MemoryReclaimerTest() = default;
  31. protected:
  32. void SetUp() override {
  33. PartitionAllocGlobalInit(HandleOOM);
  34. MemoryReclaimer::Instance()->ResetForTesting();
  35. allocator_ = std::make_unique<PartitionAllocator>();
  36. allocator_->init({
  37. PartitionOptions::AlignedAlloc::kDisallowed,
  38. PartitionOptions::ThreadCache::kDisabled,
  39. PartitionOptions::Quarantine::kAllowed,
  40. PartitionOptions::Cookie::kAllowed,
  41. PartitionOptions::BackupRefPtr::kDisabled,
  42. PartitionOptions::BackupRefPtrZapping::kDisabled,
  43. PartitionOptions::UseConfigurablePool::kNo,
  44. });
  45. allocator_->root()->UncapEmptySlotSpanMemoryForTesting();
  46. }
  47. void TearDown() override {
  48. allocator_ = nullptr;
  49. MemoryReclaimer::Instance()->ResetForTesting();
  50. PartitionAllocGlobalUninitForTesting();
  51. }
  52. void Reclaim() { MemoryReclaimer::Instance()->ReclaimNormal(); }
  53. void AllocateAndFree() {
  54. void* data = allocator_->root()->Alloc(1, "");
  55. allocator_->root()->Free(data);
  56. }
  57. std::unique_ptr<PartitionAllocator> allocator_;
  58. };
  59. TEST_F(MemoryReclaimerTest, FreesMemory) {
  60. PartitionRoot<internal::ThreadSafe>* root = allocator_->root();
  61. size_t committed_initially = root->get_total_size_of_committed_pages();
  62. AllocateAndFree();
  63. size_t committed_before = root->get_total_size_of_committed_pages();
  64. EXPECT_GT(committed_before, committed_initially);
  65. Reclaim();
  66. size_t committed_after = root->get_total_size_of_committed_pages();
  67. EXPECT_LT(committed_after, committed_before);
  68. EXPECT_LE(committed_initially, committed_after);
  69. }
  70. TEST_F(MemoryReclaimerTest, Reclaim) {
  71. PartitionRoot<internal::ThreadSafe>* root = allocator_->root();
  72. size_t committed_initially = root->get_total_size_of_committed_pages();
  73. {
  74. AllocateAndFree();
  75. size_t committed_before = root->get_total_size_of_committed_pages();
  76. EXPECT_GT(committed_before, committed_initially);
  77. MemoryReclaimer::Instance()->ReclaimAll();
  78. size_t committed_after = root->get_total_size_of_committed_pages();
  79. EXPECT_LT(committed_after, committed_before);
  80. EXPECT_LE(committed_initially, committed_after);
  81. }
  82. }
  83. #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
  84. defined(PA_THREAD_CACHE_SUPPORTED)
  85. namespace {
  86. // malloc() / free() pairs can be removed by the compiler, this is enough (for
  87. // now) to prevent that.
  88. PA_NOINLINE void FreeForTest(void* data) {
  89. free(data);
  90. }
  91. } // namespace
  92. TEST_F(MemoryReclaimerTest, DoNotAlwaysPurgeThreadCache) {
  93. // Make sure the thread cache is enabled in the main partition.
  94. base::internal::PartitionAllocMalloc::Allocator()
  95. ->EnableThreadCacheIfSupported();
  96. for (size_t i = 0; i < ThreadCache::kDefaultSizeThreshold; i++) {
  97. void* data = malloc(i);
  98. FreeForTest(data);
  99. }
  100. auto* tcache = ThreadCache::Get();
  101. ASSERT_TRUE(tcache);
  102. size_t cached_size = tcache->CachedMemory();
  103. Reclaim();
  104. // No thread cache purging during periodic purge, but with ReclaimAll().
  105. //
  106. // Cannot assert on the exact size of the thread cache, since it can shrink
  107. // when a buffer is overfull, and this may happen through other malloc()
  108. // allocations in the test harness.
  109. EXPECT_GT(tcache->CachedMemory(), cached_size / 2);
  110. Reclaim();
  111. EXPECT_GT(tcache->CachedMemory(), cached_size / 2);
  112. MemoryReclaimer::Instance()->ReclaimAll();
  113. EXPECT_LT(tcache->CachedMemory(), cached_size / 2);
  114. }
  115. #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
  116. // defined(PA_THREAD_CACHE_SUPPORTED)
  117. } // namespace partition_alloc
  118. #endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)