diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..45aaa58 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,578 @@ +/* + * Copyright 2010, Intel Corporation + * + * This 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. + * + * getopt code is taken from "The GNU C Library" reference manual, + * section 24.2 "Parsing program options using getopt" + * http://www.gnu.org/s/libc/manual/html_node/Getopt-Long-Option-Example.html + * Manual published under the terms of the Free Documentation License. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <getopt.h> +#include <unistd.h> +#include <locale.h> +#include <sys/resource.h> +#include <limits.h> +#include <pthread.h> + +#include "cpu/cpu.h" +#include "process/process.h" +#include "perf/perf.h" +#include "perf/perf_bundle.h" +#include "lib.h" +#include "../config.h" + + +#include "devices/device.h" +#include "devices/devfreq.h" +#include "devices/usb.h" +#include "devices/ahci.h" +#include "measurement/measurement.h" +#include "parameters/parameters.h" +#include "calibrate/calibrate.h" + + +#include "tuning/tuning.h" +#include "wakeup/wakeup.h" + +#include "display.h" +#include "devlist.h" +#include "report/report.h" + +#define DEBUGFS_MAGIC 0x64626720 + +#define NR_OPEN_DEF 1024 * 1024 + +int debug_learning = 0; +unsigned time_out = 20; +int leave_powertop = 0; +void (*ui_notify_user) (const char *frmt, ...); + +enum { + OPT_AUTO_TUNE = CHAR_MAX + 1, + OPT_EXTECH, + OPT_DEBUG +}; + +static const struct option long_options[] = +{ + /* These options set a flag. */ + {"auto-tune", no_argument, NULL, OPT_AUTO_TUNE}, + {"calibrate", no_argument, NULL, 'c'}, + {"csv", optional_argument, NULL, 'C'}, + {"debug", no_argument, &debug_learning, OPT_DEBUG}, + {"extech", optional_argument, NULL, OPT_EXTECH}, + {"html", optional_argument, NULL, 'r'}, + {"iteration", optional_argument, NULL, 'i'}, + {"quiet", no_argument, NULL, 'q'}, + {"sample", optional_argument, NULL, 's'}, + {"time", optional_argument, NULL, 't'}, + {"workload", optional_argument, NULL, 'w'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} +}; + +static void print_version() +{ + printf(_("PowerTOP version " PACKAGE_VERSION "\n")); +} + +static bool set_refresh_timeout() +{ + static char buf[4]; + mvprintw(1, 0, "%s (currently %u): ", _("Set refresh time out"), time_out); + memset(buf, '\0', sizeof(buf)); + get_user_input(buf, sizeof(buf) - 1); + show_tab(0); + unsigned time = strtoul(buf, NULL, 0); + if (!time) return 0; + if (time > 32) time = 32; + time_out = time; + return 1; +} + +static void print_usage() +{ + printf("%s\n\n", _("Usage: powertop [OPTIONS]")); + printf(" --auto-tune\t %s\n", _("sets all tunable options to their GOOD setting")); + printf(" -c, --calibrate\t %s\n", _("runs powertop in calibration mode")); + printf(" -C, --csv%s\t %s\n", _("[=filename]"), _("generate a csv report")); + printf(" --debug\t\t %s\n", _("run in \"debug\" mode")); + printf(" --extech%s\t %s\n", _("[=devnode]"), _("uses an Extech Power Analyzer for measurements")); + printf(" -r, --html%s\t %s\n", _("[=filename]"), _("generate a html report")); + printf(" -i, --iteration%s\n", _("[=iterations] number of times to run each test")); + printf(" -q, --quiet\t\t %s\n", _("suppress stderr output")); + printf(" -s, --sample%s\t %s\n", _("[=seconds]"), _("interval for power consumption measurement")); + printf(" -t, --time%s\t %s\n", _("[=seconds]"), _("generate a report for 'x' seconds")); + printf(" -w, --workload%s %s\n", _("[=workload]"), _("file to execute for workload")); + printf(" -V, --version\t\t %s\n", _("print version information")); + printf(" -h, --help\t\t %s\n", _("print this help menu")); + printf("\n"); + printf("%s\n\n", _("For more help please refer to the 'man 8 powertop'")); +} + +static void do_sleep(int seconds) +{ + time_t target; + int delta; + + if (!ncurses_initialized()) { + sleep(seconds); + return; + } + target = time(NULL) + seconds; + delta = seconds; + do { + int c; + usleep(6000); + halfdelay(delta * 10); + + c = getch(); + switch (c) { + case KEY_BTAB: + show_prev_tab(); + break; + case '\t': + show_next_tab(); + break; + case KEY_RIGHT: + cursor_right(); + break; + case KEY_LEFT: + cursor_left(); + break; + case KEY_NPAGE: + case KEY_DOWN: + cursor_down(); + break; + case KEY_PPAGE: + case KEY_UP: + cursor_up(); + break; + case ' ': + case '\n': + cursor_enter(); + break; + case 's': + if (set_refresh_timeout()) + return; + break; + case 'r': + window_refresh(); + return; + case KEY_EXIT: + case 'q': + case 27: // Escape + leave_powertop = 1; + return; + } + + delta = target - time(NULL); + if (delta <= 0) + break; + + } while (1); +} + +extern "C" { + static volatile bool end_thread; + void* measure_background_thread(void *arg) + { + int sleep_time = *((int *) arg); + while (!end_thread) { + do_sleep(sleep_time); + global_sample_power(); + } + return 0; + } +} + +void one_measurement(int seconds, int sample_interval, char *workload) +{ + create_all_usb_devices(); + start_power_measurement(); + devices_start_measurement(); + start_devfreq_measurement(); + start_process_measurement(); + start_cpu_measurement(); + + if (workload && workload[0]) { + pthread_t thread = 0UL; + end_thread = false; + if (pthread_create(&thread, NULL, measure_background_thread, &sample_interval)) + fprintf(stderr, "ERROR: workload measurement thread creation failed\n"); + + if (system(workload)) + fprintf(stderr, _("Unknown issue running workload!\n")); + + if (thread) + { + end_thread = true; + pthread_join( thread, NULL); + } + global_sample_power(); + } else { + while (seconds > 0) + { + do_sleep(sample_interval > seconds ? seconds : sample_interval); + seconds -= sample_interval; + global_sample_power(); + } + } + end_cpu_measurement(); + end_process_measurement(); + collect_open_devices(); + end_devfreq_measurement(); + devices_end_measurement(); + end_power_measurement(); + + process_cpu_data(); + process_process_data(); + + /* output stats */ + process_update_display(); + report_summary(); + w_display_cpu_cstates(); + w_display_cpu_pstates(); + if (reporttype != REPORT_OFF) { + report_display_cpu_cstates(); + report_display_cpu_pstates(); + } + report_process_update_display(); + tuning_update_display(); + wakeup_update_display(); + end_process_data(); + + global_power(); + compute_bundle(); + + show_report_devices(); + report_show_open_devices(); + + report_devices(); + display_devfreq_devices(); + report_devfreq_devices(); + ahci_create_device_stats_table(); + store_results(measurement_time); + end_cpu_data(); +} + +void out_of_memory() +{ + reset_display(); + printf("%s...\n",_("PowerTOP is out of memory. PowerTOP is Aborting")); + abort(); +} + +void make_report(int time, char *workload, int iterations, int sample_interval, char *file) +{ + + /* one to warm up everything */ + fprintf(stderr, _("Preparing to take measurements\n")); + utf_ok = 0; + one_measurement(1, sample_interval, NULL); + + if (!workload[0]) + fprintf(stderr, _("Taking %d measurement(s) for a duration of %d second(s) each.\n"),iterations,time); + else + fprintf(stderr, _("Measuring workload %s.\n"), workload); + for (int i=0; i != iterations; i++){ + init_report_output(file, iterations); + initialize_tuning(); + initialize_wakeup(); + /* and then the real measurement */ + one_measurement(time, sample_interval, workload); + report_show_tunables(); + report_show_wakeup(); + finish_report_output(); + clear_tuning(); + } + /* and wrap up */ + learn_parameters(50, 0); + save_all_results("saved_results.powertop"); + save_parameters("saved_parameters.powertop"); + end_pci_access(); + exit(0); +} + +static void checkroot() { + int uid; + uid = getuid(); + + if (uid != 0) { + printf(_("PowerTOP " PACKAGE_VERSION " must be run with root privileges.\n")); + printf(_("exiting...\n")); + exit(EXIT_FAILURE); + } + +} + +static int get_nr_open(void) { + int nr_open = NR_OPEN_DEF; + ifstream file; + + file.open("/proc/sys/fs/nr_open", ios::in); + if (file) { + file >> nr_open; + file.close(); + } + return nr_open; +} + +static void powertop_init(int auto_tune) +{ + static char initialized = 0; + int ret; + struct statfs st_fs; + struct rlimit rlmt; + + if (initialized) + return; + + checkroot(); + + rlmt.rlim_cur = rlmt.rlim_max = get_nr_open(); + setrlimit (RLIMIT_NOFILE, &rlmt); + + if (system("/sbin/modprobe cpufreq_stats > /dev/null 2>&1")) + fprintf(stderr, _("modprobe cpufreq_stats failed\n")); +#if defined(__i386__) || defined(__x86_64__) + if (system("/sbin/modprobe msr > /dev/null 2>&1")) + fprintf(stderr, _("modprobe msr failed\n")); +#endif + statfs("/sys/kernel/debug", &st_fs); + + if (st_fs.f_type != (long) DEBUGFS_MAGIC) { + if (access("/bin/mount", X_OK) == 0) { + ret = system("/bin/mount -t debugfs debugfs /sys/kernel/debug > /dev/null 2>&1"); + } else { + ret = system("mount -t debugfs debugfs /sys/kernel/debug > /dev/null 2>&1"); + } + if (ret != 0) { + if (!auto_tune) { + fprintf(stderr, _("Failed to mount debugfs!\n")); + fprintf(stderr, _("exiting...\n")); + exit(EXIT_FAILURE); + } else { + fprintf(stderr, _("Failed to mount debugfs!\n")); + fprintf(stderr, _("Should still be able to auto tune...\n")); + } + } + } + + srand(time(NULL)); + + if (access("/var/cache/", W_OK) == 0) + mkdir("/var/cache/powertop", 0600); + else + mkdir("/data/local/powertop", 0600); + + load_results("saved_results.powertop"); + load_parameters("saved_parameters.powertop"); + + enumerate_cpus(); + create_all_devices(); + create_all_devfreq_devices(); + detect_power_meters(); + + register_parameter("base power", 100, 0.5); + register_parameter("cpu-wakeups", 39.5); + register_parameter("cpu-consumption", 1.56); + register_parameter("gpu-operations", 0.5576); + register_parameter("disk-operations-hard", 0.2); + register_parameter("disk-operations", 0.0); + register_parameter("xwakes", 0.1); + + load_parameters("saved_parameters.powertop"); + + initialized = 1; +} + +void clean_shutdown() +{ + close_results(); + clean_open_devices(); + clear_all_devices(); + clear_all_devfreq(); + clear_all_cpus(); + + return; +} + + +int main(int argc, char **argv) +{ + int option_index; + int c; + char filename[PATH_MAX]; + char workload[PATH_MAX] = {0}; + int iterations = 1, auto_tune = 0, sample_interval = 5; + + set_new_handler(out_of_memory); + + setlocale (LC_ALL, ""); + +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif + ui_notify_user = ui_notify_user_ncurses; + while (1) { /* parse commandline options */ + c = getopt_long(argc, argv, "cC::r::i:qt:w:Vh", long_options, &option_index); + /* Detect the end of the options. */ + if (c == -1) + break; + switch (c) { + case OPT_AUTO_TUNE: + auto_tune = 1; + leave_powertop = 1; + ui_notify_user = ui_notify_user_console; + break; + case 'c': + powertop_init(0); + calibrate(); + break; + case 'C': /* csv report */ + reporttype = REPORT_CSV; + snprintf(filename, sizeof(filename), "%s", optarg ? optarg : "powertop.csv"); + if (!strlen(filename)) + { + fprintf(stderr, _("Invalid CSV filename\n")); + exit(1); + } + break; + case OPT_DEBUG: + /* implemented using getopt_long(3) flag */ + break; + case OPT_EXTECH: /* Extech power analyzer support */ + checkroot(); + extech_power_meter(optarg ? optarg : "/dev/ttyUSB0"); + break; + case 'r': /* html report */ + reporttype = REPORT_HTML; + snprintf(filename, sizeof(filename), "%s", optarg ? optarg : "powertop.html"); + if (!strlen(filename)) + { + fprintf(stderr, _("Invalid HTML filename\n")); + exit(1); + } + break; + case 'i': + iterations = (optarg ? atoi(optarg) : 1); + break; + case 'q': + if (freopen("/dev/null", "a", stderr)) + fprintf(stderr, _("Quiet mode failed!\n")); + break; + case 's': + sample_interval = (optarg ? atoi(optarg) : 5); + break; + case 't': + time_out = (optarg ? atoi(optarg) : 20); + break; + case 'w': /* measure workload */ + snprintf(workload, sizeof(workload), "%s", optarg ? optarg : ""); + break; + case 'V': + print_version(); + exit(0); + break; + case 'h': + print_usage(); + exit(0); + break; + case '?': /* Unknown option */ + /* getopt_long already printed an error message. */ + exit(1); + break; + } + } + + powertop_init(auto_tune); + + if (reporttype != REPORT_OFF) + make_report(time_out, workload, iterations, sample_interval, filename); + + if (debug_learning) + printf("Learning debugging enabled\n"); + + learn_parameters(250, 0); + save_parameters("saved_parameters.powertop"); + + + if (debug_learning) { + learn_parameters(1000, 1); + dump_parameter_bundle(); + end_pci_access(); + exit(0); + } + if (!auto_tune) + init_display(); + + initialize_devfreq(); + initialize_tuning(); + initialize_wakeup(); + /* first one is short to not let the user wait too long */ + one_measurement(1, sample_interval, NULL); + + if (!auto_tune) { + tuning_update_display(); + show_tab(0); + } else { + auto_toggle_tuning(); + } + + while (!leave_powertop) { + if (!auto_tune) + show_cur_tab(); + one_measurement(time_out, sample_interval, NULL); + learn_parameters(15, 0); + } + if (!auto_tune) + endwin(); + fprintf(stderr, "%s\n", _("Leaving PowerTOP")); + + end_process_data(); + clear_process_data(); + end_cpu_data(); + clear_cpu_data(); + + save_all_results("saved_results.powertop"); + save_parameters("saved_parameters.powertop"); + learn_parameters(500, 0); + save_parameters("saved_parameters.powertop"); + end_pci_access(); + clear_tuning(); + reset_display(); + + clean_shutdown(); + + return 0; +} |