Chromium Code Reviews| 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(601, Task::SANDBOX_HELPER, "Cycle 3", -1); | |
|
afakhry
2016/06/03 17:53:42
Same group but different PIDs?
ncarter (slow)
2016/06/20 17:40:00
Good catch! Fixed.
| |
| 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 "Child of Cycle 4\n" // Child of 4, PLUGIN | |
| 208 "Cycle 3\n" // Child of 4, SANDBOX_HELPER (> PLUGIN) | |
| 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 |