469 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.4 *5 * Output format inspired by Len Brown's <lenb@kernel.org> turbostat tool.6 */7 8 9#include <stdio.h>10#include <unistd.h>11#include <stdlib.h>12#include <string.h>13#include <time.h>14#include <signal.h>15#include <sys/types.h>16#include <sys/wait.h>17#include <libgen.h>18 19#include "idle_monitor/cpupower-monitor.h"20#include "idle_monitor/idle_monitors.h"21#include "helpers/helpers.h"22 23/* Define pointers to all monitors. */24#define DEF(x) & x ## _monitor ,25struct cpuidle_monitor *all_monitors[] = {26#include "idle_monitors.def"27028};29 30int cpu_count;31 32static struct cpuidle_monitor *monitors[MONITORS_MAX];33static unsigned int avail_monitors;34 35static char *progname;36 37enum operation_mode_e { list = 1, show, show_all };38static enum operation_mode_e mode;39static int interval = 1;40static char *show_monitors_param;41static struct cpupower_topology cpu_top;42static unsigned int wake_cpus;43 44/* ToDo: Document this in the manpage */45static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', };46 47static void print_wrong_arg_exit(void)48{49 printf(_("invalid or unknown argument\n"));50 exit(EXIT_FAILURE);51}52 53long long timespec_diff_us(struct timespec start, struct timespec end)54{55 struct timespec temp;56 if ((end.tv_nsec - start.tv_nsec) < 0) {57 temp.tv_sec = end.tv_sec - start.tv_sec - 1;58 temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;59 } else {60 temp.tv_sec = end.tv_sec - start.tv_sec;61 temp.tv_nsec = end.tv_nsec - start.tv_nsec;62 }63 return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000);64}65 66void print_n_spaces(int n)67{68 int x;69 for (x = 0; x < n; x++)70 printf(" ");71}72 73/*s is filled with left and right spaces74 *to make its length atleast n+175 */76int fill_string_with_spaces(char *s, int n)77{78 char *temp;79 int len = strlen(s);80 81 if (len >= n)82 return -1;83 84 temp = malloc(sizeof(char) * (n+1));85 for (; len < n; len++)86 s[len] = ' ';87 s[len] = '\0';88 snprintf(temp, n+1, " %s", s);89 strcpy(s, temp);90 free(temp);91 return 0;92}93 94#define MAX_COL_WIDTH 695void print_header(int topology_depth)96{97 int unsigned mon;98 int state, need_len;99 cstate_t s;100 char buf[128] = "";101 102 fill_string_with_spaces(buf, topology_depth * 5 - 1);103 printf("%s|", buf);104 105 for (mon = 0; mon < avail_monitors; mon++) {106 need_len = monitors[mon]->hw_states_num * (MAX_COL_WIDTH + 1)107 - 1;108 if (mon != 0)109 printf("||");110 sprintf(buf, "%s", monitors[mon]->name);111 fill_string_with_spaces(buf, need_len);112 printf("%s", buf);113 }114 printf("\n");115 116 if (topology_depth > 2)117 printf(" PKG|");118 if (topology_depth > 1)119 printf("CORE|");120 if (topology_depth > 0)121 printf(" CPU|");122 123 for (mon = 0; mon < avail_monitors; mon++) {124 if (mon != 0)125 printf("||");126 for (state = 0; state < monitors[mon]->hw_states_num; state++) {127 if (state != 0)128 printf("|");129 s = monitors[mon]->hw_states[state];130 sprintf(buf, "%s", s.name);131 fill_string_with_spaces(buf, MAX_COL_WIDTH);132 printf("%s", buf);133 }134 printf(" ");135 }136 printf("\n");137}138 139 140void print_results(int topology_depth, int cpu)141{142 unsigned int mon;143 int state, ret;144 double percent;145 unsigned long long result;146 cstate_t s;147 148 /* Be careful CPUs may got resorted for pkg value do not just use cpu */149 if (!bitmask_isbitset(cpus_chosen, cpu_top.core_info[cpu].cpu))150 return;151 if (!cpu_top.core_info[cpu].is_online &&152 cpu_top.core_info[cpu].pkg == -1)153 return;154 155 if (topology_depth > 2)156 printf("%4d|", cpu_top.core_info[cpu].pkg);157 if (topology_depth > 1)158 printf("%4d|", cpu_top.core_info[cpu].core);159 if (topology_depth > 0)160 printf("%4d|", cpu_top.core_info[cpu].cpu);161 162 for (mon = 0; mon < avail_monitors; mon++) {163 if (mon != 0)164 printf("||");165 166 for (state = 0; state < monitors[mon]->hw_states_num; state++) {167 if (state != 0)168 printf("|");169 170 s = monitors[mon]->hw_states[state];171 172 if (s.get_count_percent) {173 ret = s.get_count_percent(s.id, &percent,174 cpu_top.core_info[cpu].cpu);175 if (ret)176 printf("******");177 else if (percent >= 100.0)178 printf("%6.1f", percent);179 else180 printf("%6.2f", percent);181 } else if (s.get_count) {182 ret = s.get_count(s.id, &result,183 cpu_top.core_info[cpu].cpu);184 if (ret)185 printf("******");186 else187 printf("%6llu", result);188 } else {189 printf(_("Monitor %s, Counter %s has no count "190 "function. Implementation error\n"),191 monitors[mon]->name, s.name);192 exit(EXIT_FAILURE);193 }194 }195 }196 /*197 * The monitor could still provide useful data, for example198 * AMD HW counters partly sit in PCI config space.199 * It's up to the monitor plug-in to check .is_online, this one200 * is just for additional info.201 */202 if (!cpu_top.core_info[cpu].is_online &&203 cpu_top.core_info[cpu].pkg != -1) {204 printf(_(" *is offline\n"));205 return;206 } else207 printf("\n");208}209 210 211/* param: string passed by -m param (The list of monitors to show)212 *213 * Monitors must have been registered already, matching monitors214 * are picked out and available monitors array is overridden215 * with matching ones216 *217 * Monitors get sorted in the same order the user passes them218*/219 220static void parse_monitor_param(char *param)221{222 unsigned int num;223 int mon, hits = 0;224 char *tmp = param, *token;225 struct cpuidle_monitor *tmp_mons[MONITORS_MAX];226 227 228 for (mon = 0; mon < MONITORS_MAX; mon++, tmp = NULL) {229 token = strtok(tmp, ",");230 if (token == NULL)231 break;232 if (strlen(token) >= MONITOR_NAME_LEN) {233 printf(_("%s: max monitor name length"234 " (%d) exceeded\n"), token, MONITOR_NAME_LEN);235 continue;236 }237 238 for (num = 0; num < avail_monitors; num++) {239 if (!strcmp(monitors[num]->name, token)) {240 dprint("Found requested monitor: %s\n", token);241 tmp_mons[hits] = monitors[num];242 hits++;243 }244 }245 }246 if (hits == 0) {247 printf(_("No matching monitor found in %s, "248 "try -l option\n"), param);249 exit(EXIT_FAILURE);250 }251 /* Override detected/registerd monitors array with requested one */252 memcpy(monitors, tmp_mons,253 sizeof(struct cpuidle_monitor *) * MONITORS_MAX);254 avail_monitors = hits;255}256 257void list_monitors(void)258{259 unsigned int mon;260 int state;261 cstate_t s;262 263 for (mon = 0; mon < avail_monitors; mon++) {264 printf(_("Monitor \"%s\" (%d states) - Might overflow after %u "265 "s\n"),266 monitors[mon]->name, monitors[mon]->hw_states_num,267 monitors[mon]->overflow_s);268 269 for (state = 0; state < monitors[mon]->hw_states_num; state++) {270 s = monitors[mon]->hw_states[state];271 /*272 * ToDo show more state capabilities:273 * percent, time (granlarity)274 */275 printf("%s\t[%c] -> %s\n", s.name, range_abbr[s.range],276 gettext(s.desc));277 }278 }279}280 281int fork_it(char **argv)282{283 int status;284 unsigned int num;285 unsigned long long timediff;286 pid_t child_pid;287 struct timespec start, end;288 289 child_pid = fork();290 clock_gettime(CLOCK_REALTIME, &start);291 292 for (num = 0; num < avail_monitors; num++)293 monitors[num]->start();294 295 if (!child_pid) {296 /* child */297 execvp(argv[0], argv);298 } else {299 /* parent */300 if (child_pid == -1) {301 perror("fork");302 exit(1);303 }304 305 signal(SIGINT, SIG_IGN);306 signal(SIGQUIT, SIG_IGN);307 if (waitpid(child_pid, &status, 0) == -1) {308 perror("wait");309 exit(1);310 }311 }312 clock_gettime(CLOCK_REALTIME, &end);313 for (num = 0; num < avail_monitors; num++)314 monitors[num]->stop();315 316 timediff = timespec_diff_us(start, end);317 if (WIFEXITED(status))318 printf(_("%s took %.5f seconds and exited with status %d\n"),319 argv[0], timediff / (1000.0 * 1000),320 WEXITSTATUS(status));321 return 0;322}323 324int do_interval_measure(int i)325{326 unsigned int num;327 int cpu;328 329 if (wake_cpus)330 for (cpu = 0; cpu < cpu_count; cpu++)331 bind_cpu(cpu);332 333 for (num = 0; num < avail_monitors; num++) {334 dprint("HW C-state residency monitor: %s - States: %d\n",335 monitors[num]->name, monitors[num]->hw_states_num);336 monitors[num]->start();337 }338 339 sleep(i);340 341 if (wake_cpus)342 for (cpu = 0; cpu < cpu_count; cpu++)343 bind_cpu(cpu);344 345 for (num = 0; num < avail_monitors; num++)346 monitors[num]->stop();347 348 349 return 0;350}351 352static void cmdline(int argc, char *argv[])353{354 int opt;355 progname = basename(argv[0]);356 357 while ((opt = getopt(argc, argv, "+lci:m:")) != -1) {358 switch (opt) {359 case 'l':360 if (mode)361 print_wrong_arg_exit();362 mode = list;363 break;364 case 'i':365 /* only allow -i with -m or no option */366 if (mode && mode != show)367 print_wrong_arg_exit();368 interval = atoi(optarg);369 break;370 case 'm':371 if (mode)372 print_wrong_arg_exit();373 mode = show;374 show_monitors_param = optarg;375 break;376 case 'c':377 wake_cpus = 1;378 break;379 default:380 print_wrong_arg_exit();381 }382 }383 if (!mode)384 mode = show_all;385}386 387int cmd_monitor(int argc, char **argv)388{389 unsigned int num;390 struct cpuidle_monitor *test_mon;391 int cpu;392 393 cmdline(argc, argv);394 cpu_count = get_cpu_topology(&cpu_top);395 if (cpu_count < 0) {396 printf(_("Cannot read number of available processors\n"));397 return EXIT_FAILURE;398 }399 400 if (!cpu_top.core_info[0].is_online)401 printf("WARNING: at least one cpu is offline\n");402 403 /* Default is: monitor all CPUs */404 if (bitmask_isallclear(cpus_chosen))405 bitmask_setall(cpus_chosen);406 407 dprint("System has up to %d CPU cores\n", cpu_count);408 409 for (num = 0; all_monitors[num]; num++) {410 dprint("Try to register: %s\n", all_monitors[num]->name);411 test_mon = all_monitors[num]->do_register();412 if (test_mon) {413 if (test_mon->flags.needs_root && !run_as_root) {414 fprintf(stderr, _("Available monitor %s needs "415 "root access\n"), test_mon->name);416 continue;417 }418 monitors[avail_monitors] = test_mon;419 dprint("%s registered\n", all_monitors[num]->name);420 avail_monitors++;421 }422 }423 424 if (avail_monitors == 0) {425 printf(_("No HW Cstate monitors found\n"));426 return 1;427 }428 429 if (mode == list) {430 list_monitors();431 exit(EXIT_SUCCESS);432 }433 434 if (mode == show)435 parse_monitor_param(show_monitors_param);436 437 dprint("Packages: %d - Cores: %d - CPUs: %d\n",438 cpu_top.pkgs, cpu_top.cores, cpu_count);439 440 /*441 * if any params left, it must be a command to fork442 */443 if (argc - optind)444 fork_it(argv + optind);445 else446 do_interval_measure(interval);447 448 /* ToDo: Topology parsing needs fixing first to do449 this more generically */450 if (cpu_top.pkgs > 1)451 print_header(3);452 else453 print_header(1);454 455 for (cpu = 0; cpu < cpu_count; cpu++) {456 if (cpu_top.pkgs > 1)457 print_results(3, cpu);458 else459 print_results(1, cpu);460 }461 462 for (num = 0; num < avail_monitors; num++) {463 if (monitors[num]->unregister)464 monitors[num]->unregister();465 }466 cpu_topology_release(cpu_top);467 return 0;468}469