0

Return expected from ProcessMetrics CPU methods

Also adds base::OptionalFromExpected and base::OptionalToExpected to
base/types/optional_util.h, because converting to an optional in one
line would require annoying casting.

Bug: 40285287
Change-Id: I84c4d789d4b7b31b25fb87d7032bcf26a5e8bf4e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5362194
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: David Roger <droger@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Auto-Submit: Joe Mason <joenotcharles@google.com>
Commit-Queue: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1277773}
This commit is contained in:
Joe Mason
2024-03-25 18:07:31 +00:00
committed by Chromium LUCI CQ
parent 7c312cdcad
commit 550eed6bb7
19 changed files with 236 additions and 83 deletions

@ -123,12 +123,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage(
return 100.0 * cpu_time_delta / time_delta;
}
std::optional<double> ProcessMetrics::GetPlatformIndependentCPUUsage() {
const std::optional<TimeDelta> cpu_usage = GetCumulativeCPUUsage();
if (!cpu_usage.has_value()) {
return std::nullopt;
}
return GetPlatformIndependentCPUUsage(cpu_usage.value());
base::expected<double, ProcessCPUUsageError>
ProcessMetrics::GetPlatformIndependentCPUUsage() {
return GetCumulativeCPUUsage().transform([this](base::TimeDelta cpu_usage) {
return GetPlatformIndependentCPUUsage(cpu_usage);
});
}
#endif

@ -12,7 +12,6 @@
#include <stdint.h>
#include <memory>
#include <optional>
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
@ -20,6 +19,7 @@
#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
@ -64,6 +64,19 @@ struct PageFaultCounts {
// Convert a POSIX timeval to microseconds.
BASE_EXPORT int64_t TimeValToMicroseconds(const struct timeval& tv);
enum class ProcessCPUUsageError {
// The OS returned an error while measuring the CPU usage. The possible causes
// vary by platform.
kSystemError,
// Process CPU usage couldn't be measured because the process wasn't running.
// Some platforms may return kSystemError instead in this situation.
kProcessNotFound,
// CPU usage measurement isn't implemented on this platform.
kNotImplemented,
};
// Provides performance metrics for a specified process (CPU usage and IO
// counters). Use CreateCurrentProcessMetrics() to get an instance for the
// current process, or CreateProcessMetrics() to get an instance for an
@ -127,14 +140,16 @@ class BASE_EXPORT ProcessMetrics {
// Same as the above, but automatically calls GetCumulativeCPUUsage() to
// determine the current cumulative CPU. Returns nullopt if
// GetCumulativeCPUUsage() fails.
[[nodiscard]] std::optional<double> GetPlatformIndependentCPUUsage();
[[nodiscard]] base::expected<double, ProcessCPUUsageError>
GetPlatformIndependentCPUUsage();
// Returns the cumulative CPU usage across all threads of the process since
// process start, or nullopt on error. In case of multi-core processors, a
// process can consume CPU at a rate higher than wall-clock time, e.g. two
// cores at full utilization will result in a time delta of 2 seconds/per 1
// wall-clock second.
[[nodiscard]] std::optional<TimeDelta> GetCumulativeCPUUsage();
[[nodiscard]] base::expected<TimeDelta, ProcessCPUUsageError>
GetCumulativeCPUUsage();
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
BUILDFLAG(IS_AIX)

@ -21,6 +21,7 @@
#include "base/notimplemented.h"
#include "base/numerics/safe_math.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_MAC)
@ -44,16 +45,21 @@ namespace base {
namespace {
bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
base::expected<task_basic_info_64, ProcessCPUUsageError> GetTaskInfo(
mach_port_t task) {
if (task == MACH_PORT_NULL) {
return false;
return base::unexpected(ProcessCPUUsageError::kProcessNotFound);
}
task_basic_info_64 task_info_data{};
mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
kern_return_t kr =
task_info(task, TASK_BASIC_INFO_64,
reinterpret_cast<task_info_t>(task_info_data), &count);
reinterpret_cast<task_info_t>(&task_info_data), &count);
// Most likely cause for failure: |task| is a zombie.
return kr == KERN_SUCCESS;
if (kr != KERN_SUCCESS) {
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
return base::ok(task_info_data);
}
MachVMRegionResult ParseOutputFromMachVMRegion(kern_return_t kr) {
@ -95,10 +101,11 @@ mach_port_t ProcessMetrics::TaskForHandle(ProcessHandle process_handle) const {
return task;
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
mach_port_t task = TaskForHandle(process_);
if (task == MACH_PORT_NULL) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kProcessNotFound);
}
// Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
@ -110,12 +117,13 @@ std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
&thread_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie.
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
task_basic_info_64 task_info_data;
if (!GetTaskInfo(task, &task_info_data)) {
return std::nullopt;
const base::expected<task_basic_info_64, ProcessCPUUsageError>
task_info_data = GetTaskInfo(task);
if (!task_info_data.has_value()) {
return base::unexpected(task_info_data.error());
}
/* Set total_time. */
@ -126,8 +134,8 @@ std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
timeradd(&user_timeval, &system_timeval, &task_timeval);
// ... task info contains terminated time.
TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval);
TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval);
TIME_VALUE_TO_TIMEVAL(&task_info_data->user_time, &user_timeval);
TIME_VALUE_TO_TIMEVAL(&task_info_data->system_time, &system_timeval);
timeradd(&user_timeval, &task_timeval, &task_timeval);
timeradd(&system_timeval, &task_timeval, &task_timeval);
@ -139,10 +147,10 @@ std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
// a lag before it shows up in the terminated thread times returned by
// GetTaskInfo(). Make sure CPU usage doesn't appear to go backwards if
// GetCumulativeCPUUsage() is called in the interval.
return std::optional(last_measured_cpu_);
return base::ok(last_measured_cpu_);
}
last_measured_cpu_ = measured_cpu;
return std::optional(measured_cpu);
return base::ok(measured_cpu);
}
int ProcessMetrics::GetPackageIdleWakeupsPerSecond() {

@ -23,20 +23,22 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return WrapUnique(new ProcessMetrics(process));
}
std::optional<double> ProcessMetrics::GetPlatformIndependentCPUUsage() {
base::expected<double, ProcessCPUUsageError>
ProcessMetrics::GetPlatformIndependentCPUUsage() {
struct kinfo_proc info;
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process_};
size_t length = sizeof(info);
if (sysctl(mib, std::size(mib), &info, &length, NULL, 0) < 0)
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
return std::optional(double{info.ki_pctcpu} / FSCALE * 100.0);
return base::ok(double{info.ki_pctcpu} / FSCALE * 100.0);
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kNotImplemented);
}
size_t GetSystemCommitCharge() {

@ -35,16 +35,17 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return base::WrapUnique(new ProcessMetrics(process));
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
zx_info_task_runtime_t stats;
zx_status_t status = zx::unowned_process(process_)->get_info(
ZX_INFO_TASK_RUNTIME, &stats, sizeof(stats), nullptr, nullptr);
if (status != ZX_OK) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
return std::optional(TimeDelta::FromZxDuration(stats.cpu_time));
return base::ok(TimeDelta::FromZxDuration(stats.cpu_time));
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {

@ -35,6 +35,7 @@
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/threading/thread_restrictions.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/strings/ascii.h"
@ -59,26 +60,27 @@ uint64_t ReadFileToUint64(const FilePath& file) {
}
#endif
// Get the total CPU from a proc stat buffer. Return value is a TimeDelta
// converted from a number of jiffies on success or nullopt if parsing failed.
std::optional<TimeDelta> ParseTotalCPUTimeFromStats(
// Get the total CPU from a proc stat buffer. Return value is a TimeDelta
// converted from a number of jiffies on success or an error code if parsing
// failed.
base::expected<TimeDelta, ProcessCPUUsageError> ParseTotalCPUTimeFromStats(
base::span<const std::string> proc_stats) {
const std::optional<int64_t> utime =
internal::GetProcStatsFieldAsOptionalInt64(proc_stats,
internal::VM_UTIME);
if (utime.value_or(-1) < 0) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
const std::optional<int64_t> stime =
internal::GetProcStatsFieldAsOptionalInt64(proc_stats,
internal::VM_UTIME);
if (stime.value_or(-1) < 0) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
const TimeDelta cpu_time = internal::ClockTicksToTimeDelta(
base::ClampAdd(utime.value(), stime.value()));
CHECK(!cpu_time.is_negative());
return std::optional(cpu_time);
return base::ok(cpu_time);
}
} // namespace
@ -94,12 +96,13 @@ size_t ProcessMetrics::GetResidentSetSize() const {
checked_cast<size_t>(getpagesize());
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
std::string buffer;
std::vector<std::string> proc_stats;
if (!internal::ReadProcStats(process_, &buffer) ||
!internal::ParseProcStats(buffer, &proc_stats)) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
return ParseTotalCPUTimeFromStats(proc_stats);
@ -121,7 +124,7 @@ bool ProcessMetrics::GetCumulativeCPUUsagePerThread(
return;
}
const std::optional<TimeDelta> thread_time =
const base::expected<TimeDelta, ProcessCPUUsageError> thread_time =
ParseTotalCPUTimeFromStats(proc_stats);
if (thread_time.has_value()) {
cpu_per_thread.emplace_back(tid, thread_time.value());

@ -8,31 +8,31 @@
#include <stdint.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <optional>
#include "base/memory/ptr_util.h"
#include "base/types/expected.h"
namespace base {
namespace {
static std::optional<int> GetProcessCPU(pid_t pid) {
base::expected<int, ProcessCPUUsageError> GetProcessCPU(pid_t pid) {
struct kinfo_proc info;
size_t length;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
sizeof(struct kinfo_proc), 0 };
if (sysctl(mib, std::size(mib), NULL, &length, NULL, 0) < 0) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
mib[5] = (length / sizeof(struct kinfo_proc));
if (sysctl(mib, std::size(mib), &info, &length, NULL, 0) < 0) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
return std::optional(info.p_pctcpu);
return base::ok(info.p_pctcpu);
}
} // namespace
@ -43,27 +43,29 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return WrapUnique(new ProcessMetrics(process));
}
std::optional<double> ProcessMetrics::GetPlatformIndependentCPUUsage() {
base::expected<double, ProcessCPUUsageError>
ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeTicks time = TimeTicks::Now();
if (last_cpu_time_.is_zero()) {
// First call, just set the last values.
last_cpu_time_ = time;
return std::optional(0.0);
return base::ok(0.0);
}
const std::optional<int> cpu = GetProcessCPU(process_);
const base::expected<int, ProcessCPUUsageError> cpu = GetProcessCPU(process_);
if (!cpu.has_value()) {
return std::nullopt;
return base::unexpected(cpu.error());
}
last_cpu_time_ = time;
return std::optional(double{cpu.value()} / FSCALE * 100.0);
return base::ok(double{cpu.value()} / FSCALE * 100.0);
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kNotImplemented);
}
ProcessMetrics::ProcessMetrics(ProcessHandle process)

@ -8,7 +8,6 @@
#include <stdint.h>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <utility>
@ -30,10 +29,12 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/test/gmock_expected_support.h"
#include "base/test/gtest_util.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "base/types/expected.h"
#include "build/blink_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@ -66,11 +67,13 @@ namespace base::debug {
namespace {
using base::test::ErrorIs;
using base::test::ValueIs;
using ::testing::_;
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
using ::testing::Ge;
using ::testing::Optional;
#if ENABLE_CPU_TESTS
@ -88,10 +91,10 @@ void BusyWork(std::vector<std::string>* vec) {
// returns an empty TimeDelta so that each test doesn't need to check for
// nullopt repeatedly.
TimeDelta TestCumulativeCPU(ProcessMetrics* metrics, TimeDelta prev_cpu_usage) {
const std::optional<TimeDelta> current_cpu_usage =
const base::expected<TimeDelta, ProcessCPUUsageError> current_cpu_usage =
metrics->GetCumulativeCPUUsage();
EXPECT_THAT(current_cpu_usage, Optional(Ge(prev_cpu_usage)));
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), Optional(Ge(0.0)));
EXPECT_THAT(current_cpu_usage, ValueIs(Ge(prev_cpu_usage)));
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ValueIs(Ge(0.0)));
return current_cpu_usage.value_or(TimeDelta());
}
@ -634,7 +637,7 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
std::unique_ptr<ProcessMetrics> metrics =
ProcessMetrics::CreateCurrentProcessMetrics();
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), Optional(Ge(0.0)));
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ValueIs(Ge(0.0)));
Thread thread1("thread1");
Thread thread2("thread2");
@ -699,8 +702,8 @@ TEST_F(SystemMetricsTest, MeasureChildCpuUsage) {
TestCumulativeCPU(metrics.get(), cpu_usage2);
#else
// All other platforms return an error.
EXPECT_EQ(metrics->GetCumulativeCPUUsage(), std::nullopt);
EXPECT_EQ(metrics->GetPlatformIndependentCPUUsage(), std::nullopt);
EXPECT_THAT(metrics->GetCumulativeCPUUsage(), ErrorIs(_));
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ErrorIs(_));
#endif
}
@ -714,8 +717,8 @@ TEST_F(SystemMetricsTest, InvalidProcessCpuUsage) {
std::unique_ptr<ProcessMetrics> metrics =
ProcessMetrics::CreateProcessMetrics(kNullProcessHandle);
#endif
EXPECT_EQ(metrics->GetCumulativeCPUUsage(), std::nullopt);
EXPECT_EQ(metrics->GetPlatformIndependentCPUUsage(), std::nullopt);
EXPECT_THAT(metrics->GetCumulativeCPUUsage(), ErrorIs(_));
EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), ErrorIs(_));
}
#endif // ENABLE_CPU_TESTS

