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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/task_manager/sampling/task_group.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/stl_util.h"
13 #include "build/build_config.h"
14 #include "chrome/browser/task_manager/sampling/shared_sampler.h"
15 #include "chrome/browser/task_manager/task_manager_observer.h"
16 #include "components/nacl/browser/nacl_browser.h"
17 #include "content/public/browser/browser_task_traits.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "gpu/ipc/common/memory_stats.h"
20
21 #if defined(OS_WIN)
22 #include <windows.h>
23 #endif
24
25 namespace task_manager {
26
27 namespace {
28
29 // A mask for the refresh types that are done in the background thread.
30 const int kBackgroundRefreshTypesMask =
31 REFRESH_TYPE_CPU | REFRESH_TYPE_SWAPPED_MEM | REFRESH_TYPE_IDLE_WAKEUPS |
32 #if defined(OS_WIN)
33 REFRESH_TYPE_START_TIME | REFRESH_TYPE_CPU_TIME |
34 #endif // defined(OS_WIN)
35 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
36 REFRESH_TYPE_FD_COUNT |
37 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
38 #if BUILDFLAG(ENABLE_NACL)
39 REFRESH_TYPE_NACL |
40 #endif // BUILDFLAG(ENABLE_NACL)
41 REFRESH_TYPE_PRIORITY;
42
43 #if defined(OS_WIN)
44 // Gets the GDI and USER Handles on Windows at one shot.
GetWindowsHandles(base::ProcessHandle handle,int64_t * out_gdi_current,int64_t * out_gdi_peak,int64_t * out_user_current,int64_t * out_user_peak)45 void GetWindowsHandles(base::ProcessHandle handle,
46 int64_t* out_gdi_current,
47 int64_t* out_gdi_peak,
48 int64_t* out_user_current,
49 int64_t* out_user_peak) {
50 *out_gdi_current = 0;
51 *out_gdi_peak = 0;
52 *out_user_current = 0;
53 *out_user_peak = 0;
54 // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
55 HANDLE current_process = GetCurrentProcess();
56 HANDLE process_with_query_rights;
57 if (DuplicateHandle(current_process, handle, current_process,
58 &process_with_query_rights, PROCESS_QUERY_INFORMATION,
59 false, 0)) {
60 *out_gdi_current = static_cast<int64_t>(
61 GetGuiResources(process_with_query_rights, GR_GDIOBJECTS));
62 *out_gdi_peak = static_cast<int64_t>(
63 GetGuiResources(process_with_query_rights, GR_GDIOBJECTS_PEAK));
64 *out_user_current = static_cast<int64_t>(
65 GetGuiResources(process_with_query_rights, GR_USEROBJECTS));
66 *out_user_peak = static_cast<int64_t>(
67 GetGuiResources(process_with_query_rights, GR_USEROBJECTS_PEAK));
68 CloseHandle(process_with_query_rights);
69 }
70 }
71 #endif // defined(OS_WIN)
72
73 #if BUILDFLAG(ENABLE_NACL)
GetNaClDebugStubPortOnIoThread(int process_id)74 int GetNaClDebugStubPortOnIoThread(int process_id) {
75 return nacl::NaClBrowser::GetInstance()->GetProcessGdbDebugStubPort(
76 process_id);
77 }
78 #endif // BUILDFLAG(ENABLE_NACL)
79
80 } // namespace
81
TaskGroup(base::ProcessHandle proc_handle,base::ProcessId proc_id,bool is_running_in_vm,const base::RepeatingClosure & on_background_calculations_done,const scoped_refptr<SharedSampler> & shared_sampler,const scoped_refptr<base::SequencedTaskRunner> & blocking_pool_runner)82 TaskGroup::TaskGroup(
83 base::ProcessHandle proc_handle,
84 base::ProcessId proc_id,
85 bool is_running_in_vm,
86 const base::RepeatingClosure& on_background_calculations_done,
87 const scoped_refptr<SharedSampler>& shared_sampler,
88 const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner)
89 : process_handle_(proc_handle),
90 process_id_(proc_id),
91 is_running_in_vm_(is_running_in_vm),
92 on_background_calculations_done_(on_background_calculations_done),
93 worker_thread_sampler_(nullptr),
94 shared_sampler_(shared_sampler),
95 #if defined(OS_CHROMEOS)
96 arc_shared_sampler_(nullptr),
97 #endif // defined(OS_CHROMEOS)
98 expected_on_bg_done_flags_(kBackgroundRefreshTypesMask),
99 current_on_bg_done_flags_(0),
100 platform_independent_cpu_usage_(std::numeric_limits<double>::quiet_NaN()),
101 swapped_mem_bytes_(-1),
102 memory_footprint_(-1),
103 gpu_memory_(-1),
104 per_process_network_usage_rate_(-1),
105 cumulative_per_process_network_usage_(0),
106 #if defined(OS_WIN)
107 gdi_current_handles_(-1),
108 gdi_peak_handles_(-1),
109 user_current_handles_(-1),
110 user_peak_handles_(-1),
111 hard_faults_per_second_(-1),
112 #endif // defined(OS_WIN)
113 #if BUILDFLAG(ENABLE_NACL)
114 nacl_debug_stub_port_(nacl::kGdbDebugStubPortUnknown),
115 #endif // BUILDFLAG(ENABLE_NACL)
116 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
117 open_fd_count_(-1),
118 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
119 idle_wakeups_per_second_(-1),
120 gpu_memory_has_duplicates_(false),
121 is_backgrounded_(false) {
122 if (process_id_ != base::kNullProcessId && !is_running_in_vm_) {
123 worker_thread_sampler_ = base::MakeRefCounted<TaskGroupSampler>(
124 base::Process::Open(process_id_), blocking_pool_runner,
125 base::BindRepeating(&TaskGroup::OnCpuRefreshDone,
126 weak_ptr_factory_.GetWeakPtr()),
127 base::BindRepeating(&TaskGroup::OnSwappedMemRefreshDone,
128 weak_ptr_factory_.GetWeakPtr()),
129 base::BindRepeating(&TaskGroup::OnIdleWakeupsRefreshDone,
130 weak_ptr_factory_.GetWeakPtr()),
131 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
132 base::BindRepeating(&TaskGroup::OnOpenFdCountRefreshDone,
133 weak_ptr_factory_.GetWeakPtr()),
134 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
135 base::BindRepeating(&TaskGroup::OnProcessPriorityDone,
136 weak_ptr_factory_.GetWeakPtr()));
137
138 shared_sampler_->RegisterCallback(
139 process_id_, base::BindRepeating(&TaskGroup::OnSamplerRefreshDone,
140 base::Unretained(this)));
141 }
142 }
143
~TaskGroup()144 TaskGroup::~TaskGroup() {
145 shared_sampler_->UnregisterCallback(process_id_);
146 #if defined(OS_CHROMEOS)
147 if (arc_shared_sampler_)
148 arc_shared_sampler_->UnregisterCallback(process_id_);
149 #endif // defined(OS_CHROMEOS)
150 }
151
AddTask(Task * task)152 void TaskGroup::AddTask(Task* task) {
153 DCHECK(task);
154 tasks_.push_back(task);
155 }
156
RemoveTask(Task * task)157 void TaskGroup::RemoveTask(Task* task) {
158 DCHECK(task);
159 base::Erase(tasks_, task);
160 }
161
Refresh(const gpu::VideoMemoryUsageStats & gpu_memory_stats,base::TimeDelta update_interval,int64_t refresh_flags)162 void TaskGroup::Refresh(const gpu::VideoMemoryUsageStats& gpu_memory_stats,
163 base::TimeDelta update_interval,
164 int64_t refresh_flags) {
165 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
166 DCHECK(!empty());
167 if (is_running_in_vm_)
168 refresh_flags &= ~kUnsupportedVMRefreshFlags;
169
170 expected_on_bg_done_flags_ = refresh_flags & kBackgroundRefreshTypesMask;
171 // If a refresh type was recently disabled, we need to account for that too.
172 current_on_bg_done_flags_ &= expected_on_bg_done_flags_;
173
174 // First refresh the enabled non-expensive resources usages on the UI thread.
175 // 1- Refresh all the tasks as well as the total network usage (if enabled).
176 const bool network_usage_refresh_enabled =
177 TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_NETWORK_USAGE,
178 refresh_flags);
179
180 per_process_network_usage_rate_ = network_usage_refresh_enabled ? 0 : -1;
181 cumulative_per_process_network_usage_ = 0;
182 for (Task* task : tasks_) {
183 task->Refresh(update_interval, refresh_flags);
184 if (network_usage_refresh_enabled) {
185 per_process_network_usage_rate_ += task->network_usage_rate();
186 cumulative_per_process_network_usage_ += task->cumulative_network_usage();
187 }
188 }
189
190 // 2- Refresh GPU memory (if enabled).
191 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_GPU_MEMORY,
192 refresh_flags)) {
193 RefreshGpuMemory(gpu_memory_stats);
194 }
195
196 // 3- Refresh Windows handles (if enabled).
197 #if defined(OS_WIN)
198 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_HANDLES,
199 refresh_flags)) {
200 RefreshWindowsHandles();
201 }
202 #endif // defined(OS_WIN)
203
204 // 4- Refresh the NACL debug stub port (if enabled). This calls out to
205 // NaClBrowser on the browser's IO thread, completing asynchronously.
206 #if BUILDFLAG(ENABLE_NACL)
207 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_NACL,
208 refresh_flags)) {
209 RefreshNaClDebugStubPort(tasks_[0]->GetChildProcessUniqueID());
210 }
211 #endif // BUILDFLAG(ENABLE_NACL)
212
213 int64_t shared_refresh_flags =
214 refresh_flags & shared_sampler_->GetSupportedFlags();
215
216 // 5- Refresh resources via SharedSampler if the current platform
217 // implementation supports that. The actual work is done on the worker thread.
218 // At the moment this is supported only on OS_WIN.
219 if (shared_refresh_flags != 0) {
220 shared_sampler_->Refresh(process_id_, shared_refresh_flags);
221 refresh_flags &= ~shared_refresh_flags;
222 }
223
224 // The remaining resource refreshes are time consuming and cannot be done on
225 // the UI thread. Do them all on the worker thread using the TaskGroupSampler.
226 // 6- CPU usage.
227 // 7- Memory usage.
228 // 8- Idle Wakeups per second.
229 // 9- (Linux and ChromeOS only) The number of file descriptors current open.
230 // 10- Process priority (foreground vs. background).
231 if (worker_thread_sampler_)
232 worker_thread_sampler_->Refresh(refresh_flags);
233 }
234
GetTaskById(TaskId task_id) const235 Task* TaskGroup::GetTaskById(TaskId task_id) const {
236 for (Task* task : tasks_) {
237 if (task->task_id() == task_id)
238 return task;
239 }
240 NOTREACHED();
241 return nullptr;
242 }
243
ClearCurrentBackgroundCalculationsFlags()244 void TaskGroup::ClearCurrentBackgroundCalculationsFlags() {
245 current_on_bg_done_flags_ = 0;
246 }
247
AreBackgroundCalculationsDone() const248 bool TaskGroup::AreBackgroundCalculationsDone() const {
249 return expected_on_bg_done_flags_ == current_on_bg_done_flags_;
250 }
251
252 #if defined(OS_CHROMEOS)
SetArcSampler(ArcSharedSampler * sampler)253 void TaskGroup::SetArcSampler(ArcSharedSampler* sampler) {
254 DCHECK(sampler);
255 arc_shared_sampler_ = sampler;
256 arc_shared_sampler_->RegisterCallback(
257 process_id_, base::BindRepeating(&TaskGroup::OnArcSamplerRefreshDone,
258 weak_ptr_factory_.GetWeakPtr()));
259 }
260 #endif // defined(OS_CHROMEOS)
261
RefreshGpuMemory(const gpu::VideoMemoryUsageStats & gpu_memory_stats)262 void TaskGroup::RefreshGpuMemory(
263 const gpu::VideoMemoryUsageStats& gpu_memory_stats) {
264 auto itr = gpu_memory_stats.process_map.find(process_id_);
265 if (itr == gpu_memory_stats.process_map.end()) {
266 gpu_memory_ = -1;
267 gpu_memory_has_duplicates_ = false;
268 return;
269 }
270
271 gpu_memory_ = itr->second.video_memory;
272 gpu_memory_has_duplicates_ = itr->second.has_duplicates;
273 }
274
RefreshWindowsHandles()275 void TaskGroup::RefreshWindowsHandles() {
276 #if defined(OS_WIN)
277 GetWindowsHandles(process_handle_,
278 &gdi_current_handles_,
279 &gdi_peak_handles_,
280 &user_current_handles_,
281 &user_peak_handles_);
282 #endif // defined(OS_WIN)
283 }
284
285 #if BUILDFLAG(ENABLE_NACL)
RefreshNaClDebugStubPort(int child_process_unique_id)286 void TaskGroup::RefreshNaClDebugStubPort(int child_process_unique_id) {
287 content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
288 FROM_HERE,
289 base::BindOnce(&GetNaClDebugStubPortOnIoThread, child_process_unique_id),
290 base::BindOnce(&TaskGroup::OnRefreshNaClDebugStubPortDone,
291 weak_ptr_factory_.GetWeakPtr()));
292 }
293
OnRefreshNaClDebugStubPortDone(int nacl_debug_stub_port)294 void TaskGroup::OnRefreshNaClDebugStubPortDone(int nacl_debug_stub_port) {
295 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
296
297 nacl_debug_stub_port_ = nacl_debug_stub_port;
298 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_NACL);
299 }
300 #endif // BUILDFLAG(ENABLE_NACL)
301
302 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
OnOpenFdCountRefreshDone(int open_fd_count)303 void TaskGroup::OnOpenFdCountRefreshDone(int open_fd_count) {
304 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
305
306 open_fd_count_ = open_fd_count;
307 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_FD_COUNT);
308 }
309 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD)
310
OnCpuRefreshDone(double cpu_usage)311 void TaskGroup::OnCpuRefreshDone(double cpu_usage) {
312 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
313
314 platform_independent_cpu_usage_ = cpu_usage;
315 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_CPU);
316 }
317
OnSwappedMemRefreshDone(int64_t swapped_mem_bytes)318 void TaskGroup::OnSwappedMemRefreshDone(int64_t swapped_mem_bytes) {
319 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
320
321 swapped_mem_bytes_ = swapped_mem_bytes;
322 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_SWAPPED_MEM);
323 }
324
OnProcessPriorityDone(bool is_backgrounded)325 void TaskGroup::OnProcessPriorityDone(bool is_backgrounded) {
326 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
327
328 is_backgrounded_ = is_backgrounded;
329 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_PRIORITY);
330 }
331
OnIdleWakeupsRefreshDone(int idle_wakeups_per_second)332 void TaskGroup::OnIdleWakeupsRefreshDone(int idle_wakeups_per_second) {
333 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
334
335 idle_wakeups_per_second_ = idle_wakeups_per_second;
336 OnBackgroundRefreshTypeFinished(REFRESH_TYPE_IDLE_WAKEUPS);
337 }
338
OnSamplerRefreshDone(base::Optional<SharedSampler::SamplingResult> results)339 void TaskGroup::OnSamplerRefreshDone(
340 base::Optional<SharedSampler::SamplingResult> results) {
341 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
342
343 // If any of the Optional<> fields have no value then replace them with
344 // sentinel values.
345 // TODO(wez): Migrate the TaskGroup fields to Optional<> so we can remove
346 // the need for all this sentinel-handling logic.
347 if (results) {
348 cpu_time_ = results->cpu_time;
349 idle_wakeups_per_second_ = results->idle_wakeups_per_second;
350 #if defined(OS_WIN)
351 hard_faults_per_second_ = results->hard_faults_per_second;
352 #endif
353 start_time_ = results->start_time;
354 } else {
355 cpu_time_ = base::TimeDelta();
356 idle_wakeups_per_second_ = -1;
357 #if defined(OS_WIN)
358 hard_faults_per_second_ = 0;
359 #endif
360 start_time_ = base::Time();
361 }
362
363 OnBackgroundRefreshTypeFinished(expected_on_bg_done_flags_ &
364 shared_sampler_->GetSupportedFlags());
365 }
366
367 #if defined(OS_CHROMEOS)
OnArcSamplerRefreshDone(base::Optional<ArcSharedSampler::MemoryFootprintBytes> memory_footprint)368 void TaskGroup::OnArcSamplerRefreshDone(
369 base::Optional<ArcSharedSampler::MemoryFootprintBytes> memory_footprint) {
370 if (memory_footprint)
371 set_footprint_bytes(*memory_footprint);
372 }
373 #endif // defined(OS_CHROMEOS)
374
OnBackgroundRefreshTypeFinished(int64_t finished_refresh_type)375 void TaskGroup::OnBackgroundRefreshTypeFinished(int64_t finished_refresh_type) {
376 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
377
378 current_on_bg_done_flags_ |= finished_refresh_type;
379 if (AreBackgroundCalculationsDone())
380 on_background_calculations_done_.Run();
381 }
382
383 } // namespace task_manager
384