From 9cee8a5abe02cd6845f02dba8cc795aaad979ea0 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Fri, 14 Jul 2017 13:25:18 -0400 Subject: Add support for Genpd. This patch adds support to dump/display the powerdomain statistics in powerdebug. Signed-off-by: Thara Gopinath --- Makefile | 2 +- display.c | 1 + display.h | 2 +- genpd.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ powerdebug.c | 20 +++- powerdebug.h | 6 +- 6 files changed, 382 insertions(+), 7 deletions(-) create mode 100644 genpd.c diff --git a/Makefile b/Makefile index 2da9d67..4da3160 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ MANDIR=/usr/share/man/man8 CFLAGS?=-O1 -g -Wall -Wshadow CC?=gcc -OBJS = powerdebug.o sensor.o clocks.o regulator.o gpio.o \ +OBJS = powerdebug.o sensor.o clocks.o regulator.o gpio.o genpd.o\ display.o tree.o utils.o mainloop.o default: powerdebug diff --git a/display.c b/display.c index 5665ed5..9cd3b9d 100644 --- a/display.c +++ b/display.c @@ -73,6 +73,7 @@ struct windata windata[] = { [REGULATOR] = { .name = "Regulators" }, [SENSOR] = { .name = "Sensors" }, [GPIO] = { .name = "Gpio" }, + [GENPD] = { .name = "Powerdomains" }, }; static void display_fini(void) diff --git a/display.h b/display.h index 6083c53..36c2b52 100644 --- a/display.h +++ b/display.h @@ -19,7 +19,7 @@ * */ -enum { CLOCK, REGULATOR, SENSOR, GPIO }; +enum { CLOCK, REGULATOR, SENSOR, GPIO, GENPD }; struct display_ops { int (*display)(bool refresh); diff --git a/genpd.c b/genpd.c new file mode 100644 index 0000000..7b69dbb --- /dev/null +++ b/genpd.c @@ -0,0 +1,358 @@ +/* + * Powerdebug : power debugging tool + * + * Copyright (C) 2016, Linaro Limited. + * + * Author: + * Thara Gopinath + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#define DEBUGFS_GENPD "/sys/kernel/debug/pm_genpd" +#define NAME_MAX 16 +#define DEVICE_NAME_MAX 256 + +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "display.h" +#include "powerdebug.h" +#include "tree.h" +#include "utils.h" + +struct genpd_idle_state { + char name[NAME_MAX]; + long long idle_time; +}; + +struct genpd_info { + long long active_time; + long long total_idle_time; + char current_state[NAME_MAX]; + struct genpd_idle_state *idle_states; + char (*devices)[DEVICE_NAME_MAX]; + char (*sub_domains)[NAME_MAX]; + int nr_devices; + int nr_subdomains; + int nr_states; +}; + +static struct tree *genpd_tree; + +static struct genpd_info *genpd_alloc(void) +{ + struct genpd_info *genpd; + + genpd = malloc(sizeof(*genpd)); + if (genpd) + memset(genpd, 0, sizeof(*genpd)); + + return genpd; +} + +static int genpd_filter_cb(const char *name) +{ + /* Ignore the summary directory */ + if (!strcmp(name, "pm_genpd_summary")) + return -1; + if (!strcmp(name, "pm_genpd")) + return -1; + + return 0; +} + +static int genpd_dump_cb(struct tree *t, void *data) +{ + struct genpd_info *genpd = t->private; + int i; + + if (!t->parent) + return 0; + + printf("\n%s:\n", t->name); + printf("current_state: %s\n", genpd->current_state); + printf("active_time: %lld ms\n", genpd->active_time); + printf("total_idle_time: %lld ms\n", genpd->total_idle_time); + printf("Idle States:\n"); + for (i = 0; i < genpd->nr_states; i++) { + struct genpd_idle_state state = genpd->idle_states[i]; + + if (!i) + printf("%*s State %*s Time\n", 12, "", 10, ""); + printf("%*s %-16s %lld\n", 12, "", state.name, + state.idle_time); + } + printf("Devices:\n"); + for (i = 0; i < genpd->nr_devices; i++) + printf("%*s %s\n", 8, "", genpd->devices[i]); + printf("Subdomains:\n"); + for (i = 0; i < genpd->nr_subdomains; i++) + printf("%*s %s\n", 11, "", genpd->sub_domains[i]); + + return 0; +} + +static int genpd_display_cb(struct tree *t, void *data) +{ + struct genpd_info *genpd = t->private; + int *line = data; + int nr_states = 0, nr_devices = 0, nr_domains = 0, i = 0; + char *buf; + + if (!t->parent) + return 0; + + while (1) { + char *state_buf, *device_buf, *domain_buf; + + if ((i) && (nr_states == genpd->nr_states) && + (nr_devices == genpd->nr_devices) && + (nr_domains == genpd->nr_subdomains)) { + display_print_line(GENPD, *line, " ", 1, t); + (*line)++; + break; + } + + if (nr_states < genpd->nr_states) { + if (asprintf(&state_buf, "%-10s %lld", + genpd->idle_states[nr_states].name, + genpd->idle_states[nr_states].idle_time) < 0) + return -1; + nr_states++; + } else { + if (asprintf(&state_buf, "%s", "") < 0) + return -1; + } + + if (nr_devices < genpd->nr_devices) { + if (asprintf(&device_buf, "%s", + genpd->devices[nr_devices]) < 0) + return -1; + nr_devices++; + } else { + if (asprintf(&device_buf, "%s", "") < 0) + return -1; + } + + if (nr_domains < genpd->nr_subdomains) { + if (asprintf(&domain_buf, "%s", + genpd->sub_domains[nr_domains]) < 0) + return -1; + nr_domains++; + } else { + if (asprintf(&domain_buf, "%s", "") < 0) + return -1; + } + + if (!i) { + if (asprintf(&buf, "%-9s %-18s %-20lld %-24lld %-34s " + "%-50s %-15s", t->name, genpd->current_state, + genpd->active_time, genpd->total_idle_time, + state_buf, device_buf, domain_buf) < 0) + return -1; + } else { + if (asprintf(&buf, "%-74s %-34s %-50s %-15s", + "", state_buf, device_buf, domain_buf) < 0) + return -1; + } + + display_print_line(GENPD, *line, buf, 1, t); + (*line)++; + free(buf); + free(state_buf); + free(device_buf); + free(domain_buf); + i++; + } + + return 0; +} + +static int genpd_print_header(void) +{ + char *buf; + int ret; + + if (asprintf(&buf, "%-9s %-18s %-20s %-24s %-34s %-50s %-15s", "Name", + "Current State", "Active Time(ms)", "Total Idle Time(ms)", + "Idle States(State,Time ms)", "Devices", "Subdomains") < 0) + return -1; + + ret = display_column_name(buf); + free(buf); + + return ret; +} + +static int genpd_print_info(struct tree *t) +{ + int ret, line = 0; + + display_reset_cursor(GENPD); + + genpd_print_header(); + + ret = tree_for_each(t, genpd_display_cb, &line); + + display_refresh_pad(GENPD); + + return ret; +} + +static int read_genpd_cb(struct tree *t, void *data) +{ + struct genpd_info *genpd = t->private; + FILE *fp; + char line[256]; + int nr_states = 0, nr_devices = 0, nr_sub_domains = 0; + + file_read_value(t->path, "active_time", "%lld", &genpd->active_time); + file_read_value(t->path, "total_idle_time", "%lld", + &genpd->total_idle_time); + file_read_value(t->path, "current_state", "%s", &genpd->current_state); + file_open(&fp, t->path, "idle_states", "r"); + while (!(file_read_line(&fp, line, sizeof(line)))) { + if (!strncmp(line, "State", 5)) + continue; + + genpd->idle_states = realloc(genpd->idle_states, + sizeof(struct genpd_idle_state) * (nr_states + 1)); + if (!genpd->idle_states) + continue; + sscanf(line, "%s %lld", genpd->idle_states[nr_states].name, + &(genpd->idle_states[nr_states].idle_time)); + + nr_states++; + } + file_close(&fp); + + file_open(&fp, t->path, "devices", "r"); + while (!(file_read_line(&fp, line, sizeof(line)))) { + int len; + + genpd->devices = realloc(genpd->devices, + sizeof(*(genpd->devices)) * (nr_devices + 1)); + if (!genpd->devices) + continue; + + len = strlen(line); + line[len - 1] = '\0'; + strcpy(genpd->devices[nr_devices], line); + nr_devices++; + } + file_close(&fp); + + file_open(&fp, t->path, "sub_domains", "r"); + while (!(file_read_line(&fp, line, sizeof(line)))) { + int len; + + genpd->sub_domains = realloc(genpd->sub_domains, + sizeof(*(genpd->sub_domains)) * (nr_sub_domains + 1)); + if (!genpd->sub_domains) + continue; + + len = strlen(line); + line[len - 1] = '\0'; + strcpy(genpd->sub_domains[nr_sub_domains], line); + nr_sub_domains++; + } + + genpd->nr_states = nr_states; + genpd->nr_devices = nr_devices; + genpd->nr_subdomains = nr_sub_domains; + + return 0; +} + +static int read_genpd_info(struct tree *t) +{ + return tree_for_each(t, read_genpd_cb, NULL); +} + +static int fill_genpd_cb(struct tree *t, void *data) +{ + struct genpd_info *genpd; + + genpd = genpd_alloc(); + if (!genpd) { + printf("error: unable to allocate memory for genpd\n"); + return -1; + } + + t->private = genpd; + + return read_genpd_cb(t, data); +} + +static int fill_genpd_tree(void) +{ + return tree_for_each(genpd_tree, fill_genpd_cb, NULL); +} + +static int genpd_info_load(void) +{ + if (genpd_tree) + return 0; + + genpd_tree = tree_load(DEBUGFS_GENPD, genpd_filter_cb, false); + if (!genpd_tree) + return -1; + + if (fill_genpd_tree()) + return -1; + + return 0; +} + +int genpd_dump(void) +{ + if (!genpd_tree) + genpd_info_load(); + else + read_genpd_info(genpd_tree); + + return tree_for_each(genpd_tree, genpd_dump_cb, NULL); + + return 0; +} + +static int genpd_display(bool refresh) +{ + if (genpd_info_load()) { + display_print_error(GENPD, 0, "Failed to read genpd info"); + return 0; + } + + if (refresh && read_genpd_info(genpd_tree)) + return -1; + + return genpd_print_info(genpd_tree); +} + +static struct display_ops genpd_ops = { + .display = genpd_display, +}; + +int genpd_init(struct powerdebug_options *options) +{ + if (!(options->flags & GENPD_OPTION)) + return 0; + + return display_register(GENPD, &genpd_ops); +} + diff --git a/powerdebug.c b/powerdebug.c index 188c91d..e18670e 100644 --- a/powerdebug.c +++ b/powerdebug.c @@ -35,14 +35,15 @@ void usage(void) printf("Usage: powerdebug [OPTIONS]\n"); printf("\n"); printf("powerdebug -d [ -r ] [ -s ] [ -c [ -f ] ] " - "[ -g ] [ -v ]\n"); - printf("powerdebug [ -r | -s | -c | -g]\n"); + "[ -g ] [ -p ] [ -v ]\n"); + printf("powerdebug [ -r | -s | -c | -g | -p]\n"); printf(" -r, --regulator Show regulator information\n"); printf(" -s, --sensor Show sensor information\n"); printf(" -c, --clock Show clock information\n"); printf(" -f, --findparents Show all parents for a particular" " clock\n"); printf(" -g, --gpio Show gpio information\n"); + printf(" -p, --powerdomain Show powerdomain information\n"); printf(" -t, --time Set ticktime in seconds (eg. 10.0)\n"); printf(" -d, --dump Dump information once (no refresh)\n"); printf(" -v, --verbose Verbose mode (use with -r and/or" @@ -62,6 +63,7 @@ void version() * -s, --sensor : sensors * -c, --clock : clocks * -g, --gpio : gpios + * -p, --powerdomain : powerdomains * -f, --findparents : clockname whose parents have to be found * -t, --time : ticktime * -d, --dump : dump @@ -76,6 +78,7 @@ static struct option long_options[] = { { "sensor", 0, 0, 's' }, { "clock", 0, 0, 'c' }, { "gpio", 0, 0, 'g' }, + { "powerdomain", 0, 0, 'p' }, { "findparents", 1, 0, 'f' }, { "time", 1, 0, 't' }, { "dump", 0, 0, 'd' }, @@ -95,7 +98,7 @@ int getoptions(int argc, char *argv[], struct powerdebug_options *options) while (1) { int optindex = 0; - c = getopt_long(argc, argv, "rscgf:t:dvVh", + c = getopt_long(argc, argv, "rscgpf:t:dvVh", long_options, &optindex); if (c == -1) break; @@ -113,6 +116,9 @@ int getoptions(int argc, char *argv[], struct powerdebug_options *options) case 'g': options->flags |= GPIO_OPTION; break; + case 'p': + options->flags |= GENPD_OPTION; + break; case 'd': options->flags |= DUMP_OPTION; break; @@ -162,6 +168,9 @@ static int powerdebug_dump(struct powerdebug_options *options) if (options->flags & GPIO_OPTION) gpio_dump(); + if (options->flags & GENPD_OPTION) + genpd_dump(); + return 0; } @@ -240,6 +249,11 @@ int main(int argc, char **argv) options->flags &= GPIO_OPTION; } + if (genpd_init(options)) { + printf("failed to initialize genpd details\n"); + options->flags &= GENPD_OPTION; + } + ret = options->flags & DUMP_OPTION ? powerdebug_dump(options) : powerdebug_display(options); diff --git a/powerdebug.h b/powerdebug.h index a8073a6..992c5c0 100644 --- a/powerdebug.h +++ b/powerdebug.h @@ -25,12 +25,12 @@ #define REGULATOR_OPTION 0x02 #define SENSOR_OPTION 0x04 #define GPIO_OPTION 0x08 - +#define GENPD_OPTION 0x10 #define VERBOSE_OPTION 0x1000 #define DUMP_OPTION 0x2000 #define DEFAULT_OPTION (REGULATOR_OPTION | SENSOR_OPTION | \ - CLOCK_OPTION | GPIO_OPTION) + CLOCK_OPTION | GPIO_OPTION | GENPD_OPTION) struct powerdebug_options { int flags; @@ -50,3 +50,5 @@ extern int gpio_dump(void); extern int sensor_init(struct powerdebug_options *options); extern int sensor_dump(void); +extern int genpd_init(struct powerdebug_options *options); +extern int genpd_dump(void); -- cgit v1.2.3