@ -120,7 +120,7 @@ struct SYSTEM_PERFORMANCE_INFORMATION {
ULONG SystemCalls;
};
std::optional<TimeDelta> GetImpreciseCumulativeCPUUsage(
base::expected<TimeDelta, ProcessCPUUsageError> GetImpreciseCumulativeCPUUsage(
const win::ScopedHandle& process) {
FILETIME creation_time;
FILETIME exit_time;
@ -128,18 +128,18 @@ std::optional<TimeDelta> GetImpreciseCumulativeCPUUsage(
FILETIME user_time;
if (!process.is_valid()) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
if (!GetProcessTimes(process.get(), &creation_time, &exit_time, &kernel_time,
&user_time)) {
// This should never fail when the handle is valid.
NOTREACHED(NotFatalUntil::M125);
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
return std::optional(TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time));
return base::ok(TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time));
}
} // namespace
@ -161,7 +161,8 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return WrapUnique(new ProcessMetrics(process));
}
std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
base::expected<TimeDelta, ProcessCPUUsageError>
ProcessMetrics::GetCumulativeCPUUsage() {
#if defined(ARCH_CPU_ARM64)
// Precise CPU usage is not available on Arm CPUs because they don't support
// constant rate TSC.
@ -181,18 +182,18 @@ std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
}
if (!process_.is_valid()) {
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kProcessNotFound);
}
ULONG64 process_cycle_time = 0;
if (!QueryProcessCycleTime(process_.get(), &process_cycle_time)) {
// This should never fail when the handle is valid.
NOTREACHED(NotFatalUntil::M125);
return std::nullopt;
return base::unexpected(ProcessCPUUsageError::kSystemError);
}
const double process_time_seconds = process_cycle_time / tsc_ticks_per_second;
return std::optional(Seconds(process_time_seconds));
return base::ok(Seconds(process_time_seconds));
#endif // !defined(ARCH_CPU_ARM64)
}

