Add temp workaround Easel power stats on 2017

While snapshot Easel power data is captured in bugreports via dumpstate,
Easel does not provide low power stats on a recurring basis via
PowerHAL, which is the type of data need to detect the presence and
scope of power drain issues in the field.  As a temporary workaround,
this set of changes keeps cumulative counts of the number of times
PowerHAL saw Easel's state (an existing sysfs node) as "on" (state 1)
or "not on" (state 0 or 2), and logs the "on" count as cumulative count
and the "not on" count as cumulative duration.

This does not sufficiently address the long term need for cumulative
stats, since this will just be comprised of essentially random snapshots
of Easel's current state.  However, for the known issue already being
investigated, this should be enough to gauge the scope of the issue.

sepolicy updates allow hal_power to search/read the directory/file
containing Easel's current state: /sys/devices/virtual/misc/mnh_sm/state

Bug: 77208137
Bug: 36576572
Test: Installed on taimen, used camera for various functions, used
easel debug commands and properties to force it into different states,
captured a bugreport and verified the content against observed "current
state" values from monitoring the state file while performing similar
camera functions.

Change-Id: Ib1ee92db477d2a6c9d6f293fb4fcc2f753b8335a
This commit is contained in:
Kelly Rossmoyer
2018-04-11 01:00:05 -07:00
committed by Ajay Dudani
parent e794fdd39f
commit b296bbf7f0
4 changed files with 134 additions and 18 deletions

View File

@@ -23,6 +23,8 @@
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include <mutex>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -279,21 +281,21 @@ done:
return Void();
}
static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
static int get_wlan_low_power_stats(struct PowerStateSubsystem *subsystem) {
uint64_t stats[WLAN_POWER_PARAMS_COUNT] = {0};
struct PowerStateSubsystemSleepState *state;
int ret;
ret = extract_wlan_stats(stats);
if (ret)
return ret;
subsystem->name = "wlan";
subsystem.name = "wlan";
subsystem.states.resize(WLAN_STATES_COUNT);
if (extract_wlan_stats(stats) != 0) {
subsystem->states.resize(0);
return -1;
}
subsystem->states.resize(WLAN_STATES_COUNT);
/* Update statistics for Active State */
state = &subsystem.states[WLAN_STATE_ACTIVE];
state = &subsystem->states[WLAN_STATE_ACTIVE];
state->name = "Active";
state->residencyInMsecSinceBoot = stats[CUMULATIVE_TOTAL_ON_TIME_MS];
state->totalTransitions = stats[DEEP_SLEEP_ENTER_COUNTER];
@@ -301,7 +303,7 @@ static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
state->supportedOnlyInSuspend = false;
/* Update statistics for Deep-Sleep state */
state = &subsystem.states[WLAN_STATE_DEEP_SLEEP];
state = &subsystem->states[WLAN_STATE_DEEP_SLEEP];
state->name = "Deep-Sleep";
state->residencyInMsecSinceBoot = stats[CUMULATIVE_SLEEP_TIME_MS];
state->totalTransitions = stats[DEEP_SLEEP_ENTER_COUNTER];
@@ -311,22 +313,85 @@ static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
return 0;
}
enum easel_state {
EASEL_OFF = 0,
EASEL_ON,
EASEL_SUSPENDED,
NUM_EASEL_STATES
};
// Get low power stats for easel subsystem
static int get_easel_low_power_stats(struct PowerStateSubsystem *subsystem) {
// This implementation is a workaround to provide minimal visibility into
// Easel state behavior until canonical low power stats are supported.
// It takes an "external observer" snapshot of the current Easel state every
// time it is called, and synthesizes an artificial sleep state that will
// behave similarly to real stats if Easel gets "wedged" in the "on" state.
static std::mutex statsLock;
static uint64_t totalOnSnapshotCount = 0;
static uint64_t totalNotOnSnapshotCount = 0;
unsigned long currentState;
struct PowerStateSubsystemSleepState *state;
subsystem->name = "Easel";
if (get_easel_state(&currentState) != 0) {
subsystem->states.resize(0);
return -1;
}
if (currentState >= NUM_EASEL_STATES) {
ALOGE("%s: unrecognized Easel state(%lu)", __func__, currentState);
return -1;
}
subsystem->states.resize(1);
// Since we are storing stats locally but can have multiple parallel
// callers, locking is required to ensure stats are not corrupted.
std::lock_guard<std::mutex> lk(statsLock);
// Update statistics for synthetic sleep state. We combine OFF and
// SUSPENDED to act as a composite "not on" state so the numbers will behave
// like a real sleep state.
if ((currentState == EASEL_OFF) || (currentState == EASEL_SUSPENDED)) {
totalNotOnSnapshotCount++;
} else {
totalOnSnapshotCount++;
}
// Update statistics for synthetic sleep state, where
// totalTransitions = cumulative count of Easel state0 (as seen by PowerHAL)
// residencyInMsecsSinceBoot = cumulative count of Easel state1 (as seen by
// PowerHAL)
// lastEntryTimestampMs = cumulative count of Easel state2 (as seen by
// PowerHAL)
state = &subsystem->states[0];
state->name = "SyntheticSleep";
state->totalTransitions = totalOnSnapshotCount;
state->residencyInMsecSinceBoot = totalNotOnSnapshotCount;
state->lastEntryTimestampMs = 0; // No added value for the workaround
state->supportedOnlyInSuspend = false;
return 0;
}
// Methods from ::android::hardware::power::V1_1::IPower follow.
Return<void> Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) {
hidl_vec<PowerStateSubsystem> subsystems;
int ret;
subsystems.resize(SUBSYSTEM_COUNT);
//We currently have only one Subsystem for WLAN
ret = get_wlan_low_power_stats(subsystems[SUBSYSTEM_WLAN]);
if (ret != 0)
goto done;
// Get WLAN subsystem low power stats.
if (get_wlan_low_power_stats(&subsystems[SUBSYSTEM_WLAN]) != 0) {
ALOGE("%s: failed to process wlan stats", __func__);
}
//Add query for other subsystems here
// Get Easel subsystem low power stats.
if (get_easel_low_power_stats(&subsystems[SUBSYSTEM_EASEL]) != 0) {
ALOGE("%s: failed to process Easel stats", __func__);
}
done:
_hidl_cb(subsystems, Status::SUCCESS);
return Void();
}

