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