@ -5,7 +5,11 @@
#ifndef BASE_TYPES_OPTIONAL_UTIL_H_
#define BASE_TYPES_OPTIONAL_UTIL_H_
#include <concepts>
#include <optional>
#include <utility>
#include "base/types/expected.h"
namespace base {
@ -60,6 +64,48 @@ std::optional<T> OptionalFromPtr(const T* value) {
return value ? std::optional<T>(*value) : std::nullopt;
}
// Helper for creating a `base::expected<U, F>` from an `std::optional<T>` and
// an error of type E, where T is convertible to U and E is convertible to F. If
// `opt` contains a value, this copies it into the `base::expected`, otherwise
// it moves `err` in.
template <class T, class E, class U = T, class F = E>
base::expected<U, F> OptionalToExpected(const std::optional<T>& opt, E&& err)
requires(std::convertible_to<T, U> && std::copyable<T> &&
std::convertible_to<E, F> && std::movable<E>)
{
if (opt.has_value()) {
return base::ok(opt.value());
}
return base::unexpected(std::move(err));
}
// As above, but copies `err` into the `base:expected` if `opt` doesn't contain
// a value.
template <class T, class E, class U = T, class F = E>
base::expected<U, F> OptionalToExpected(const std::optional<T>& opt,
const E& err)
requires(std::convertible_to<T, U> && std::copyable<T> &&
std::convertible_to<E, F> && std::copyable<E>)
{
if (opt.has_value()) {
return base::ok(opt.value());
}
return base::unexpected(err);
}
// Helper for creating an `std::optional<U>` from a `base::expected<T, E>`,
// where T is convertible to U. If `exp` contains a value, this copies it into
// the `std::optional`, otherwise it returns std::nullopt.
template <class T, class E, class U = T>
std::optional<U> OptionalFromExpected(const base::expected<T, E>& exp)
requires(std::convertible_to<T, U>)
{
if (exp.has_value()) {
return std::optional(exp.value());
}
return std::nullopt;
}
} // namespace base
#endif // BASE_TYPES_OPTIONAL_UTIL_H_

