blob: 7b69dbbdb7a283c1faa88bdfefff6da350833950 [file] [log] [blame]
/*
* 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);
}