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