@ -4,6 +4,11 @@
#include "base/types/optional_util.h"
#include <memory>
#include <optional>
#include <string>
#include "base/types/expected.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@ -27,5 +32,65 @@ TEST(OptionalUtilTest, OptionalFromPtr) {
EXPECT_EQ(optional_f, OptionalFromPtr(&f));
}
TEST(OptionalUtilTest, OptionalToExpected) {
std::optional<int> i_opt;
// No conversions.
base::expected<int, int> i_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(i_exp, base::unexpected(-1));
// Error type converted.
i_exp = OptionalToExpected(i_opt, -1.0);
EXPECT_EQ(i_exp, base::unexpected(-1));
i_opt = 2;
// No conversions.
i_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(i_exp, base::ok(2));
// Value type converted.
base::expected<float, int> f_exp = OptionalToExpected(i_opt, -1);
EXPECT_EQ(f_exp, base::ok(2.0));
// Non-movable error type. "is null" is a const char array, which must be
// copied before converting to a string. Forces the compiler to choose the
// OptionalToExpected override that copies its error argument, to validate
// that it's copied correctly.
auto exp_with_str_error =
OptionalToExpected<int, std::string>(std::nullopt, "is null");
EXPECT_EQ(exp_with_str_error, base::unexpected("is null"));
// Non-copyable error type. Forces the compiler to choose the
// OptionalToExpected override that moves its error argument, to validate that
// it's moved correctly.
auto exp_with_ptr_error = OptionalToExpected<int, std::unique_ptr<int>>(
std::nullopt, std::make_unique<int>(-1));
ASSERT_FALSE(exp_with_ptr_error.has_value());
EXPECT_EQ(*(exp_with_ptr_error.error()), -1);
}
TEST(OptionalUtilTest, OptionalFromExpected) {
base::expected<int, std::string> i_exp = base::unexpected("uninitialized");
// No conversion.
std::optional<int> i_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(i_opt, std::nullopt);
// Value type converted.
std::optional<float> f_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(f_opt, std::nullopt);
i_exp = base::ok(1);
// No conversion.
i_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(i_opt, 1);
// Value type converted.
f_opt = OptionalFromExpected(i_exp);
EXPECT_EQ(f_opt, 1.0);
}
} // namespace
} // namespace base

