| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "powerhal-adaptivecpu" |
| #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) |
| |
| #include "KernelCpuFeatureReader.h" |
| |
| #include <android-base/logging.h> |
| #include <utils/Trace.h> |
| |
| #include <ostream> |
| |
| namespace aidl { |
| namespace google { |
| namespace hardware { |
| namespace power { |
| namespace impl { |
| namespace pixel { |
| |
| constexpr std::string_view kKernelFilePath("/proc/vendor_sched/acpu_stats"); |
| constexpr size_t kReadBufferSize = sizeof(acpu_stats) * NUM_CPU_CORES; |
| |
| bool KernelCpuFeatureReader::Init() { |
| ATRACE_CALL(); |
| if (!OpenStatsFile(&mStatsFile)) { |
| return false; |
| } |
| return ReadStats(&mPreviousStats, &mPreviousReadTime); |
| } |
| |
| bool KernelCpuFeatureReader::GetRecentCpuFeatures( |
| std::array<double, NUM_CPU_POLICIES> *cpuPolicyAverageFrequencyHz, |
| std::array<double, NUM_CPU_CORES> *cpuCoreIdleTimesPercentage) { |
| ATRACE_CALL(); |
| std::array<acpu_stats, NUM_CPU_CORES> stats; |
| std::chrono::nanoseconds readTime; |
| if (!ReadStats(&stats, &readTime)) { |
| return false; |
| } |
| const std::chrono::nanoseconds timeDelta = readTime - mPreviousReadTime; |
| |
| for (size_t i = 0; i < NUM_CPU_POLICIES; i++) { |
| // acpu_stats has data per-CPU, but frequency data is equivalent for all CPUs in a policy. |
| // So, we only read the first CPU in each policy. |
| const size_t statsIdx = CPU_POLICY_INDICES[i]; |
| if (stats[statsIdx].weighted_sum_freq < mPreviousStats[statsIdx].weighted_sum_freq) { |
| LOG(WARNING) << "New weighted_sum_freq is less than old: new=" |
| << stats[statsIdx].weighted_sum_freq |
| << ", old=" << mPreviousStats[statsIdx].weighted_sum_freq; |
| mPreviousStats[statsIdx].weighted_sum_freq = stats[statsIdx].weighted_sum_freq; |
| } |
| (*cpuPolicyAverageFrequencyHz)[i] = |
| static_cast<double>(stats[statsIdx].weighted_sum_freq - |
| mPreviousStats[statsIdx].weighted_sum_freq) / |
| timeDelta.count(); |
| } |
| for (size_t i = 0; i < NUM_CPU_CORES; i++) { |
| if (stats[i].total_idle_time_ns < mPreviousStats[i].total_idle_time_ns) { |
| LOG(WARNING) << "New total_idle_time_ns is less than old: new=" |
| << stats[i].total_idle_time_ns |
| << ", old=" << mPreviousStats[i].total_idle_time_ns; |
| mPreviousStats[i].total_idle_time_ns = stats[i].total_idle_time_ns; |
| } |
| (*cpuCoreIdleTimesPercentage)[i] = |
| static_cast<double>(stats[i].total_idle_time_ns - |
| mPreviousStats[i].total_idle_time_ns) / |
| timeDelta.count(); |
| } |
| |
| mPreviousStats = stats; |
| mPreviousReadTime = readTime; |
| return true; |
| } |
| |
| bool KernelCpuFeatureReader::OpenStatsFile(std::unique_ptr<std::istream> *file) { |
| ATRACE_CALL(); |
| return mFilesystem->ReadFileStream(kKernelFilePath.data(), file); |
| } |
| |
| bool KernelCpuFeatureReader::ReadStats(std::array<acpu_stats, NUM_CPU_CORES> *stats, |
| std::chrono::nanoseconds *readTime) { |
| ATRACE_CALL(); |
| *readTime = mTimeSource->GetKernelTime(); |
| if (!mFilesystem->ResetFileStream(mStatsFile)) { |
| return false; |
| } |
| char buffer[kReadBufferSize]; |
| { |
| ATRACE_NAME("read"); |
| if (!mStatsFile->read(buffer, kReadBufferSize).good()) { |
| LOG(ERROR) << "Failed to read stats file"; |
| return false; |
| } |
| } |
| const size_t bytesRead = mStatsFile->gcount(); |
| if (bytesRead != kReadBufferSize) { |
| LOG(ERROR) << "Didn't read full data: expected=" << kReadBufferSize |
| << ", actual=" << bytesRead; |
| return false; |
| } |
| const auto kernelStructs = reinterpret_cast<acpu_stats *>(buffer); |
| std::copy(kernelStructs, kernelStructs + NUM_CPU_CORES, stats->begin()); |
| return true; |
| } |
| |
| void KernelCpuFeatureReader::DumpToStream(std::ostream &stream) const { |
| ATRACE_CALL(); |
| stream << "CPU features from acpu_stats:\n"; |
| for (size_t i = 0; i < NUM_CPU_CORES; i++) { |
| stream << "- CPU " << i << ": weighted_sum_freq=" << mPreviousStats[i].weighted_sum_freq |
| << ", total_idle_time_ns=" << mPreviousStats[i].total_idle_time_ns << "\n"; |
| } |
| stream << "- Last read time: " << mPreviousReadTime.count() << "ns\n"; |
| } |
| |
| } // namespace pixel |
| } // namespace impl |
| } // namespace power |
| } // namespace hardware |
| } // namespace google |
| } // namespace aidl |