blob: 3d7d5e93db26b93ed78066965afc3f754304f769 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/memory_pressure/unnecessary_discard_monitor.h"
#include <memory>
#include "base/byte_count.h"
#include "base/functional/callback.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_pressure {
class UnnecessaryDiscardMonitorTest : public testing::Test {
public:
UnnecessaryDiscardMonitorTest() = default;
~UnnecessaryDiscardMonitorTest() override = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
UnnecessaryDiscardMonitor monitor_;
base::HistogramTester histogram_tester_;
};
TEST_F(UnnecessaryDiscardMonitorTest, TestNoEventsReportsNothing) {
// Reset the monitor to force CalculateAndReport().
monitor_.OnReclaimTargetEnd();
histogram_tester_.ExpectTotalCount("Discarding.DiscardsDrivenByStaleSignal",
0);
}
TEST_F(UnnecessaryDiscardMonitorTest, TestReclaimTargetAgeIsReported) {
base::TimeTicks reclaim_target_origin = base::TimeTicks::Now();
base::TimeDelta target_age = base::Seconds(5);
task_environment_.FastForwardBy(target_age);
// Start processing a reclaim target that was calculated some time ago.
monitor_.OnReclaimTargetBegin({base::KiB(100), reclaim_target_origin});
histogram_tester_.ExpectTimeBucketCount("Discarding.ReclaimTargetAge",
target_age, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest, TestNoUnnecessaryDiscardsIsReported) {
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
monitor_.OnDiscard(base::KiB(100), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
histogram_tester_.ExpectUniqueSample("Discarding.DiscardsDrivenByStaleSignal",
0, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest, TestSingleUnnecessaryDiscardIsReported) {
// Start processing a reclaim target that was calculated now.
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
// Store the time for the next reclaim event.
base::TimeTicks next_reclaim_event_time =
base::TimeTicks::Now() + base::Milliseconds(1000);
task_environment_.FastForwardBy(base::Milliseconds(1001));
monitor_.OnDiscard(base::KiB(100), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnReclaimTargetBegin({base::KiB(100), next_reclaim_event_time});
task_environment_.FastForwardBy(base::Milliseconds(10));
monitor_.OnDiscard(base::KiB(100), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// The first reclaim event should have no unnecessary discards, but the second
// one should have a single unnecessary discard.
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 1);
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
1, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest,
TestMultipleUnnecessaryDiscardsAreReported) {
// Start processing a reclaim target that was calculated now.
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
// Store the time for the next reclaim event.
base::TimeTicks next_reclaim_event_time =
base::TimeTicks::Now() + base::Milliseconds(1000);
// Discard a tab from this reclaim event.
task_environment_.FastForwardBy(base::Milliseconds(1001));
monitor_.OnDiscard(base::KiB(100), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// Start processing a reclaim target that was created before the first tab was
// discarded. Since this reclaim target is the same as the previous reclaim
// target, all kills from this reclaim target should be unnecessary.
monitor_.OnReclaimTargetBegin({base::KiB(100), next_reclaim_event_time});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(500), base::TimeTicks::Now());
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(500), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// The first reclaim event should have no unnecessary discards, but the second
// one should have two unnecessary discards.
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 1);
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
2, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest,
TestIncreasingReclaimTargetIsNotReportedAsUnnecessary) {
// Start processing a reclaim target that was calculated now.
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
// Store the time for the next reclaim event.
base::TimeTicks next_reclaim_event_time =
base::TimeTicks::Now() + base::Milliseconds(1000);
// Discard a tab from this reclaim event before the next reclaim event's
// origin.
task_environment_.FastForwardBy(base::Milliseconds(500));
monitor_.OnDiscard(base::KiB(80), base::TimeTicks::Now());
// Discard another tab from this reclaim event after the next reclaim event's
// origin.
task_environment_.FastForwardBy(base::Milliseconds(501));
monitor_.OnDiscard(base::KiB(30), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// In total, the first reclaim event discarded 110 when the target was 100. 30
// of that discard was discarded after next_reclaim_event_time which means
// that there will be no unnecessary discards if the next reclaim event only
// discards once.
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnReclaimTargetBegin({base::KiB(110), next_reclaim_event_time});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(120), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 2);
}
TEST_F(UnnecessaryDiscardMonitorTest,
TestIncreasingReclaimTargetWithUnnecessaryDiscard) {
// Start processing a reclaim target that was calculated now.
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
// Store the time for the next reclaim event.
base::TimeTicks next_reclaim_event_time =
base::TimeTicks::Now() + base::Milliseconds(1000);
// Discard a tab from this reclaim event before the next reclaim event's
// origin.
task_environment_.FastForwardBy(base::Milliseconds(500));
monitor_.OnDiscard(base::KiB(80), base::TimeTicks::Now());
// Discard another tab from this reclaim event after the next reclaim event's
// origin.
task_environment_.FastForwardBy(base::Milliseconds(501));
monitor_.OnDiscard(base::KiB(30), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// In total, the first reclaim event discarded 110 when the target was 100. 30
// of that discard was discarded after next_reclaim_event_time which means
// that one discard of size 80 is necessary, but an additional discard of size
// 40 is unnecessary.
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnReclaimTargetBegin({base::KiB(110), next_reclaim_event_time});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(80), base::TimeTicks::Now());
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(40), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 1);
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
1, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest,
TestEveryReclaimEventReportsZeroUnnecessaryKills) {
// The normal sequence that should have no unnecessary kills.
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(150), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
task_environment_.FastForwardBy(base::Milliseconds(1000));
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(150), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// Check that both reclaim events had no unnecessary discards
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 2);
}
TEST_F(UnnecessaryDiscardMonitorTest, TestTwoPreceedingReclaimTargets) {
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(150), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
task_environment_.FastForwardBy(base::Milliseconds(1000));
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1000));
monitor_.OnDiscard(base::KiB(150), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
// Process a reclaim target that was created before the most recent discard.
monitor_.OnReclaimTargetBegin(
{base::KiB(100), base::TimeTicks::Now() - base::Milliseconds(500)});
// This discard is unnecessary since the previous target was the same and
// already discarded.
monitor_.OnDiscard(base::KiB(200), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
0, 2);
histogram_tester_.ExpectBucketCount("Discarding.DiscardsDrivenByStaleSignal",
1, 1);
}
TEST_F(UnnecessaryDiscardMonitorTest, TestCorrectReclaimTargetToZero) {
monitor_.OnReclaimTargetBegin({base::KiB(100), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1000));
base::TimeTicks next_reclaim_target_time = base::TimeTicks::Now();
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(150), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
ReclaimTarget next_target{base::KiB(100), next_reclaim_target_time};
ReclaimTarget corrected_target = monitor_.CorrectReclaimTarget(next_target);
ASSERT_TRUE(corrected_target.target.is_zero());
}
TEST_F(UnnecessaryDiscardMonitorTest, TestCorrectReclaimTargetToNonZero) {
monitor_.OnReclaimTargetBegin({base::KiB(50), base::TimeTicks::Now()});
task_environment_.FastForwardBy(base::Milliseconds(1000));
base::TimeTicks next_reclaim_target_time = base::TimeTicks::Now();
task_environment_.FastForwardBy(base::Milliseconds(1));
monitor_.OnDiscard(base::KiB(50), base::TimeTicks::Now());
monitor_.OnReclaimTargetEnd();
ReclaimTarget next_target{base::KiB(100), next_reclaim_target_time};
ReclaimTarget corrected_target = monitor_.CorrectReclaimTarget(next_target);
ASSERT_EQ(corrected_target.target, base::KiB(50));
}
} // namespace memory_pressure