Commit e8542484 authored by Eric Seckler's avatar Eric Seckler Committed by Commit Bot

CpuTimeMetrics: Correct for idle times in approx time_in_state metric

As it turns out, the per-core time_in_state data from
/sys/devices/system/cpu/cpuX/cpufreq/stats/time_in_state is wall time
rather than CPU time and includes time the cores spend in idle states.

Consequently, the data we currently report into Power.Approx* metrics
isn't correct. We have to correct for the idle time when computing each
core type + frequency state's proportional part of the execution.

This patch adds a way to do so by polling per-core idle times from
/sys/devices/system/cpu/cpuX/cpuidle/stateY/time (via a helper in
base::CPU) and updates the CpuTimeMetrics code to account for idle time.

Bug: 1081760
Change-Id: I7b8168b47b1e4d118bc7569aebb07952fa78693e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2462088
Commit-Queue: Eric Seckler <eseckler@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816509}
parent f86efafa
......@@ -335,6 +335,8 @@ constexpr char kTimeInStatePath[] =
"/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state";
constexpr char kPhysicalPackageIdPath[] =
"/sys/devices/system/cpu/cpu%d/topology/physical_package_id";
constexpr char kCoreIdleStateTimePath[] =
"/sys/devices/system/cpu/cpu%d/cpuidle/state%d/time";
bool SupportsTimeInState() {
// Reading from time_in_state doesn't block (it amounts to reading a struct
......@@ -388,6 +390,16 @@ bool ParseTimeInState(const std::string& content,
return true;
}
bool SupportsCoreIdleTimes() {
// Reading from the cpuidle driver doesn't block.
ThreadRestrictions::ScopedAllowIO allow_io;
// Check if the path for the idle time in state 0 for core 0 is readable.
FilePath idle_state0_path(
StringPrintf(kCoreIdleStateTimePath, /*core_index=*/0, /*idle_state=*/0));
ScopedFILE file_stream(OpenFile(idle_state0_path, "rb"));
return static_cast<bool>(file_stream);
}
std::vector<CPU::CoreType> GuessCoreTypes() {
// Try to guess the CPU architecture and cores of each cluster by comparing
// the maximum frequencies of the available (online and offline) cores.
......@@ -531,6 +543,46 @@ bool CPU::GetTimeInState(TimeInState& time_in_state) {
return true;
}
// static
bool CPU::GetCumulativeCoreIdleTimes(CoreIdleTimes& idle_times) {
idle_times.clear();
// The kernel may not support the cpufreq-stats driver.
static const bool kSupportsIdleTimes = SupportsCoreIdleTimes();
if (!kSupportsIdleTimes)
return false;
// Reading from the cpuidle driver doesn't block.
ThreadRestrictions::ScopedAllowIO allow_io;
int num_cpus = SysInfo::NumberOfProcessors();
bool success = false;
for (int core_index = 0; core_index < num_cpus; ++core_index) {
std::string content;
TimeDelta idle_time;
// The number of idle states is system/CPU dependent, so we increment and
// try to read each state until we fail.
for (int state_index = 0;; ++state_index) {
auto path = StringPrintf(kCoreIdleStateTimePath, core_index, state_index);
uint64_t idle_state_time = 0;
if (!ReadFileToString(FilePath(path), &content))
break;
StringToUint64(content, &idle_state_time);
idle_time += TimeDelta::FromMicroseconds(idle_state_time);
}
idle_times.push_back(idle_time);
// At least one of the cores should have some idle time, otherwise we report
// a failure.
success |= idle_time > base::TimeDelta();
}
return success;
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) ||
// defined(OS_AIX)
......
......@@ -98,11 +98,11 @@ class BASE_EXPORT CPU final {
CPU::CoreType core_type; // type of the cores in this cluster.
uint32_t cluster_core_index; // index of the first core in the cluster.
uint64_t core_frequency_khz;
TimeDelta cumulative_cpu_time;
TimeDelta cumulative_time;
};
using TimeInState = std::vector<TimeInStateEntry>;
// Emits the device's cumulative CPU usage split by CPU cluster frequency
// For each CPU core, emits the cumulative time spent in different frequency
// states into the output parameter (replacing its current contents). One
// entry in the output parameter is added for each cluster core index
// + frequency state combination with a non-zero CPU time value. Returns false
......@@ -113,6 +113,17 @@ class BASE_EXPORT CPU final {
// NOTE: Currently only supported on Linux/Android, and only on kernels with
// cpufreq-stats driver.
static bool GetTimeInState(TimeInState&);
// For each CPU core, emits the total cumulative wall time spent in any idle
// state into the output parameter (replacing its current contents). Returns
// false on failure. We return the usage via an output parameter to allow
// reuse of TimeInState's std::vector by the caller, e.g. to avoid allocations
// between repeated calls to this method.
//
// NOTE: Currently only supported on Linux/Android, and only on kernels with
// cpuidle driver.
using CoreIdleTimes = std::vector<TimeDelta>;
static bool GetCumulativeCoreIdleTimes(CoreIdleTimes&);
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) ||
// defined(OS_AIX)
......
This diff is collapsed.
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