Commit eb27eae7 authored by Sigurdur Asgeirsson's avatar Sigurdur Asgeirsson Committed by Commit Bot

Add a GetCumulativeCPUUsage function to ProcessMetrics.

This also makes the GetPlatformIndependentCPUUsage function generic,
except for a couple of unsupported platforms that seem to retrieve
fractional CPU utilization directly from kernel.

Bug: 755840
Change-Id: I8254a1f1371d7d35d0eeec27f6f4efda03b0dbf9
Reviewed-on: https://chromium-review.googlesource.com/1058079Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Sigurður Ásgeirsson <siggi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#559110}
parent c19c5d95
......@@ -97,6 +97,32 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateCurrentProcessMetrics() {
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
}
#if !defined(OS_FREEBSD) || !defined(OS_POSIX)
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta cumulative_cpu = GetCumulativeCPUUsage();
TimeTicks time = TimeTicks::Now();
if (last_cumulative_cpu_.is_zero()) {
// First call, just set the last values.
last_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_ = time;
return 0;
}
TimeDelta system_time_delta = cumulative_cpu - last_cumulative_cpu_;
TimeDelta time_delta = time - last_cpu_time_;
DCHECK(!time_delta.is_zero());
if (time_delta.is_zero())
return 0;
last_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_ = time;
return 100.0 * system_time_delta.InMicrosecondsF() /
time_delta.InMicrosecondsF();
}
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
int ProcessMetrics::CalculateIdleWakeupsPerSecond(
uint64_t absolute_idle_wakeups) {
......
......@@ -143,6 +143,12 @@ class BASE_EXPORT ProcessMetrics {
// first call, and an actual value only on the second and subsequent calls.
double GetPlatformIndependentCPUUsage();
// Returns the cumulative CPU usage across all threads of the process since
// 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.
TimeDelta GetCumulativeCPUUsage();
// Returns the number of average idle cpu wakeups per second since the last
// call.
int GetIdleWakeupsPerSecond();
......@@ -219,8 +225,8 @@ class BASE_EXPORT ProcessMetrics {
// Used to store the previous times and CPU usage counts so we can
// compute the CPU usage between calls.
TimeTicks last_cpu_time_;
#if defined(OS_WIN) || defined(OS_MACOSX)
int64_t last_system_time_;
#if !defined(OS_FREEBSD) || !defined(OS_POSIX)
TimeDelta last_cumulative_cpu_;
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
......@@ -241,10 +247,7 @@ class BASE_EXPORT ProcessMetrics {
mach_port_t TaskForPid(ProcessHandle process) const;
PortProvider* port_provider_;
#elif defined(OS_POSIX)
// Jiffie count at the last_cpu_time_ we updated.
uint64_t last_cpu_;
#endif // defined(OS_POSIX)
#endif // defined(OS_MACOSX)
#endif // !defined(OS_IOS)
DISALLOW_COPY_AND_ASSIGN(ProcessMetrics);
......
......@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/stl_util.h"
namespace base {
......@@ -27,15 +28,20 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
struct kinfo_proc info;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process_};
size_t length = sizeof(info);
if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
return 0;
return (info.ki_pctcpu / FSCALE) * 100.0;
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return TimeDelta();
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
......@@ -45,7 +51,7 @@ size_t GetSystemCommitCharge() {
unsigned long mem_total, mem_free, mem_inactive;
size_t length = sizeof(mem_total);
if (sysctl(mib, arraysize(mib), &mem_total, &length, NULL, 0) < 0)
if (sysctl(mib, base::size(mib), &mem_total, &length, NULL, 0) < 0)
return 0;
length = sizeof(mem_free);
......
......@@ -25,9 +25,9 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return nullptr;
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTIMPLEMENTED(); // TODO(fuchsia): https://crbug.com/706592.
return 0.0;
return TimeDelta();
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
......
......@@ -25,9 +25,9 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
return WrapUnique(new ProcessMetrics(process));
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTIMPLEMENTED();
return 0;
return TimeDelta();
}
size_t GetMaxFds() {
......
......@@ -198,40 +198,8 @@ size_t ProcessMetrics::GetResidentSetSize() const {
getpagesize();
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeTicks time = TimeTicks::Now();
if (last_cpu_ == 0) {
// First call, just set the last values.
last_cpu_time_ = time;
last_cpu_ = GetProcessCPU(process_);
return 0.0;
}
TimeDelta time_delta = time - last_cpu_time_;
if (time_delta.is_zero()) {
NOTREACHED();
return 0.0;
}
int64_t cpu = GetProcessCPU(process_);
// The number of jiffies in the time period. Convert to percentage.
// Note: this means this will go *over* 100 in the case where multiple threads
// are together adding to more than one CPU's worth.
TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu);
TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_);
// The cumulative CPU time should be monotonically increasing.
DCHECK_LE(last_cpu_time, cpu_time);
double percentage =
100.0 * (cpu_time - last_cpu_time).InSecondsF() / time_delta.InSecondsF();
last_cpu_time_ = time;
last_cpu_ = cpu;
return percentage;
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
return internal::ClockTicksToTimeDelta(GetProcessCPU(process_));
}
// For the /proc/self/io file to exist, the Linux kernel must have
......@@ -332,13 +300,12 @@ int ProcessMetrics::GetOpenFdSoftLimit() const {
return -1;
}
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
#if defined(OS_LINUX) || defined(OS_AIX)
last_absolute_idle_wakeups_(0),
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process), last_absolute_idle_wakeups_(0) {}
#else
ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process) {}
#endif
last_cpu_(0) {
}
#if defined(OS_CHROMEOS)
// Private, Shared and Proportional working set sizes are obtained from
......
......@@ -127,10 +127,10 @@ ProcessMetrics::TaskVMInfo ProcessMetrics::GetTaskVMInfo() const {
(r)->tv_usec = (a)->microseconds; \
} while (0)
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
mach_port_t task = TaskForPid(process_);
if (task == MACH_PORT_NULL)
return 0;
return TimeDelta();
// Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
// in libtop.c), but this is more concise and gives the same results:
......@@ -142,12 +142,12 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
&thread_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie.
return 0;
return TimeDelta();
}
task_basic_info_64 task_info_data;
if (!GetTaskInfo(task, &task_info_data))
return 0;
return TimeDelta();
/* Set total_time. */
// thread info contains live time...
......@@ -162,26 +162,7 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
timeradd(&user_timeval, &task_timeval, &task_timeval);
timeradd(&system_timeval, &task_timeval, &task_timeval);
TimeTicks time = TimeTicks::Now();
int64_t task_time = TimeValToMicroseconds(task_timeval);
if (last_system_time_ == 0) {
// First call, just set the last values.
last_cpu_time_ = time;
last_system_time_ = task_time;
return 0;
}
int64_t system_time_delta = task_time - last_system_time_;
int64_t time_delta = (time - last_cpu_time_).InMicroseconds();
DCHECK_NE(0U, time_delta);
if (time_delta == 0)
return 0;
last_cpu_time_ = time;
last_system_time_ = task_time;
return static_cast<double>(system_time_delta * 100.0) / time_delta;
return TimeDelta::FromMicroseconds(TimeValToMicroseconds(task_timeval));
}
int ProcessMetrics::GetPackageIdleWakeupsPerSecond() {
......@@ -220,7 +201,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
ProcessMetrics::ProcessMetrics(ProcessHandle process,
PortProvider* port_provider)
: process_(process),
last_system_time_(0),
last_absolute_idle_wakeups_(0),
last_absolute_package_idle_wakeups_(0),
port_provider_(port_provider) {}
......
......@@ -45,29 +45,25 @@ static int GetProcessCPU(pid_t pid) {
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeTicks time = TimeTicks::Now();
if (last_cpu_ == 0) {
if (last_cpu_time_.is_zero()) {
// First call, just set the last values.
last_cpu_time_ = time;
last_cpu_ = GetProcessCPU(process_);
return 0;
}
int64_t time_delta = (time - last_cpu_time_).InMicroseconds();
DCHECK_NE(time_delta, 0);
if (time_delta == 0)
return 0;
int cpu = GetProcessCPU(process_);
last_cpu_time_ = time;
last_cpu_ = cpu;
double percentage = static_cast<double>((cpu * 100.0) / FSCALE);
return percentage;
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return TimeDelta();
}
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
last_cpu_(0) {}
......
......@@ -352,15 +352,25 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
thread2.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec2));
thread3.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec3));
TimeDelta prev_cpu_usage = metrics->GetCumulativeCPUUsage();
EXPECT_GE(prev_cpu_usage, TimeDelta());
EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
thread1.Stop();
TimeDelta current_cpu_usage = metrics->GetCumulativeCPUUsage();
EXPECT_GE(current_cpu_usage, prev_cpu_usage);
prev_cpu_usage = current_cpu_usage;
EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
thread2.Stop();
current_cpu_usage = metrics->GetCumulativeCPUUsage();
EXPECT_GE(current_cpu_usage, prev_cpu_usage);
prev_cpu_usage = current_cpu_usage;
EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
thread3.Stop();
current_cpu_usage = metrics->GetCumulativeCPUUsage();
EXPECT_GE(current_cpu_usage, prev_cpu_usage);
EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
}
......
......@@ -128,14 +128,7 @@ class WorkingSetInformationBuffer {
} // namespace
static uint64_t FileTimeToUTC(const FILETIME& ftime) {
LARGE_INTEGER li;
li.LowPart = ftime.dwLowDateTime;
li.HighPart = ftime.dwHighDateTime;
return li.QuadPart;
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
......@@ -146,37 +139,18 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
// We don't assert here because in some cases (such as in the Task Manager)
// we may call this function on a process that has just exited but we have
// not yet received the notification.
return 0;
}
int64_t system_time = FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time);
TimeTicks time = TimeTicks::Now();
if (last_system_time_ == 0) {
// First call, just set the last values.
last_system_time_ = system_time;
last_cpu_time_ = time;
return 0;
return TimeDelta();
}
int64_t system_time_delta = system_time - last_system_time_;
// FILETIME is in 100-nanosecond units, so this needs microseconds times 10.
int64_t time_delta = (time - last_cpu_time_).InMicroseconds() * 10;
DCHECK_NE(0U, time_delta);
if (time_delta == 0)
return 0;
last_system_time_ = system_time;
last_cpu_time_ = time;
return static_cast<double>(system_time_delta * 100) / time_delta;
return TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time);
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return GetProcessIoCounters(process_.Get(), io_counters) != FALSE;
}
ProcessMetrics::ProcessMetrics(ProcessHandle process) : last_system_time_(0) {
ProcessMetrics::ProcessMetrics(ProcessHandle process) {
if (process) {
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process,
......
......@@ -110,6 +110,14 @@ int64_t TimeDelta::InMicroseconds() const {
return delta_;
}
double TimeDelta::InMicrosecondsF() const {
if (is_max()) {
// Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_);
}
int64_t TimeDelta::InNanoseconds() const {
if (is_max()) {
// Preserve max to prevent overflow.
......
......@@ -127,6 +127,7 @@ class BASE_EXPORT TimeDelta {
#endif
#if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value);
static TimeDelta FromFileTime(FILETIME ft);
#endif
// Converts an integer value representing TimeDelta to a class. This is used
......@@ -195,6 +196,7 @@ class BASE_EXPORT TimeDelta {
int64_t InMilliseconds() const;
int64_t InMillisecondsRoundedUp() const;
int64_t InMicroseconds() const;
double InMicrosecondsF() const;
int64_t InNanoseconds() const;
constexpr TimeDelta& operator=(TimeDelta other) {
......
......@@ -1447,6 +1447,14 @@ TEST(TimeDelta, Overflows) {
EXPECT_TRUE((large_delta / 0.5).is_max());
EXPECT_TRUE((large_delta / -0.5).is_min());
// Test that double conversions overflow to infinity.
EXPECT_EQ((large_delta + kOneSecond).InSecondsF(),
std::numeric_limits<double>::infinity());
EXPECT_EQ((large_delta + kOneSecond).InMillisecondsF(),
std::numeric_limits<double>::infinity());
EXPECT_EQ((large_delta + kOneSecond).InMicrosecondsF(),
std::numeric_limits<double>::infinity());
// Test +=, -=, *= and /= operators.
TimeDelta delta = large_delta;
delta += kOneSecond;
......
......@@ -729,4 +729,9 @@ TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
return QPCValueToTimeDelta(qpc_value);
}
// static
TimeDelta TimeDelta::FromFileTime(FILETIME ft) {
return TimeDelta::FromMicroseconds(FileTimeToMicroseconds(ft));
}
} // namespace base
......@@ -309,6 +309,22 @@ TEST(TimeDelta, ConstexprInitialization) {
EXPECT_EQ(kExpectedDeltaInMilliseconds, kConstexprTimeDelta.InMilliseconds());
}
TEST(TimeDelta, FromFileTime) {
FILETIME ft;
ft.dwLowDateTime = 1001;
ft.dwHighDateTime = 0;
// 100100 ns ~= 100 us.
EXPECT_EQ(TimeDelta::FromMicroseconds(100), TimeDelta::FromFileTime(ft));
ft.dwLowDateTime = 0;
ft.dwHighDateTime = 1;
// 2^32 * 100 ns ~= 2^32 * 10 us.
EXPECT_EQ(TimeDelta::FromMicroseconds((1ull << 32) / 10),
TimeDelta::FromFileTime(ft));
}
TEST(HighResolutionTimer, GetUsage) {
EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment