diff options
author | Benjamin Schwartz <bsschwar@google.com> | 2019-08-21 16:30:55 -0700 |
---|---|---|
committer | Benjamin Schwartz <bsschwar@google.com> | 2019-09-13 15:59:29 -0700 |
commit | 37e62a1637370d1a1ed5d50d3fc897aae0e2a766 (patch) | |
tree | 3707b8b5ad953f413b63fce902a4f4182b84db0c | |
parent | b54dffd29e72e9973ede8ce890a36bb3b44b0638 (diff) | |
download | pixel-37e62a1637370d1a1ed5d50d3fc897aae0e2a766.tar.gz |
pwrstats_util: Generate statistics using protobuf
Bug: 138745474
Test: adb shell pwrstats_util
Change-Id: I2845d9b77c07d2df39017b983b370481a714d08d
-rw-r--r-- | pwrstats_util/Android.bp | 10 | ||||
-rw-r--r-- | pwrstats_util/PowerStatsAggregator.cpp | 41 | ||||
-rw-r--r-- | pwrstats_util/PowerStatsAggregator.h | 49 | ||||
-rw-r--r-- | pwrstats_util/PowerStatsCollector.cpp | 131 | ||||
-rw-r--r-- | pwrstats_util/PowerStatsCollector.h | 64 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/DataProviderHelper.cpp | 50 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/DataProviderHelper.h | 28 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.cpp | 61 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.h | 10 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/RailEnergyDataProvider.cpp | 75 | ||||
-rw-r--r-- | pwrstats_util/dataproviders/RailEnergyDataProvider.h | 10 | ||||
-rw-r--r-- | pwrstats_util/pwrstats_util.cpp | 97 | ||||
-rw-r--r-- | pwrstats_util/pwrstatsutil.proto | 50 |
13 files changed, 515 insertions, 161 deletions
diff --git a/pwrstats_util/Android.bp b/pwrstats_util/Android.bp index 1186b98b..ee9f824c 100644 --- a/pwrstats_util/Android.bp +++ b/pwrstats_util/Android.bp @@ -18,10 +18,16 @@ cc_library_static { export_include_dirs: ["."], srcs: [ "pwrstats_util.cpp", - "PowerStatsAggregator.cpp", + "PowerStatsCollector.cpp", "dataproviders/PowerEntityResidencyDataProvider.cpp", "dataproviders/RailEnergyDataProvider.cpp", + "dataproviders/DataProviderHelper.cpp", + "pwrstatsutil.proto", ], + proto: { + export_proto_headers: true, + type: "full", + }, shared_libs: [ "libhidlbase", "android.hardware.power.stats@1.0", @@ -40,6 +46,7 @@ cc_defaults { "-Werror=int-to-pointer-cast", "-Werror=type-limits", "-Werror", + "-Wno-unused-parameter", ], tidy: true, tidy_checks: [ @@ -63,5 +70,6 @@ cc_defaults { "libbase", "libutils", "liblog", + "libprotobuf-cpp-full" ], } diff --git a/pwrstats_util/PowerStatsAggregator.cpp b/pwrstats_util/PowerStatsAggregator.cpp deleted file mode 100644 index c3205050..00000000 --- a/pwrstats_util/PowerStatsAggregator.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 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 "pwrstats_util" - -#include "PowerStatsAggregator.h" - -#include <android-base/logging.h> -#include <android-base/parsedouble.h> - -#include <iostream> -#include <memory> -#include <unordered_map> - -void PowerStatsAggregator::addDataProvider(std::unique_ptr<IPowerStatsDataProvider> p) { - mDataProviders.emplace_back(std::move(p)); -} - -int PowerStatsAggregator::getData(std::unordered_map<std::string, uint64_t>* data) const { - data->clear(); - for (auto&& provider : mDataProviders) { - int ret = provider->get(data); - if (ret != 0) { - data->clear(); - return ret; - } - } - return 0; -} diff --git a/pwrstats_util/PowerStatsAggregator.h b/pwrstats_util/PowerStatsAggregator.h deleted file mode 100644 index 70ff70f5..00000000 --- a/pwrstats_util/PowerStatsAggregator.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#ifndef POWERSTATSAGGREGATOR_H -#define POWERSTATSAGGREGATOR_H - -#include <memory> -#include <unordered_map> -#include <vector> - -/** - * Classes that implement this interface can be used to provide stats in the form of key/value - * pairs to PwrStatsUtil. - **/ -class IPowerStatsDataProvider { - public: - virtual ~IPowerStatsDataProvider() = default; - virtual int get(std::unordered_map<std::string, uint64_t>* data) = 0; -}; - -/** - * This class is used to return stats in the form of key/value pairs for all registered classes - * that implement IPwrStatsUtilDataProvider. - **/ -class PowerStatsAggregator { - public: - PowerStatsAggregator() = default; - int getData(std::unordered_map<std::string, uint64_t>* data) const; - void addDataProvider(std::unique_ptr<IPowerStatsDataProvider> dataProvider); - - private: - std::vector<std::unique_ptr<IPowerStatsDataProvider>> mDataProviders; -}; - -int run(int argc, char** argv, const PowerStatsAggregator& agg); - -#endif // POWERSTATSAGGREGATOR_H diff --git a/pwrstats_util/PowerStatsCollector.cpp b/pwrstats_util/PowerStatsCollector.cpp new file mode 100644 index 00000000..d2b1c6d6 --- /dev/null +++ b/pwrstats_util/PowerStatsCollector.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 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 "pwrstats_util" + +#include "PowerStatsCollector.h" + +#include <android-base/logging.h> + +void PowerStatsCollector::addDataProvider(std::unique_ptr<IPowerStatProvider> p) { + mStatProviders.emplace(p->typeOf(), std::move(p)); +} + +int PowerStatsCollector::get(std::vector<PowerStatistic>* stats) const { + if (!stats) { + LOG(ERROR) << __func__ << ": bad args; stat is null"; + return 1; + } + + stats->clear(); + for (auto&& provider : mStatProviders) { + PowerStatistic stat; + if (provider.second->get(&stat) != 0) { + LOG(ERROR) << __func__ << ": a data provider failed"; + stats->clear(); + return 1; + } + + stats->emplace_back(stat); + } + return 0; +} + +int PowerStatsCollector::get(const std::vector<PowerStatistic>& start, + std::vector<PowerStatistic>* interval) const { + if (!interval) { + LOG(ERROR) << __func__ << ": bad args; interval is null"; + return 1; + } + + interval->clear(); + for (auto const& curStat : start) { + auto provider = mStatProviders.find(curStat.power_stat_case()); + if (provider == mStatProviders.end()) { + LOG(ERROR) << __func__ << ": a provider is missing"; + interval->clear(); + return 1; + } + + PowerStatistic curInterval; + if (provider->second->get(curStat, &curInterval) != 0) { + LOG(ERROR) << __func__ << ": a data provider failed"; + interval->clear(); + return 1; + } + interval->emplace_back(curInterval); + } + return 0; +} + +void PowerStatsCollector::dump(const std::vector<PowerStatistic>& stats, + std::ostream* output) const { + if (!output) { + LOG(ERROR) << __func__ << ": bad args; output is null"; + return; + } + + for (auto const& stat : stats) { + auto provider = mStatProviders.find(stat.power_stat_case()); + if (provider == mStatProviders.end()) { + LOG(ERROR) << __func__ << ": a provider is missing"; + return; + } + + provider->second->dump(stat, output); + } +} + +int IPowerStatProvider::get(PowerStatistic* stat) const { + if (!stat) { + LOG(ERROR) << __func__ << ": bad args; stat is null"; + return 1; + } + + return getImpl(stat); +} + +int IPowerStatProvider::get(const PowerStatistic& start, PowerStatistic* interval) const { + if (!interval) { + LOG(ERROR) << __func__ << ": bad args; interval is null"; + return 1; + } + + if (typeOf() != start.power_stat_case()) { + LOG(ERROR) << __func__ << ": bad args; start is incorrect type"; + return 1; + } + + if (0 != getImpl(interval)) { + LOG(ERROR) << __func__ << ": unable to retrieve stats"; + return 1; + } + + return getImpl(start, interval); +} + +void IPowerStatProvider::dump(const PowerStatistic& stat, std::ostream* output) const { + if (!output) { + LOG(ERROR) << __func__ << ": bad args; output is null"; + return; + } + + if (typeOf() != stat.power_stat_case()) { + LOG(ERROR) << __func__ << ": bad args; stat is incorrect type"; + return; + } + + dumpImpl(stat, output); +} diff --git a/pwrstats_util/PowerStatsCollector.h b/pwrstats_util/PowerStatsCollector.h new file mode 100644 index 00000000..cf7c991a --- /dev/null +++ b/pwrstats_util/PowerStatsCollector.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef POWERSTATSCOLLECTOR_H +#define POWERSTATSCOLLECTOR_H + +#include <memory> +#include <unordered_map> +#include <vector> + +#include <pwrstatsutil.pb.h> + +using com::google::android::pwrstatsutil::PowerStatistic; +using PowerStatCase = com::google::android::pwrstatsutil::PowerStatistic::PowerStatCase; + +/** + * Classes that inherit from this can be used to provide stats in the form of key/value + * pairs to PwrStatsUtil. + **/ +class IPowerStatProvider { + public: + virtual ~IPowerStatProvider() = default; + int get(PowerStatistic* stat) const; + int get(const PowerStatistic& start, PowerStatistic* interval) const; + void dump(const PowerStatistic& stat, std::ostream* output) const; + virtual PowerStatCase typeOf() const = 0; + + private: + virtual int getImpl(PowerStatistic* stat) const = 0; + virtual int getImpl(const PowerStatistic& start, PowerStatistic* interval) const = 0; + virtual void dumpImpl(const PowerStatistic& stat, std::ostream* output) const = 0; +}; + +/** + * This class is used to return stats in the form of key/value pairs for all registered classes + * that implement IPowerStatProvider. + **/ +class PowerStatsCollector { + public: + PowerStatsCollector() = default; + int get(std::vector<PowerStatistic>* stats) const; + int get(const std::vector<PowerStatistic>& start, std::vector<PowerStatistic>* interval) const; + void dump(const std::vector<PowerStatistic>& stats, std::ostream* output) const; + void addDataProvider(std::unique_ptr<IPowerStatProvider> statProvider); + + private: + std::unordered_map<PowerStatCase, std::unique_ptr<IPowerStatProvider>> mStatProviders; +}; + +int run(int argc, char** argv, const PowerStatsCollector& collector); + +#endif // POWERSTATSCOLLECTOR_H diff --git a/pwrstats_util/dataproviders/DataProviderHelper.cpp b/pwrstats_util/dataproviders/DataProviderHelper.cpp new file mode 100644 index 00000000..1494c54a --- /dev/null +++ b/pwrstats_util/dataproviders/DataProviderHelper.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 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 "pwrstats_util" + +#include "DataProviderHelper.h" +#include <android-base/logging.h> + +int StateResidencyInterval(const StateResidency_Residency& startResidency, + StateResidency_Residency* intervalResidency) { + // If start and interval are not the same size then they cannot have matching data + if (startResidency.size() != intervalResidency->size()) { + LOG(ERROR) << __func__ << ": mismatched data"; + return 1; + } + + for (int i = 0; i < startResidency.size(); ++i) { + // Check and make sure each entry matches. Data are in sorted order so if there is a + // mismatch then we will bail. + if (startResidency.Get(i).entity_name() != intervalResidency->Get(i).entity_name() || + startResidency.Get(i).state_name() != intervalResidency->Get(i).state_name()) { + LOG(ERROR) << __func__ << ": mismatched data"; + return 1; + } + + auto delta = intervalResidency->Get(i).time_ms() - startResidency.Get(i).time_ms(); + intervalResidency->Mutable(i)->set_time_ms(delta); + } + return 0; +} + +void StateResidencyDump(const StateResidency_Residency& stateResidency, std::ostream* output) { + for (auto const& residency : stateResidency) { + *output << residency.entity_name() << ":" << residency.state_name() << "=" + << residency.time_ms() << std::endl; + } + *output << std::endl; +} diff --git a/pwrstats_util/dataproviders/DataProviderHelper.h b/pwrstats_util/dataproviders/DataProviderHelper.h new file mode 100644 index 00000000..7f931ffc --- /dev/null +++ b/pwrstats_util/dataproviders/DataProviderHelper.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef DATAPROVIDERHELPER_H +#define DATAPROVIDERHELPER_H + +#include <pwrstatsutil.pb.h> + +using StateResidency_Residency = ::google::protobuf::RepeatedPtrField< + ::com::google::android::pwrstatsutil::StateResidency_Residency>; +int StateResidencyInterval(const StateResidency_Residency& start, + StateResidency_Residency* interval); + +void StateResidencyDump(const StateResidency_Residency& stateResidency, std::ostream* output); + +#endif // DATAPROVIDERHELPER_H diff --git a/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.cpp b/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.cpp index ae5eff4f..fc5d66a0 100644 --- a/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.cpp +++ b/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.cpp @@ -16,8 +16,11 @@ #define LOG_TAG "pwrstats_util" #include "PowerEntityResidencyDataProvider.h" +#include "DataProviderHelper.h" + #include <android-base/logging.h> #include <android/hardware/power/stats/1.0/IPowerStats.h> + using android::sp; using android::hardware::Return; @@ -26,17 +29,18 @@ using android::hardware::Return; * Provides data monitored by Power Stats HAL 1.0 **/ -int PowerEntityResidencyDataProvider::get(std::unordered_map<std::string, uint64_t>* data) { +int PowerEntityResidencyDataProvider::getImpl(PowerStatistic* stat) const { sp<android::hardware::power::stats::V1_0::IPowerStats> powerStatsService = android::hardware::power::stats::V1_0::IPowerStats::getService(); if (powerStatsService == nullptr) { - LOG(ERROR) << "Unable to get power.stats HAL service."; + LOG(ERROR) << "unable to get power.stats HAL service"; return 1; } std::unordered_map<uint32_t, std::string> entityNames; std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames; + // Create map of entity names based on entity id Return<void> ret; ret = powerStatsService->getPowerEntityInfo([&entityNames](auto infos, auto /* status */) { for (auto const& info : infos) { @@ -44,9 +48,11 @@ int PowerEntityResidencyDataProvider::get(std::unordered_map<std::string, uint64 } }); if (!ret.isOk()) { + LOG(ERROR) << __func__ << ": unable to get entity info"; return 1; } + // Create map of each entity's states based on entity and state id ret = powerStatsService->getPowerEntityStateInfo({}, [&stateNames](auto stateSpaces, auto /* status */) { for (auto const& stateSpace : stateSpaces) { @@ -59,24 +65,63 @@ int PowerEntityResidencyDataProvider::get(std::unordered_map<std::string, uint64 } }); if (!ret.isOk()) { + LOG(ERROR) << __func__ << ": unable to get state info"; return 1; } + // Retrieve residency data and create the PowerStatistic::PowerEntityStateResidency ret = powerStatsService->getPowerEntityStateResidencyData({}, [&entityNames, &stateNames, - &data](auto results, + &stat](auto results, auto /* status */) { + auto residencies = stat->mutable_power_entity_state_residency(); for (auto const& result : results) { - for (auto stateResidency : result.stateResidencyData) { - std::ostringstream key; - key << entityNames.at(result.powerEntityId) << "__" - << stateNames.at(result.powerEntityId).at(stateResidency.powerEntityStateId); - data->emplace(key.str(), static_cast<uint64_t>(stateResidency.totalTimeInStateMs)); + for (auto const& curStateResidency : result.stateResidencyData) { + auto residency = residencies->add_residency(); + residency->set_entity_name(entityNames.at(result.powerEntityId)); + residency->set_state_name(stateNames.at(result.powerEntityId) + .at(curStateResidency.powerEntityStateId)); + residency->set_time_ms(static_cast<uint64_t>(curStateResidency.totalTimeInStateMs)); } } + + // Sort entries first by entity_name, then by state_name. + // Sorting is needed to make interval processing efficient. + std::sort(residencies->mutable_residency()->begin(), + residencies->mutable_residency()->end(), [](const auto& a, const auto& b) { + if (a.entity_name() != b.entity_name()) { + return a.entity_name() < b.entity_name(); + } + + return a.state_name() < b.state_name(); + }); }); if (!ret.isOk()) { + LOG(ERROR) << __func__ << ": Unable to get residency info"; return 1; } return 0; } + +int PowerEntityResidencyDataProvider::getImpl(const PowerStatistic& start, + PowerStatistic* interval) const { + auto startResidency = start.power_entity_state_residency().residency(); + auto intervalResidency = interval->mutable_power_entity_state_residency()->mutable_residency(); + + if (0 != StateResidencyInterval(startResidency, intervalResidency)) { + interval->clear_power_entity_state_residency(); + return 1; + } + + return 0; +} + +void PowerEntityResidencyDataProvider::dumpImpl(const PowerStatistic& stat, + std::ostream* output) const { + *output << "Power Entity State Residencies:" << std::endl; + StateResidencyDump(stat.power_entity_state_residency().residency(), output); +} + +PowerStatCase PowerEntityResidencyDataProvider::typeOf() const { + return PowerStatCase::kPowerEntityStateResidency; +} diff --git a/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.h b/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.h index 73bb4532..b7264bb3 100644 --- a/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.h +++ b/pwrstats_util/dataproviders/PowerEntityResidencyDataProvider.h @@ -16,13 +16,17 @@ #ifndef POWERENTITYRESIDENCYDATAPROVIDER_H #define POWERENTITYRESIDENCYDATAPROVIDER_H -#include "PowerStatsAggregator.h" +#include "PowerStatsCollector.h" -class PowerEntityResidencyDataProvider : public IPowerStatsDataProvider { +class PowerEntityResidencyDataProvider : public IPowerStatProvider { public: PowerEntityResidencyDataProvider() = default; + PowerStatCase typeOf() const override; - int get(std::unordered_map<std::string, uint64_t>* data) override; + private: + int getImpl(PowerStatistic* stat) const override; + int getImpl(const PowerStatistic& start, PowerStatistic* interval) const override; + void dumpImpl(const PowerStatistic& stat, std::ostream* output) const override; }; #endif // POWERENTITYRESIDENCYDATAPROVIDER_H diff --git a/pwrstats_util/dataproviders/RailEnergyDataProvider.cpp b/pwrstats_util/dataproviders/RailEnergyDataProvider.cpp index 5de6530a..7a2213ef 100644 --- a/pwrstats_util/dataproviders/RailEnergyDataProvider.cpp +++ b/pwrstats_util/dataproviders/RailEnergyDataProvider.cpp @@ -24,12 +24,11 @@ using android::hardware::Return; using android::hardware::power::stats::V1_0::IPowerStats; using android::hardware::power::stats::V1_0::Status; -int RailEnergyDataProvider::get(std::unordered_map<std::string, uint64_t>* data) { - // example using the power stats HAL - sp<IPowerStats> powerStatsService = +int RailEnergyDataProvider::getImpl(PowerStatistic* stat) const { + sp<android::hardware::power::stats::V1_0::IPowerStats> powerStatsService = android::hardware::power::stats::V1_0::IPowerStats::getService(); if (powerStatsService == nullptr) { - LOG(ERROR) << "Unable to get power.stats HAL service."; + LOG(ERROR) << "unable to get power.stats HAL service"; return 1; } @@ -44,41 +43,81 @@ int RailEnergyDataProvider::get(std::unordered_map<std::string, uint64_t>* data) } for (auto const& info : railInfos) { - railNames.emplace(info.index, - std::string(info.subsysName) + "__" + std::string(info.railName)); + railNames.emplace(info.index, info.railName); } }); if (retStatus == Status::NOT_SUPPORTED) { - LOG(WARNING) << "rail energy stats not supported"; + LOG(WARNING) << __func__ << ": rail energy stats not supported"; return 0; } if (!ret.isOk() || retStatus != Status::SUCCESS) { - LOG(ERROR) << "no rail information available"; + LOG(ERROR) << __func__ << ": no rail information available"; return 1; } + auto railEntries = stat->mutable_rail_energy(); bool resultSuccess = true; ret = powerStatsService->getEnergyData( - {}, [&data, &railNames, &resultSuccess](auto energyData, auto status) { + {}, [&railEntries, &railNames, &resultSuccess](auto energyData, auto status) { if (status != Status::SUCCESS) { - LOG(ERROR) << "Error getting rail energy"; + LOG(ERROR) << __func__ << ": unable to get rail energy"; resultSuccess = false; return; } + for (auto const& energyDatum : energyData) { - auto railName = railNames.find(energyDatum.index); - if (railName == railNames.end()) { - LOG(ERROR) << "Missing one or more rail names"; - resultSuccess = false; - return; - } - data->emplace(railName->second, energyDatum.energy); + auto entry = railEntries->add_entry(); + entry->set_rail_name(railNames.at(energyDatum.index)); + entry->set_energy_uws(energyDatum.energy); } }); if (!ret.isOk() || !resultSuccess) { - LOG(ERROR) << "Failed to get rail energy stats"; + stat->clear_rail_energy(); + LOG(ERROR) << __func__ << ": failed to get rail energy stats"; return 1; } + // Sort entries by name. Sorting is needed to make interval processing efficient. + std::sort(railEntries->mutable_entry()->begin(), railEntries->mutable_entry()->end(), + [](const auto& a, const auto& b) { return a.rail_name() < b.rail_name(); }); + return 0; } + +int RailEnergyDataProvider::getImpl(const PowerStatistic& start, PowerStatistic* interval) const { + auto startEnergy = start.rail_energy().entry(); + auto intervalEnergy = interval->mutable_rail_energy()->mutable_entry(); + + // If start and interval are not the same size then they cannot have matching data + if (startEnergy.size() != intervalEnergy->size()) { + LOG(ERROR) << __func__ << ": mismatched data"; + interval->clear_rail_energy(); + return 1; + } + + for (int i = 0; i < startEnergy.size(); ++i) { + // Check and make sure each entry matches. Data are in sorted order so if there is a + // mismatch then we will bail. + if (startEnergy.Get(i).rail_name() != intervalEnergy->Get(i).rail_name()) { + LOG(ERROR) << __func__ << ": mismatched data"; + interval->clear_rail_energy(); + return 1; + } + + auto delta = intervalEnergy->Get(i).energy_uws() - startEnergy.Get(i).energy_uws(); + intervalEnergy->Mutable(i)->set_energy_uws(delta); + } + return 0; +} + +void RailEnergyDataProvider::dumpImpl(const PowerStatistic& stat, std::ostream* output) const { + *output << "Rail Energy:" << std::endl; + for (auto const& rail : stat.rail_energy().entry()) { + *output << rail.rail_name() << "=" << rail.energy_uws() << std::endl; + } + *output << std::endl; +} + +PowerStatCase RailEnergyDataProvider::typeOf() const { + return PowerStatCase::kRailEnergy; +} diff --git a/pwrstats_util/dataproviders/RailEnergyDataProvider.h b/pwrstats_util/dataproviders/RailEnergyDataProvider.h index 919eacbb..abdaa6dc 100644 --- a/pwrstats_util/dataproviders/RailEnergyDataProvider.h +++ b/pwrstats_util/dataproviders/RailEnergyDataProvider.h @@ -16,18 +16,22 @@ #ifndef RAILENERGYDATAPROVIDER_H #define RAILENERGYDATAPROVIDER_H -#include "PowerStatsAggregator.h" +#include "PowerStatsCollector.h" /** * Rail Energy data provider: * Provides data via Power Stats HAL 1.0 * data is in units of microwatt-seconds (uWs) **/ -class RailEnergyDataProvider : public IPowerStatsDataProvider { +class RailEnergyDataProvider : public IPowerStatProvider { public: RailEnergyDataProvider() = default; + PowerStatCase typeOf() const override; - int get(std::unordered_map<std::string, uint64_t>* data) override; + private: + int getImpl(PowerStatistic* stat) const override; + int getImpl(const PowerStatistic& start, PowerStatistic* interval) const override; + void dumpImpl(const PowerStatistic& stat, std::ostream* output) const override; }; #endif // RAILENERGYDATAPROVIDER_H diff --git a/pwrstats_util/pwrstats_util.cpp b/pwrstats_util/pwrstats_util.cpp index b28b4b0f..55ef0717 100644 --- a/pwrstats_util/pwrstats_util.cpp +++ b/pwrstats_util/pwrstats_util.cpp @@ -17,76 +17,93 @@ #include <android-base/logging.h> #include <fcntl.h> +#include <getopt.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> +#include <algorithm> #include <chrono> #include <csignal> #include <fstream> #include <iostream> -#include <unordered_map> -#include "PowerStatsAggregator.h" +#include <pwrstatsutil.pb.h> +#include "PowerStatsCollector.h" namespace { volatile std::sig_atomic_t gSignalStatus; } +static void signalHandler(int signal) { + gSignalStatus = signal; +} + class Options { public: + bool humanReadable; bool daemonMode; std::string filePath; }; -static void signalHandler(int signal) { - gSignalStatus = signal; -} - -static void printHelp() { - std::cout << "pwrstats_util: Prints out device power stats in the form of key/value pairs." - << std::endl - << "-d </path/to/file> : daemon mode. Spawns a daemon process and prints out" - << " its <pid>. kill -INT <pid> will trigger a write to specified file." << std::endl; -} - static Options parseArgs(int argc, char** argv) { Options opt = { + .humanReadable = false, .daemonMode = false, }; + static struct option long_options[] = {/* These options set a flag. */ + {"human-readable", no_argument, 0, 0}, + {"daemon", required_argument, 0, 'd'}, + {0, 0, 0, 0}}; + + // getopt_long stores the option index here + int option_index = 0; + int c; - while ((c = getopt(argc, argv, "d:h")) != -1) { + while ((c = getopt_long(argc, argv, "d:", long_options, &option_index)) != -1) { switch (c) { + case 0: + if ("human-readable" == std::string(long_options[option_index].name)) { + opt.humanReadable = true; + } + break; case 'd': opt.daemonMode = true; opt.filePath = std::string(optarg); break; - case 'h': - printHelp(); - exit(EXIT_SUCCESS); - default: + default: /* '?' */ + std::cerr << "pwrstats_util: Prints out device power stats." << std::endl + << "--human-readable: human-readable format" << std::endl + << "--daemon <path/to/file>, -d <path/to/file>: daemon mode. Spawns a " + "daemon process and prints out its <pid>. kill -INT <pid> will " + "trigger a write to specified file." + << std::endl; exit(EXIT_FAILURE); } } return opt; } -static void snapshot(const PowerStatsAggregator& agg) { - std::unordered_map<std::string, uint64_t> data; - int ret = agg.getData(&data); +static void snapshot(const Options& opt, const PowerStatsCollector& collector) { + std::vector<PowerStatistic> stats; + int ret = collector.get(&stats); if (ret) { exit(EXIT_FAILURE); } - for (auto const& datum : data) { - std::cout << datum.first << "=" << datum.second << std::endl; + if (opt.humanReadable) { + collector.dump(stats, &std::cout); + } else { + for (const auto& stat : stats) { + stat.SerializeToOstream(&std::cout); + } } exit(EXIT_SUCCESS); } -static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) { +static void daemon(const Options& opt, const PowerStatsCollector& collector) { // Following a subset of steps outlined in http://man7.org/linux/man-pages/man7/daemon.7.html // Call fork to create child process @@ -144,10 +161,10 @@ static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) // get the start_data auto start_time = std::chrono::system_clock::now(); - std::unordered_map<std::string, uint64_t> start_data; - int ret = agg.getData(&start_data); + std::vector<PowerStatistic> start_stats; + int ret = collector.get(&start_stats); if (ret) { - LOG(ERROR) << "failed to get start data"; + LOG(ERROR) << "failed to get start stats"; exit(EXIT_FAILURE); } @@ -157,10 +174,10 @@ static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) } // get the end data - std::unordered_map<std::string, uint64_t> end_data; - ret = agg.getData(&end_data); + std::vector<PowerStatistic> interval_stats; + ret = collector.get(start_stats, &interval_stats); if (ret) { - LOG(ERROR) << "failed to get end data"; + LOG(ERROR) << "failed to get interval stats"; exit(EXIT_FAILURE); } auto end_time = std::chrono::system_clock::now(); @@ -168,14 +185,18 @@ static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) std::chrono::duration<double> elapsed_seconds = end_time - start_time; // Write data to file - std::ofstream myfile(filePath); + std::ofstream myfile(opt.filePath, std::ios::out | std::ios::binary); if (!myfile.is_open()) { LOG(ERROR) << "failed to open file"; exit(EXIT_FAILURE); } myfile << "elapsed time: " << elapsed_seconds.count() << "s" << std::endl; - for (auto const& datum : end_data) { - myfile << datum.first << "=" << datum.second - start_data[datum.first] << std::endl; + if (opt.humanReadable) { + collector.dump(interval_stats, &myfile); + } else { + for (const auto& stat : interval_stats) { + stat.SerializeToOstream(&myfile); + } } myfile.close(); @@ -183,18 +204,18 @@ static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) exit(EXIT_SUCCESS); } -static void runWithOptions(const Options& opt, const PowerStatsAggregator& agg) { +static void runWithOptions(const Options& opt, const PowerStatsCollector& collector) { if (opt.daemonMode) { - daemon(opt.filePath, agg); + daemon(opt, collector); } else { - snapshot(agg); + snapshot(opt, collector); } } -int run(int argc, char** argv, const PowerStatsAggregator& agg) { +int run(int argc, char** argv, const PowerStatsCollector& collector) { Options opt = parseArgs(argc, argv); - runWithOptions(opt, agg); + runWithOptions(opt, collector); return 0; } diff --git a/pwrstats_util/pwrstatsutil.proto b/pwrstats_util/pwrstatsutil.proto new file mode 100644 index 00000000..ee708a61 --- /dev/null +++ b/pwrstats_util/pwrstatsutil.proto @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 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. + */ +syntax = "proto3"; + +option java_package = "com.google.android.pwrstatsutil"; +package com.google.android.pwrstatsutil; + + +message PowerStatistic { + oneof power_stat { + StateResidency power_entity_state_residency = 1; + RailEnergy rail_energy = 2; + StateResidency c_state_residency = 3; + // add new power_stats here + } +} + +// Utility message for items that provide a state residency in milliseconds +message StateResidency { + message Residency { + string entity_name = 1; + string state_name = 2; + uint64 time_ms = 3; + } + + repeated Residency residency = 1; +} + +// Rail energy data in uWs +message RailEnergy { + message RailEntry { + string rail_name = 1; + uint64 energy_uws = 2; + } + + repeated RailEntry entry = 1; +} |