C++程序  |  559行  |  21.71 KB

/*
 * 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 <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include <pixelpowerstats/PowerStats.h>

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>

#include <inttypes.h>
#include <time.h>

namespace android {
namespace hardware {
namespace power {
namespace stats {
namespace V1_0 {
namespace implementation {

void PowerStats::setRailDataProvider(std::unique_ptr<IRailDataProvider> dataProvider) {
    mRailDataProvider = std::move(dataProvider);
}

Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
    if (!mRailDataProvider) {
        _hidl_cb({}, Status::NOT_SUPPORTED);
        return Void();
    }

    return mRailDataProvider->getRailInfo(_hidl_cb);
}

Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t> &railIndices,
                                       getEnergyData_cb _hidl_cb) {
    if (!mRailDataProvider) {
        _hidl_cb({}, Status::NOT_SUPPORTED);
        return Void();
    }

    return mRailDataProvider->getEnergyData(railIndices, _hidl_cb);
}

Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
                                          streamEnergyData_cb _hidl_cb) {
    if (!mRailDataProvider) {
        _hidl_cb({}, 0, 0, Status::NOT_SUPPORTED);
        return Void();
    }

    return mRailDataProvider->streamEnergyData(timeMs, samplingRate, _hidl_cb);
}

uint32_t PowerStats::addPowerEntity(const std::string &name, PowerEntityType type) {
    uint32_t id = mPowerEntityInfos.size();
    mPowerEntityInfos.push_back({id, name, type});
    return id;
}

void PowerStats::addStateResidencyDataProvider(sp<IStateResidencyDataProvider> p) {
    std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
    for (auto stateSpace : stateSpaces) {
        mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
        mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
    }
}

Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
    // If not configured, return NOT_SUPPORTED
    if (mPowerEntityInfos.empty()) {
        _hidl_cb({}, Status::NOT_SUPPORTED);
        return Void();
    }

    _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
    return Void();
}

Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t> &powerEntityIds,
                                                 getPowerEntityStateInfo_cb _hidl_cb) {
    // If not configured, return NOT_SUPPORTED
    if (mPowerEntityStateSpaces.empty()) {
        _hidl_cb({}, Status::NOT_SUPPORTED);
        return Void();
    }

    std::vector<PowerEntityStateSpace> stateSpaces;

    // If powerEntityIds is empty then return state space info for all entities
    if (powerEntityIds.size() == 0) {
        stateSpaces.reserve(mPowerEntityStateSpaces.size());
        for (auto i : mPowerEntityStateSpaces) {
            stateSpaces.emplace_back(i.second);
        }
        _hidl_cb(stateSpaces, Status::SUCCESS);
        return Void();
    }

    // Return state space information only for valid ids
    auto ret = Status::SUCCESS;
    stateSpaces.reserve(powerEntityIds.size());
    for (const uint32_t id : powerEntityIds) {
        auto stateSpace = mPowerEntityStateSpaces.find(id);
        if (stateSpace != mPowerEntityStateSpaces.end()) {
            stateSpaces.emplace_back(stateSpace->second);
        } else {
            ret = Status::INVALID_INPUT;
        }
    }

    _hidl_cb(stateSpaces, ret);
    return Void();
}

Return<void> PowerStats::getPowerEntityStateResidencyData(
    const hidl_vec<uint32_t> &powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
    // If not configured, return NOT_SUPPORTED
    if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
        _hidl_cb({}, Status::NOT_SUPPORTED);
        return Void();
    }

    // If powerEntityIds is empty then return data for all supported entities
    if (powerEntityIds.size() == 0) {
        std::vector<uint32_t> ids;
        for (auto stateSpace : mPowerEntityStateSpaces) {
            ids.emplace_back(stateSpace.first);
        }
        return getPowerEntityStateResidencyData(ids, _hidl_cb);
    }

    std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
    std::vector<PowerEntityStateResidencyResult> results;
    results.reserve(powerEntityIds.size());

    // return results for only the given powerEntityIds
    bool invalidInput = false;
    bool filesystemError = false;
    for (auto id : powerEntityIds) {
        auto dataProvider = mStateResidencyDataProviders.find(id);
        // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
        if (dataProvider == mStateResidencyDataProviders.end()) {
            invalidInput = true;
            continue;
        }

        // get the results if we have not already done so.
        if (stateResidencies.find(id) == stateResidencies.end()) {
            if (!dataProvider->second->getResults(stateResidencies)) {
                filesystemError = true;
            }
        }

        // append results
        auto stateResidency = stateResidencies.find(id);
        if (stateResidency != stateResidencies.end()) {
            results.emplace_back(stateResidency->second);
        }
    }

    auto ret = Status::SUCCESS;
    if (filesystemError) {
        ret = Status::FILESYSTEM_ERROR;
    } else if (invalidInput) {
        ret = Status::INVALID_INPUT;
    }

    _hidl_cb(results, ret);
    return Void();
}

//
// Debugging utilities to support printing data via debug()
//

static uint64_t getTimeElapsedMs(const struct timespec &now, const struct timespec &then) {
    uint64_t thenMs = then.tv_sec * 1000 + (then.tv_nsec / 1000000);
    uint64_t nowMs = now.tv_sec * 1000 + (now.tv_nsec / 1000000);
    return (nowMs - thenMs);
}

static const char RESIDENCY_HEADER[] =
        "\n============= PowerStats HAL 1.0 state residencies ==============\n";
static const char RESIDENCY_FOOTER[] =
        "========== End of PowerStats HAL 1.0 state residencies ==========\n";

static bool DumpResidencyDataToFd(
        const std::unordered_map<uint32_t, std::string> &entityNames,
        const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
        const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
    std::ostringstream dumpStats;
    const char *headerFormat = "  %14s   %14s   %16s   %15s   %17s\n";
    const char *dataFormat =
            "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %14" PRIu64 " ms\n";

    dumpStats << RESIDENCY_HEADER;
    dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
                                             "Total entries", "Last entry tstamp");

    for (auto result : results) {
        for (auto stateResidency : result.stateResidencyData) {
            dumpStats << android::base::StringPrintf(
                    dataFormat, entityNames.at(result.powerEntityId).c_str(),
                    stateNames.at(result.powerEntityId)
                              .at(stateResidency.powerEntityStateId).c_str(),
                    stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
                    stateResidency.lastEntryTimestampMs);
        }
    }

    dumpStats << RESIDENCY_FOOTER;

    return android::base::WriteStringToFd(dumpStats.str(), fd);
}

static bool DumpResidencyDataDiffToFd(
        const std::unordered_map<uint32_t, std::string> &entityNames,
        const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
        uint64_t elapsedTimeMs, const hidl_vec<PowerEntityStateResidencyResult> &prevResults,
        const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
    std::ostringstream dumpStats;
    const char *headerFormat = "  %14s   %14s   %16s (%14s)   %15s (%16s)   %17s (%14s)\n";
    const char *dataFormatWithDelta =
            "  %14s   %14s   %13" PRIu64 " ms (%14" PRId64 ")   %15" PRIu64 " (%16" PRId64 ")"
            "   %14" PRIu64 " ms (%14" PRId64 ")\n";
    const char *dataFormatWithoutDelta =
            "  %14s   %14s   %13" PRIu64 " ms (          none)   %15" PRIu64 " (            none)"
            "   %14" PRIu64 " ms (          none)\n";

    dumpStats << RESIDENCY_HEADER;
    dumpStats << "Elapsed time: "
              << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";

    dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
                                             "Delta   ", "Total entries", "Delta   ",
                                             "Last entry tstamp", "Delta ");

    // Process prevResults into a 2-tier lookup table for easy reference
    std::unordered_map<uint32_t, std::unordered_map<uint32_t, PowerEntityStateResidencyData>>
            prevResultsMap;
    for (auto prevResult : prevResults) {
        prevResultsMap.emplace(prevResult.powerEntityId,
                               std::unordered_map<uint32_t, PowerEntityStateResidencyData>());
        for (auto stateResidency : prevResult.stateResidencyData) {
            prevResultsMap.at(prevResult.powerEntityId)
                    .emplace(stateResidency.powerEntityStateId, stateResidency);
        }
    }

    // Iterate over the new result data (one "result" per entity)
    for (auto result : results) {
        uint32_t entityId = result.powerEntityId;
        const char *entityName = entityNames.at(entityId).c_str();

        // Look up previous result data for the same entity
        auto prevEntityResultIt = prevResultsMap.find(entityId);

        // Iterate over individual states within the current entity's new result
        for (auto stateResidency : result.stateResidencyData) {
            uint32_t stateId = stateResidency.powerEntityStateId;
            const char *stateName = stateNames.at(entityId).at(stateId).c_str();

            // If a previous result was found for the same entity, see if that
            // result also contains data for the current state
            bool prevValueFound = false;
            if (prevEntityResultIt != prevResultsMap.end()) {
                auto prevStateResidencyIt = prevEntityResultIt->second.find(stateId);
                // If a previous result was found for the current entity and state, calculate the
                // deltas and display them along with new result
                if (prevStateResidencyIt != prevEntityResultIt->second.end()) {
                    int64_t deltaTotalTime = stateResidency.totalTimeInStateMs -
                                             prevStateResidencyIt->second.totalTimeInStateMs;
                    int64_t deltaTotalCount = stateResidency.totalStateEntryCount -
                                              prevStateResidencyIt->second.totalStateEntryCount;
                    int64_t deltaTimestamp = stateResidency.lastEntryTimestampMs -
                                             prevStateResidencyIt->second.lastEntryTimestampMs;

                    dumpStats << android::base::StringPrintf(
                            dataFormatWithDelta, entityName, stateName,
                            stateResidency.totalTimeInStateMs, deltaTotalTime,
                            stateResidency.totalStateEntryCount, deltaTotalCount,
                            stateResidency.lastEntryTimestampMs, deltaTimestamp);
                    prevValueFound = true;
                }
            }

            // If no previous result was found for the current entity and state, display the new
            // result without deltas
            if (!prevValueFound) {
                dumpStats << android::base::StringPrintf(
                        dataFormatWithoutDelta, entityName, stateName,
                        stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
                        stateResidency.lastEntryTimestampMs);
            }
        }
    }

    dumpStats << RESIDENCY_FOOTER;

    return android::base::WriteStringToFd(dumpStats.str(), fd);
}

void PowerStats::debugStateResidency(const std::unordered_map<uint32_t, std::string> &entityNames,
                                     int fd, bool delta) {
    static struct timespec prevDataTime;
    static bool prevDataTimeValid = false;
    struct timespec dataTime;
    bool dataTimeValid;

    // Get power entity state space information
    Status status;
    hidl_vec<PowerEntityStateSpace> stateSpaces;
    getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
        status = rStatus;
        stateSpaces = rStateSpaces;
    });
    if (status != Status::SUCCESS) {
        LOG(ERROR) << "Error getting state info";
        return;
    }

    // Construct lookup table of powerEntityId, powerEntityStateId to state name
    std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
    for (auto stateSpace : stateSpaces) {
        stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
        auto &entityStateNames = stateNames.at(stateSpace.powerEntityId);
        for (auto state : stateSpace.states) {
            entityStateNames.emplace(state.powerEntityStateId, state.powerEntityStateName);
        }
    }

    // Get power entity state residency data
    hidl_vec<PowerEntityStateResidencyResult> results;
    getPowerEntityStateResidencyData(
            {}, [&status, &results, &dataTime, &dataTimeValid](auto rResults, auto rStatus) {
                status = rStatus;
                results = rResults;
                dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
            });

    // This implementation of getPowerEntityStateResidencyData supports the
    // return of partial results if status == FILESYSTEM_ERROR.
    if (status != Status::SUCCESS) {
        LOG(ERROR) << "Error getting residency data -- Some results missing";
    }

    if (!delta) {
        // If no delta argument was supplied, just dump the latest data
        if (!DumpResidencyDataToFd(entityNames, stateNames, results, fd)) {
            PLOG(ERROR) << "Failed to dump residency data to fd";
        }
    } else {
        // If the delta argument was supplied, calculate the elapsed time since the previous
        // result and then dump the latest data along with elapsed time and deltas
        static hidl_vec<PowerEntityStateResidencyResult> prevResults;
        uint64_t elapsedTimeMs = 0;
        if (dataTimeValid && prevDataTimeValid) {
            elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
        }

        if (!DumpResidencyDataDiffToFd(entityNames, stateNames, elapsedTimeMs, prevResults,
                                       results, fd)) {
            PLOG(ERROR) << "Failed to dump residency data delta to fd";
        }

        prevResults = results;
        prevDataTime = dataTime;
        prevDataTimeValid = dataTimeValid;
    }
}

static const char ENERGYDATA_HEADER[] =
        "\n============= PowerStats HAL 1.0 rail energy data ==============\n";
static const char ENERGYDATA_FOOTER[] =
        "========== End of PowerStats HAL 1.0 rail energy data ==========\n";

static bool DumpEnergyDataToFd(
        const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
        const hidl_vec<EnergyData> &energyData, int fd) {
    std::ostringstream dumpStats;
    const char *headerFormat = "  %14s   %18s   %18s\n";
    const char *dataFormat = "  %14s   %18s   %14.2f mWs\n";

    dumpStats << ENERGYDATA_HEADER;
    dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy");

    for (auto data : energyData) {
        dumpStats << android::base::StringPrintf(dataFormat, railNames.at(data.index).first.c_str(),
                                                 railNames.at(data.index).second.c_str(),
                                                 static_cast<float>(data.energy) / 1000.0);
    }

    dumpStats << ENERGYDATA_FOOTER;

    return android::base::WriteStringToFd(dumpStats.str(), fd);
}

static bool DumpEnergyDataDiffToFd(
        const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
        uint64_t elapsedTimeMs, const hidl_vec<EnergyData> &prevEnergyData,
        const hidl_vec<EnergyData> &energyData, int fd) {
    std::ostringstream dumpStats;
    const char *headerFormat = "  %14s   %18s   %18s (%14s)\n";
    const char *dataFormatWithDelta = "  %14s   %18s   %14.2f mWs (%14.2f)\n";
    const char *dataFormatWithoutDelta = "  %14s   %18s   %14.2f mWs (          none)\n";

    dumpStats << ENERGYDATA_HEADER;
    dumpStats << "Elapsed time: "
              << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";

    dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy",
                                             "Delta   ");

    std::unordered_map<uint32_t, uint64_t> prevEnergyDataMap;
    for (auto data : prevEnergyData) {
        prevEnergyDataMap.emplace(data.index, data.energy);
    }

    for (auto data : energyData) {
        const char *subsysName = railNames.at(data.index).first.c_str();
        const char *railName = railNames.at(data.index).second.c_str();

        auto prevEnergyDataIt = prevEnergyDataMap.find(data.index);

        if (prevEnergyDataIt != prevEnergyDataMap.end()) {
            int64_t deltaEnergy = data.energy - prevEnergyDataIt->second;

            dumpStats << android::base::StringPrintf(dataFormatWithDelta, subsysName, railName,
                                                     static_cast<float>(data.energy) / 1000.0,
                                                     static_cast<float>(deltaEnergy) / 1000.0);
        } else {
            dumpStats << android::base::StringPrintf(dataFormatWithoutDelta, subsysName, railName,
                                                     static_cast<float>(data.energy) / 1000.0);
        }
    }

    dumpStats << ENERGYDATA_FOOTER;

    return android::base::WriteStringToFd(dumpStats.str(), fd);
}

void PowerStats::debugEnergyData(int fd, bool delta) {
    static struct timespec prevDataTime;
    static bool prevDataTimeValid = false;
    struct timespec dataTime;
    bool dataTimeValid = false;

    std::unordered_map<uint32_t, std::pair<std::string, std::string>> railNames;
    getRailInfo([&railNames](auto infos, auto /* status */) {
        // Don't care about the status. infos will be nonempty if rail energy is supported.
        for (auto info : infos) {
            railNames.emplace(info.index, std::make_pair(info.subsysName, info.railName));
        }
    });
    if (railNames.empty()) {
        return;
    }

    Status status;
    hidl_vec<EnergyData> energyData;
    getEnergyData(
            {}, [&status, &energyData, &dataTime, &dataTimeValid](auto rEnergyData, auto rStatus) {
                status = rStatus;
                energyData = rEnergyData;
                dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
            });

    // getEnergyData returns no results if status != SUCCESS.
    if (status != Status::SUCCESS) {
        LOG(ERROR) << "Error getting rail data";
        return;
    }

    if (!delta) {
        if (!DumpEnergyDataToFd(railNames, energyData, fd)) {
            PLOG(ERROR) << "Failed to dump energy data to fd";
        }
    } else {
        // If the delta argument was supplied, calculate the elapsed time since the previous
        // result and then dump the latest data along with elapsed time and deltas
        static hidl_vec<EnergyData> prevEnergyData;
        uint64_t elapsedTimeMs = 0;
        if (dataTimeValid && prevDataTimeValid) {
            elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
        }

        if (!DumpEnergyDataDiffToFd(railNames, elapsedTimeMs, prevEnergyData, energyData, fd)) {
            PLOG(ERROR) << "Failed to dump energy data delta to fd";
        }

        prevEnergyData = energyData;
        prevDataTime = dataTime;
        prevDataTimeValid = dataTimeValid;
    }
}

Return<void> PowerStats::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &args) {
    if (handle == nullptr || handle->numFds < 1) {
        return Void();
    }

    int fd = handle->data[0];
    bool delta = (args.size() == 1) && (args[0] == "delta");

    // Get power entity information, which is common across all supported data categories
    Status status;
    hidl_vec<PowerEntityInfo> stateInfos;
    getPowerEntityInfo([&status, &stateInfos](auto rInfos, auto rStatus) {
        status = rStatus;
        stateInfos = rInfos;
    });
    if (status != Status::SUCCESS) {
        LOG(ERROR) << "Error getting power entity info";
        return Void();
    }

    // Construct lookup table of powerEntityId to name
    std::unordered_map<uint32_t, std::string> entityNames;
    for (auto info : stateInfos) {
        entityNames.emplace(info.powerEntityId, info.powerEntityName);
    }

    // Generate debug output for supported data categories
    debugStateResidency(entityNames, fd, delta);

    // Generate debug output for energy data
    debugEnergyData(fd, delta);

    fsync(fd);
    return Void();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace stats
}  // namespace power
}  // namespace hardware
}  // namespace android