aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThara Gopinath <thara.gopinath@linaro.org>2017-07-14 13:25:18 -0400
committerLisa Nguyen <lisa.nguyen@linaro.org>2017-09-19 13:22:47 -0700
commit9cee8a5abe02cd6845f02dba8cc795aaad979ea0 (patch)
treed96a3f12113ce9926537f4bfb29ad2b6d08d9974
parent56f6178da2825ff6b9f894c0e2812591817718e7 (diff)
Add support for Genpd.HEADmaster
This patch adds support to dump/display the powerdomain statistics in powerdebug. Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>
-rw-r--r--Makefile2
-rw-r--r--display.c1
-rw-r--r--display.h2
-rw-r--r--genpd.c358
-rw-r--r--powerdebug.c20
-rw-r--r--powerdebug.h6
6 files changed, 382 insertions, 7 deletions
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 <thara.gopinath@linaro.org>
+ *
+ * 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 <stdio.h>
+#undef _GNU_SOURCE
+#include <sys/types.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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 <clock-name> ] ] "
- "[ -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);