| /* |
| * Copyright (C) 2018 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 "libpixelpowerstats" |
| |
| #include <android-base/logging.h> |
| #include <android-base/strings.h> |
| #include <pixelpowerstats/GenericStateResidencyDataProvider.h> |
| #include <pixelpowerstats/PowerStatsUtils.h> |
| #include <cstdio> |
| #include <cstring> |
| #include <memory> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| namespace android { |
| namespace hardware { |
| namespace google { |
| namespace pixel { |
| namespace powerstats { |
| |
| std::vector<StateResidencyConfig> generateGenericStateResidencyConfigs( |
| const StateResidencyConfig &stateConfig, |
| const std::vector<std::pair<std::string, std::string>> &stateHeaders) { |
| std::vector<StateResidencyConfig> stateResidencyConfigs; |
| stateResidencyConfigs.reserve(stateHeaders.size()); |
| for (auto h : stateHeaders) { |
| StateResidencyConfig cfg = {stateConfig}; |
| cfg.name = h.first; |
| cfg.header = h.second; |
| stateResidencyConfigs.emplace_back(cfg); |
| } |
| return stateResidencyConfigs; |
| } |
| |
| PowerEntityConfig::PowerEntityConfig(const std::vector<StateResidencyConfig> &stateResidencyConfigs) |
| : PowerEntityConfig("", stateResidencyConfigs) {} |
| |
| PowerEntityConfig::PowerEntityConfig(const std::string &header, |
| const std::vector<StateResidencyConfig> &stateResidencyConfigs) |
| : mHeader(header) { |
| mStateResidencyConfigs.reserve(stateResidencyConfigs.size()); |
| for (uint32_t i = 0; i < stateResidencyConfigs.size(); ++i) { |
| mStateResidencyConfigs.emplace_back(i, stateResidencyConfigs[i]); |
| } |
| } |
| |
| static bool parseState(PowerEntityStateResidencyData &data, const StateResidencyConfig &config, |
| FILE *fp, char *&line, size_t &len) { |
| size_t numFieldsRead = 0; |
| const size_t numFields = |
| config.entryCountSupported + config.totalTimeSupported + config.lastEntrySupported; |
| |
| while ((numFieldsRead < numFields) && (getline(&line, &len, fp) != -1)) { |
| uint64_t stat = 0; |
| // Attempt to extract data from the current line |
| if (config.entryCountSupported && utils::extractStat(line, config.entryCountPrefix, stat)) { |
| data.totalStateEntryCount = |
| config.entryCountTransform ? config.entryCountTransform(stat) : stat; |
| ++numFieldsRead; |
| } else if (config.totalTimeSupported && |
| utils::extractStat(line, config.totalTimePrefix, stat)) { |
| data.totalTimeInStateMs = |
| config.totalTimeTransform ? config.totalTimeTransform(stat) : stat; |
| ++numFieldsRead; |
| } else if (config.lastEntrySupported && |
| utils::extractStat(line, config.lastEntryPrefix, stat)) { |
| data.lastEntryTimestampMs = |
| config.lastEntryTransform ? config.lastEntryTransform(stat) : stat; |
| ++numFieldsRead; |
| } |
| } |
| |
| // End of file was reached and not all state data was parsed. Something |
| // went wrong |
| if (numFieldsRead != numFields) { |
| LOG(ERROR) << __func__ << ": failed to parse stats for:" << config.name; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| template <class T, class Func> |
| static auto findNext(const std::vector<T> &collection, FILE *fp, char *&line, size_t &len, |
| Func pred) { |
| // handling the case when there is no header to look for |
| if (pred(collection.front(), "")) { |
| return collection.cbegin(); |
| } |
| |
| while (getline(&line, &len, fp) != -1) { |
| for (auto it = collection.cbegin(); it != collection.cend(); ++it) { |
| if (pred(*it, line)) { |
| return it; |
| } |
| } |
| } |
| |
| return collection.cend(); |
| } |
| |
| static bool getStateData( |
| PowerEntityStateResidencyResult &result, |
| const std::vector<std::pair<uint32_t, StateResidencyConfig>> &stateResidencyConfigs, FILE *fp, |
| char *&line, size_t &len) { |
| size_t numStatesRead = 0; |
| size_t numStates = stateResidencyConfigs.size(); |
| auto nextState = stateResidencyConfigs.cbegin(); |
| auto endState = stateResidencyConfigs.cend(); |
| auto pred = [](auto a, char const *b) { |
| // return true if b matches the header contained in a, ignoring whitespace |
| return (a.second.header == android::base::Trim(std::string(b))); |
| }; |
| |
| result.stateResidencyData.resize(numStates); |
| |
| // Search for state headers until we have found them all or can't find anymore |
| while ((numStatesRead < numStates) && |
| (nextState = findNext<std::pair<uint32_t, StateResidencyConfig>>( |
| stateResidencyConfigs, fp, line, len, pred)) != endState) { |
| // Found a matching state header. Parse the contents |
| PowerEntityStateResidencyData data = {.powerEntityStateId = nextState->first}; |
| if (parseState(data, nextState->second, fp, line, len)) { |
| result.stateResidencyData[numStatesRead] = data; |
| ++numStatesRead; |
| } else { |
| break; |
| } |
| } |
| |
| // There was a problem parsing and we failed to get data for all of the states |
| if (numStatesRead != numStates) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool GenericStateResidencyDataProvider::getResults( |
| std::unordered_map<uint32_t, PowerEntityStateResidencyResult> &results) { |
| // Using FILE* instead of std::ifstream for performance reasons (b/122253123) |
| std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(mPath.c_str(), "r"), fclose); |
| if (!fp) { |
| PLOG(ERROR) << __func__ << ":Failed to open file " << mPath |
| << " Error = " << strerror(errno); |
| return false; |
| } |
| |
| size_t len = 0; |
| char *line = nullptr; |
| size_t numEntitiesRead = 0; |
| size_t numEntities = mPowerEntityConfigs.size(); |
| auto nextConfig = mPowerEntityConfigs.cbegin(); |
| auto endConfig = mPowerEntityConfigs.cend(); |
| auto pred = [](auto a, char const *b) { |
| // return true if b matches the header contained in a, ignoring whitespace |
| return (a.second.mHeader == android::base::Trim(std::string(b))); |
| }; |
| |
| // Search for entity headers until we have found them all or can't find anymore |
| while ((numEntitiesRead < numEntities) && |
| (nextConfig = findNext<decltype(mPowerEntityConfigs)::value_type>( |
| mPowerEntityConfigs, fp.get(), line, len, pred)) != endConfig) { |
| // Found a matching header. Retrieve its state data |
| PowerEntityStateResidencyResult result = {.powerEntityId = nextConfig->first}; |
| if (getStateData(result, nextConfig->second.mStateResidencyConfigs, fp.get(), line, len)) { |
| results.emplace(nextConfig->first, result); |
| ++numEntitiesRead; |
| } else { |
| break; |
| } |
| } |
| |
| free(line); |
| |
| // There was a problem gathering state residency data for one or more entities |
| if (numEntitiesRead != numEntities) { |
| LOG(ERROR) << __func__ << ":Failed to get results for " << mPath; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void GenericStateResidencyDataProvider::addEntity(uint32_t id, const PowerEntityConfig &config) { |
| mPowerEntityConfigs.emplace_back(id, config); |
| } |
| |
| std::vector<PowerEntityStateSpace> GenericStateResidencyDataProvider::getStateSpaces() { |
| std::vector<PowerEntityStateSpace> stateSpaces; |
| stateSpaces.reserve(mPowerEntityConfigs.size()); |
| for (auto config : mPowerEntityConfigs) { |
| PowerEntityStateSpace s = {.powerEntityId = config.first}; |
| s.states.resize(config.second.mStateResidencyConfigs.size()); |
| |
| for (uint32_t i = 0; i < config.second.mStateResidencyConfigs.size(); ++i) { |
| s.states[i] = { |
| .powerEntityStateId = config.second.mStateResidencyConfigs[i].first, |
| .powerEntityStateName = config.second.mStateResidencyConfigs[i].second.name}; |
| } |
| stateSpaces.emplace_back(s); |
| } |
| return stateSpaces; |
| } |
| |
| } // namespace powerstats |
| } // namespace pixel |
| } // namespace google |
| } // namespace hardware |
| } // namespace android |