blob: 487239075005992997f2769648e920447186f930 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/task_manager/sampling/task_group_sampler.h"
#include <limits>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/process/process_metrics.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "chrome/browser/task_manager/task_manager_observer.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_thread.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <psapi.h>
#endif
namespace task_manager {
namespace {
std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
base::ProcessHandle handle) {
#if !BUILDFLAG(IS_MAC)
return base::ProcessMetrics::CreateProcessMetrics(handle);
#else
return base::ProcessMetrics::CreateProcessMetrics(
handle, content::BrowserChildProcessHost::GetPortProvider());
#endif
}
} // namespace
TaskGroupSampler::TaskGroupSampler(
base::Process process,
const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner,
const OnCpuRefreshCallback& on_cpu_refresh,
const OnSwappedMemRefreshCallback& on_swapped_mem_refresh,
const OnIdleWakeupsCallback& on_idle_wakeups,
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
const OnOpenFdCountCallback& on_open_fd_count,
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
const OnProcessPriorityCallback& on_process_priority)
: process_(std::move(process)),
process_metrics_(CreateProcessMetrics(process_.Handle())),
blocking_pool_runner_(blocking_pool_runner),
on_cpu_refresh_callback_(on_cpu_refresh),
on_swapped_mem_refresh_callback_(on_swapped_mem_refresh),
on_idle_wakeups_callback_(on_idle_wakeups),
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
on_open_fd_count_callback_(on_open_fd_count),
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
on_process_priority_callback_(on_process_priority) {
DCHECK(blocking_pool_runner.get());
// This object will be created on the UI thread, however the sequenced checker
// will be used to assert we're running the expensive operations on one of the
// blocking pool threads.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DETACH_FROM_SEQUENCE(worker_pool_sequenced_checker_);
}
void TaskGroupSampler::Refresh(int64_t refresh_flags) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_CPU,
refresh_flags)) {
blocking_pool_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&TaskGroupSampler::RefreshCpuUsage, this),
base::BindOnce(on_cpu_refresh_callback_));
}
if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_SWAPPED_MEM,
refresh_flags)) {
blocking_pool_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&TaskGroupSampler::RefreshSwappedMem, this),
base::BindOnce(on_swapped_mem_refresh_callback_));
}
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS,
refresh_flags)) {
blocking_pool_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TaskGroupSampler::RefreshIdleWakeupsPerSecond, this),
base::BindOnce(on_idle_wakeups_callback_));
}
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_FD_COUNT,
refresh_flags)) {
blocking_pool_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&TaskGroupSampler::RefreshOpenFdCount, this),
base::BindOnce(on_open_fd_count_callback_));
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_PRIORITY,
refresh_flags)) {
blocking_pool_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TaskGroupSampler::RefreshProcessPriority, this),
base::BindOnce(on_process_priority_callback_));
}
}
TaskGroupSampler::~TaskGroupSampler() = default;
double TaskGroupSampler::RefreshCpuUsage() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
// TODO(https://crbug.com/331250452): Errors are converted to 0.0 for
// backwards compatibility. The values returned from this are surfaced by the
// `chrome.processes` extension API, so changing this will need developer
// outreach.
double cpu_usage =
process_metrics_->GetPlatformIndependentCPUUsage().value_or(0.0);
if (!cpu_usage_calculated_) {
// First successful call to GetPlatformIndependentCPUUsage returns 0. Ignore
// it, and return NaN.
cpu_usage_calculated_ = true;
return std::numeric_limits<double>::quiet_NaN();
}
return cpu_usage;
}
base::ByteCount TaskGroupSampler::RefreshSwappedMem() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
#if BUILDFLAG(IS_CHROMEOS)
auto info = process_metrics_->GetMemoryInfo();
if (!info.has_value()) {
return base::ByteCount(0);
}
return base::ByteCount(info->vm_swap_bytes);
#else
return base::ByteCount(0);
#endif // BUILDFLAG(IS_CHROMEOS)
}
int TaskGroupSampler::RefreshIdleWakeupsPerSecond() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
return process_metrics_->GetIdleWakeupsPerSecond();
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
int TaskGroupSampler::RefreshOpenFdCount() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
return process_metrics_->GetOpenFdCount();
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
base::Process::Priority TaskGroupSampler::RefreshProcessPriority() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
#if BUILDFLAG(IS_MAC)
if (process_.is_current()) {
base::SelfPortProvider self_provider;
return process_.GetPriority(&self_provider);
}
return process_.GetPriority(
content::BrowserChildProcessHost::GetPortProvider());
#else
return process_.GetPriority();
#endif // BUILDFLAG(IS_MAC)
}
} // namespace task_manager