| // Copyright 2021 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/allocator/partition_alloc_support.h" | |
| #include <array> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "base/allocator/partition_alloc_features.h" | |
| #include "base/command_line.h" | |
| #include "base/memory/memory_pressure_listener_registry.h" | |
| #include "base/memory/mock_memory_pressure_listener.h" | |
| #include "base/memory_coordinator/memory_pressure_source.h" | |
| #include "base/run_loop.h" | |
| #include "base/test/gtest_util.h" | |
| #include "base/test/scoped_command_line.h" | |
| #include "base/test/scoped_feature_list.h" | |
| #include "base/test/task_environment.h" | |
| #include "build/build_config.h" | |
| #include "partition_alloc/address_pool_manager.h" | |
| #include "partition_alloc/buildflags.h" | |
| #include "partition_alloc/dangling_raw_ptr_checks.h" | |
| #include "partition_alloc/page_allocator.h" | |
| #include "partition_alloc/partition_alloc_base/cpu.h" | |
| #include "partition_alloc/partition_alloc_constants.h" | |
| #include "testing/gmock/include/gmock/gmock.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #if BUILDFLAG(IS_IOS) | |
| #include "base/ios/ios_util.h" | |
| #endif | |
| namespace base::allocator { | |
| using testing::AllOf; | |
| using testing::HasSubstr; | |
| TEST(PartitionAllocSupportTest, | |
| ProposeSyntheticFinchTrials_DanglingPointerDetector) { | |
| std::string dpd_group = | |
| ProposeSyntheticFinchTrials()["DanglingPointerDetector"]; | |
| #if PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) | |
| EXPECT_EQ(dpd_group, "Enabled"); | |
| #else | |
| EXPECT_EQ(dpd_group, "Disabled"); | |
| #endif | |
| } | |
| // - Death tests misbehave on Android, http://crbug.com/643760. | |
| #if PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && !BUILDFLAG(IS_ANDROID) && \ | |
| defined(GTEST_HAS_DEATH_TEST) | |
| namespace { | |
| // Install dangling raw_ptr handler and restore them when going out of scope. | |
| class ScopedInstallDanglingRawPtrChecks { | |
| public: | |
| struct ConstructorParams { | |
| std::string mode = "crash"; | |
| std::string type = "all"; | |
| }; | |
| explicit ScopedInstallDanglingRawPtrChecks(ConstructorParams params) { | |
| enabled_feature_list_.InitWithFeaturesAndParameters( | |
| {{features::kPartitionAllocDanglingPtr, | |
| {{"mode", params.mode}, {"type", params.type}}}}, | |
| {/* disabled_features */}); | |
| old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn(); | |
| old_dereferenced_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn(); | |
| InstallDanglingRawPtrChecks(); | |
| } | |
| ScopedInstallDanglingRawPtrChecks() | |
| : ScopedInstallDanglingRawPtrChecks(ConstructorParams{}) {} | |
| ~ScopedInstallDanglingRawPtrChecks() { | |
| InstallDanglingRawPtrChecks(); // Check for leaks. | |
| partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_); | |
| partition_alloc::SetDanglingRawPtrReleasedFn(old_dereferenced_fn_); | |
| } | |
| private: | |
| test::ScopedFeatureList enabled_feature_list_; | |
| partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_; | |
| partition_alloc::DanglingRawPtrReleasedFn* old_dereferenced_fn_; | |
| }; | |
| } // namespace | |
| TEST(PartitionAllocDanglingPtrChecks, Basic) { | |
| EXPECT_DEATH( | |
| { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(42); | |
| }, | |
| AllOf(HasSubstr("[DanglingSignature]\t"), | |
| HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."), | |
| HasSubstr("[DanglingPtr](2/3) First, the memory was freed at:"), | |
| HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was " | |
| "released at:"))); | |
| } | |
| // The StackTrace buffer might run out of storage and not record where the | |
| // memory was freed. Anyway, it must still report the error. | |
| TEST(PartitionAllocDanglingPtrChecks, FreeNotRecorded) { | |
| EXPECT_DEATH( | |
| { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(42); | |
| }, | |
| AllOf(HasSubstr("[DanglingSignature]\tmissing\tmissing\t"), | |
| HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."), | |
| HasSubstr("[DanglingPtr](2/3) It was not recorded where the memory " | |
| "was freed."), | |
| HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was " | |
| "released at:"))); | |
| } | |
| // TODO(crbug.com/40260713): Check for leaked refcount on Android. | |
| #if BUILDFLAG(IS_ANDROID) | |
| // Some raw_ptr might never release their refcount. Make sure this cause a | |
| // crash on exit. | |
| TEST(PartitionAllocDanglingPtrChecks, ReleaseNotRecorded) { | |
| EXPECT_DEATH( | |
| { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| }, | |
| HasSubstr("A freed allocation is still referenced by a dangling pointer " | |
| "at exit, or at test end. Leaked raw_ptr/raw_ref " | |
| "could cause PartitionAlloc's quarantine memory bloat." | |
| "\n\n" | |
| "Memory was released on:")); | |
| } | |
| #endif | |
| // Getting the same allocation reported twice in a row, without matching | |
| // `DanglingRawPtrReleased` in between is unexpected. Make sure this kind of | |
| // potential regression would be detected. | |
| TEST(PartitionAllocDanglingPtrChecks, DoubleDetection) { | |
| EXPECT_DCHECK_DEATH_WITH( | |
| { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| }, | |
| "Check failed: !entry \\|\\| entry->id != id"); | |
| } | |
| // Free and release from two different tasks with cross task dangling pointer | |
| // detection enabled. | |
| TEST(PartitionAllocDanglingPtrChecks, CrossTask) { | |
| BASE_EXPECT_DEATH( | |
| { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ | |
| .type = "cross_task", | |
| }); | |
| base::test::TaskEnvironment task_environment; | |
| task_environment.GetMainThreadTaskRunner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(partition_alloc::GetDanglingRawPtrDetectedFn(), 42)); | |
| task_environment.GetMainThreadTaskRunner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42)); | |
| task_environment.RunUntilIdle(); | |
| }, | |
| AllOf(HasSubstr("[DanglingSignature]\t"), | |
| HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."), | |
| HasSubstr("[DanglingPtr](2/3) First, the memory was freed at:"), | |
| HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was " | |
| "released at:"))); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoredFailuresClearsCache) { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ | |
| .type = "cross_task", | |
| }); | |
| base::test::TaskEnvironment task_environment; | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(42); | |
| task_environment.GetMainThreadTaskRunner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42)); | |
| task_environment.RunUntilIdle(); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresNoTask) { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ | |
| .type = "cross_task", | |
| }); | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(42); | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(42); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresSameTask) { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ | |
| .type = "cross_task", | |
| }); | |
| base::test::TaskEnvironment task_environment; | |
| task_environment.GetMainThreadTaskRunner()->PostTask( | |
| FROM_HERE, base::BindOnce([] { | |
| partition_alloc::GetDanglingRawPtrDetectedFn()(37); | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(37); | |
| })); | |
| task_environment.RunUntilIdle(); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, CrossTaskNoFreeConsideredCrossTask) { | |
| ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ | |
| .type = "cross_task", | |
| }); | |
| partition_alloc::GetDanglingRawPtrReleasedFn()(42); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, | |
| ExtractDanglingPtrSignatureMacStackTrace) { | |
| const std::string stack_trace_output = | |
| "0 lib_1 0x0000000115fdfa12 base::F1(**) + 18\r\n" | |
| "1 lib_1 0x0000000115ec0043 base::F2() + 19\r\n" | |
| "2 lib_1 0x000000011601fb01 " | |
| "allocator_shim::internal::PartitionFree(foo) + 13265\r\n" | |
| "3 lib_1 0x0000000114831027 base::F3(bar) + 42\r\n" | |
| "4 lib_2 0x00000001148eae35 base::F4() + 437\r\n"; | |
| EXPECT_EQ("base::F3(bar)", | |
| PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( | |
| stack_trace_output)); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, ExtractDanglingPtrSignatureMacTaskTrace) { | |
| const std::string task_trace_output = | |
| "Task trace:\r\n" | |
| "0 lib_1 0x00000001161fd431 base::F1() + 257\r\n" | |
| "1 lib_1 0x0000000115a49404 base::F2() + 68\r\n"; | |
| EXPECT_EQ("base::F1()", | |
| PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( | |
| task_trace_output)); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, | |
| ExtractDanglingPtrSignatureWindowsStackTrace) { | |
| const std::string stack_trace_output = | |
| "\tbase::F1 [0x055643C3+19] (o:\\base\\F1.cc:329)\r\n" | |
| "\tallocator_shim::internal::PartitionFree [0x0648F87B+5243] " | |
| "(o:\\path.cc:441)\r\n" | |
| "\t_free_base [0x0558475D+29] (o:\\file_path.cc:142)\r\n" | |
| "\tbase::F2 [0x04E5B317+23] (o:\\base\\F2.cc:91)\r\n" | |
| "\tbase::F3 [0x04897800+544] (o:\\base\\F3.cc:638)\r\n"; | |
| EXPECT_EQ("base::F2", | |
| PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( | |
| stack_trace_output)); | |
| } | |
| TEST(PartitionAllocDanglingPtrChecks, | |
| ExtractDanglingPtrSignatureWindowsTaskTrace) { | |
| const std::string task_trace_output = | |
| "Task trace:\r\n" | |
| "\tbase::F1 [0x049068A3+813] (o:\\base\\F1.cc:207)\r\n" | |
| "\tbase::F2 [0x0490614C+192] (o:\\base\\F2.cc:116)\r\n"; | |
| EXPECT_EQ("base::F1", | |
| PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( | |
| task_trace_output)); | |
| } | |
| #endif | |
| #if PA_BUILDFLAG(HAS_MEMORY_TAGGING) | |
| TEST(PartitionAllocSupportTest, | |
| ProposeSyntheticFinchTrials_MemoryTaggingDogfood) { | |
| { | |
| test::ScopedFeatureList scope; | |
| scope.InitWithFeatures({}, {features::kPartitionAllocMemoryTagging}); | |
| auto trials = ProposeSyntheticFinchTrials(); | |
| auto group_iter = trials.find("MemoryTaggingDogfood"); | |
| EXPECT_EQ(group_iter, trials.end()); | |
| } | |
| { | |
| test::ScopedFeatureList scope; | |
| scope.InitWithFeatures({features::kPartitionAllocMemoryTagging}, {}); | |
| auto trials = ProposeSyntheticFinchTrials(); | |
| std::string expectation = | |
| partition_alloc::internal::base::CPU::GetInstanceNoAllocation() | |
| .has_mte() | |
| ? "Enabled" | |
| : "Disabled"; | |
| auto group_iter = trials.find("MemoryTaggingDogfood"); | |
| EXPECT_NE(group_iter, trials.end()); | |
| EXPECT_EQ(group_iter->second, expectation); | |
| } | |
| } | |
| #endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) | |
| class MemoryReclaimerSupportTest : public ::testing::Test { | |
| public: | |
| void SetUp() override { | |
| MemoryReclaimerSupport::Instance().ResetForTesting(); | |
| } | |
| protected: | |
| test::ScopedFeatureList feature_list_; | |
| base::test::SingleThreadTaskEnvironment task_environment_{ | |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; | |
| }; | |
| TEST_F(MemoryReclaimerSupportTest, StartSeveralTimes) { | |
| test::ScopedFeatureList feature_list{ | |
| base::features::kPartitionAllocMemoryReclaimer}; | |
| auto& instance = MemoryReclaimerSupport::Instance(); | |
| EXPECT_FALSE(instance.has_pending_task_for_testing()); | |
| instance.Start(task_environment_.GetMainThreadTaskRunner()); | |
| instance.Start(task_environment_.GetMainThreadTaskRunner()); | |
| instance.Start(task_environment_.GetMainThreadTaskRunner()); | |
| // Only one task. | |
| EXPECT_TRUE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount()); | |
| } | |
| TEST_F(MemoryReclaimerSupportTest, ForegroundToBackground) { | |
| test::ScopedFeatureList feature_list{ | |
| base::features::kPartitionAllocMemoryReclaimer}; | |
| auto& instance = MemoryReclaimerSupport::Instance(); | |
| EXPECT_FALSE(instance.has_pending_task_for_testing()); | |
| instance.Start(task_environment_.GetMainThreadTaskRunner()); | |
| EXPECT_TRUE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount()); | |
| task_environment_.FastForwardBy( | |
| MemoryReclaimerSupport::kFirstPAPurgeOrReclaimDelay); | |
| // Task gets reposted. | |
| EXPECT_TRUE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount()); | |
| instance.SetForegrounded(false); | |
| task_environment_.FastForwardBy(MemoryReclaimerSupport::GetInterval()); | |
| // But not once in background. | |
| EXPECT_FALSE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(0u, task_environment_.GetPendingMainThreadTaskCount()); | |
| } | |
| TEST_F(MemoryReclaimerSupportTest, ForegroundToBackgroundAndBack) { | |
| test::ScopedFeatureList feature_list{ | |
| base::features::kPartitionAllocMemoryReclaimer}; | |
| auto& instance = MemoryReclaimerSupport::Instance(); | |
| instance.Start(task_environment_.GetMainThreadTaskRunner()); | |
| task_environment_.FastForwardBy( | |
| MemoryReclaimerSupport::kFirstPAPurgeOrReclaimDelay); | |
| // Task gets reposted. | |
| EXPECT_TRUE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount()); | |
| instance.SetForegrounded(false); | |
| task_environment_.FastForwardBy(MemoryReclaimerSupport::GetInterval()); | |
| // But not once in background. | |
| EXPECT_FALSE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(0u, task_environment_.GetPendingMainThreadTaskCount()); | |
| // Until we go to foreground again. | |
| instance.SetForegrounded(true); | |
| EXPECT_TRUE(instance.has_pending_task_for_testing()); | |
| EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount()); | |
| } | |
| TEST(PartitionAllocSupportTest, GetProcessTypeIdentifierBrowser) { | |
| test::ScopedCommandLine scoped_command_line; | |
| EXPECT_EQ("browser", GetProcessTypeIdentifier()); | |
| } | |
| TEST(PartitionAllocSupportTest, GetProcessTypeIdentifierRenderer) { | |
| test::ScopedCommandLine scoped_command_line; | |
| CommandLine* cl = scoped_command_line.GetProcessCommandLine(); | |
| cl->AppendSwitchASCII("type", "renderer"); | |
| EXPECT_EQ("renderer", GetProcessTypeIdentifier()); | |
| } | |
| TEST(PartitionAllocSupportTest, GetProcessTypeIdentifierUtility) { | |
| test::ScopedCommandLine scoped_command_line; | |
| CommandLine* cl = scoped_command_line.GetProcessCommandLine(); | |
| cl->AppendSwitchASCII("type", "utility"); | |
| EXPECT_EQ("utility", GetProcessTypeIdentifier()); | |
| } | |
| TEST(PartitionAllocSupportTest, GetProcessTypeIdentifierUtilitySubType) { | |
| test::ScopedCommandLine scoped_command_line; | |
| CommandLine* cl = scoped_command_line.GetProcessCommandLine(); | |
| cl->AppendSwitchASCII("type", "utility"); | |
| cl->AppendSwitchASCII("utility-sub-type", "network.mojom.NetworkService"); | |
| EXPECT_EQ("utility.network.mojom.NetworkService", GetProcessTypeIdentifier()); | |
| } | |
| #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ | |
| PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \ | |
| !PA_BUILDFLAG(FORCE_DISABLE_BACKUP_REF_PTR_FEATURE) | |
| TEST(PartitionAllocSupportTest, GetBrpConfiguration) { | |
| test::ScopedFeatureList scoped_feature_list; | |
| scoped_feature_list.InitAndEnableFeature( | |
| features::kPartitionAllocBackupRefPtr); | |
| PartitionAllocSupport::BrpConfiguration config = | |
| PartitionAllocSupport::GetBrpConfiguration(""); | |
| #if BUILDFLAG(IS_IOS) | |
| EXPECT_EQ(config.enable_brp, ios::IsRunningOnOrLater(26, 5, 0)); | |
| #else | |
| EXPECT_TRUE(config.enable_brp); | |
| #endif | |
| EXPECT_FALSE(config.suppress_corruption_detected_crash); | |
| } | |
| #endif | |
| #if PA_BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_POSIX) | |
| class PartitionAllocMemoryPressureTest : public testing::Test { | |
| protected: | |
| static constexpr size_t kTestPoolPages = 100; | |
| static constexpr size_t kTestPoolSize = | |
| kTestPoolPages * partition_alloc::internal::kSuperPageSize; | |
| static constexpr auto kPoolHandle = | |
| partition_alloc::internal::kConfigurablePoolHandle; | |
| void SetUp() override { | |
| base_address_ = partition_alloc::AllocPages( | |
| kTestPoolSize, partition_alloc::internal::kSuperPageSize, | |
| partition_alloc::PageAccessibilityConfiguration( | |
| partition_alloc::PageAccessibilityConfiguration::kInaccessible), | |
| partition_alloc::PageTag::kPartitionAlloc); | |
| ASSERT_NE(base_address_, 0u); | |
| manager().Add(kPoolHandle, base_address_, kTestPoolSize); | |
| } | |
| void TearDown() override { | |
| PartitionAllocMemoryPressureMonitor::Instance().ResetForTesting(); | |
| manager().Remove(kPoolHandle); | |
| partition_alloc::FreePages(base_address_, kTestPoolSize); | |
| } | |
| partition_alloc::internal::AddressPoolManager& manager() { | |
| return partition_alloc::internal::AddressPoolManager::GetInstance(); | |
| } | |
| test::TaskEnvironment task_environment_; | |
| uintptr_t base_address_ = 0; | |
| }; | |
| TEST_F(PartitionAllocMemoryPressureTest, MonitorEndToEnd) { | |
| MemoryPressureSource source; | |
| MemoryPressureListenerRegistry registry; | |
| RegisteredMockMemoryPressureListener listener; | |
| auto& monitor = PartitionAllocMemoryPressureMonitor::Instance(); | |
| monitor.Start(task_environment_.GetMainThreadTaskRunner()); | |
| // 1. Transition to CRITICAL pressure (reserve 90 out of 100 pages = 90%). | |
| base::RunLoop critical_run_loop; | |
| EXPECT_CALL(listener, OnMemoryPressure(base::MEMORY_PRESSURE_LEVEL_CRITICAL)) | |
| .WillOnce([&] { critical_run_loop.Quit(); }); | |
| uintptr_t addr1 = manager().Reserve( | |
| kPoolHandle, 0, 90 * partition_alloc::internal::kSuperPageSize); | |
| ASSERT_NE(addr1, 0u); | |
| critical_run_loop.Run(); | |
| // Unreserve 2 pages to 88% (inside (85%, 90%) hysteresis deadband) -> should | |
| // not trigger MEMORY_PRESSURE_LEVEL_NONE. | |
| manager().UnreserveAndDecommit( | |
| kPoolHandle, addr1 + 88 * partition_alloc::internal::kSuperPageSize, | |
| 2 * partition_alloc::internal::kSuperPageSize); | |
| EXPECT_EQ(PartitionAllocSupport::GetMemoryPressureLevel(), | |
| PartitionAllocSupport::MemoryPressureLevel::kCriticalPressure); | |
| // 2. Transition back to NONE pressure (unreserve 3 more pages -> 85% = 90% - | |
| // 5%). | |
| base::RunLoop none_run_loop; | |
| EXPECT_CALL(listener, OnMemoryPressure(base::MEMORY_PRESSURE_LEVEL_NONE)) | |
| .WillOnce([&] { none_run_loop.Quit(); }); | |
| manager().UnreserveAndDecommit( | |
| kPoolHandle, addr1 + 85 * partition_alloc::internal::kSuperPageSize, | |
| 3 * partition_alloc::internal::kSuperPageSize); | |
| none_run_loop.Run(); | |
| manager().UnreserveAndDecommit( | |
| kPoolHandle, addr1, 85 * partition_alloc::internal::kSuperPageSize); | |
| } | |
| TEST_F(PartitionAllocMemoryPressureTest, MonitorStartupAlreadyCritical) { | |
| MemoryPressureSource source; | |
| MemoryPressureListenerRegistry registry; | |
| RegisteredMockMemoryPressureListener listener; | |
| // Allocate 92 out of 100 pages (92% >= 90%) BEFORE starting monitor. | |
| uintptr_t addr = manager().Reserve( | |
| kPoolHandle, 0, 92 * partition_alloc::internal::kSuperPageSize); | |
| ASSERT_NE(addr, 0u); | |
| EXPECT_EQ(PartitionAllocSupport::GetMemoryPressureLevel(), | |
| PartitionAllocSupport::MemoryPressureLevel::kCriticalPressure); | |
| // Starting the monitor when already critical should immediately trigger | |
| // MEMORY_PRESSURE_LEVEL_CRITICAL. | |
| base::RunLoop critical_run_loop; | |
| EXPECT_CALL(listener, OnMemoryPressure(base::MEMORY_PRESSURE_LEVEL_CRITICAL)) | |
| .WillOnce([&] { critical_run_loop.Quit(); }); | |
| auto& monitor = PartitionAllocMemoryPressureMonitor::Instance(); | |
| monitor.Start(task_environment_.GetMainThreadTaskRunner()); | |
| critical_run_loop.Run(); | |
| manager().UnreserveAndDecommit( | |
| kPoolHandle, addr, 92 * partition_alloc::internal::kSuperPageSize); | |
| } | |
| #endif // PA_BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_POSIX) | |
| } // namespace base::allocator |