/* * 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. */ // TODO(b/167628903): Delete this file #define LOG_TAG "libpixelpowerstats" #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace hardware { namespace google { namespace pixel { namespace powerstats { std::vector generateGenericStateResidencyConfigs( const StateResidencyConfig &stateConfig, const std::vector> &stateHeaders) { std::vector 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 &stateResidencyConfigs) : PowerEntityConfig("", stateResidencyConfigs) {} PowerEntityConfig::PowerEntityConfig(const std::string &header, const std::vector &stateResidencyConfigs) : PowerEntityConfig(0, header, stateResidencyConfigs) {} PowerEntityConfig::PowerEntityConfig(const uint32_t start_id, const std::string &header, const std::vector &stateResidencyConfigs) : mHeader(header) { mStateResidencyConfigs.reserve(stateResidencyConfigs.size()); for (uint32_t i = start_id; i < start_id + stateResidencyConfigs.size(); ++i) { mStateResidencyConfigs.emplace_back(i, stateResidencyConfigs[i - start_id]); } } 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 static auto findNext(const std::vector &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> &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>( 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 &results) { // Using FILE* instead of std::ifstream for performance reasons (b/122253123) std::unique_ptr 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))); }; bool skipFindNext = false; // Search for entity headers until we have found them all or can't find anymore while ((numEntitiesRead < numEntities) && (skipFindNext || (nextConfig = findNext( 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)) { // If a power entity already exists, then merge in the // StateResidencyData. if (results.find(nextConfig->first) != results.end()) { uint32_t size = results[nextConfig->first].stateResidencyData.size(); results[nextConfig->first].stateResidencyData.resize( size + result.stateResidencyData.size()); for (uint32_t i = 0; i < result.stateResidencyData.size(); i++) { results[nextConfig->first].stateResidencyData[size + i] = result.stateResidencyData[i]; } } else { results.emplace(nextConfig->first, result); } ++numEntitiesRead; } else { break; } // If the header of the next PowerEntityConfig is equal to the // current, don't search for it within the file since we'll be search // for more states. auto currConfig = nextConfig++; if (nextConfig != endConfig && nextConfig->second.mHeader == currConfig->second.mHeader) { skipFindNext = true; } else { skipFindNext = false; } } 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 GenericStateResidencyDataProvider::getStateSpaces() { std::vector 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