Skip to content

Commit

Permalink
[Mac] Report total idle wakeups instead of just package exits.
Browse files Browse the repository at this point in the history
Currently, we report only a subset of idle wakeups in the task manager:
"package idle exits" (essentially a wakeup of the IC that includes the
processor, caches etc; see man powermetrics for details). This is a
useful metric, but it isn't what's shown in Activity Monitor or used
when calculating EXC_RESOURCE RESOURCE_TYPE_WAKEUPS exceptions (see
crbug.com/684051).

This changes |ProcessMetrics::GetIdleWakeupsPerSecond| to report
total wakeups, but maintains the package idle exit calculation
for reporting to UMA (will be in a follow-up CL).

Bug: 649641
Change-Id: I6034f258a101b26a2fd6b4b72708a0682c63b19a
Reviewed-on: https://chromium-review.googlesource.com/702049
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Jayson Adams <shrike@chromium.org>
Commit-Queue: Leonard Grey <lgrey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#507396}
  • Loading branch information
speednoisemovement authored and Commit Bot committed Oct 9, 2017
1 parent 3dbd401 commit d1fc0b4
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 41 deletions.
71 changes: 47 additions & 24 deletions base/process/process_metrics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,40 @@
#include "base/values.h"
#include "build/build_config.h"

#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
namespace {
int CalculateEventsPerSecond(uint64_t event_count,
uint64_t* last_event_count,
base::TimeTicks* last_calculated) {
base::TimeTicks time = base::TimeTicks::Now();

if (*last_event_count == 0) {
// First call, just set the last values.
*last_calculated = time;
*last_event_count = event_count;
return 0;
}

int64_t events_delta = event_count - *last_event_count;
int64_t time_delta = (time - *last_calculated).InMicroseconds();
if (time_delta == 0) {
NOTREACHED();
return 0;
}

*last_calculated = time;
*last_event_count = event_count;

int64_t events_delta_for_ms =
events_delta * base::Time::kMicrosecondsPerSecond;
// Round the result up by adding 1/2 (the second term resolves to 1/2 without
// dropping down into floating point).
return (events_delta_for_ms + time_delta / 2) / time_delta;
}

} // namespace
#endif // defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)