@ -16,6 +16,8 @@
#include "base/process/process_metrics.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/types/optional_util.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
#include "chrome/browser/metrics/power/power_metrics_constants.h"
@ -58,7 +60,8 @@ std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
ProcessMonitor::Metrics SampleMetrics(base::ProcessMetrics& process_metrics) {
ProcessMonitor::Metrics metrics;
metrics.cpu_usage = process_metrics.GetPlatformIndependentCPUUsage();
metrics.cpu_usage = base::OptionalFromExpected(
process_metrics.GetPlatformIndependentCPUUsage());
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_AIX)

@ -5,13 +5,13 @@
#include "chrome/browser/task_manager/sampling/task_group_sampler.h"
#include <limits>
#include <optional>
#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 "build/chromeos_buildflags.h"
#include "chrome/browser/task_manager/task_manager_observer.h"
@ -119,7 +119,7 @@ TaskGroupSampler::~TaskGroupSampler() {
double TaskGroupSampler::RefreshCpuUsage() {
DCHECK_CALLED_ON_VALID_SEQUENCE(worker_pool_sequenced_checker_);
const std::optional<double> cpu_usage =
const base::expected<double, base::ProcessCPUUsageError> cpu_usage =
process_metrics_->GetPlatformIndependentCPUUsage();
if (!cpu_usage.has_value()) {
return std::numeric_limits<double>::quiet_NaN();

@ -9,6 +9,7 @@
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/process/process_metrics.h"
#include "base/types/optional_util.h"
#include "build/build_config.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/resource_attribution/cpu_measurement_monitor.h"
@ -47,7 +48,8 @@ CPUMeasurementDelegateImpl::CPUMeasurementDelegateImpl(
std::optional<base::TimeDelta>
CPUMeasurementDelegateImpl::GetCumulativeCPUUsage() {
return process_metrics_->GetCumulativeCPUUsage();
// TODO(crbug.com/40285287): Return the error code to callers.
return base::OptionalFromExpected(process_metrics_->GetCumulativeCPUUsage());
}
// The default production factory for CPUMeasurementDelegateImpl objects.

@ -17,6 +17,8 @@
#include "base/process/launch.h"
#include "base/time/time.h"
#include "base/tracing/protos/chrome_track_event.pbzero.h"
#include "base/types/expected.h"
#include "base/types/optional_util.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_launcher_utils.h"
@ -47,7 +49,7 @@ std::optional<base::TimeDelta> GetCPUUsage(base::ProcessHandle process_handle) {
std::unique_ptr<base::ProcessMetrics> process_metrics =
base::ProcessMetrics::CreateProcessMetrics(process_handle);
#endif
return process_metrics->GetCumulativeCPUUsage();
return base::OptionalFromExpected(process_metrics->GetCumulativeCPUUsage());
}
#endif // !BUILDFLAG(IS_ANDROID)

@ -12,6 +12,7 @@
#include "base/functional/bind.h"
#include "base/process/process_metrics.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"

@ -8,7 +8,6 @@
#include <atomic>
#include <memory>
#include <optional>
#include <utility>
#include "base/command_line.h"
@ -32,6 +31,7 @@
#include "base/task/thread_pool.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/types/expected.h"
#include "content/common/process_visibility_tracker.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
@ -224,8 +224,8 @@ class ProcessCpuTimeMetrics::DetailedCpuTimeMetrics {
return;
}
const std::optional<base::TimeDelta> cumulative_cpu_time =
process_metrics_->GetCumulativeCPUUsage();
const base::expected<base::TimeDelta, base::ProcessCPUUsageError>
cumulative_cpu_time = process_metrics_->GetCumulativeCPUUsage();
base::TimeDelta process_cpu_time_delta;
if (cumulative_cpu_time.has_value()) {
process_cpu_time_delta = cumulative_cpu_time.value() - reported_cpu_time_;
@ -428,8 +428,8 @@ void ProcessCpuTimeMetrics::CollectHighLevelMetricsOnThreadPool() {
return;
}
const std::optional<base::TimeDelta> cumulative_cpu_usage =
process_metrics_->GetCumulativeCPUUsage();
const base::expected<base::TimeDelta, base::ProcessCPUUsageError>
cumulative_cpu_usage = process_metrics_->GetCumulativeCPUUsage();
base::TimeDelta process_cpu_time_delta;
if (cumulative_cpu_usage.has_value()) {
process_cpu_time_delta = cumulative_cpu_usage.value() - reported_cpu_time_;

@ -3,7 +3,6 @@
// found in the LICENSE file.
#include <memory>
#include <optional>
#include <string_view>
#include <tuple>
@ -18,6 +17,7 @@
#include "base/test/perf_log.h"
#include "base/test/task_environment.h"
#include "base/timer/timer.h"
#include "base/types/expected.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_perftest_messages.h"
#include "ipc/ipc_perftest_util.h"

@ -4,12 +4,12 @@
#include "third_party/blink/renderer/core/inspector/inspector_performance_agent.h"
#include <optional>
#include <utility>
#include "base/process/process.h"
#include "base/process/process_metrics.h"
#include "base/time/time_override.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/execution_context/agent.h"