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