namespace base {

SystemMemoryInfoKB::SystemMemoryInfoKB() = default;
Expand Down Expand Up @@ -62,35 +96,24 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateCurrentProcessMetrics() {
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
int ProcessMetrics::CalculateIdleWakeupsPerSecond(
uint64_t absolute_idle_wakeups) {
TimeTicks time = TimeTicks::Now();

if (last_absolute_idle_wakeups_ == 0) {
// First call, just set the last values.
last_idle_wakeups_time_ = time;
last_absolute_idle_wakeups_ = absolute_idle_wakeups;
return 0;
}

int64_t wakeups_delta = absolute_idle_wakeups - last_absolute_idle_wakeups_;
int64_t time_delta = (time - last_idle_wakeups_time_).InMicroseconds();
if (time_delta == 0) {
NOTREACHED();
return 0;
}

last_idle_wakeups_time_ = time;
last_absolute_idle_wakeups_ = absolute_idle_wakeups;

int64_t wakeups_delta_for_ms = wakeups_delta * Time::kMicrosecondsPerSecond;
// Round the result up by adding 1/2 (the second term resolves to 1/2 without
// dropping down into floating point).
return (wakeups_delta_for_ms + time_delta / 2) / time_delta;
return CalculateEventsPerSecond(absolute_idle_wakeups,
&last_absolute_idle_wakeups_,
&last_idle_wakeups_time_);
}
#else
int ProcessMetrics::GetIdleWakeupsPerSecond() {
NOTIMPLEMENTED(); // http://crbug.com/120488
return 0;
}
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
#endif // defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)

#if defined(OS_MACOSX)
int ProcessMetrics::CalculatePackageIdleWakeupsPerSecond(
uint64_t absolute_package_idle_wakeups) {
return CalculateEventsPerSecond(absolute_package_idle_wakeups,
&last_absolute_package_idle_wakeups_,
&last_package_idle_wakeups_time_);
}

#endif // defined(OS_MACOSX)
} // namespace base
29 changes: 29 additions & 0 deletions base/process/process_metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,23 @@ class BASE_EXPORT ProcessMetrics {
// call.
int GetIdleWakeupsPerSecond();

#if defined(OS_MACOSX)
// Returns the number of average "package idle exits" per second, which have
// a higher energy impact than a regular wakeup, since the last call.
//
// From the powermetrics man page:
// "With the exception of some Mac Pro systems, Mac and
// iOS systems are typically single package systems, wherein all CPUs are
// part of a single processor complex (typically a single IC die) with shared
// logic that can include (depending on system specifics) shared last level
// caches, an integrated memory controller etc. When all CPUs in the package
// are idle, the hardware can power-gate significant portions of the shared
// logic in addition to each individual processor's logic, as well as take
// measures such as placing DRAM in to self-refresh (also referred to as
// auto-refresh), place interconnects into lower-power states etc"
int GetPackageIdleWakeupsPerSecond();
#endif

// Retrieves accounting information for all I/O operations performed by the
// process.
// If IO information is retrieved successfully, the function returns true
Expand Down Expand Up @@ -268,6 +285,12 @@ class BASE_EXPORT ProcessMetrics {
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
int CalculateIdleWakeupsPerSecond(uint64_t absolute_idle_wakeups);
#endif
#if defined(OS_MACOSX)
// The subset of wakeups that cause a "package exit" can be tracked on macOS.
// See |GetPackageIdleWakeupsForSecond| comment for more info.
int CalculatePackageIdleWakeupsPerSecond(
uint64_t absolute_package_idle_wakeups);
#endif

#if defined(OS_WIN)
win::ScopedHandle process_;
Expand All @@ -286,6 +309,12 @@ class BASE_EXPORT ProcessMetrics {
uint64_t last_absolute_idle_wakeups_;
#endif

#if defined(OS_MACOSX)
// And same thing for package idle exit wakeups.
TimeTicks last_package_idle_wakeups_time_;
uint64_t last_absolute_package_idle_wakeups_;
#endif

#if !defined(OS_IOS)
#if defined(OS_MACOSX)
// Queries the port provider if it's set.
Expand Down
44 changes: 27 additions & 17 deletions base/process/process_metrics_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ MachVMRegionResult ParseOutputFromMachVMRegion(kern_return_t kr) {
return MachVMRegionResult::Success;
}

bool GetPowerInfo(mach_port_t task, task_power_info* power_info_data) {
if (task == MACH_PORT_NULL)
return false;

mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT;
kern_return_t kr = task_info(task, TASK_POWER_INFO,
reinterpret_cast<task_info_t>(power_info_data),
&power_info_count);
// Most likely cause for failure: |task| is a zombie.
return kr == KERN_SUCCESS;
}

} // namespace

// Getting a mach task from a pid for another process requires permissions in
Expand Down Expand Up @@ -365,22 +377,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
return static_cast<double>(system_time_delta * 100.0) / time_delta;
}

int ProcessMetrics::GetIdleWakeupsPerSecond() {
int ProcessMetrics::GetPackageIdleWakeupsPerSecond() {
mach_port_t task = TaskForPid(process_);
if (task == MACH_PORT_NULL)
return 0;

task_power_info power_info_data;
mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT;
kern_return_t kr = task_info(task,
TASK_POWER_INFO,
reinterpret_cast<task_info_t>(&power_info_data),
&power_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie, or this is on a pre-10.8.4 system
// where TASK_POWER_INFO isn't supported yet.
return 0;
}

GetPowerInfo(task, &power_info_data);

// The task_power_info struct contains two wakeup counters:
// task_interrupt_wakeups and task_platform_idle_wakeups.
Expand All @@ -392,10 +393,19 @@ int ProcessMetrics::GetIdleWakeupsPerSecond() {
// in a greater power increase than the other interrupts which occur while the
// CPU is already working, and reducing them has a greater overall impact on
// power usage. See the powermetrics man page for more info.
return CalculateIdleWakeupsPerSecond(
return CalculatePackageIdleWakeupsPerSecond(
power_info_data.task_platform_idle_wakeups);
}

int ProcessMetrics::GetIdleWakeupsPerSecond() {
mach_port_t task = TaskForPid(process_);
task_power_info power_info_data;

GetPowerInfo(task, &power_info_data);

return CalculateIdleWakeupsPerSecond(power_info_data.task_interrupt_wakeups);
}

bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
Expand All @@ -405,8 +415,8 @@ ProcessMetrics::ProcessMetrics(ProcessHandle process,
: process_(process),
last_system_time_(0),
last_absolute_idle_wakeups_(0),
port_provider_(port_provider) {
}
last_absolute_package_idle_wakeups_(0),
port_provider_(port_provider) {}

mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
mach_port_t task = MACH_PORT_NULL;
Expand Down

0 comments on commit d1fc0b4

Please sign in to comment.