blob: c65537a1d64a6eabf627059fcccdf39ba4b8ccca [file] [log] [blame]
/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_NIDEBUG 0
#define LOG_TAG "android.hardware.power@1.3-service.crosshatch-libperfmgr"
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <log/log.h>
#include "power-helper.h"
#ifndef MASTER_STATS_FILE
#define MASTER_STATS_FILE "/sys/power/rpmh_stats/master_stats"
#endif
#ifndef SYSTEM_STATS_FILE
#define SYSTEM_STATS_FILE "/sys/power/system_sleep/stats"
#endif
#ifndef WLAN_STATS_FILE
#define WLAN_STATS_FILE "/d/wlan0/power_stats"
#endif
#ifndef EASEL_STATS_FILE
#define EASEL_STATS_FILE "/d/mnh_sm/power_stats"
#endif
#define LINE_SIZE 128
const char *master_stats_labels[MASTER_STATS_COUNT] = {
"Sleep Accumulated Duration",
"Sleep Count",
"Sleep Last Entered At",
};
struct stats_section master_sections[MASTER_COUNT] = {
{ MASTER_APSS, "APSS", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
{ MASTER_MPSS, "MPSS", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
{ MASTER_ADSP, "ADSP", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
{ MASTER_SLPI, "SLPI", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
{ MASTER_CDSP, "CDSP", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
// The following masters are currently unused
//{ MASTER_GPU, "GPU", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
//{ MASTER_DISPLAY, "DISPLAY", master_stats_labels, ARRAY_SIZE(master_stats_labels) },
};
const char *wlan_stats_labels[WLAN_STATS_COUNT] = {
"cumulative_sleep_time_ms",
"cumulative_total_on_time_ms",
"deep_sleep_enter_counter",
"last_deep_sleep_enter_tstamp_ms"
};
struct stats_section wlan_sections[] = {
{ SUBSYSTEM_WLAN, "POWER DEBUG STATS", wlan_stats_labels, ARRAY_SIZE(wlan_stats_labels) },
};
const char *easel_stats_labels[EASEL_STATS_COUNT] = {
"Cumulative count",
"Cumulative duration msec",
"Last entry timestamp msec"
};
struct stats_section easel_sections[] = {
{ SUBSYSTEM_EASEL, "OFF", easel_stats_labels, ARRAY_SIZE(easel_stats_labels) },
{ SUBSYSTEM_EASEL, "ACTIVE", easel_stats_labels, ARRAY_SIZE(easel_stats_labels) },
{ SUBSYSTEM_EASEL, "SUSPEND", easel_stats_labels, ARRAY_SIZE(easel_stats_labels) },
};
const char *system_stats_labels[SYSTEM_STATE_STATS_COUNT] = {
"count",
"actual last sleep(msec)"
};
struct stats_section system_sections[] = {
{ SYSTEM_STATES, "RPM Mode:aosd", system_stats_labels, ARRAY_SIZE(system_stats_labels) },
{ SYSTEM_STATES, "RPM Mode:cxsd", system_stats_labels, ARRAY_SIZE(system_stats_labels) },
};
static int parse_stats(const char **stat_labels, size_t num_stats,
uint64_t *list, FILE *fp) {
ssize_t nread;
size_t len = LINE_SIZE;
char *line;
size_t stats_read = 0;
size_t i;
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
return -ENOMEM;
}
while ((stats_read < num_stats) &&
(nread = getline(&line, &len, fp) > 0)) {
char *key = line + strspn(line, " \t");
char *value = strchr(key, ':');
if (!value || (value > (line + len)))
continue;
*value++ = '\0';
for (i = 0; i < num_stats; i++) {
if (!strncmp(key, stat_labels[i], strlen(stat_labels[i]))) {
list[i] = strtoull(value, NULL, 0);
stats_read++;
break;
}
}
}
free(line);
return stats_read;
}
static int extract_stats(uint64_t *stats_list, size_t entries_per_section, char *file,
struct stats_section *sections, size_t num_sections) {
FILE *fp;
ssize_t read;
size_t len = LINE_SIZE;
char *line;
size_t i;
size_t sections_read = 0;
size_t stats_read = 0;
int ret = 0;
fp = fopen(file, "re");
if (fp == NULL) {
ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
return -errno;
}
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
fclose(fp);
return -ENOMEM;
}
// Ensure that any missing stats default to 0
for (i = 0; i < (entries_per_section * num_sections); i++) {
stats_list[i] = 0L;
}
// Iterate over the sections we expect to find in the file, calling parse_stats()
// to process each section as we detect section headers
while ((sections_read < num_sections) && (read = getline(&line, &len, fp) != -1)) {
size_t begin = strspn(line, " \t");
for (i = 0; i < num_sections; i++) {
if (!strncmp(line + begin, sections[i].label, strlen(sections[i].label))) {
sections_read++;
break;
}
}
if (i == num_sections) {
continue;
}
stats_read = parse_stats(sections[i].stats_labels, sections[i].num_stats,
&stats_list[i * entries_per_section], fp);
// If we don't find all of the stats we expect in this section, our understanding of
// the input is wrong, and we can't just carry on as if everything is okay. Better
// to log the error and give up than potentially return incorrect data as stats.
if (stats_read != sections[i].num_stats) {
ALOGE("%s: failed to read all stats for %s section (%zu of %zu)", __func__,
sections[i].label, stats_read, sections[i].num_stats);
break;
}
}
free(line);
fclose(fp);
return ret;
}
int extract_master_stats(uint64_t *list, size_t list_length) {
size_t entries_per_section = list_length / ARRAY_SIZE(master_sections);
if (list_length % entries_per_section != 0) {
ALOGW("%s: stats list size not an even multiple of section count", __func__);
}
return extract_stats(list, entries_per_section, MASTER_STATS_FILE,
master_sections, ARRAY_SIZE(master_sections));
}
int extract_wlan_stats(uint64_t *list, size_t list_length) {
size_t entries_per_section = list_length / ARRAY_SIZE(wlan_sections);
if (list_length % entries_per_section != 0) {
ALOGW("%s: stats list size not an even multiple of section count", __func__);
}
return extract_stats(list, entries_per_section, WLAN_STATS_FILE,
wlan_sections, ARRAY_SIZE(wlan_sections));
}
int extract_easel_stats(uint64_t *list, size_t list_length) {
size_t entries_per_section = list_length / ARRAY_SIZE(easel_sections);
if (list_length % entries_per_section != 0) {
ALOGW("%s: stats list size not an even multiple of section count", __func__);
}
return extract_stats(list, entries_per_section, EASEL_STATS_FILE,
easel_sections, ARRAY_SIZE(easel_sections));
}
int extract_system_stats(uint64_t *list, size_t list_length) {
size_t entries_per_section = list_length / ARRAY_SIZE(system_sections);
if (list_length % entries_per_section != 0) {
ALOGW("%s: stats list size not an even multiple of section count", __func__);
}
return extract_stats(list, entries_per_section, SYSTEM_STATS_FILE,
system_sections, ARRAY_SIZE(system_sections));
}