| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/task_management/sampling/task_group.h" |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/task_management/task_manager_observer.h" |
| #include "components/nacl/browser/nacl_browser.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| namespace task_management { |
| |
| namespace { |
| |
| inline bool IsResourceRefreshEnabled(RefreshType refresh_type, |
| int refresh_flags) { |
| return (refresh_flags & refresh_type) != 0; |
| } |
| |
| #if defined(OS_WIN) |
| // Gets the GDI and USER Handles on Windows at one shot. |
| void GetWindowsHandles(base::ProcessHandle handle, |
| int64* out_gdi_current, |
| int64* out_gdi_peak, |
| int64* out_user_current, |
| int64* out_user_peak) { |
| *out_gdi_current = 0; |
| *out_gdi_peak = 0; |
| *out_user_current = 0; |
| *out_user_peak = 0; |
| // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. |
| HANDLE current_process = GetCurrentProcess(); |
| HANDLE process_with_query_rights; |
| if (DuplicateHandle(current_process, handle, current_process, |
| &process_with_query_rights, PROCESS_QUERY_INFORMATION, |
| false, 0)) { |
| *out_gdi_current = static_cast<int64>( |
| GetGuiResources(process_with_query_rights, GR_GDIOBJECTS)); |
| *out_gdi_peak = static_cast<int64>( |
| GetGuiResources(process_with_query_rights, GR_GDIOBJECTS_PEAK)); |
| *out_user_current = static_cast<int64>( |
| GetGuiResources(process_with_query_rights, GR_USEROBJECTS)); |
| *out_user_peak = static_cast<int64>( |
| GetGuiResources(process_with_query_rights, GR_USEROBJECTS_PEAK)); |
| CloseHandle(process_with_query_rights); |
| } |
| } |
| #endif // defined(OS_WIN) |
| |
| } // namespace |
| |
| TaskGroup::TaskGroup( |
| base::ProcessHandle proc_handle, |
| base::ProcessId proc_id, |
| const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner) |
| : process_handle_(proc_handle), |
| process_id_(proc_id), |
| worker_thread_sampler_(nullptr), |
| tasks_(), |
| cpu_usage_(0.0), |
| memory_usage_(), |
| gpu_memory_(-1), |
| #if defined(OS_WIN) |
| gdi_current_handles_(-1), |
| gdi_peak_handles_(-1), |
| user_current_handles_(-1), |
| user_peak_handles_(-1), |
| #endif // defined(OS_WIN) |
| #if !defined(DISABLE_NACL) |
| nacl_debug_stub_port_(-1), |
| #endif // !defined(DISABLE_NACL) |
| idle_wakeups_per_second_(-1), |
| gpu_memory_has_duplicates_(false), |
| weak_ptr_factory_(this) { |
| scoped_refptr<TaskGroupSampler> sampler( |
| new TaskGroupSampler(proc_handle, |
| blocking_pool_runner, |
| base::Bind(&TaskGroup::OnCpuRefreshDone, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&TaskGroup::OnMemoryUsageRefreshDone, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&TaskGroup::OnIdleWakeupsRefreshDone, |
| weak_ptr_factory_.GetWeakPtr()))); |
| worker_thread_sampler_.swap(sampler); |
| } |
| |
| TaskGroup::~TaskGroup() { |
| } |
| |
| void TaskGroup::AddTask(Task* task) { |
| DCHECK(task); |
| DCHECK(task->process_id() == process_id_); |
| DCHECK(!ContainsKey(tasks_, task->task_id())); |
| |
| tasks_[task->task_id()] = task; |
| } |
| |
| void TaskGroup::RemoveTask(Task* task) { |
| DCHECK(task); |
| DCHECK(ContainsKey(tasks_, task->task_id())); |
| |
| tasks_.erase(task->task_id()); |
| } |
| |
| void TaskGroup::Refresh( |
| const content::GPUVideoMemoryUsageStats& gpu_memory_stats, |
| base::TimeDelta update_interval, |
| int64 refresh_flags) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // First refresh the enabled non-expensive resources usages on the UI thread. |
| // 1- Refresh all the tasks. |
| for (auto& task_pair : tasks_) |
| task_pair.second->Refresh(update_interval, refresh_flags); |
| |
| // 2- Refresh GPU memory (if enabled). |
| if (IsResourceRefreshEnabled(REFRESH_TYPE_GPU_MEMORY, refresh_flags)) |
| RefreshGpuMemory(gpu_memory_stats); |
| |
| // 3- Refresh Windows handles (if enabled). |
| #if defined(OS_WIN) |
| if (IsResourceRefreshEnabled(REFRESH_TYPE_HANDLES, refresh_flags)) |
| RefreshWindowsHandles(); |
| #endif // defined(OS_WIN) |
| |
| // 4- Refresh the NACL debug stub port (if enabled). |
| #if !defined(DISABLE_NACL) |
| if (IsResourceRefreshEnabled(REFRESH_TYPE_NACL, refresh_flags) && |
| !tasks_.empty()) { |
| RefreshNaClDebugStubPort(tasks_.begin()->second->GetChildProcessUniqueID()); |
| } |
| #endif // !defined(DISABLE_NACL) |
| |
| // The remaining resource refreshes are time consuming and cannot be done on |
| // the UI thread. Do them all on the worker thread using the TaskGroupSampler. |
| // 5- CPU usage. |
| // 6- Memory usage. |
| // 7- Idle Wakeups per second. |
| worker_thread_sampler_->Refresh(refresh_flags); |
| } |
| |
| void TaskGroup::AppendSortedTaskIds(TaskIdList* out_list) const { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(out_list); |
| |
| for (const auto& task_pair : tasks_) |
| out_list->push_back(task_pair.first); |
| } |
| |
| Task* TaskGroup::GetTaskById(TaskId task_id) const { |
| DCHECK(ContainsKey(tasks_, task_id)); |
| |
| return tasks_.at(task_id); |
| } |
| |
| void TaskGroup::RefreshGpuMemory( |
| const content::GPUVideoMemoryUsageStats& gpu_memory_stats) { |
| auto itr = gpu_memory_stats.process_map.find(process_id_); |
| if (itr == gpu_memory_stats.process_map.end()) { |
| gpu_memory_ = -1; |
| gpu_memory_has_duplicates_ = false; |
| return; |
| } |
| |
| gpu_memory_ = static_cast<int64>(itr->second.video_memory); |
| gpu_memory_has_duplicates_ = itr->second.has_duplicates; |
| } |
| |
| void TaskGroup::RefreshWindowsHandles() { |
| #if defined(OS_WIN) |
| GetWindowsHandles(process_handle_, |
| &gdi_current_handles_, |
| &gdi_peak_handles_, |
| &user_current_handles_, |
| &user_peak_handles_); |
| #endif // defined(OS_WIN) |
| } |
| |
| void TaskGroup::RefreshNaClDebugStubPort(int child_process_unique_id) { |
| #if !defined(DISABLE_NACL) |
| nacl::NaClBrowser* nacl_browser = nacl::NaClBrowser::GetInstance(); |
| nacl_debug_stub_port_ = |
| nacl_browser->GetProcessGdbDebugStubPort(child_process_unique_id); |
| #endif // !defined(DISABLE_NACL) |
| } |
| |
| void TaskGroup::OnCpuRefreshDone(double cpu_usage) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| cpu_usage_ = cpu_usage; |
| } |
| |
| void TaskGroup::OnMemoryUsageRefreshDone(MemoryUsageStats memory_usage) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| memory_usage_ = memory_usage; |
| } |
| |
| void TaskGroup::OnIdleWakeupsRefreshDone(int idle_wakeups_per_second) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| idle_wakeups_per_second_ = idle_wakeups_per_second; |
| } |
| |
| } // namespace task_management |