1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "powerhal-adaptivecpu"
18 #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
19
20 #include "CpuLoadReaderSysDevices.h"
21
22 #include <android-base/logging.h>
23 #include <inttypes.h>
24 #include <utils/Trace.h>
25
26 #include <fstream>
27 #include <sstream>
28 #include <string>
29
30 namespace aidl {
31 namespace google {
32 namespace hardware {
33 namespace power {
34 namespace impl {
35 namespace pixel {
36
getKernelTime()37 std::chrono::nanoseconds getKernelTime() {
38 timespec ts;
39 clock_gettime(CLOCK_MONOTONIC, &ts);
40 return std::chrono::nanoseconds(ts.tv_sec * 1000000000UL + ts.tv_nsec);
41 }
42
Init()43 bool CpuLoadReaderSysDevices::Init() {
44 mIdleStateNames.clear();
45 if (!ReadIdleStateNames(&mIdleStateNames)) {
46 return false;
47 }
48 return ReadCpuTimes(&mPreviousCpuTimes);
49 }
50
GetRecentCpuLoads(std::array<double,NUM_CPU_CORES> * cpuCoreIdleTimesPercentage)51 bool CpuLoadReaderSysDevices::GetRecentCpuLoads(
52 std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) {
53 ATRACE_CALL();
54 if (cpuCoreIdleTimesPercentage == nullptr) {
55 LOG(ERROR) << "Got nullptr output in getRecentCpuLoads";
56 return false;
57 }
58 std::array<CpuTime, NUM_CPU_CORES> cpuTimes;
59 if (!ReadCpuTimes(&cpuTimes)) {
60 return false;
61 }
62 if (cpuTimes.empty()) {
63 LOG(ERROR) << "Failed to find any CPU times";
64 return false;
65 }
66 for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
67 const auto cpuTime = cpuTimes[cpuId];
68 const auto previousCpuTime = mPreviousCpuTimes[cpuId];
69 auto recentIdleTime = cpuTime.idleTime - previousCpuTime.idleTime;
70 const auto recentTotalTime = cpuTime.totalTime - previousCpuTime.totalTime;
71 if (recentIdleTime > recentTotalTime) {
72 // This happens occasionally, as we use the idle time from the kernel, and the current
73 // time from userspace.
74 recentIdleTime = recentTotalTime;
75 }
76 const double idleTimePercentage =
77 static_cast<double>(recentIdleTime.count()) / recentTotalTime.count();
78 (*cpuCoreIdleTimesPercentage)[cpuId] = idleTimePercentage;
79 }
80 mPreviousCpuTimes = cpuTimes;
81 return true;
82 }
83
DumpToStream(std::stringstream & stream) const84 void CpuLoadReaderSysDevices::DumpToStream(std::stringstream &stream) const {
85 stream << "CPU loads from /sys/devices/system/cpu/cpuN/cpuidle:\n";
86 for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
87 stream << "- CPU=" << cpuId << ", idleTime=" << mPreviousCpuTimes[cpuId].idleTime.count()
88 << "ms, totalTime=" << mPreviousCpuTimes[cpuId].totalTime.count() << "ms\n";
89 }
90 }
91
ReadCpuTimes(std::array<CpuTime,NUM_CPU_CORES> * result) const92 bool CpuLoadReaderSysDevices::ReadCpuTimes(std::array<CpuTime, NUM_CPU_CORES> *result) const {
93 ATRACE_CALL();
94 const auto totalTime = mTimeSource->GetTime();
95
96 for (size_t cpuId = 0; cpuId < NUM_CPU_CORES; cpuId++) {
97 std::chrono::microseconds idleTime;
98 for (const auto &idleStateName : mIdleStateNames) {
99 std::stringstream cpuIdlePath;
100 cpuIdlePath << "/sys/devices/system/cpu/"
101 << "cpu" << cpuId << "/cpuidle/" << idleStateName << "/time";
102 std::unique_ptr<std::istream> file;
103 if (!mFilesystem->ReadFileStream(cpuIdlePath.str(), &file)) {
104 return false;
105 }
106 // Times are reported in microseconds:
107 // https://www.kernel.org/doc/Documentation/cpuidle/sysfs.txt
108 std::string idleTimeUs(std::istreambuf_iterator<char>(*file), {});
109 idleTime += std::chrono::microseconds(std::atoi(idleTimeUs.c_str()));
110 }
111 (*result)[cpuId] = {
112 .idleTime = idleTime,
113 .totalTime = std::chrono::duration_cast<std::chrono::microseconds>(totalTime),
114 };
115 }
116
117 return true;
118 }
119
ReadIdleStateNames(std::vector<std::string> * result) const120 bool CpuLoadReaderSysDevices::ReadIdleStateNames(std::vector<std::string> *result) const {
121 std::vector<std::string> idleStateNames;
122 if (!mFilesystem->ListDirectory("/sys/devices/system/cpu/cpu0/cpuidle", &idleStateNames)) {
123 return false;
124 }
125 for (const auto &idleStateName : idleStateNames) {
126 if (idleStateName.length() == 0 || idleStateName[0] == '.') {
127 continue;
128 }
129 std::vector<std::string> files;
130 if (!mFilesystem->ListDirectory(
131 std::string("/sys/devices/system/cpu/cpu0/cpuidle/") + idleStateName, &files)) {
132 return false;
133 }
134 if (std::find(files.begin(), files.end(), "time") == files.end()) {
135 continue;
136 }
137 result->push_back(idleStateName);
138 }
139 if (idleStateNames.empty()) {
140 LOG(ERROR) << "Found no idle state names";
141 return false;
142 }
143 return true;
144 }
145
146 } // namespace pixel
147 } // namespace impl
148 } // namespace power
149 } // namespace hardware
150 } // namespace google
151 } // namespace aidl
152