power.stats: Add more informative default implementation

The original default implementation for power.stats HAL did not have a
good example implementation of the power statistics related APIs. Adding
a new default implementation that gives a more informative example

Bug: 122267057
Test: run vts -m VtsHalPowerStatsV1_0Target
Test: adb shell "lshal debug android.hardware.power.stats@1.0::IPowerStats/default"
Observed the following output
========== PowerStats HAL 1.0 state residencies ==========
          Entity            State         Total time     Total entries   Last entry timestamp
   DefaultEntity           Active               1 ms                 2               3 ms
   DefaultEntity            Sleep               4 ms                 5               6 ms
========== End of PowerStats HAL 1.0 state residencies ==========

Change-Id: Ida0951c267f609b16bb6406da150ed2e504ced9a
This commit is contained in:
Benjamin Schwartz
2019-01-15 14:07:56 -08:00
parent 94acf41dcc
commit a51d9b91ee
5 changed files with 277 additions and 10 deletions

View File

@@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
cc_library_shared {
cc_binary {
name: "android.hardware.power.stats@1.0-service",
relative_install_path: "hw",
init_rc: ["android.hardware.power.stats@1.0-service.rc"],

View File

@@ -0,0 +1,3 @@
krossmo@google.com
bsschwar@google.com
tstrudel@google.com

View File

@@ -287,25 +287,218 @@ Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate
return Void();
}
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(std::shared_ptr<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) {
hidl_vec<PowerEntityInfo> eInfo;
_hidl_cb(eInfo, Status::NOT_SUPPORTED);
// 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) {
(void)powerEntityIds;
hidl_vec<PowerEntityStateSpace> powerEntityStateSpaces;
_hidl_cb(powerEntityStateSpaces, Status::NOT_SUPPORTED);
// 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) {
(void)powerEntityIds;
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();
}
bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
const hidl_vec<PowerEntityStateSpace>& stateSpaces,
const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
// construct lookup table of powerEntityId to name
std::unordered_map<uint32_t, std::string> entityNames;
for (auto info : infos) {
entityNames.emplace(info.powerEntityId, info.powerEntityName);
}
// 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>());
for (auto state : stateSpace.states) {
stateNames.at(stateSpace.powerEntityId)
.emplace(state.powerEntityStateId, state.powerEntityStateName);
}
}
std::ostringstream dumpStats;
dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
const char* headerFormat = " %14s %14s %16s %15s %16s\n";
const char* dataFormat =
" %14s %14s %13" PRIu64 " ms %15" PRIu64 " %13" PRIu64 " ms\n";
dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
"Total entries", "Last entry timestamp");
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 << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
return android::base::WriteStringToFd(dumpStats.str(), fd);
}
Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
if (handle == nullptr || handle->numFds < 1) {
return Void();
}
int fd = handle->data[0];
Status status;
hidl_vec<PowerEntityInfo> infos;
// Get power entity information
getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
status = rStatus;
infos = rInfos;
});
if (status != Status::SUCCESS) {
LOG(ERROR) << "Error getting power entity info";
return Void();
}
// Get power entity state information
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 Void();
}
// Get power entity state residency data
hidl_vec<PowerEntityStateResidencyResult> results;
_hidl_cb(results, Status::NOT_SUPPORTED);
getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
status = rStatus;
results = rResults;
});
// 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 (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
PLOG(ERROR) << "Failed to dump residency data to fd";
}
fsync(fd);
return Void();
}

View File

@@ -21,6 +21,7 @@
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <unordered_map>
namespace android {
namespace hardware {
@@ -60,8 +61,19 @@ struct OnDeviceMmt {
std::unique_ptr<MessageQueueSync> fmqSynchronized;
};
class IStateResidencyDataProvider {
public:
virtual ~IStateResidencyDataProvider() = default;
virtual bool getResults(
std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) = 0;
virtual std::vector<PowerEntityStateSpace> getStateSpaces() = 0;
};
struct PowerStats : public IPowerStats {
public:
PowerStats();
uint32_t addPowerEntity(const std::string& name, PowerEntityType type);
void addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p);
// Methods from ::android::hardware::power::stats::V1_0::IPowerStats follow.
Return<void> getRailInfo(getRailInfo_cb _hidl_cb) override;
Return<void> getEnergyData(const hidl_vec<uint32_t>& railIndices,
@@ -75,12 +87,19 @@ struct PowerStats : public IPowerStats {
const hidl_vec<uint32_t>& powerEntityIds,
getPowerEntityStateResidencyData_cb _hidl_cb) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
private:
OnDeviceMmt mPm;
void findIioPowerMonitorNodes();
size_t parsePowerRails();
int parseIioEnergyNode(std::string devName);
Status parseIioEnergyNodes();
std::vector<PowerEntityInfo> mPowerEntityInfos;
std::unordered_map<uint32_t, PowerEntityStateSpace> mPowerEntityStateSpaces;
std::unordered_map<uint32_t, std::shared_ptr<IStateResidencyDataProvider>>
mStateResidencyDataProviders;
};
} // namespace implementation

View File

@@ -31,17 +31,69 @@ using android::hardware::joinRpcThreadpool;
// Generated HIDL files
using android::hardware::power::stats::V1_0::IPowerStats;
using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
using android::hardware::power::stats::V1_0::PowerEntityType;
using android::hardware::power::stats::V1_0::implementation::IStateResidencyDataProvider;
using android::hardware::power::stats::V1_0::implementation::PowerStats;
class DefaultStateResidencyDataProvider : public IStateResidencyDataProvider {
public:
DefaultStateResidencyDataProvider(uint32_t id)
: mPowerEntityId(id), mActiveStateId(0), mSleepStateId(1) {}
~DefaultStateResidencyDataProvider() = default;
bool getResults(std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) {
PowerEntityStateResidencyResult result = { .powerEntityId = mPowerEntityId };
result.stateResidencyData.resize(2);
// Using fake numbers here for display only. A real implementation would
// use actual tracked stats.
result.stateResidencyData[0] = {
.powerEntityStateId = mActiveStateId,
.totalTimeInStateMs = 1,
.totalStateEntryCount = 2,
.lastEntryTimestampMs = 3
};
result.stateResidencyData[1] = {
.powerEntityStateId = mSleepStateId,
.totalTimeInStateMs = 4,
.totalStateEntryCount = 5,
.lastEntryTimestampMs = 6,
};
results.emplace(mPowerEntityId, result);
return true;
}
std::vector<PowerEntityStateSpace> getStateSpaces() {
return {{
.powerEntityId = mPowerEntityId,
.states = {
{.powerEntityStateId = mActiveStateId, .powerEntityStateName = "Active"},
{.powerEntityStateId = mSleepStateId, .powerEntityStateName = "Sleep"}
}
}};
}
private:
const uint32_t mPowerEntityId;
const uint32_t mActiveStateId;
const uint32_t mSleepStateId;
};
int main(int /* argc */, char** /* argv */) {
ALOGI("power.stats service 1.0 is starting.");
android::sp<IPowerStats> service = new PowerStats();
PowerStats* service = new PowerStats();
if (service == nullptr) {
ALOGE("Can not create an instance of power.stats HAL Iface, exiting.");
return 1;
}
uint32_t defaultId = service->addPowerEntity("DefaultEntity", PowerEntityType::SUBSYSTEM);
auto defaultSdp = std::make_shared<DefaultStateResidencyDataProvider>(defaultId);
service->addStateResidencyDataProvider(std::move(defaultSdp));
configureRpcThreadpool(1, true /*callerWillJoin*/);
status_t status = service->registerAsService();