powerstats: timeout mechanism for AoC data provider

Reading data from an AoC sysfs node by getline takes 1 second. In total
there are 17 AoC sysfs nodes that must be read. The worst case is taking
17 seconds long that is greater than dumpsys timeout. Therefore, we need
the timeout mechanism to ignore the AoC power stats reporting when AoC
latency exceeds the allowed time.

Bug: 219630658
Test: dumpsys android.hardware.power.stats.IPowerStats/default
Change-Id: I71a9e681780471f31141599c53bb516aef445add
Signed-off-by: Darren Hsu <darrenhsu@google.com>
This commit is contained in:
Darren Hsu 2022-04-19 08:40:05 +08:00
parent f19ccbfa65
commit df08a31b76
2 changed files with 96 additions and 9 deletions

View File

@ -17,6 +17,8 @@
#include "AocStateResidencyDataProvider.h" #include "AocStateResidencyDataProvider.h"
#include <android-base/logging.h> #include <android-base/logging.h>
#include <chrono>
#include <pthread.h>
namespace aidl { namespace aidl {
namespace android { namespace android {
@ -24,12 +26,23 @@ namespace hardware {
namespace power { namespace power {
namespace stats { namespace stats {
struct async_data_t {
pthread_cond_t *cond;
pthread_mutex_t *lock;
uint64_t timeoutMillis;
std::unordered_map<std::string, std::vector<StateResidency>> *residencies;
std::unordered_map<std::string,
std::vector<std::unique_ptr<GenericStateResidencyDataProvider>>> *providers;
};
AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pair<std::string, AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pair<std::string,
std::string>> ids, std::vector<std::pair<std::string, std::string>> states) { std::string>> ids, std::vector<std::pair<std::string, std::string>> states,
const uint64_t timeoutMillis) {
// AoC stats are reported in ticks of 244.140625ns. The transform // AoC stats are reported in ticks of 244.140625ns. The transform
// function converts ticks to milliseconds. // function converts ticks to milliseconds.
// 1000000 / 244.140625 = 4096. // 1000000 / 244.140625 = 4096.
static const uint64_t AOC_CLK = 4096; static const uint64_t AOC_CLK = 4096;
static const uint64_t TIMEOUT_MILLIS = 120;
std::function<uint64_t(uint64_t)> aocTickToMs = [](uint64_t a) { return a / AOC_CLK; }; std::function<uint64_t(uint64_t)> aocTickToMs = [](uint64_t a) { return a / AOC_CLK; };
GenericStateResidencyDataProvider::StateResidencyConfig config = { GenericStateResidencyDataProvider::StateResidencyConfig config = {
.entryCountSupported = true, .entryCountSupported = true,
@ -54,13 +67,17 @@ AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pa
mProviders[id.first].push_back(std::move(sdp)); mProviders[id.first].push_back(std::move(sdp));
} }
} }
mStateCount = states.size();
mTimeoutMillis = timeoutMillis == 0 ? TIMEOUT_MILLIS : timeoutMillis;
} }
bool AocStateResidencyDataProvider::getStateResidencies( void *getStateResidenciesAsync(void *arg) {
std::unordered_map<std::string, std::vector<StateResidency>> *residencies) { struct timespec start, end;
struct async_data_t *async = (struct async_data_t*)arg;
uint64_t timeoutUs = async->timeoutMillis * 1000;
// States from the same power entity are merged. // States from the same power entity are merged.
bool ret = true; for (const auto &providerList : *async->providers) {
for (const auto &providerList : mProviders) {
int32_t stateId = 0; int32_t stateId = 0;
std::string curEntity = providerList.first; std::string curEntity = providerList.first;
std::vector<StateResidency> stateResidencies; std::vector<StateResidency> stateResidencies;
@ -68,7 +85,18 @@ bool AocStateResidencyDataProvider::getStateResidencies(
// Iterate over each provider in the providerList, appending each of the states // Iterate over each provider in the providerList, appending each of the states
for (const auto &provider : providerList.second) { for (const auto &provider : providerList.second) {
std::unordered_map<std::string, std::vector<StateResidency>> residency; std::unordered_map<std::string, std::vector<StateResidency>> residency;
ret &= provider->getStateResidencies(&residency);
clock_gettime(CLOCK_REALTIME, &start);
provider->getStateResidencies(&residency);
clock_gettime(CLOCK_REALTIME, &end);
uint64_t elapsedUs =
((end.tv_sec - start.tv_sec) * 1000000) + ((end.tv_nsec - start.tv_nsec) / 1000);
if (elapsedUs >= timeoutUs) {
LOG(WARNING) << "getStateResidencies latency for " << curEntity
<< " exceeds time allowed: " << elapsedUs << " us";
return 0;
}
// Each provider should only return data for curEntity but checking anyway // Each provider should only return data for curEntity but checking anyway
if (residency.find(curEntity) != residency.end()) { if (residency.find(curEntity) != residency.end()) {
@ -84,8 +112,64 @@ bool AocStateResidencyDataProvider::getStateResidencies(
} }
} }
residencies->emplace(curEntity, stateResidencies); async->residencies->emplace(curEntity, stateResidencies);
} }
pthread_mutex_lock(async->lock);
pthread_cond_signal(async->cond);
pthread_mutex_unlock(async->lock);
return 0;
}
bool AocStateResidencyDataProvider::getStateResidencies(
std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
bool ret = true;
int condResult = 0;
pthread_t tid;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
std::unordered_map<std::string, std::vector<StateResidency>> stateResidencies;
struct timespec start, timeout;
struct async_data_t async = {
.cond = &cond,
.lock = &lock,
.timeoutMillis = mTimeoutMillis,
.residencies = &stateResidencies,
.providers = &mProviders
};
pthread_create(&tid, NULL, &getStateResidenciesAsync, (void*)&async);
clock_gettime(CLOCK_REALTIME, &start);
uint64_t expirationMillis = mTimeoutMillis * mStateCount;
timeout.tv_sec = start.tv_sec + expirationMillis / 1000;
uint64_t nsec = start.tv_nsec + (expirationMillis % 1000) * 1000000;
if (nsec < 1000000000) {
timeout.tv_nsec = nsec;
} else {
timeout.tv_sec += 1;
timeout.tv_nsec = nsec - 1000000000;
}
pthread_mutex_lock(&lock);
condResult = pthread_cond_timedwait(&cond, &lock, &timeout);
pthread_mutex_unlock(&lock);
if (condResult != 0) {
if (condResult == ETIMEDOUT) {
LOG(WARNING) << __func__ << " latency for AoC timeout: " << expirationMillis << " ms";
} else {
LOG(ERROR) << "Failed to wait for the condition variable: " << condResult;
}
ret = false;
} else {
for (const auto &residency : stateResidencies) {
residencies->emplace(residency.first, residency.second);
}
}
return ret; return ret;
} }

View File

@ -27,7 +27,8 @@ namespace stats {
class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider { class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
public: public:
AocStateResidencyDataProvider(std::vector<std::pair<std::string, std::string>> ids, AocStateResidencyDataProvider(std::vector<std::pair<std::string, std::string>> ids,
std::vector<std::pair<std::string, std::string>> states); std::vector<std::pair<std::string, std::string>> states,
const uint64_t timeoutMillis);
~AocStateResidencyDataProvider() = default; ~AocStateResidencyDataProvider() = default;
bool getStateResidencies( bool getStateResidencies(
std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override; std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
@ -36,10 +37,12 @@ class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProv
private: private:
std::unordered_map<std::string /* entity name */, std::unordered_map<std::string /* entity name */,
std::vector<std::unique_ptr<GenericStateResidencyDataProvider>> /* providers */> mProviders; std::vector<std::unique_ptr<GenericStateResidencyDataProvider>> /* providers */> mProviders;
int32_t mStateCount;
uint64_t mTimeoutMillis;
}; };
} // namespace stats } // namespace stats
} // namespace power } // namespace power
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android
} // namespace aidl } // namespace aidl