| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 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 <string> |
| 6 #include <utility> |
| 7 #include <vector> |
| 8 |
| 9 #include "base/macros.h" |
| 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "chrome/browser/task_management/providers/task.h" |
| 12 #include "chrome/browser/task_management/sampling/task_manager_impl.h" |
| 13 #include "chrome/browser/task_management/task_manager_observer.h" |
| 14 #include "content/public/test/test_browser_thread_bundle.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 namespace task_management { |
| 18 |
| 19 namespace { |
| 20 |
| 21 // A Task for unittests, not backed by a real process, that can report any given |
| 22 // value. |
| 23 class FakeTask : public Task { |
| 24 public: |
| 25 FakeTask(base::ProcessId process_id, |
| 26 Type type, |
| 27 const std::string& title, |
| 28 int tab_id) |
| 29 : Task(base::ASCIIToUTF16(title), |
| 30 "FakeTask", |
| 31 nullptr, |
| 32 base::kNullProcessHandle, |
| 33 process_id), |
| 34 type_(type), |
| 35 parent_(nullptr), |
| 36 tab_id_(tab_id) { |
| 37 TaskManagerImpl::GetInstance()->TaskAdded(this); |
| 38 } |
| 39 |
| 40 ~FakeTask() override { TaskManagerImpl::GetInstance()->TaskRemoved(this); } |
| 41 |
| 42 Type GetType() const override { return type_; } |
| 43 |
| 44 int GetChildProcessUniqueID() const override { return 0; } |
| 45 |
| 46 const Task* GetParentTask() const override { return parent_; } |
| 47 |
| 48 int GetTabId() const override { return tab_id_; } |
| 49 |
| 50 void SetParent(Task* parent) { parent_ = parent; } |
| 51 |
| 52 private: |
| 53 Type type_; |
| 54 Task* parent_; |
| 55 int tab_id_; |
| 56 |
| 57 DISALLOW_COPY_AND_ASSIGN(FakeTask); |
| 58 }; |
| 59 |
| 60 } // namespace |
| 61 |
| 62 class TaskManagerImplTest : public testing::Test, public TaskManagerObserver { |
| 63 public: |
| 64 TaskManagerImplTest() |
| 65 : TaskManagerObserver(base::TimeDelta::FromSeconds(1), |
| 66 REFRESH_TYPE_NONE) { |
| 67 TaskManagerImpl::GetInstance()->AddObserver(this); |
| 68 } |
| 69 ~TaskManagerImplTest() override { |
| 70 tasks_.clear(); |
| 71 observed_task_manager()->RemoveObserver(this); |
| 72 } |
| 73 |
| 74 FakeTask* AddTask(int pid_offset, |
| 75 Task::Type type, |
| 76 const std::string& title, |
| 77 int tab_id) { |
| 78 // Offset based on the current process id, to avoid collisions with the |
| 79 // browser process task. |
| 80 base::ProcessId process_id = base::GetCurrentProcId() + pid_offset; |
| 81 tasks_.emplace_back(new FakeTask(process_id, type, title, tab_id)); |
| 82 return tasks_.back().get(); |
| 83 } |
| 84 |
| 85 std::string DumpSortedTasks() { |
| 86 std::string result; |
| 87 for (TaskId task_id : observed_task_manager()->GetTaskIdsList()) { |
| 88 result += base::UTF16ToUTF8(observed_task_manager()->GetTitle(task_id)); |
| 89 result += "\n"; |
| 90 } |
| 91 return result; |
| 92 } |
| 93 |
| 94 private: |
| 95 content::TestBrowserThreadBundle thread_bundle_; |
| 96 std::vector<std::unique_ptr<FakeTask>> tasks_; |
| 97 DISALLOW_COPY_AND_ASSIGN(TaskManagerImplTest); |
| 98 }; |
| 99 |
| 100 TEST_F(TaskManagerImplTest, SortingTypes) { |
| 101 AddTask(100, Task::GPU, "Gpu Process", -1); |
| 102 |
| 103 Task* tab1 = AddTask(200, Task::RENDERER, "Tab One", 10); |
| 104 AddTask(400, Task::EXTENSION, "Extension Subframe: Tab One", 10) |
| 105 ->SetParent(tab1); |
| 106 AddTask(300, Task::RENDERER, "Subframe: Tab One", 10)->SetParent(tab1); |
| 107 |
| 108 Task* tab2 = |
| 109 AddTask(200, Task::RENDERER, "Tab Two: sharing process with Tab One", 20); |
| 110 |
| 111 AddTask(301, Task::RENDERER, "Subframe: Tab Two", 20)->SetParent(tab2); |
| 112 AddTask(400, Task::EXTENSION, "Extension Subframe: Tab Two", 20) |
| 113 ->SetParent(tab2); |
| 114 |
| 115 AddTask(600, Task::ARC, "ARC", -1); |
| 116 AddTask(800, Task::UTILITY, "Utility One", -1); |
| 117 AddTask(700, Task::UTILITY, "Utility Two", -1); |
| 118 AddTask(1000, Task::GUEST, "Guest", 20); |
| 119 AddTask(900, Task::WORKER, "Worker", -1); |
| 120 AddTask(500, Task::ZYGOTE, "Zygote", -1); |
| 121 |
| 122 AddTask(300, Task::RENDERER, "Subframe: Tab One (2)", 10)->SetParent(tab1); |
| 123 AddTask(300, Task::RENDERER, "Subframe: Tab One (third)", 10) |
| 124 ->SetParent(tab1); |
| 125 AddTask(300, Task::RENDERER, "Subframe: Tab One (4)", 10)->SetParent(tab1); |
| 126 |
| 127 EXPECT_EQ( |
| 128 "Browser\n" |
| 129 "Gpu Process\n" |
| 130 "ARC\n" |
| 131 "Zygote\n" |
| 132 "Utility One\n" |
| 133 "Utility Two\n" |
| 134 "Tab One\n" |
| 135 "Tab Two: sharing process with Tab One\n" |
| 136 "Subframe: Tab One\n" |
| 137 "Subframe: Tab One (2)\n" |
| 138 "Subframe: Tab One (third)\n" |
| 139 "Subframe: Tab One (4)\n" |
| 140 "Extension Subframe: Tab One\n" |
| 141 "Extension Subframe: Tab Two\n" |
| 142 "Subframe: Tab Two\n" |
| 143 "Guest\n" |
| 144 "Worker\n", |
| 145 DumpSortedTasks()); |
| 146 } |
| 147 |
| 148 TEST_F(TaskManagerImplTest, SortingCycles) { |
| 149 // Two tabs, with subframes in the other's process. This induces a cycle in |
| 150 // the TaskGroup dependencies, without being a cycle in the Tasks. This can |
| 151 // happen in practice. |
| 152 Task* tab1 = AddTask(200, Task::RENDERER, "Tab 1: Process 200", 10); |
| 153 AddTask(300, Task::RENDERER, "Subframe in Tab 1: Process 300", 10) |
| 154 ->SetParent(tab1); |
| 155 Task* tab2 = AddTask(300, Task::RENDERER, "Tab 2: Process 300", 20); |
| 156 AddTask(200, Task::RENDERER, "Subframe in Tab 2: Process 200", 20) |
| 157 ->SetParent(tab2); |
| 158 |
| 159 // Simulated GPU process. |
| 160 AddTask(100, Task::GPU, "Gpu Process", -1); |
| 161 |
| 162 // Two subframes that list each other as a parent (a true cycle). This |
| 163 // shouldn't happen in practice, but we want the sorting code to handle it |
| 164 // gracefully. |
| 165 FakeTask* cycle1 = AddTask(501, Task::SANDBOX_HELPER, "Cycle 1", -1); |
| 166 FakeTask* cycle2 = AddTask(500, Task::ARC, "Cycle 2", -1); |
| 167 cycle1->SetParent(cycle2); |
| 168 cycle2->SetParent(cycle1); |
| 169 |
| 170 // A cycle where both elements are in the same group. |
| 171 FakeTask* cycle3 = AddTask(600, Task::SANDBOX_HELPER, "Cycle 3", -1); |
| 172 FakeTask* cycle4 = AddTask(600, Task::ARC, "Cycle 4", -1); |
| 173 cycle3->SetParent(cycle4); |
| 174 cycle4->SetParent(cycle3); |
| 175 |
| 176 // Tasks listing a cycle as their parent. |
| 177 FakeTask* lollipop5 = AddTask(701, Task::EXTENSION, "Child of Cycle 3", -1); |
| 178 lollipop5->SetParent(cycle3); |
| 179 FakeTask* lollipop6 = AddTask(700, Task::PLUGIN, "Child of Cycle 4", -1); |
| 180 lollipop6->SetParent(cycle4); |
| 181 |
| 182 // A task listing itself as parent. |
| 183 FakeTask* self_cycle = AddTask(800, Task::RENDERER, "Self Cycle", 5); |
| 184 self_cycle->SetParent(self_cycle); |
| 185 |
| 186 // Add a plugin child to tab1 and tab2. |
| 187 AddTask(900, Task::PLUGIN, "Plugin: Tab 2", 20)->SetParent(tab1); |
| 188 AddTask(901, Task::PLUGIN, "Plugin: Tab 1", 10)->SetParent(tab1); |
| 189 |
| 190 // Finish with a normal renderer task. |
| 191 AddTask(903, Task::RENDERER, "Tab: Normal Renderer", 30); |
| 192 |
| 193 // Cycles should wind up on the bottom of the list. |
| 194 EXPECT_EQ( |
| 195 "Browser\n" |
| 196 "Gpu Process\n" |
| 197 "Tab 1: Process 200\n" |
| 198 "Subframe in Tab 2: Process 200\n" |
| 199 "Tab 2: Process 300\n" |
| 200 "Subframe in Tab 1: Process 300\n" |
| 201 "Plugin: Tab 1\n" |
| 202 "Plugin: Tab 2\n" |
| 203 "Tab: Normal Renderer\n" |
| 204 "Cycle 2\n" // ARC |
| 205 "Cycle 1\n" // Child of 2 |
| 206 "Cycle 4\n" // ARC; task_id > Cycle 2's |
| 207 "Cycle 3\n" // Same-process child of 4 (SANDBOX_HELPER > ARC) |
| 208 "Child of Cycle 4\n" // Child of 4 |
| 209 "Child of Cycle 3\n" // Child of 3 |
| 210 "Self Cycle\n", // RENDERER (> ARC) |
| 211 DumpSortedTasks()); |
| 212 } |
| 213 |
| 214 } // namespace task_management |
| OLD | NEW |