/* * 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 */ #include #include #include #include #include #include #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 DIR *dir = NULL; static vector all_devfreq; devfreq::devfreq(const char* dpath): device() { pt_strcpy(dir_name, dpath); } 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]; snprintf(filename, sizeof(filename), "/sys/class/devfreq/%s/trans_stat", dir_name); file.open(filename); if (!file) return; 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; 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; istart_measurement(); } void end_devfreq_measurement(void) { unsigned int i; for (i=0; iend_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) { int num = 0; std::string p = "/sys/class/devfreq/"; dir = opendir(p.c_str()); if (dir == NULL) { fprintf(stderr, "Devfreq not enabled\n"); is_enabled = false; return; } while(readdir(dir) != NULL) num++; if (num == 2) { fprintf(stderr, "Devfreq not enabled\n"); is_enabled = false; closedir(dir); dir = NULL; 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; idevice_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, "%s", fline); } wprintw(win, "\n"); } } void report_devfreq_devices(void) { if (!is_enabled) { return; } /* todo: adapt to new report format */ } 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]; delete df; } all_devfreq.clear(); /* close /sys/class/devfreq */ if (dir != NULL) { closedir(dir); dir = NULL; } }