blob: e16951cc7d2bd8953c99385d0174d96d8236f7cc [file] [log] [blame]
/*
* Copyright 2012, Linaro
*
* This file is part of PowerTOP
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
* or just google for it.
*
* Authors:
* Rajagopal Venkat <rajagopal.venkat@linaro.org>
*/
#include <iostream>
#include <fstream>
#include <dirent.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "device.h"
#include "devfreq.h"
#include "../display.h"
#include "../cpu/cpu.h"
#include "../report/report.h"
#include "../report/report-maker.h"
static bool is_enabled = true;
static vector<class devfreq *> all_devfreq;
devfreq::devfreq(const char* dpath): device()
{
strncpy(dir_name, dpath, sizeof(dir_name));
}
uint64_t devfreq::parse_freq_time(char* pchr)
{
char *cptr, *pptr = pchr;
uint64_t ctime;
cptr = strtok(pchr, " :");
while (cptr != NULL) {
cptr = strtok(NULL, " :");
if (cptr )
pptr = cptr;
}
ctime = strtoull(pptr, NULL, 10);
return ctime;
}
void devfreq::process_time_stamps()
{
unsigned int i;
uint64_t active_time = 0;
sample_time = (1000000.0 * (stamp_after.tv_sec - stamp_before.tv_sec))
+ ((stamp_after.tv_usec - stamp_before.tv_usec) );
for (i=0; i < dstates.size()-1; i++) {
struct frequency *state = dstates[i];
state->time_after = 1000 * (state->time_after - state->time_before);
active_time += state->time_after;
}
/* Compute idle time for the device */
dstates[i]->time_after = sample_time - active_time;
}
void devfreq::add_devfreq_freq_state(uint64_t freq, uint64_t time)
{
struct frequency *state;
state = new(std::nothrow) struct frequency;
if (!state)
return;
memset(state, 0, sizeof(*state));
dstates.push_back(state);
state->freq = freq;
if (freq == 0)
strcpy(state->human_name, "Idle");
else
hz_to_human(freq, state->human_name);
state->time_before = time;
}
void devfreq::update_devfreq_freq_state(uint64_t freq, uint64_t time)
{
unsigned int i;
struct frequency *state = NULL;
for(i=0; i < dstates.size(); i++) {
if (freq == dstates[i]->freq)
state = dstates[i];
}
if (state == NULL) {
add_devfreq_freq_state(freq, time);
return;
}
state->time_after = time;
}
void devfreq::parse_devfreq_trans_stat(char *dname)
{
ifstream file;
char filename[256];
sprintf(filename, "/sys/class/devfreq/%s/trans_stat", dir_name);
file.open(filename);
if (file) {
char line[1024];
char *c;
while (file) {
uint64_t freq;
uint64_t time;
char *pchr;
memset(line, 0, sizeof(line));
file.getline(line, sizeof(line));
pchr = strchr(line, '*');
pchr = (pchr != NULL) ? pchr+1 : line;
freq = strtoull(pchr, &c, 10);
if (!freq)
continue;
time = parse_freq_time(pchr);
update_devfreq_freq_state(freq, time);
}
}
file.close();
}
void devfreq::start_measurement(void)
{
unsigned int i;
ifstream file;
for (i=0; i < dstates.size(); i++)
delete dstates[i];
dstates.resize(0);
sample_time = 0;
gettimeofday(&stamp_before, NULL);
parse_devfreq_trans_stat(dir_name);
/* add device idle state */
update_devfreq_freq_state(0, 0);
}
void devfreq::end_measurement(void)
{
parse_devfreq_trans_stat(dir_name);
gettimeofday(&stamp_after, NULL);
process_time_stamps();
}
double devfreq::power_usage(struct result_bundle *result, struct parameter_bundle *bundle)
{
return 0;
}
double devfreq::utilization(void)
{
return 0;
}
void devfreq::fill_freq_utilization(unsigned int idx, char *buf)
{
buf[0] = 0;
if (idx < dstates.size() && dstates[idx]) {
struct frequency *state = dstates[idx];
sprintf(buf, " %5.1f%% ", percentage(1.0 * state->time_after / sample_time));
}
}
void devfreq::fill_freq_name(unsigned int idx, char *buf)
{
buf[0] = 0;
if (idx < dstates.size() && dstates[idx]) {
sprintf(buf, "%-15s", dstates[idx]->human_name);
}
}
void start_devfreq_measurement(void)
{
unsigned int i;
for (i=0; i<all_devfreq.size(); i++)
all_devfreq[i]->start_measurement();
}
void end_devfreq_measurement(void)
{
unsigned int i;
for (i=0; i<all_devfreq.size(); i++)
all_devfreq[i]->end_measurement();
}
static void devfreq_dev_callback(const char *d_name)
{
devfreq *df = new(std::nothrow) class devfreq(d_name);
if (df)
all_devfreq.push_back(df);
}
void create_all_devfreq_devices(void)
{
DIR *dir;
std::string p = "/sys/class/devfreq/";
dir = opendir(p.c_str());
if (dir == NULL) {
is_enabled = false;
return;
}
callback fn = &devfreq_dev_callback;
process_directory(p.c_str(), fn);
}
void initialize_devfreq(void)
{
if (is_enabled)
create_tab("Device Freq stats", _("Device Freq stats"));
}
void display_devfreq_devices(void)
{
unsigned int i, j;
WINDOW *win;
char fline[1024];
char buf[128];
win = get_ncurses_win("Device Freq stats");
if (!win)
return;
wclear(win);
wmove(win, 2,0);
if (!is_enabled) {
wprintw(win, _(" Devfreq is not enabled"));
return;
}
if (!all_devfreq.size()) {
wprintw(win, _(" No devfreq devices available"));
return;
}
for (i=0; i<all_devfreq.size(); i++) {
class devfreq *df = all_devfreq[i];
wprintw(win, "\n%s\n", df->device_name());
for(j=0; j < df->dstates.size(); j++) {
memset(fline, 0, sizeof(fline));
strcpy(fline, "\t");
df->fill_freq_name(j, buf);
strcat(fline, buf);
df->fill_freq_utilization(j, buf);
strcat(fline, buf);
strcat(fline, "\n");
wprintw(win, fline);
}
wprintw(win, "\n");
}
}
void report_devfreq_devices(void)
{
char buffer[512];
unsigned int i, j;
if (!is_enabled) {
return;
}
report.begin_section(SECTION_DEVFREQ);
report.add_header("Device Frequency Report");
report.begin_table(TABLE_WIDE);
if (!all_devfreq.size()) {
report.begin_row();
report.add(" No devfreq devices available");
return;
}
for (i = 0; i < all_devfreq.size(); i++) {
buffer[0] = 0;
class devfreq *df = all_devfreq[i];
report.begin_row();
report.begin_cell(CELL_CPU_PSTATE_HEADER);
report.addf("%s", df->device_name());
for (j = 0; j < df->dstates.size(); j++) {
report.begin_row();
report.begin_cell(CELL_CPU_STATE_VALUE);
df->fill_freq_name(j, buffer);
report.add(buffer);
report.begin_cell(CELL_CPU_STATE_VALUE);
df->fill_freq_utilization(j, buffer);
report.add(buffer);
}
}
}
void clear_all_devfreq()
{
unsigned int i, j;
for (i=0; i < all_devfreq.size(); i++) {
class devfreq *df = all_devfreq[i];
for(j=0; j < df->dstates.size(); j++)
delete df->dstates[j];
df->dstates.resize(0);
delete df;
}
all_devfreq.clear();
}