linux/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
<<
>>
Prefs
   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 <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"
  270
  28};
  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 int 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 spaces
  74 *to make its length atleast n+1
  75 */
  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 6
  95void 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                                else
 180                                        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                                else
 187                                        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 example
 198         * AMD HW counters partly sit in PCI config space.
 199         * It's up to the monitor plug-in to check .is_online, this one
 200         * 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        } else
 207                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 monitors
 214 * are picked out and available monitors array is overridden
 215 * with matching ones
 216 *
 217 * Monitors get sorted in the same order the user passes them
 218*/
 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 fork
 442         */
 443        if (argc - optind)
 444                fork_it(argv + optind);
 445        else
 446                do_interval_measure(interval);
 447
 448        /* ToDo: Topology parsing needs fixing first to do
 449           this more generically */
 450        if (cpu_top.pkgs > 1)
 451                print_header(3);
 452        else
 453                print_header(1);
 454
 455        for (cpu = 0; cpu < cpu_count; cpu++) {
 456                if (cpu_top.pkgs > 1)
 457                        print_results(3, cpu);
 458                else
 459                        print_results(1, cpu);
 460        }
 461
 462        for (num = 0; num < avail_monitors; num++)
 463                monitors[num]->unregister();
 464
 465        cpu_topology_release(cpu_top);
 466        return 0;
 467}
 468