View File

@@ -51,6 +51,10 @@
#define WLAN_POWER_STAT "/d/wlan0/power_stats"
#endif
#ifndef EASEL_STATE_FILE
#define EASEL_STATE_FILE "/sys/devices/virtual/misc/mnh_sm/state"
#endif
#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
#define LINE_SIZE 128
@@ -174,3 +178,44 @@ int extract_platform_stats(uint64_t *list) {
int extract_wlan_stats(uint64_t *list) {
return extract_stats(list, WLAN_POWER_STAT, wlan_stat_map, ARRAY_SIZE(wlan_stat_map));
}
int get_easel_state(unsigned long *current_state) {
FILE *fp = NULL;
static const size_t EASEL_STATE_LINE_SIZE = 16;
char buffer[EASEL_STATE_LINE_SIZE];
char *parse_end = buffer;
unsigned long state;
if (current_state == NULL) {
ALOGD("%s: null current_state pointer from caller", __func__);
return -1;
}
fp = fopen(EASEL_STATE_FILE, "re");
if (fp == NULL) {
ALOGE("%s: failed to open: %s Error = %s", __func__, EASEL_STATE_FILE,
strerror(errno));
return -errno;
}
if (fgets(buffer, EASEL_STATE_LINE_SIZE, fp) == NULL) {
fclose(fp);
ALOGE("%s: failed to read: %s", __func__, EASEL_STATE_FILE);
return -1;
}
fclose(fp);
parse_end = buffer;
state = strtoul(buffer, &parse_end, 10);
if ((parse_end == buffer) || (state > 2)) {
ALOGE("%s: unrecognized format: %s '%s'", __func__, EASEL_STATE_FILE,
buffer);
return -1;
}
*current_state = state;
return 0;
}

View File

@@ -53,6 +53,7 @@ enum stats_type {
enum subsystem_type {
SUBSYSTEM_WLAN = 0,
SUBSYSTEM_EASEL,
//Don't add any lines after this line
SUBSYSTEM_COUNT
@@ -92,6 +93,7 @@ struct stat_pair {
int extract_platform_stats(uint64_t *list);
int extract_wlan_stats(uint64_t *list);
int get_easel_state(unsigned long *current_state);
#ifdef __cplusplus
}

View File

@@ -6,6 +6,10 @@ allow hal_power_default debugfs_rpm:file r_file_perms;
allow hal_power_default debugfs_wlan:dir r_dir_perms;
allow hal_power_default debugfs_wlan:file r_file_perms;
allow hal_power_default sysfs_easel:dir search;
allow hal_power_default sysfs_easel:file r_file_perms;
# To do powerhint on nodes defined in powerhint.json
allow hal_power_default sysfs_msm_subsys:dir search;
allow hal_power_default sysfs_msm_subsys:file rw_file_perms;