1 // Copyright 2021 The Chromium Authors
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 "partition_alloc/starscan/pcscan_scheduling.h"
6
7 #include "partition_alloc/partition_alloc_base/time/time.h"
8 #include "partition_alloc/partition_alloc_base/time/time_override.h"
9 #include "partition_alloc/partition_lock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace partition_alloc::internal {
13
14 namespace {
15 constexpr size_t kMB = 1024 * 1024;
16 } // namespace
17
TEST(PartitionAllocPCScanSchedulerLimitBackendTest,NoScanBelowMinimumScanningThreshold)18 TEST(PartitionAllocPCScanSchedulerLimitBackendTest,
19 NoScanBelowMinimumScanningThreshold) {
20 PCScanScheduler scheduler;
21 LimitBackend limit_backend(scheduler);
22 scheduler.SetNewSchedulingBackend(limit_backend);
23 constexpr size_t kMinimumScanningThreshold =
24 QuarantineData::kQuarantineSizeMinLimit;
25 EXPECT_FALSE(scheduler.AccountFreed(kMinimumScanningThreshold / 2));
26 EXPECT_FALSE(scheduler.AccountFreed(kMinimumScanningThreshold -
27 kMinimumScanningThreshold / 2));
28 EXPECT_TRUE(scheduler.AccountFreed(1));
29 }
30
TEST(PartitionAllocPCScanSchedulerLimitBackendTest,ScanAtQuarantineSizeFraction)31 TEST(PartitionAllocPCScanSchedulerLimitBackendTest,
32 ScanAtQuarantineSizeFraction) {
33 PCScanScheduler scheduler;
34 LimitBackend limit_backend(scheduler);
35 scheduler.SetNewSchedulingBackend(limit_backend);
36 constexpr size_t kHeapSize = 100 * kMB;
37 constexpr size_t kNoSurvivedBytes = 0;
38 limit_backend.UpdateScheduleAfterScan(kNoSurvivedBytes, base::TimeDelta(),
39 kHeapSize);
40 constexpr size_t kExpectedTriggerSize = static_cast<size_t>(
41 static_cast<double>(kHeapSize) * LimitBackend::kQuarantineSizeFraction);
42 EXPECT_FALSE(scheduler.AccountFreed(kExpectedTriggerSize / 2));
43 EXPECT_FALSE(
44 scheduler.AccountFreed(kExpectedTriggerSize - kExpectedTriggerSize / 2));
45 EXPECT_TRUE(scheduler.AccountFreed(1));
46 }
47
48 class PartitionAllocPCScanMUAwareTaskBasedBackendTest : public ::testing::Test {
49 public:
50 static constexpr size_t kHeapSize = 100 * kMB;
51
HardLimitSize(size_t heap_size)52 static constexpr size_t HardLimitSize(size_t heap_size) {
53 return static_cast<size_t>(
54 static_cast<double>(heap_size) *
55 MUAwareTaskBasedBackend::kHardLimitQuarantineSizePercent) +
56 1;
57 }
58
SoftLimitSize(size_t heap_size)59 static constexpr size_t SoftLimitSize(size_t heap_size) {
60 return static_cast<size_t>(
61 static_cast<double>(heap_size) *
62 MUAwareTaskBasedBackend::kSoftLimitQuarantineSizePercent) +
63 1;
64 }
65
PartitionAllocPCScanMUAwareTaskBasedBackendTest()66 PartitionAllocPCScanMUAwareTaskBasedBackendTest()
67 : backend_(scheduler_, &IncrementDelayedScanScheduledCount) {
68 scheduler_.SetNewSchedulingBackend(backend_);
69 constexpr size_t kNoSurvivedBytes = 0;
70 constexpr base::TimeDelta kZeroTimeForScan;
71 backend_.UpdateScheduleAfterScan(kNoSurvivedBytes, kZeroTimeForScan,
72 kHeapSize);
73 }
74
SetUp()75 void SetUp() override { delayed_scan_scheduled_count_ = 0; }
76
scheduler()77 PCScanScheduler& scheduler() { return scheduler_; }
backend()78 MUAwareTaskBasedBackend& backend() { return backend_; }
delayed_scan_scheduled_count() const79 size_t delayed_scan_scheduled_count() const {
80 return delayed_scan_scheduled_count_;
81 }
82
83 private:
IncrementDelayedScanScheduledCount(int64_t delay_in_microseconds)84 static void IncrementDelayedScanScheduledCount(
85 int64_t delay_in_microseconds) {
86 ++delayed_scan_scheduled_count_;
87 }
88
89 static size_t delayed_scan_scheduled_count_;
90 PCScanScheduler scheduler_;
91 MUAwareTaskBasedBackend backend_;
92 };
93
94 size_t PartitionAllocPCScanMUAwareTaskBasedBackendTest::
95 delayed_scan_scheduled_count_ = 0;
96
97 namespace {
98
99 class ScopedTimeTicksOverride final {
100 public:
ScopedTimeTicksOverride()101 ScopedTimeTicksOverride()
102 : ScopedTimeTicksOverride(InitializeTimeAndReturnTimeTicksNow()) {}
103
AddTicksToNow(base::TimeDelta ticks)104 void AddTicksToNow(base::TimeDelta ticks) { now_ticks_ += ticks; }
105
106 private:
Now()107 static base::TimeTicks Now() { return now_ticks_; }
108
InitializeTimeAndReturnTimeTicksNow()109 static base::TimeTicksNowFunction InitializeTimeAndReturnTimeTicksNow() {
110 now_ticks_ = base::TimeTicks::Now();
111 return &Now;
112 }
113
ScopedTimeTicksOverride(base::TimeTicksNowFunction time_ticks_function)114 explicit ScopedTimeTicksOverride(
115 base::TimeTicksNowFunction time_ticks_function)
116 : overrides_(nullptr, time_ticks_function, nullptr) {}
117
118 static base::TimeTicks now_ticks_;
119
120 base::subtle::ScopedTimeClockOverrides overrides_;
121 };
122
123 // static
124 base::TimeTicks ScopedTimeTicksOverride::now_ticks_;
125
126 } // namespace
127
TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,SoftLimitSchedulesScanIfMUNotSatisfied)128 TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,
129 SoftLimitSchedulesScanIfMUNotSatisfied) {
130 // Stop the time.
131 ScopedTimeTicksOverride now_ticks_override;
132 // Simulate PCScan that processed kHeapSize in 1s. Since time is stopped that
133 // schedule is not reachable.
134 backend().UpdateScheduleAfterScan(0, base::Seconds(1), kHeapSize);
135
136 EXPECT_EQ(0u, delayed_scan_scheduled_count());
137 EXPECT_FALSE(scheduler().AccountFreed(SoftLimitSize(kHeapSize)));
138 EXPECT_EQ(1u, delayed_scan_scheduled_count());
139 }
140
TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,SoftLimitInvokesScanIfMUSatisfied)141 TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,
142 SoftLimitInvokesScanIfMUSatisfied) {
143 // Stop the time.
144 ScopedTimeTicksOverride now_ticks_override;
145 // Simulate PCScan that processed kHeapSize in 0s. The next scan should thus
146 // happen immediately.
147 backend().UpdateScheduleAfterScan(0, base::Seconds(0), kHeapSize);
148
149 EXPECT_EQ(0u, delayed_scan_scheduled_count());
150 EXPECT_TRUE(scheduler().AccountFreed(SoftLimitSize(kHeapSize)));
151 EXPECT_EQ(0u, delayed_scan_scheduled_count());
152 }
153
TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,HardLimitSchedulesScanImmediately)154 TEST_F(PartitionAllocPCScanMUAwareTaskBasedBackendTest,
155 HardLimitSchedulesScanImmediately) {
156 // Stop the time.
157 ScopedTimeTicksOverride now_ticks_override;
158 // Simulate PCScan that processed kHeapSize in 1s. Since time is stopped that
159 // schedule is not reachable.
160 backend().UpdateScheduleAfterScan(0, base::Seconds(0), kHeapSize);
161
162 EXPECT_EQ(0u, delayed_scan_scheduled_count());
163 // Triogering the hard limit should immediately require a scan and not
164 // schedule anything.
165 EXPECT_TRUE(scheduler().AccountFreed(HardLimitSize(kHeapSize)));
166 EXPECT_EQ(0u, delayed_scan_scheduled_count());
167 }
168
169 } // namespace partition_alloc::internal
170