/* * 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); }