diff --git a/base/process/process_metrics.cc b/base/process/process_metrics.cc index a0c41748b5ae28..0d9140a99e84e2 100644 --- a/base/process/process_metrics.cc +++ b/base/process/process_metrics.cc @@ -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; @@ -62,35 +96,24 @@ std::unique_ptr 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 diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h index 328922440ec472..844cd73cb4a096 100644 --- a/base/process/process_metrics.h +++ b/base/process/process_metrics.h @@ -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 @@ -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_; @@ -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. diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc index d3d8bb48504571..98793ff2413bda 100644 --- a/base/process/process_metrics_mac.cc +++ b/base/process/process_metrics_mac.cc @@ -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(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 @@ -365,22 +377,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() { return static_cast(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(&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. @@ -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; } @@ -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;