Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/memory/memory_pressure_monitor_mac.h" | 5 #include "base/memory/memory_pressure_monitor_mac.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/mac/scoped_cftyperef.h" | |
| 7 #include "base/macros.h" | 10 #include "base/macros.h" |
| 11 #include "base/test/histogram_tester.h" | |
| 8 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 9 | 13 |
| 10 namespace base { | 14 namespace base { |
| 11 namespace mac { | 15 namespace mac { |
| 12 | 16 |
| 13 class TestMemoryPressureMonitor : public MemoryPressureMonitor { | 17 class TestMemoryPressureMonitor : public MemoryPressureMonitor { |
| 14 public: | 18 public: |
| 15 using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure; | 19 using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel; |
| 20 | |
| 21 // A HistogramTester for verifying correct UMA stat generation. | |
| 22 base::HistogramTester tester; | |
| 16 | 23 |
| 17 TestMemoryPressureMonitor() { } | 24 TestMemoryPressureMonitor() { } |
| 18 | 25 |
| 26 // Clears the next run loop update time so that the next pass of the run | |
| 27 // loop checks the memory pressure level immediately. Normally there's a | |
| 28 // 5 second delay between pressure readings. | |
| 29 void ResetRunLoopUpdateTime() { next_run_loop_update_time_ = 0; } | |
| 30 | |
| 31 // Access to the last-recorded memory pressure level. | |
| 32 MemoryPressureListener::MemoryPressureLevel LastPressureLevel() { | |
| 33 return last_pressure_level_; | |
| 34 } | |
| 35 | |
| 36 // Sets the last UMA stat report time. Time spent in memory pressure is | |
| 37 // recorded in 5-second "ticks" from the last time statistics were recorded. | |
| 38 void SetLastStatisticReportTime(CFTimeInterval time) { | |
| 39 last_statistic_report_time_ = time; | |
| 40 } | |
| 41 | |
| 42 // Sets the raw macOS memory pressure level read by the memory pressure | |
| 43 // monitor. | |
| 44 int macos_pressure_level_for_testing_; | |
| 45 | |
| 46 // Exposes the UpdatePressureLevel() method for testing. | |
| 47 void UpdatePressureLevel() { MemoryPressureMonitor::UpdatePressureLevel(); } | |
| 48 | |
| 49 // Returns the number of seconds left over from the last UMA tick | |
| 50 // calculation. | |
| 51 int SubTickSeconds() { return subtick_seconds_; } | |
| 52 | |
| 53 // Returns the number of seconds per UMA tick. | |
| 54 static int GetSecondsPerUMATick() { | |
| 55 return MemoryPressureMonitor::GetSecondsPerUMATick(); | |
| 56 } | |
| 57 | |
| 19 private: | 58 private: |
| 20 DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); | 59 DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); |
| 60 | |
| 61 int GetMacMemoryPressureLevel() override { | |
| 62 return macos_pressure_level_for_testing_; | |
| 63 } | |
| 21 }; | 64 }; |
| 22 | 65 |
| 23 TEST(MacMemoryPressureMonitorTest, MemoryPressureFromMacMemoryPressure) { | 66 TEST(MacMemoryPressureMonitorTest, MemoryPressureFromMacMemoryPressure) { |
| 24 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | 67 EXPECT_EQ( |
| 25 TestMemoryPressureMonitor:: | 68 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| 26 MemoryPressureLevelForMacMemoryPressure( | 69 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 27 DISPATCH_MEMORYPRESSURE_NORMAL)); | 70 DISPATCH_MEMORYPRESSURE_NORMAL)); |
| 28 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, | 71 EXPECT_EQ( |
| 29 TestMemoryPressureMonitor:: | 72 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, |
| 30 MemoryPressureLevelForMacMemoryPressure( | 73 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 31 DISPATCH_MEMORYPRESSURE_WARN)); | 74 DISPATCH_MEMORYPRESSURE_WARN)); |
| 32 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, | 75 EXPECT_EQ( |
| 33 TestMemoryPressureMonitor:: | 76 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, |
| 34 MemoryPressureLevelForMacMemoryPressure( | 77 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 35 DISPATCH_MEMORYPRESSURE_CRITICAL)); | 78 DISPATCH_MEMORYPRESSURE_CRITICAL)); |
| 36 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | 79 EXPECT_EQ( |
| 37 TestMemoryPressureMonitor:: | 80 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| 38 MemoryPressureLevelForMacMemoryPressure(0)); | 81 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 39 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | 82 0)); |
| 40 TestMemoryPressureMonitor:: | 83 EXPECT_EQ( |
| 41 MemoryPressureLevelForMacMemoryPressure(3)); | 84 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| 42 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | 85 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 43 TestMemoryPressureMonitor:: | 86 3)); |
| 44 MemoryPressureLevelForMacMemoryPressure(5)); | 87 EXPECT_EQ( |
| 45 EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | 88 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| 46 TestMemoryPressureMonitor:: | 89 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 47 MemoryPressureLevelForMacMemoryPressure(-1)); | 90 5)); |
| 91 EXPECT_EQ( | |
| 92 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | |
| 93 TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( | |
| 94 -1)); | |
| 48 } | 95 } |
| 49 | 96 |
| 50 TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { | 97 TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { |
| 51 TestMemoryPressureMonitor monitor; | 98 TestMemoryPressureMonitor monitor; |
| 99 | |
| 52 MemoryPressureListener::MemoryPressureLevel memory_pressure = | 100 MemoryPressureListener::MemoryPressureLevel memory_pressure = |
| 53 monitor.GetCurrentPressureLevel(); | 101 monitor.GetCurrentPressureLevel(); |
| 54 EXPECT_TRUE(memory_pressure == | 102 EXPECT_TRUE(memory_pressure == |
| 55 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE || | 103 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE || |
| 56 memory_pressure == | 104 memory_pressure == |
| 57 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE || | 105 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE || |
| 58 memory_pressure == | 106 memory_pressure == |
| 59 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | 107 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| 60 } | 108 } |
| 61 | 109 |
| 110 TEST(MacMemoryPressureMonitorTest, MemoryPressureConversion) { | |
| 111 TestMemoryPressureMonitor monitor; | |
| 112 | |
| 113 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; | |
| 114 MemoryPressureListener::MemoryPressureLevel memory_pressure = | |
| 115 monitor.GetCurrentPressureLevel(); | |
| 116 EXPECT_EQ(memory_pressure, | |
| 117 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | |
| 118 | |
| 119 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; | |
| 120 memory_pressure = monitor.GetCurrentPressureLevel(); | |
| 121 EXPECT_EQ(memory_pressure, | |
| 122 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | |
| 123 | |
| 124 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; | |
| 125 memory_pressure = monitor.GetCurrentPressureLevel(); | |
| 126 EXPECT_EQ(memory_pressure, | |
| 127 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | |
| 128 } | |
| 129 | |
| 130 TEST(MacMemoryPressureMonitorTest, MemoryPressureRunLoopChecking) { | |
| 131 TestMemoryPressureMonitor monitor; | |
| 132 | |
| 133 // To test grabbing the memory presure at the end of the run loop, we have to | |
| 134 // run the run loop, but to do that the run loop needs a run loop source. Add | |
| 135 // a timer as the source. We know that the exit observer is attached to | |
| 136 // the kMessageLoopExclusiveRunLoopMode mode, so use that mode. | |
| 137 ScopedCFTypeRef<CFRunLoopTimerRef> timer_ref(CFRunLoopTimerCreate( | |
| 138 NULL, CFAbsoluteTimeGetCurrent() + 10, 0, 0, 0, nullptr, nullptr)); | |
| 139 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer_ref, | |
| 140 kMessageLoopExclusiveRunLoopMode); | |
| 141 | |
| 142 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; | |
| 143 monitor.ResetRunLoopUpdateTime(); | |
| 144 CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); | |
| 145 EXPECT_EQ(monitor.LastPressureLevel(), | |
| 146 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | |
| 147 | |
| 148 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; | |
| 149 monitor.ResetRunLoopUpdateTime(); | |
| 150 CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); | |
| 151 EXPECT_EQ(monitor.LastPressureLevel(), | |
| 152 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | |
| 153 | |
| 154 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; | |
| 155 monitor.ResetRunLoopUpdateTime(); | |
| 156 CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); | |
| 157 EXPECT_EQ(monitor.LastPressureLevel(), | |
| 158 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | |
| 159 | |
| 160 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer_ref, | |
| 161 kMessageLoopExclusiveRunLoopMode); | |
| 162 } | |
| 163 | |
| 164 TEST(MacMemoryPressureMonitorTest, RecordMemoryPressureStats) { | |
| 165 TestMemoryPressureMonitor monitor; | |
| 166 const char* kHistogram = "Memory.PressureLevel"; | |
| 167 CFTimeInterval now = CFAbsoluteTimeGetCurrent(); | |
| 168 const int seconds_per_tick = | |
| 169 TestMemoryPressureMonitor::GetSecondsPerUMATick(); | |
| 170 | |
| 171 // Set the initial pressure level. | |
| 172 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; | |
| 173 // Incur one UMA tick of time (and include one extra second of elapsed time). | |
| 174 monitor.SetLastStatisticReportTime(now - (seconds_per_tick + 1)); | |
| 175 monitor.UpdatePressureLevel(); | |
| 176 monitor.tester.ExpectTotalCount(kHistogram, 1); | |
| 177 monitor.tester.ExpectBucketCount(kHistogram, 0, 1); | |
| 178 // The report time above included an extra second so there should be 1 | |
| 179 // sub-tick second left over. | |
| 180 EXPECT_EQ(1, monitor.SubTickSeconds()); | |
| 181 | |
| 182 // Simulate sitting in normal pressure for 1 second less than 6 UMA tick | |
| 183 // seconds and then elevating to warning. With the left over sub-tick second | |
| 184 // from above, the total elapsed ticks should be an even 6 UMA ticks. | |
| 185 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; | |
| 186 monitor.SetLastStatisticReportTime(now - (seconds_per_tick * 6 - 1)); | |
| 187 monitor.UpdatePressureLevel(); | |
| 188 monitor.tester.ExpectTotalCount(kHistogram, 7); | |
| 189 monitor.tester.ExpectBucketCount(kHistogram, 0, 7); | |
| 190 monitor.tester.ExpectBucketCount(kHistogram, 1, 0); | |
| 191 EXPECT_EQ(0, monitor.SubTickSeconds()); | |
| 192 | |
| 193 // Simulate sitting in warning pressure for 20 UMA ticks and 2 seconds, and | |
| 194 // then elevating to critical. | |
| 195 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; | |
| 196 monitor.SetLastStatisticReportTime(now - (20 * seconds_per_tick + 2)); | |
| 197 monitor.UpdatePressureLevel(); | |
| 198 monitor.tester.ExpectTotalCount(kHistogram, 27); | |
| 199 monitor.tester.ExpectBucketCount(kHistogram, 0, 7); | |
| 200 monitor.tester.ExpectBucketCount(kHistogram, 1, 20); | |
| 201 monitor.tester.ExpectBucketCount(kHistogram, 2, 0); | |
| 202 EXPECT_EQ(2, monitor.SubTickSeconds()); | |
|
lgrey
2017/02/22 21:33:40
I wonder if we should always be rounding up for co
shrike
2017/02/22 23:56:25
I think if we do, and if we get a lot of subtick_s
lgrey
2017/02/23 15:49:29
Nah, no problem. I guess the inaccuracy is baked i
| |
| 203 | |
| 204 // A quick update while critical - the stats should not budge because less | |
| 205 // than 1 tick of time has elapsed. | |
| 206 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; | |
| 207 monitor.SetLastStatisticReportTime(now - 1); | |
| 208 monitor.UpdatePressureLevel(); | |
| 209 monitor.tester.ExpectTotalCount(kHistogram, 27); | |
| 210 monitor.tester.ExpectBucketCount(kHistogram, 0, 7); | |
| 211 monitor.tester.ExpectBucketCount(kHistogram, 1, 20); | |
| 212 monitor.tester.ExpectBucketCount(kHistogram, 2, 0); | |
| 213 EXPECT_EQ(3, monitor.SubTickSeconds()); | |
| 214 | |
| 215 // A quick change back to normal. Less than 1 tick of time has elapsed, but | |
| 216 // in this case the pressure level changed, so the critical bucket should | |
| 217 // get another sample (otherwise we could miss quick level changes). | |
| 218 monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; | |
| 219 monitor.SetLastStatisticReportTime(now - 1); | |
| 220 monitor.UpdatePressureLevel(); | |
| 221 monitor.tester.ExpectTotalCount(kHistogram, 28); | |
| 222 monitor.tester.ExpectBucketCount(kHistogram, 0, 7); | |
| 223 monitor.tester.ExpectBucketCount(kHistogram, 1, 20); | |
| 224 monitor.tester.ExpectBucketCount(kHistogram, 2, 1); | |
| 225 // When less than 1 tick of time has elapsed but the pressure level changed, | |
| 226 // the subtick remainder gets zeroed out. | |
| 227 EXPECT_EQ(0, monitor.SubTickSeconds()); | |
| 228 } | |
| 62 } // namespace mac | 229 } // namespace mac |
| 63 } // namespace base | 230 } // namespace base |
| OLD | NEW |