Home | History | Annotate | Download | only in default
      1 /*
      2  * Copyright (C) 2018 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 "android.hardware.power.stats (at) 1.0-service-mock"
     18 
     19 #include "PowerStats.h"
     20 #include <android-base/file.h>
     21 #include <android-base/logging.h>
     22 #include <android-base/properties.h>
     23 #include <android-base/stringprintf.h>
     24 #include <android-base/strings.h>
     25 #include <inttypes.h>
     26 #include <stdlib.h>
     27 #include <algorithm>
     28 #include <exception>
     29 #include <thread>
     30 
     31 namespace android {
     32 namespace hardware {
     33 namespace power {
     34 namespace stats {
     35 namespace V1_0 {
     36 namespace implementation {
     37 
     38 #define MAX_FILE_PATH_LEN 128
     39 #define MAX_DEVICE_NAME_LEN 64
     40 #define MAX_QUEUE_SIZE 8192
     41 
     42 constexpr char kIioDirRoot[] = "/sys/bus/iio/devices/";
     43 constexpr char kDeviceName[] = "pm_device_name";
     44 constexpr char kDeviceType[] = "iio:device";
     45 constexpr uint32_t MAX_SAMPLING_RATE = 10;
     46 constexpr uint64_t WRITE_TIMEOUT_NS = 1000000000;
     47 
     48 void PowerStats::findIioPowerMonitorNodes() {
     49     struct dirent* ent;
     50     int fd;
     51     char devName[MAX_DEVICE_NAME_LEN];
     52     char filePath[MAX_FILE_PATH_LEN];
     53     DIR* iioDir = opendir(kIioDirRoot);
     54     if (!iioDir) {
     55         ALOGE("Error opening directory: %s", kIioDirRoot);
     56         return;
     57     }
     58     while (ent = readdir(iioDir), ent) {
     59         if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0 &&
     60             strlen(ent->d_name) > strlen(kDeviceType) &&
     61             strncmp(ent->d_name, kDeviceType, strlen(kDeviceType)) == 0) {
     62             snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", ent->d_name, "name");
     63             fd = openat(dirfd(iioDir), filePath, O_RDONLY);
     64             if (fd < 0) {
     65                 ALOGW("Failed to open directory: %s", filePath);
     66                 continue;
     67             }
     68             if (read(fd, devName, MAX_DEVICE_NAME_LEN) < 0) {
     69                 ALOGW("Failed to read device name from file: %s(%d)", filePath, fd);
     70                 close(fd);
     71                 continue;
     72             }
     73 
     74             if (strncmp(devName, kDeviceName, strlen(kDeviceName)) == 0) {
     75                 snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", kIioDirRoot, ent->d_name);
     76                 mPm.devicePaths.push_back(filePath);
     77             }
     78             close(fd);
     79         }
     80     }
     81     closedir(iioDir);
     82     return;
     83 }
     84 
     85 size_t PowerStats::parsePowerRails() {
     86     std::string data;
     87     std::string railFileName;
     88     std::string spsFileName;
     89     uint32_t index = 0;
     90     uint32_t samplingRate;
     91     for (const auto& path : mPm.devicePaths) {
     92         railFileName = path + "/enabled_rails";
     93         spsFileName = path + "/sampling_rate";
     94         if (!android::base::ReadFileToString(spsFileName, &data)) {
     95             ALOGW("Error reading file: %s", spsFileName.c_str());
     96             continue;
     97         }
     98         samplingRate = strtoul(data.c_str(), NULL, 10);
     99         if (!samplingRate || samplingRate == ULONG_MAX) {
    100             ALOGE("Error parsing: %s", spsFileName.c_str());
    101             break;
    102         }
    103         if (!android::base::ReadFileToString(railFileName, &data)) {
    104             ALOGW("Error reading file: %s", railFileName.c_str());
    105             continue;
    106         }
    107         std::istringstream railNames(data);
    108         std::string line;
    109         while (std::getline(railNames, line)) {
    110             std::vector<std::string> words = android::base::Split(line, ":");
    111             if (words.size() == 2) {
    112                 mPm.railsInfo.emplace(words[0], RailData{.devicePath = path,
    113                                                          .index = index,
    114                                                          .subsysName = words[1],
    115                                                          .samplingRate = samplingRate});
    116                 index++;
    117             } else {
    118                 ALOGW("Unexpected format in file: %s", railFileName.c_str());
    119             }
    120         }
    121     }
    122     return index;
    123 }
    124 
    125 int PowerStats::parseIioEnergyNode(std::string devName) {
    126     int ret = 0;
    127     std::string data;
    128     std::string fileName = devName + "/energy_value";
    129     if (!android::base::ReadFileToString(fileName, &data)) {
    130         ALOGE("Error reading file: %s", fileName.c_str());
    131         return -1;
    132     }
    133 
    134     std::istringstream energyData(data);
    135     std::string line;
    136     uint64_t timestamp = 0;
    137     bool timestampRead = false;
    138     while (std::getline(energyData, line)) {
    139         std::vector<std::string> words = android::base::Split(line, ",");
    140         if (timestampRead == false) {
    141             if (words.size() == 1) {
    142                 timestamp = strtoull(words[0].c_str(), NULL, 10);
    143                 if (timestamp == 0 || timestamp == ULLONG_MAX) {
    144                     ALOGW("Potentially wrong timestamp: %" PRIu64, timestamp);
    145                 }
    146                 timestampRead = true;
    147             }
    148         } else if (words.size() == 2) {
    149             std::string railName = words[0];
    150             if (mPm.railsInfo.count(railName) != 0) {
    151                 size_t index = mPm.railsInfo[railName].index;
    152                 mPm.reading[index].index = index;
    153                 mPm.reading[index].timestamp = timestamp;
    154                 mPm.reading[index].energy = strtoull(words[1].c_str(), NULL, 10);
    155                 if (mPm.reading[index].energy == ULLONG_MAX) {
    156                     ALOGW("Potentially wrong energy value: %" PRIu64, mPm.reading[index].energy);
    157                 }
    158             }
    159         } else {
    160             ALOGW("Unexpected format in file: %s", fileName.c_str());
    161             ret = -1;
    162             break;
    163         }
    164     }
    165     return ret;
    166 }
    167 
    168 Status PowerStats::parseIioEnergyNodes() {
    169     Status ret = Status::SUCCESS;
    170     if (mPm.hwEnabled == false) {
    171         return Status::NOT_SUPPORTED;
    172     }
    173 
    174     for (const auto& devicePath : mPm.devicePaths) {
    175         if (parseIioEnergyNode(devicePath) < 0) {
    176             ALOGE("Error in parsing power stats");
    177             ret = Status::FILESYSTEM_ERROR;
    178             break;
    179         }
    180     }
    181     return ret;
    182 }
    183 
    184 PowerStats::PowerStats() {
    185     findIioPowerMonitorNodes();
    186     size_t numRails = parsePowerRails();
    187     if (mPm.devicePaths.empty() || numRails == 0) {
    188         mPm.hwEnabled = false;
    189     } else {
    190         mPm.hwEnabled = true;
    191         mPm.reading.resize(numRails);
    192     }
    193 }
    194 
    195 Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
    196     hidl_vec<RailInfo> rInfo;
    197     Status ret = Status::SUCCESS;
    198     size_t index;
    199     std::lock_guard<std::mutex> _lock(mPm.mLock);
    200     if (mPm.hwEnabled == false) {
    201         _hidl_cb(rInfo, Status::NOT_SUPPORTED);
    202         return Void();
    203     }
    204     rInfo.resize(mPm.railsInfo.size());
    205     for (const auto& railData : mPm.railsInfo) {
    206         index = railData.second.index;
    207         rInfo[index].railName = railData.first;
    208         rInfo[index].subsysName = railData.second.subsysName;
    209         rInfo[index].index = index;
    210         rInfo[index].samplingRate = railData.second.samplingRate;
    211     }
    212     _hidl_cb(rInfo, ret);
    213     return Void();
    214 }
    215 
    216 Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t>& railIndices,
    217                                        getEnergyData_cb _hidl_cb) {
    218     hidl_vec<EnergyData> eVal;
    219     std::lock_guard<std::mutex> _lock(mPm.mLock);
    220     Status ret = parseIioEnergyNodes();
    221 
    222     if (ret != Status::SUCCESS) {
    223         ALOGE("Failed to getEnergyData");
    224         _hidl_cb(eVal, ret);
    225         return Void();
    226     }
    227 
    228     if (railIndices.size() == 0) {
    229         eVal.resize(mPm.railsInfo.size());
    230         memcpy(&eVal[0], &mPm.reading[0], mPm.reading.size() * sizeof(EnergyData));
    231     } else {
    232         eVal.resize(railIndices.size());
    233         int i = 0;
    234         for (const auto& railIndex : railIndices) {
    235             if (railIndex >= mPm.reading.size()) {
    236                 ret = Status::INVALID_INPUT;
    237                 eVal.resize(0);
    238                 break;
    239             }
    240             memcpy(&eVal[i], &mPm.reading[railIndex], sizeof(EnergyData));
    241             i++;
    242         }
    243     }
    244     _hidl_cb(eVal, ret);
    245     return Void();
    246 }
    247 
    248 Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
    249                                           streamEnergyData_cb _hidl_cb) {
    250     std::lock_guard<std::mutex> _lock(mPm.mLock);
    251     if (mPm.fmqSynchronized != nullptr) {
    252         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
    253         return Void();
    254     }
    255     uint32_t sps = std::min(samplingRate, MAX_SAMPLING_RATE);
    256     uint32_t numSamples = timeMs * sps / 1000;
    257     mPm.fmqSynchronized.reset(new (std::nothrow) MessageQueueSync(MAX_QUEUE_SIZE, true));
    258     if (mPm.fmqSynchronized == nullptr || mPm.fmqSynchronized->isValid() == false) {
    259         mPm.fmqSynchronized = nullptr;
    260         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
    261         return Void();
    262     }
    263     std::thread pollThread = std::thread([this, sps, numSamples]() {
    264         uint64_t sleepTimeUs = 1000000 / sps;
    265         uint32_t currSamples = 0;
    266         while (currSamples < numSamples) {
    267             mPm.mLock.lock();
    268             if (parseIioEnergyNodes() == Status::SUCCESS) {
    269                 mPm.fmqSynchronized->writeBlocking(&mPm.reading[0], mPm.reading.size(),
    270                                                    WRITE_TIMEOUT_NS);
    271                 mPm.mLock.unlock();
    272                 currSamples++;
    273                 if (usleep(sleepTimeUs) < 0) {
    274                     ALOGW("Sleep interrupted");
    275                     break;
    276                 }
    277             } else {
    278                 mPm.mLock.unlock();
    279                 break;
    280             }
    281         }
    282         mPm.mLock.lock();
    283         mPm.fmqSynchronized = nullptr;
    284         mPm.mLock.unlock();
    285         return;
    286     });
    287     pollThread.detach();
    288     _hidl_cb(*(mPm.fmqSynchronized)->getDesc(), numSamples, mPm.reading.size(), Status::SUCCESS);
    289     return Void();
    290 }
    291 
    292 uint32_t PowerStats::addPowerEntity(const std::string& name, PowerEntityType type) {
    293     uint32_t id = mPowerEntityInfos.size();
    294     mPowerEntityInfos.push_back({id, name, type});
    295     return id;
    296 }
    297 
    298 void PowerStats::addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p) {
    299     std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
    300     for (auto stateSpace : stateSpaces) {
    301         mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
    302         mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
    303     }
    304 }
    305 
    306 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
    307     // If not configured, return NOT_SUPPORTED
    308     if (mPowerEntityInfos.empty()) {
    309         _hidl_cb({}, Status::NOT_SUPPORTED);
    310         return Void();
    311     }
    312 
    313     _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
    314     return Void();
    315 }
    316 
    317 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
    318                                                  getPowerEntityStateInfo_cb _hidl_cb) {
    319     // If not configured, return NOT_SUPPORTED
    320     if (mPowerEntityStateSpaces.empty()) {
    321         _hidl_cb({}, Status::NOT_SUPPORTED);
    322         return Void();
    323     }
    324 
    325     std::vector<PowerEntityStateSpace> stateSpaces;
    326 
    327     // If powerEntityIds is empty then return state space info for all entities
    328     if (powerEntityIds.size() == 0) {
    329         stateSpaces.reserve(mPowerEntityStateSpaces.size());
    330         for (auto i : mPowerEntityStateSpaces) {
    331             stateSpaces.emplace_back(i.second);
    332         }
    333         _hidl_cb(stateSpaces, Status::SUCCESS);
    334         return Void();
    335     }
    336 
    337     // Return state space information only for valid ids
    338     auto ret = Status::SUCCESS;
    339     stateSpaces.reserve(powerEntityIds.size());
    340     for (const uint32_t id : powerEntityIds) {
    341         auto stateSpace = mPowerEntityStateSpaces.find(id);
    342         if (stateSpace != mPowerEntityStateSpaces.end()) {
    343             stateSpaces.emplace_back(stateSpace->second);
    344         } else {
    345             ret = Status::INVALID_INPUT;
    346         }
    347     }
    348 
    349     _hidl_cb(stateSpaces, ret);
    350     return Void();
    351 }
    352 
    353 Return<void> PowerStats::getPowerEntityStateResidencyData(
    354         const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
    355     // If not configured, return NOT_SUPPORTED
    356     if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
    357         _hidl_cb({}, Status::NOT_SUPPORTED);
    358         return Void();
    359     }
    360 
    361     // If powerEntityIds is empty then return data for all supported entities
    362     if (powerEntityIds.size() == 0) {
    363         std::vector<uint32_t> ids;
    364         for (auto stateSpace : mPowerEntityStateSpaces) {
    365             ids.emplace_back(stateSpace.first);
    366         }
    367         return getPowerEntityStateResidencyData(ids, _hidl_cb);
    368     }
    369 
    370     std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
    371     std::vector<PowerEntityStateResidencyResult> results;
    372     results.reserve(powerEntityIds.size());
    373 
    374     // return results for only the given powerEntityIds
    375     bool invalidInput = false;
    376     bool filesystemError = false;
    377     for (auto id : powerEntityIds) {
    378         auto dataProvider = mStateResidencyDataProviders.find(id);
    379         // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
    380         if (dataProvider == mStateResidencyDataProviders.end()) {
    381             invalidInput = true;
    382             continue;
    383         }
    384 
    385         // get the results if we have not already done so.
    386         if (stateResidencies.find(id) == stateResidencies.end()) {
    387             if (!dataProvider->second->getResults(stateResidencies)) {
    388                 filesystemError = true;
    389             }
    390         }
    391 
    392         // append results
    393         auto stateResidency = stateResidencies.find(id);
    394         if (stateResidency != stateResidencies.end()) {
    395             results.emplace_back(stateResidency->second);
    396         }
    397     }
    398 
    399     auto ret = Status::SUCCESS;
    400     if (filesystemError) {
    401         ret = Status::FILESYSTEM_ERROR;
    402     } else if (invalidInput) {
    403         ret = Status::INVALID_INPUT;
    404     }
    405 
    406     _hidl_cb(results, ret);
    407     return Void();
    408 }
    409 
    410 bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
    411                            const hidl_vec<PowerEntityStateSpace>& stateSpaces,
    412                            const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
    413     // construct lookup table of powerEntityId to name
    414     std::unordered_map<uint32_t, std::string> entityNames;
    415     for (auto info : infos) {
    416         entityNames.emplace(info.powerEntityId, info.powerEntityName);
    417     }
    418 
    419     // construct lookup table of powerEntityId, powerEntityStateId to state name
    420     std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
    421     for (auto stateSpace : stateSpaces) {
    422         stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
    423         for (auto state : stateSpace.states) {
    424             stateNames.at(stateSpace.powerEntityId)
    425                     .emplace(state.powerEntityStateId, state.powerEntityStateName);
    426         }
    427     }
    428 
    429     std::ostringstream dumpStats;
    430     dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
    431 
    432     const char* headerFormat = "  %14s   %14s   %16s   %15s   %16s\n";
    433     const char* dataFormat =
    434             "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %13" PRIu64 " ms\n";
    435     dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
    436                                              "Total entries", "Last entry timestamp");
    437 
    438     for (auto result : results) {
    439         for (auto stateResidency : result.stateResidencyData) {
    440             dumpStats << android::base::StringPrintf(
    441                     dataFormat, entityNames.at(result.powerEntityId).c_str(),
    442                     stateNames.at(result.powerEntityId)
    443                             .at(stateResidency.powerEntityStateId)
    444                             .c_str(),
    445                     stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
    446                     stateResidency.lastEntryTimestampMs);
    447         }
    448     }
    449 
    450     dumpStats << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
    451 
    452     return android::base::WriteStringToFd(dumpStats.str(), fd);
    453 }
    454 
    455 Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
    456     if (handle == nullptr || handle->numFds < 1) {
    457         return Void();
    458     }
    459 
    460     int fd = handle->data[0];
    461     Status status;
    462     hidl_vec<PowerEntityInfo> infos;
    463 
    464     // Get power entity information
    465     getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
    466         status = rStatus;
    467         infos = rInfos;
    468     });
    469     if (status != Status::SUCCESS) {
    470         LOG(ERROR) << "Error getting power entity info";
    471         return Void();
    472     }
    473 
    474     // Get power entity state information
    475     hidl_vec<PowerEntityStateSpace> stateSpaces;
    476     getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
    477         status = rStatus;
    478         stateSpaces = rStateSpaces;
    479     });
    480     if (status != Status::SUCCESS) {
    481         LOG(ERROR) << "Error getting state info";
    482         return Void();
    483     }
    484 
    485     // Get power entity state residency data
    486     hidl_vec<PowerEntityStateResidencyResult> results;
    487     getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
    488         status = rStatus;
    489         results = rResults;
    490     });
    491 
    492     // This implementation of getPowerEntityStateResidencyData supports the
    493     // return of partial results if status == FILESYSTEM_ERROR.
    494     if (status != Status::SUCCESS) {
    495         LOG(ERROR) << "Error getting residency data -- Some results missing";
    496     }
    497 
    498     if (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
    499         PLOG(ERROR) << "Failed to dump residency data to fd";
    500     }
    501 
    502     fsync(fd);
    503 
    504     return Void();
    505 }
    506 
    507 }  // namespace implementation
    508 }  // namespace V1_0
    509 }  // namespace stats
    510 }  // namespace power
    511 }  // namespace hardware
    512 }  // namespace android
    513