blob: 24bf4eb5bddd98feae2855d1de188f8b1649945d [file] [log] [blame]
From 1793b7e0837efdf44ba08e9289ac0d24727a1947 Mon Sep 17 00:00:00 2001
From: Nathan Muggli <nmuggli@google.com>
Date: Wed, 13 Mar 2024 16:07:37 -0600
Subject: [PATCH] Revert "Return nullopt on error from ProcessMetrics CPU
measurements"
This reverts commit 94912573bfad4a687bc86cbbcbafad7ec0dad19e.
---
base/process/internal_linux.cc | 15 ++---
base/process/internal_linux.h | 9 ---
base/process/process_metrics.cc | 8 +--
base/process/process_metrics.h | 15 ++---
base/process/process_metrics_apple.cc | 14 ++---
base/process/process_metrics_linux.cc | 56 ++++++++----------
base/process/process_metrics_unittest.cc | 72 ++++++++++++++++--------
ipc/ipc_cpu_perftest.cc | 6 +-
8 files changed, 90 insertions(+), 105 deletions(-)
diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc
index c8f38dc28c..b9d3f0f851 100644
--- a/base/process/internal_linux.cc
+++ b/base/process/internal_linux.cc
@@ -14,7 +14,6 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/notreached.h"
-#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -207,17 +206,10 @@ void ParseProcStat(const std::string& contents, ProcStatMap* output) {
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
- return GetProcStatsFieldAsOptionalInt64(proc_stats, field_num).value_or(0);
-}
+ CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
-std::optional<int64_t> GetProcStatsFieldAsOptionalInt64(
- base::span<const std::string> proc_stats,
- ProcStatsFields field_num) {
int64_t value;
- if (StringToInt64(proc_stats[size_t{field_num}], &value)) {
- return value;
- }
- return std::nullopt;
+ return StringToInt64(proc_stats[field_num], &value) ? value : 0;
}
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
@@ -250,7 +242,8 @@ int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num) {
return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
}
-size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num) {
+size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
+ ProcStatsFields field_num) {
std::string stats_data;
if (!ReadProcStats(pid, &stats_data))
return 0;
diff --git a/base/process/internal_linux.h b/base/process/internal_linux.h
index e7b70601bc..a2657a7bc3 100644
--- a/base/process/internal_linux.h
+++ b/base/process/internal_linux.h
@@ -11,11 +11,9 @@
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
-#include <optional>
#include <string>
#include <vector>
-#include "base/containers/span.h"
#include "base/files/dir_reader_posix.h"
#include "base/files/file_path.h"
#include "base/process/process_handle.h"
@@ -102,13 +100,6 @@ enum ProcStatsFields {
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
-// Reads the `field_num`th field from `proc_stats`. Asserts that `field_num` is
-// a valid index into `proc_stats`. Returns nullopt if the field doesn't contain
-// a valid integer.
-std::optional<int64_t> GetProcStatsFieldAsOptionalInt64(
- base::span<const std::string> proc_stats,
- ProcStatsFields field_num);
-
// Same as GetProcStatsFieldAsInt64(), but for size_t values.
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
diff --git a/base/process/process_metrics.cc b/base/process/process_metrics.cc
index e9d90dcad8..e59252c6e3 100644
--- a/base/process/process_metrics.cc
+++ b/base/process/process_metrics.cc
@@ -123,12 +123,8 @@ 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());
+double ProcessMetrics::GetPlatformIndependentCPUUsage() {
+ return GetPlatformIndependentCPUUsage(GetCumulativeCPUUsage());
}
#endif
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index db4ea9d024..b9688fe6ed 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -12,7 +12,6 @@
#include <stdint.h>
#include <memory>
-#include <optional>
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
@@ -125,16 +124,14 @@ class BASE_EXPORT ProcessMetrics {
[[nodiscard]] double GetPlatformIndependentCPUUsage(TimeDelta cumulative_cpu);
// Same as the above, but automatically calls GetCumulativeCPUUsage() to
- // determine the current cumulative CPU. Returns nullopt if
- // GetCumulativeCPUUsage() fails.
- [[nodiscard]] std::optional<double> GetPlatformIndependentCPUUsage();
+ // determine the current cumulative CPU.
+ [[nodiscard]] double 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();
+ // process start. 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]] TimeDelta GetCumulativeCPUUsage();
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
BUILDFLAG(IS_AIX)
diff --git a/base/process/process_metrics_apple.cc b/base/process/process_metrics_apple.cc
index 685e0c4360..1a23c8a083 100644
--- a/base/process/process_metrics_apple.cc
+++ b/base/process/process_metrics_apple.cc
@@ -11,8 +11,6 @@
#include <stdint.h>
#include <sys/sysctl.h>
-#include <optional>
-
#include "base/apple/mach_logging.h"
#include "base/apple/scoped_mach_port.h"
#include "base/logging.h"
@@ -95,10 +93,10 @@ mach_port_t ProcessMetrics::TaskForHandle(ProcessHandle process_handle) const {
return task;
}
-std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
+TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
mach_port_t task = TaskForHandle(process_);
if (task == MACH_PORT_NULL) {
- return std::nullopt;
+ return TimeDelta();
}
// Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
@@ -110,12 +108,12 @@ std::optional<TimeDelta> ProcessMetrics::GetCumulativeCPUUsage() {
&thread_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie.
- return std::nullopt;
+ return TimeDelta();
}
task_basic_info_64 task_info_data;
if (!GetTaskInfo(task, &task_info_data)) {
- return std::nullopt;
+ return TimeDelta();
}
/* Set total_time. */
@@ -139,10 +137,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 last_measured_cpu_;
}
last_measured_cpu_ = measured_cpu;
- return std::optional(measured_cpu);
+ return measured_cpu;
}
int ProcessMetrics::GetPackageIdleWakeupsPerSecond() {
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 0ed005ff09..5d11ac6638 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -17,7 +17,6 @@
#include <optional>
#include <utility>
-#include "base/containers/span.h"
#include "base/cpu.h"
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
@@ -58,26 +57,24 @@ 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(
- 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;
- }
- const std::optional<int64_t> stime =
- internal::GetProcStatsFieldAsOptionalInt64(proc_stats,
- internal::VM_UTIME);
- if (stime.value_or(-1) < 0) {
- return std::nullopt;
+// Get the total CPU from a proc stat buffer. Return value is number of jiffies
+// on success or 0 if parsing failed.
+int64_t ParseTotalCPUTimeFromStats(const std::vector<std::string>& proc_stats) {
+ return internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME) +
+ internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME);
+}
+
+// Get the total CPU of a single process. Return value is number of jiffies
+// on success or -1 on error.
+int64_t GetProcessCPU(pid_t pid) {
+ std::string buffer;
+ std::vector<std::string> proc_stats;
+ if (!internal::ReadProcStats(pid, &buffer) ||
+ !internal::ParseProcStats(buffer, &proc_stats)) {
+ return -1;
}
- const TimeDelta cpu_time = internal::ClockTicksToTimeDelta(
- base::ClampAdd(utime.value(), stime.value()));
- CHECK(!cpu_time.is_negative());
- return std::optional(cpu_time);
+
+ return ParseTotalCPUTimeFromStats(proc_stats);
}
} // namespace
@@ -93,15 +90,8 @@ size_t ProcessMetrics::GetResidentSetSize() const {
checked_cast<size_t>(getpagesize());
}
-std::optional<TimeDelta> 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 ParseTotalCPUTimeFromStats(proc_stats);
+TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
+ return internal::ClockTicksToTimeDelta(GetProcessCPU(process_));
}
bool ProcessMetrics::GetCumulativeCPUUsagePerThread(
@@ -120,11 +110,9 @@ bool ProcessMetrics::GetCumulativeCPUUsagePerThread(
return;
}
- const std::optional<TimeDelta> thread_time =
- ParseTotalCPUTimeFromStats(proc_stats);
- if (thread_time.has_value()) {
- cpu_per_thread.emplace_back(tid, thread_time.value());
- }
+ TimeDelta thread_time = internal::ClockTicksToTimeDelta(
+ ParseTotalCPUTimeFromStats(proc_stats));
+ cpu_per_thread.emplace_back(tid, thread_time);
});
return !cpu_per_thread.empty();
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index 230d22499e..84baef0f11 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -8,7 +8,6 @@
#include <stdint.h>
#include <memory>
-#include <optional>
#include <sstream>
#include <string>
#include <utility>
@@ -37,7 +36,6 @@
#include "build/blink_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
-#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
@@ -66,12 +64,6 @@ namespace base::debug {
namespace {
-using ::testing::AssertionFailure;
-using ::testing::AssertionResult;
-using ::testing::AssertionSuccess;
-using ::testing::Ge;
-using ::testing::Optional;
-
#if ENABLE_CPU_TESTS
void BusyWork(std::vector<std::string>* vec) {
@@ -82,21 +74,19 @@ void BusyWork(std::vector<std::string>* vec) {
}
}
-// Tests that GetCumulativeCPUUsage() returns a valid result that's equal to or
-// greater than `prev_cpu_usage`, and returns the result. If
-// GetCumulativeCPUUsage() returns an error, records a failed expectation and
-// 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 =
- metrics->GetCumulativeCPUUsage();
- EXPECT_THAT(current_cpu_usage, Optional(Ge(prev_cpu_usage)));
- EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), Optional(Ge(0.0)));
- return current_cpu_usage.value_or(TimeDelta());
+ const TimeDelta current_cpu_usage = metrics->GetCumulativeCPUUsage();
+ EXPECT_GE(current_cpu_usage, prev_cpu_usage);
+ EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
+ return current_cpu_usage;
}
#endif // ENABLE_CPU_TESTS
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
// Helper to deal with Mac process launching complexity. On other platforms this
// is just a thin wrapper around SpawnMultiProcessTestChild.
class TestChildLauncher {
@@ -634,7 +624,7 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
std::unique_ptr<ProcessMetrics> metrics =
ProcessMetrics::CreateCurrentProcessMetrics();
- EXPECT_THAT(metrics->GetPlatformIndependentCPUUsage(), Optional(Ge(0.0)));
+ EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
Thread thread1("thread1");
Thread thread2("thread2");
@@ -697,15 +687,38 @@ TEST_F(SystemMetricsTest, MeasureChildCpuUsage) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
// Windows and Fuchsia return final measurements of a process after it exits.
TestCumulativeCPU(metrics.get(), cpu_usage2);
+#elif BUILDFLAG(IS_APPLE)
+ // Mac and iOS return 0 on error. GetPlatformIndependentCPUUsage subtracts
+ // from this to get a negative.
+ EXPECT_EQ(metrics->GetCumulativeCPUUsage(), TimeDelta());
+ EXPECT_LT(metrics->GetPlatformIndependentCPUUsage(), 0.0);
#else
- // All other platforms return an error.
- EXPECT_EQ(metrics->GetCumulativeCPUUsage(), std::nullopt);
- EXPECT_EQ(metrics->GetPlatformIndependentCPUUsage(), std::nullopt);
+ // All other platforms return a negative time value to indicate an error.
+ EXPECT_LT(metrics->GetCumulativeCPUUsage(), TimeDelta());
+ EXPECT_LT(metrics->GetPlatformIndependentCPUUsage(), 0.0);
#endif
}
#endif // !BUILDFLAG(IS_APPLE) || BUILDFLAG(USE_BLINK)
+#if BUILDFLAG(IS_FUCHSIA)
+
+// Fuchsia CHECK's when measuring an invalid procses.
+using SystemMetricsDeathTest = SystemMetricsTest;
+TEST_F(SystemMetricsDeathTest, InvalidProcessCpuUsage) {
+ std::unique_ptr<ProcessMetrics> metrics =
+ ProcessMetrics::CreateProcessMetrics(kNullProcessHandle);
+ EXPECT_CHECK_DEATH(
+ { [[maybe_unused]] TimeDelta usage = metrics->GetCumulativeCPUUsage(); });
+ EXPECT_CHECK_DEATH({
+ [[maybe_unused]] double usage = metrics->GetPlatformIndependentCPUUsage();
+ });
+}
+
+#else // !BUILDFLAG(IS_FUCHSIA)
+
+// Windows, Mac and iOS also return 0 on error. All other platforms return a
+// negative value to indicate an error.
TEST_F(SystemMetricsTest, InvalidProcessCpuUsage) {
#if BUILDFLAG(IS_MAC)
std::unique_ptr<ProcessMetrics> metrics =
@@ -714,9 +727,20 @@ 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);
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
+ EXPECT_EQ(metrics->GetCumulativeCPUUsage(), TimeDelta());
+#else
+ EXPECT_LT(metrics->GetCumulativeCPUUsage(), TimeDelta());
+#endif
+
+ // The first call always caches the result of GetCumulativeCPUUsage() and
+ // returns 0, even if it's an error value. The second call returns the delta
+ // with the cached result, which is 0 when the second GetCumulativeCPUUsage()
+ // call returns the same error value.
+ EXPECT_EQ(metrics->GetPlatformIndependentCPUUsage(), 0.0);
+ EXPECT_EQ(metrics->GetPlatformIndependentCPUUsage(), 0.0);
}
+#endif // !BUILDFLAG(IS_FUCHSIA)
#endif // ENABLE_CPU_TESTS
diff --git a/ipc/ipc_cpu_perftest.cc b/ipc/ipc_cpu_perftest.cc
index dacbdc987b..a58d6b401a 100644
--- a/ipc/ipc_cpu_perftest.cc
+++ b/ipc/ipc_cpu_perftest.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include <memory>
-#include <optional>
#include <string_view>
#include <tuple>
@@ -74,7 +73,7 @@ class PerfCpuLogger {
process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
// Query the CPU usage once to start the recording interval.
const double inital_cpu_usage =
- process_metrics_->GetPlatformIndependentCPUUsage().value_or(-1.0);
+ process_metrics_->GetPlatformIndependentCPUUsage();
// This should have been the first call so the reported cpu usage should be
// exactly zero.
DCHECK_EQ(inital_cpu_usage, 0.0);
@@ -84,8 +83,7 @@ class PerfCpuLogger {
PerfCpuLogger& operator=(const PerfCpuLogger&) = delete;
~PerfCpuLogger() {
- const double result =
- process_metrics_->GetPlatformIndependentCPUUsage().value_or(-1.0);
+ double result = process_metrics_->GetPlatformIndependentCPUUsage();
base::LogPerfResult(test_name_.c_str(), result, "%");
}
--
2.44.0.278.ge034bb2e1d-goog