| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 | |
| 5 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/process/process_handle.h" | |
| 12 #include "chromeos/dbus/fake_debug_daemon_client.h" | |
| 13 #include "content/public/test/test_browser_thread_bundle.h" | |
| 14 #include "testing/gtest/include/gtest/gtest.h" | |
| 15 | |
| 16 namespace memory { | |
| 17 | |
| 18 class TabManagerDelegateTest : public testing::Test { | |
| 19 public: | |
| 20 TabManagerDelegateTest() {} | |
| 21 ~TabManagerDelegateTest() override {} | |
| 22 | |
| 23 private: | |
| 24 content::TestBrowserThreadBundle thread_bundle_; | |
| 25 }; | |
| 26 | |
| 27 namespace { | |
| 28 constexpr bool kIsFocused = true; | |
| 29 constexpr bool kNotFocused = false; | |
| 30 } // namespace | |
| 31 | |
| 32 TEST_F(TabManagerDelegateTest, CandidatesSorted) { | |
| 33 std::vector<arc::ArcProcess> arc_processes; | |
| 34 arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP, | |
| 35 kIsFocused, 100); | |
| 36 arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP, | |
| 37 kNotFocused, 200); | |
| 38 arc_processes.emplace_back( | |
| 39 3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500); | |
| 40 arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP, | |
| 41 kNotFocused, 150); | |
| 42 | |
| 43 TabStats tab1, tab2, tab3, tab4, tab5; | |
| 44 tab1.tab_contents_id = 100; | |
| 45 tab1.is_pinned = true; | |
| 46 | |
| 47 tab2.tab_contents_id = 200; | |
| 48 tab2.is_internal_page = true; | |
| 49 | |
| 50 tab3.tab_contents_id = 300; | |
| 51 tab3.is_pinned = true; | |
| 52 tab3.is_media = true; | |
| 53 | |
| 54 tab4.tab_contents_id = 400; | |
| 55 tab4.is_media = true; | |
| 56 | |
| 57 tab5.tab_contents_id = 500; | |
| 58 tab5.is_app = true; | |
| 59 TabStatsList tab_list = { | |
| 60 tab1, tab2, tab3, tab4, tab5 | |
| 61 }; | |
| 62 | |
| 63 std::vector<TabManagerDelegate::Candidate> candidates; | |
| 64 | |
| 65 candidates = TabManagerDelegate::GetSortedCandidates( | |
| 66 tab_list, arc_processes); | |
| 67 ASSERT_EQ(9U, candidates.size()); | |
| 68 | |
| 69 // focused app. | |
| 70 ASSERT_TRUE(candidates[0].app()); | |
| 71 EXPECT_EQ("focused", candidates[0].app()->process_name()); | |
| 72 // visible app 1, last_activity_time larger than visible app 2. | |
| 73 ASSERT_TRUE(candidates[1].app()); | |
| 74 EXPECT_EQ("visible1", candidates[1].app()->process_name()); | |
| 75 // visible app 2, last_activity_time less than visible app 1. | |
| 76 ASSERT_TRUE(candidates[2].app()); | |
| 77 EXPECT_EQ("visible2", candidates[2].app()->process_name()); | |
| 78 // pinned and media. | |
| 79 ASSERT_TRUE(candidates[3].tab()); | |
| 80 EXPECT_EQ(300, candidates[3].tab()->tab_contents_id); | |
| 81 // media. | |
| 82 ASSERT_TRUE(candidates[4].tab()); | |
| 83 EXPECT_EQ(400, candidates[4].tab()->tab_contents_id); | |
| 84 // pinned. | |
| 85 ASSERT_TRUE(candidates[5].tab()); | |
| 86 EXPECT_EQ(100, candidates[5].tab()->tab_contents_id); | |
| 87 // chrome app. | |
| 88 ASSERT_TRUE(candidates[6].tab()); | |
| 89 EXPECT_EQ(500, candidates[6].tab()->tab_contents_id); | |
| 90 // internal page. | |
| 91 ASSERT_TRUE(candidates[7].tab()); | |
| 92 EXPECT_EQ(200, candidates[7].tab()->tab_contents_id); | |
| 93 // background service. | |
| 94 ASSERT_TRUE(candidates[8].app()); | |
| 95 EXPECT_EQ("service", candidates[8].app()->process_name()); | |
| 96 } | |
| 97 | |
| 98 // Occasionally, Chrome sees both FOCUSED_TAB and FOCUSED_APP at the same time. | |
| 99 // Test that Chrome treats the former as a more important process. | |
| 100 TEST_F(TabManagerDelegateTest, CandidatesSortedWithFocusedAppAndTab) { | |
| 101 std::vector<arc::ArcProcess> arc_processes; | |
| 102 arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP, | |
| 103 kIsFocused, 100); | |
| 104 TabStats tab1; | |
| 105 tab1.tab_contents_id = 100; | |
| 106 tab1.is_pinned = true; | |
| 107 tab1.is_selected = true; | |
| 108 const TabStatsList tab_list = {tab1}; | |
| 109 | |
| 110 const std::vector<TabManagerDelegate::Candidate> candidates = | |
| 111 TabManagerDelegate::GetSortedCandidates(tab_list, arc_processes); | |
| 112 ASSERT_EQ(2U, candidates.size()); | |
| 113 // FOCUSED_TAB should be the first one. | |
| 114 ASSERT_TRUE(candidates[0].tab()); | |
| 115 EXPECT_EQ(100, candidates[0].tab()->tab_contents_id); | |
| 116 ASSERT_TRUE(candidates[1].app()); | |
| 117 EXPECT_EQ("focused", candidates[1].app()->process_name()); | |
| 118 } | |
| 119 | |
| 120 class MockTabManagerDelegate : public TabManagerDelegate { | |
| 121 public: | |
| 122 MockTabManagerDelegate() | |
| 123 : TabManagerDelegate(nullptr), | |
| 124 always_return_true_from_is_recently_killed_(false) {} | |
| 125 | |
| 126 explicit MockTabManagerDelegate(TabManagerDelegate::MemoryStat* mem_stat) | |
| 127 : TabManagerDelegate(nullptr, mem_stat), | |
| 128 always_return_true_from_is_recently_killed_(false) {} | |
| 129 | |
| 130 // unit test. | |
| 131 std::vector<int> GetKilledArcProcesses() { | |
| 132 return killed_arc_processes_; | |
| 133 } | |
| 134 | |
| 135 // unit test. | |
| 136 std::vector<int64_t> GetKilledTabs() { | |
| 137 return killed_tabs_; | |
| 138 } | |
| 139 | |
| 140 // unit test. | |
| 141 void Clear() { | |
| 142 killed_arc_processes_.clear(); | |
| 143 killed_tabs_.clear(); | |
| 144 } | |
| 145 | |
| 146 // unit test. | |
| 147 void set_always_return_true_from_is_recently_killed( | |
| 148 bool always_return_true_from_is_recently_killed) { | |
| 149 always_return_true_from_is_recently_killed_ = | |
| 150 always_return_true_from_is_recently_killed; | |
| 151 } | |
| 152 | |
| 153 bool IsRecentlyKilledArcProcess(const std::string& process_name, | |
| 154 const base::TimeTicks& now) override { | |
| 155 if (always_return_true_from_is_recently_killed_) | |
| 156 return true; | |
| 157 return TabManagerDelegate::IsRecentlyKilledArcProcess(process_name, now); | |
| 158 } | |
| 159 | |
| 160 protected: | |
| 161 bool KillArcProcess(const int nspid) override { | |
| 162 killed_arc_processes_.push_back(nspid); | |
| 163 return true; | |
| 164 } | |
| 165 | |
| 166 bool KillTab(int64_t tab_id) override { | |
| 167 killed_tabs_.push_back(tab_id); | |
| 168 return true; | |
| 169 } | |
| 170 | |
| 171 chromeos::DebugDaemonClient* GetDebugDaemonClient() override { | |
| 172 return &debugd_client_; | |
| 173 } | |
| 174 | |
| 175 private: | |
| 176 chromeos::FakeDebugDaemonClient debugd_client_; | |
| 177 std::vector<int> killed_arc_processes_; | |
| 178 std::vector<int64_t> killed_tabs_; | |
| 179 bool always_return_true_from_is_recently_killed_; | |
| 180 }; | |
| 181 | |
| 182 class MockMemoryStat : public TabManagerDelegate::MemoryStat { | |
| 183 public: | |
| 184 MockMemoryStat() {} | |
| 185 ~MockMemoryStat() override {} | |
| 186 | |
| 187 int TargetMemoryToFreeKB() override { | |
| 188 return target_memory_to_free_kb_; | |
| 189 } | |
| 190 | |
| 191 int EstimatedMemoryFreedKB(base::ProcessHandle pid) override { | |
| 192 return process_pss_[pid]; | |
| 193 } | |
| 194 | |
| 195 // unittest. | |
| 196 void SetTargetMemoryToFreeKB(const int target) { | |
| 197 target_memory_to_free_kb_ = target; | |
| 198 } | |
| 199 | |
| 200 // unittest. | |
| 201 void SetProcessPss(base::ProcessHandle pid, int pss) { | |
| 202 process_pss_[pid] = pss; | |
| 203 } | |
| 204 | |
| 205 private: | |
| 206 int target_memory_to_free_kb_; | |
| 207 std::map<base::ProcessHandle, int> process_pss_; | |
| 208 }; | |
| 209 | |
| 210 TEST_F(TabManagerDelegateTest, SetOomScoreAdj) { | |
| 211 MockTabManagerDelegate tab_manager_delegate; | |
| 212 | |
| 213 std::vector<arc::ArcProcess> arc_processes; | |
| 214 arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP, | |
| 215 kIsFocused, 100); | |
| 216 arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP, | |
| 217 kNotFocused, 200); | |
| 218 arc_processes.emplace_back( | |
| 219 3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500); | |
| 220 arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP, | |
| 221 kNotFocused, 150); | |
| 222 arc_processes.emplace_back(5, 50, "persistent", | |
| 223 arc::mojom::ProcessState::PERSISTENT, kNotFocused, | |
| 224 600); | |
| 225 arc_processes.emplace_back(6, 60, "persistent_ui", | |
| 226 arc::mojom::ProcessState::PERSISTENT_UI, | |
| 227 kNotFocused, 700); | |
| 228 | |
| 229 TabStats tab1, tab2, tab3, tab4, tab5; | |
| 230 tab1.is_pinned = true; | |
| 231 tab1.renderer_handle = 11; | |
| 232 | |
| 233 tab2.is_internal_page = true; | |
| 234 tab2.renderer_handle = 11; | |
| 235 | |
| 236 tab3.is_pinned = true; | |
| 237 tab3.is_media = true; | |
| 238 tab3.renderer_handle = 12; | |
| 239 | |
| 240 tab4.is_media = true; | |
| 241 tab4.renderer_handle = 12; | |
| 242 | |
| 243 tab5.is_app = true; | |
| 244 tab5.renderer_handle = 12; | |
| 245 TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5}; | |
| 246 | |
| 247 // Sorted order (by GetSortedCandidates): | |
| 248 // app "focused" pid: 10 | |
| 249 // app "persistent" pid: 50 | |
| 250 // app "persistent_ui" pid: 60 | |
| 251 // app "visible1" pid: 20 | |
| 252 // app "visible2" pid: 40 | |
| 253 // tab3 pid: 12 | |
| 254 // tab4 pid: 12 | |
| 255 // tab1 pid: 11 | |
| 256 // tab5 pid: 12 | |
| 257 // tab2 pid: 11 | |
| 258 // app "service" pid: 30 | |
| 259 tab_manager_delegate.AdjustOomPrioritiesImpl(tab_list, arc_processes); | |
| 260 auto& oom_score_map = tab_manager_delegate.oom_score_map_; | |
| 261 | |
| 262 // 6 PIDs for apps + 2 PIDs for tabs. | |
| 263 EXPECT_EQ(6U + 2U, oom_score_map.size()); | |
| 264 | |
| 265 // Non-killable part. AdjustOomPrioritiesImpl() does make a focused app/tab | |
| 266 // kernel-killable, but does not do that for PERSISTENT and PERSISTENT_UI | |
| 267 // apps. | |
| 268 EXPECT_EQ(TabManagerDelegate::kLowestOomScore, oom_score_map[50]); | |
| 269 EXPECT_EQ(TabManagerDelegate::kLowestOomScore, oom_score_map[60]); | |
| 270 | |
| 271 // Higher priority part. | |
| 272 EXPECT_EQ(300, oom_score_map[10]); | |
| 273 EXPECT_EQ(344, oom_score_map[20]); | |
| 274 EXPECT_EQ(388, oom_score_map[40]); | |
| 275 EXPECT_EQ(431, oom_score_map[12]); | |
| 276 EXPECT_EQ(475, oom_score_map[11]); | |
| 277 | |
| 278 // Lower priority part. | |
| 279 EXPECT_EQ(650, oom_score_map[30]); | |
| 280 } | |
| 281 | |
| 282 TEST_F(TabManagerDelegateTest, IsRecentlyKilledArcProcess) { | |
| 283 constexpr char kProcessName1[] = "org.chromium.arc.test1"; | |
| 284 constexpr char kProcessName2[] = "org.chromium.arc.test2"; | |
| 285 | |
| 286 // Not owned. | |
| 287 MockMemoryStat* memory_stat = new MockMemoryStat(); | |
| 288 // Instantiate the mock instance. | |
| 289 MockTabManagerDelegate tab_manager_delegate(memory_stat); | |
| 290 | |
| 291 // When the process name is not in the map, IsRecentlyKilledArcProcess should | |
| 292 // return false. | |
| 293 const base::TimeTicks now = base::TimeTicks::Now(); | |
| 294 EXPECT_FALSE( | |
| 295 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName1, now)); | |
| 296 EXPECT_FALSE( | |
| 297 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName2, now)); | |
| 298 | |
| 299 // Update the map to tell the manager that the process was killed very | |
| 300 // recently. | |
| 301 tab_manager_delegate.recently_killed_arc_processes_[kProcessName1] = now; | |
| 302 EXPECT_TRUE( | |
| 303 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName1, now)); | |
| 304 EXPECT_FALSE( | |
| 305 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName2, now)); | |
| 306 tab_manager_delegate.recently_killed_arc_processes_[kProcessName1] = | |
| 307 now - base::TimeDelta::FromMicroseconds(1); | |
| 308 EXPECT_TRUE( | |
| 309 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName1, now)); | |
| 310 EXPECT_FALSE( | |
| 311 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName2, now)); | |
| 312 tab_manager_delegate.recently_killed_arc_processes_[kProcessName1] = | |
| 313 now - TabManagerDelegate::GetArcRespawnKillDelay(); | |
| 314 EXPECT_TRUE( | |
| 315 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName1, now)); | |
| 316 EXPECT_FALSE( | |
| 317 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName2, now)); | |
| 318 | |
| 319 // Update the map to tell the manager that the process was killed | |
| 320 // (GetArcRespawnKillDelay() + 1) seconds ago. In this case, | |
| 321 // IsRecentlyKilledArcProcess(kProcessName1) should return false. | |
| 322 tab_manager_delegate.recently_killed_arc_processes_[kProcessName1] = | |
| 323 now - TabManagerDelegate::GetArcRespawnKillDelay() - | |
| 324 base::TimeDelta::FromSeconds(1); | |
| 325 EXPECT_FALSE( | |
| 326 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName1, now)); | |
| 327 EXPECT_FALSE( | |
| 328 tab_manager_delegate.IsRecentlyKilledArcProcess(kProcessName2, now)); | |
| 329 } | |
| 330 | |
| 331 TEST_F(TabManagerDelegateTest, DoNotKillRecentlyKilledArcProcesses) { | |
| 332 // Not owned. | |
| 333 MockMemoryStat* memory_stat = new MockMemoryStat(); | |
| 334 | |
| 335 // Instantiate the mock instance. | |
| 336 MockTabManagerDelegate tab_manager_delegate(memory_stat); | |
| 337 tab_manager_delegate.set_always_return_true_from_is_recently_killed(true); | |
| 338 | |
| 339 std::vector<arc::ArcProcess> arc_processes; | |
| 340 arc_processes.emplace_back( | |
| 341 1, 10, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500); | |
| 342 | |
| 343 memory_stat->SetTargetMemoryToFreeKB(250000); | |
| 344 memory_stat->SetProcessPss(30, 10000); | |
| 345 TabStatsList tab_list; | |
| 346 tab_manager_delegate.LowMemoryKillImpl(tab_list, arc_processes); | |
| 347 | |
| 348 auto killed_arc_processes = tab_manager_delegate.GetKilledArcProcesses(); | |
| 349 EXPECT_EQ(0U, killed_arc_processes.size()); | |
| 350 } | |
| 351 | |
| 352 TEST_F(TabManagerDelegateTest, KillMultipleProcesses) { | |
| 353 // Not owned. | |
| 354 MockMemoryStat* memory_stat = new MockMemoryStat(); | |
| 355 | |
| 356 // Instantiate the mock instance. | |
| 357 MockTabManagerDelegate tab_manager_delegate(memory_stat); | |
| 358 | |
| 359 std::vector<arc::ArcProcess> arc_processes; | |
| 360 arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP, | |
| 361 kIsFocused, 100); | |
| 362 arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP, | |
| 363 kNotFocused, 200); | |
| 364 arc_processes.emplace_back( | |
| 365 3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500); | |
| 366 arc_processes.emplace_back(4, 40, "visible2", | |
| 367 arc::mojom::ProcessState::IMPORTANT_FOREGROUND, | |
| 368 kNotFocused, 150); | |
| 369 arc_processes.emplace_back(5, 50, "not-visible", | |
| 370 arc::mojom::ProcessState::IMPORTANT_BACKGROUND, | |
| 371 kNotFocused, 300); | |
| 372 arc_processes.emplace_back(6, 60, "persistent", | |
| 373 arc::mojom::ProcessState::PERSISTENT, kNotFocused, | |
| 374 400); | |
| 375 | |
| 376 TabStats tab1, tab2, tab3, tab4, tab5; | |
| 377 tab1.is_pinned = true; | |
| 378 tab1.renderer_handle = 11; | |
| 379 tab1.tab_contents_id = 1; | |
| 380 | |
| 381 tab2.is_internal_page = true; | |
| 382 tab2.renderer_handle = 11; | |
| 383 tab2.tab_contents_id = 2; | |
| 384 | |
| 385 tab3.is_pinned = true; | |
| 386 tab3.is_media = true; | |
| 387 tab3.renderer_handle = 12; | |
| 388 tab3.tab_contents_id = 3; | |
| 389 | |
| 390 tab4.is_media = true; | |
| 391 tab4.renderer_handle = 12; | |
| 392 tab4.tab_contents_id = 4; | |
| 393 | |
| 394 tab5.is_app = true; | |
| 395 tab5.renderer_handle = 12; | |
| 396 tab5.tab_contents_id = 5; | |
| 397 TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5}; | |
| 398 | |
| 399 // Sorted order (by GetSortedCandidates): | |
| 400 // app "focused" pid: 10 nspid 1 | |
| 401 // app "persistent" pid: 60 nspid 6 | |
| 402 // app "visible1" pid: 20 nspid 2 | |
| 403 // app "visible2" pid: 40 nspid 4 | |
| 404 // tab3 pid: 12 tab_contents_id 3 | |
| 405 // tab4 pid: 12 tab_contents_id 4 | |
| 406 // tab1 pid: 11 tab_contents_id 1 | |
| 407 // tab5 pid: 12 tab_contents_id 5 | |
| 408 // tab2 pid: 11 tab_contents_id 2 | |
| 409 // app "not-visible" pid: 50 nspid 5 | |
| 410 // app "service" pid: 30 nspid 3 | |
| 411 memory_stat->SetTargetMemoryToFreeKB(250000); | |
| 412 // Entities to be killed. | |
| 413 memory_stat->SetProcessPss(30, 10000); | |
| 414 memory_stat->SetProcessPss(50, 5000); | |
| 415 memory_stat->SetProcessPss(11, 200000); | |
| 416 memory_stat->SetProcessPss(12, 30000); | |
| 417 // Should not be used. | |
| 418 memory_stat->SetProcessPss(60, 500000); | |
| 419 memory_stat->SetProcessPss(40, 50000); | |
| 420 memory_stat->SetProcessPss(20, 30000); | |
| 421 memory_stat->SetProcessPss(10, 100000); | |
| 422 | |
| 423 tab_manager_delegate.LowMemoryKillImpl(tab_list, arc_processes); | |
| 424 | |
| 425 auto killed_arc_processes = tab_manager_delegate.GetKilledArcProcesses(); | |
| 426 auto killed_tabs = tab_manager_delegate.GetKilledTabs(); | |
| 427 | |
| 428 // Killed apps and their nspid. | |
| 429 ASSERT_EQ(2U, killed_arc_processes.size()); | |
| 430 EXPECT_EQ(3, killed_arc_processes[0]); | |
| 431 EXPECT_EQ(5, killed_arc_processes[1]); | |
| 432 // Killed tabs and their content id. | |
| 433 // Note that process with pid 11 is counted twice. But so far I don't have a | |
| 434 // good way to estimate the memory freed if multiple tabs share one process. | |
| 435 ASSERT_EQ(3U, killed_tabs.size()); | |
| 436 EXPECT_EQ(2, killed_tabs[0]); | |
| 437 EXPECT_EQ(5, killed_tabs[1]); | |
| 438 EXPECT_EQ(1, killed_tabs[2]); | |
| 439 | |
| 440 // Check that killed apps are in the map. | |
| 441 const TabManagerDelegate::KilledArcProcessesMap& processes_map = | |
| 442 tab_manager_delegate.recently_killed_arc_processes_; | |
| 443 EXPECT_EQ(2U, processes_map.size()); | |
| 444 EXPECT_EQ(1U, processes_map.count("service")); | |
| 445 EXPECT_EQ(1U, processes_map.count("not-visible")); | |
| 446 } | |
| 447 | |
| 448 } // namespace memory | |
| OLD | NEW |