162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Ideas taken over from the perf userspace tool (included in the Linus 662306a36Sopenharmony_ci * kernel git repo): subcommand builtins and param parsing. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <unistd.h> 1362306a36Sopenharmony_ci#include <errno.h> 1462306a36Sopenharmony_ci#include <sched.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <sys/stat.h> 1762306a36Sopenharmony_ci#include <sys/utsname.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "builtin.h" 2062306a36Sopenharmony_ci#include "helpers/helpers.h" 2162306a36Sopenharmony_ci#include "helpers/bitmask.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int cmd_help(int argc, const char **argv); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Global cpu_info object available for all binaries 2862306a36Sopenharmony_ci * Info only retrieved from CPU 0 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Values will be zero/unknown on non X86 archs 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistruct cpupower_cpu_info cpupower_cpu_info; 3362306a36Sopenharmony_ciint run_as_root; 3462306a36Sopenharmony_ciint base_cpu; 3562306a36Sopenharmony_ci/* Affected cpus chosen by -c/--cpu param */ 3662306a36Sopenharmony_cistruct bitmask *cpus_chosen; 3762306a36Sopenharmony_cistruct bitmask *online_cpus; 3862306a36Sopenharmony_cistruct bitmask *offline_cpus; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef DEBUG 4162306a36Sopenharmony_ciint be_verbose; 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void print_help(void); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct cmd_struct { 4762306a36Sopenharmony_ci const char *cmd; 4862306a36Sopenharmony_ci int (*main)(int, const char **); 4962306a36Sopenharmony_ci int needs_root; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct cmd_struct commands[] = { 5362306a36Sopenharmony_ci { "frequency-info", cmd_freq_info, 0 }, 5462306a36Sopenharmony_ci { "frequency-set", cmd_freq_set, 1 }, 5562306a36Sopenharmony_ci { "idle-info", cmd_idle_info, 0 }, 5662306a36Sopenharmony_ci { "idle-set", cmd_idle_set, 1 }, 5762306a36Sopenharmony_ci { "powercap-info", cmd_cap_info, 0 }, 5862306a36Sopenharmony_ci { "set", cmd_set, 1 }, 5962306a36Sopenharmony_ci { "info", cmd_info, 0 }, 6062306a36Sopenharmony_ci { "monitor", cmd_monitor, 0 }, 6162306a36Sopenharmony_ci { "help", cmd_help, 0 }, 6262306a36Sopenharmony_ci /* { "bench", cmd_bench, 1 }, */ 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void print_help(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci unsigned int i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#ifdef DEBUG 7062306a36Sopenharmony_ci printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n")); 7162306a36Sopenharmony_ci#else 7262306a36Sopenharmony_ci printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n")); 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci printf(_("Supported commands are:\n")); 7562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(commands); i++) 7662306a36Sopenharmony_ci printf("\t%s\n", commands[i].cmd); 7762306a36Sopenharmony_ci printf(_("\nNot all commands can make use of the -c cpulist option.\n")); 7862306a36Sopenharmony_ci printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n")); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int print_man_page(const char *subpage) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int len; 8462306a36Sopenharmony_ci char *page; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci len = 10; /* enough for "cpupower-" */ 8762306a36Sopenharmony_ci if (subpage != NULL) 8862306a36Sopenharmony_ci len += strlen(subpage); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci page = malloc(len); 9162306a36Sopenharmony_ci if (!page) 9262306a36Sopenharmony_ci return -ENOMEM; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sprintf(page, "cpupower"); 9562306a36Sopenharmony_ci if ((subpage != NULL) && strcmp(subpage, "help")) { 9662306a36Sopenharmony_ci strcat(page, "-"); 9762306a36Sopenharmony_ci strcat(page, subpage); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci execlp("man", "man", page, NULL); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* should not be reached */ 10362306a36Sopenharmony_ci return -EINVAL; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int cmd_help(int argc, const char **argv) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci if (argc > 1) { 10962306a36Sopenharmony_ci print_man_page(argv[1]); /* exits within execlp() */ 11062306a36Sopenharmony_ci return EXIT_FAILURE; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci print_help(); 11462306a36Sopenharmony_ci return EXIT_SUCCESS; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void print_version(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci printf(PACKAGE " " VERSION "\n"); 12062306a36Sopenharmony_ci printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void handle_options(int *argc, const char ***argv) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int ret, x, new_argc = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (*argc < 1) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (x = 0; x < *argc && ((*argv)[x])[0] == '-'; x++) { 13162306a36Sopenharmony_ci const char *param = (*argv)[x]; 13262306a36Sopenharmony_ci if (!strcmp(param, "-h") || !strcmp(param, "--help")) { 13362306a36Sopenharmony_ci print_help(); 13462306a36Sopenharmony_ci exit(EXIT_SUCCESS); 13562306a36Sopenharmony_ci } else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) { 13662306a36Sopenharmony_ci if (*argc < 2) { 13762306a36Sopenharmony_ci print_help(); 13862306a36Sopenharmony_ci exit(EXIT_FAILURE); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci if (!strcmp((*argv)[x+1], "all")) 14162306a36Sopenharmony_ci bitmask_setall(cpus_chosen); 14262306a36Sopenharmony_ci else { 14362306a36Sopenharmony_ci ret = bitmask_parselist( 14462306a36Sopenharmony_ci (*argv)[x+1], cpus_chosen); 14562306a36Sopenharmony_ci if (ret < 0) { 14662306a36Sopenharmony_ci fprintf(stderr, _("Error parsing cpu " 14762306a36Sopenharmony_ci "list\n")); 14862306a36Sopenharmony_ci exit(EXIT_FAILURE); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci x += 1; 15262306a36Sopenharmony_ci /* Cut out param: cpupower -c 1 info -> cpupower info */ 15362306a36Sopenharmony_ci new_argc += 2; 15462306a36Sopenharmony_ci continue; 15562306a36Sopenharmony_ci } else if (!strcmp(param, "-v") || 15662306a36Sopenharmony_ci !strcmp(param, "--version")) { 15762306a36Sopenharmony_ci print_version(); 15862306a36Sopenharmony_ci exit(EXIT_SUCCESS); 15962306a36Sopenharmony_ci#ifdef DEBUG 16062306a36Sopenharmony_ci } else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) { 16162306a36Sopenharmony_ci be_verbose = 1; 16262306a36Sopenharmony_ci new_argc++; 16362306a36Sopenharmony_ci continue; 16462306a36Sopenharmony_ci#endif 16562306a36Sopenharmony_ci } else { 16662306a36Sopenharmony_ci fprintf(stderr, "Unknown option: %s\n", param); 16762306a36Sopenharmony_ci print_help(); 16862306a36Sopenharmony_ci exit(EXIT_FAILURE); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci *argc -= new_argc; 17262306a36Sopenharmony_ci *argv += new_argc; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciint main(int argc, const char *argv[]) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci const char *cmd; 17862306a36Sopenharmony_ci unsigned int i, ret; 17962306a36Sopenharmony_ci struct stat statbuf; 18062306a36Sopenharmony_ci struct utsname uts; 18162306a36Sopenharmony_ci char pathname[32]; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); 18462306a36Sopenharmony_ci online_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); 18562306a36Sopenharmony_ci offline_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci argc--; 18862306a36Sopenharmony_ci argv += 1; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci handle_options(&argc, &argv); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci cmd = argv[0]; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (argc < 1) { 19562306a36Sopenharmony_ci print_help(); 19662306a36Sopenharmony_ci return EXIT_FAILURE; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci setlocale(LC_ALL, ""); 20062306a36Sopenharmony_ci textdomain(PACKAGE); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Turn "perf cmd --help" into "perf help cmd" */ 20362306a36Sopenharmony_ci if (argc > 1 && !strcmp(argv[1], "--help")) { 20462306a36Sopenharmony_ci argv[1] = argv[0]; 20562306a36Sopenharmony_ci argv[0] = cmd = "help"; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci base_cpu = sched_getcpu(); 20962306a36Sopenharmony_ci if (base_cpu < 0) { 21062306a36Sopenharmony_ci fprintf(stderr, _("No valid cpus found.\n")); 21162306a36Sopenharmony_ci return EXIT_FAILURE; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci get_cpu_info(&cpupower_cpu_info); 21562306a36Sopenharmony_ci run_as_root = !geteuid(); 21662306a36Sopenharmony_ci if (run_as_root) { 21762306a36Sopenharmony_ci ret = uname(&uts); 21862306a36Sopenharmony_ci sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); 21962306a36Sopenharmony_ci if (!ret && !strcmp(uts.machine, "x86_64") && 22062306a36Sopenharmony_ci stat(pathname, &statbuf) != 0) { 22162306a36Sopenharmony_ci if (system("modprobe msr") == -1) 22262306a36Sopenharmony_ci fprintf(stderr, _("MSR access not available.\n")); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(commands); i++) { 22762306a36Sopenharmony_ci struct cmd_struct *p = commands + i; 22862306a36Sopenharmony_ci if (strcmp(p->cmd, cmd)) 22962306a36Sopenharmony_ci continue; 23062306a36Sopenharmony_ci if (!run_as_root && p->needs_root) { 23162306a36Sopenharmony_ci fprintf(stderr, _("Subcommand %s needs root " 23262306a36Sopenharmony_ci "privileges\n"), cmd); 23362306a36Sopenharmony_ci return EXIT_FAILURE; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci ret = p->main(argc, argv); 23662306a36Sopenharmony_ci if (cpus_chosen) 23762306a36Sopenharmony_ci bitmask_free(cpus_chosen); 23862306a36Sopenharmony_ci if (online_cpus) 23962306a36Sopenharmony_ci bitmask_free(online_cpus); 24062306a36Sopenharmony_ci if (offline_cpus) 24162306a36Sopenharmony_ci bitmask_free(offline_cpus); 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci print_help(); 24562306a36Sopenharmony_ci return EXIT_FAILURE; 24662306a36Sopenharmony_ci} 247