linux/tools/perf/perf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * perf.c
   4 *
   5 * Performance analysis utility.
   6 *
   7 * This is the main hub from which the sub-commands (perf stat,
   8 * perf top, perf record, perf report, etc.) are started.
   9 */
  10#include "builtin.h"
  11
  12#include "util/env.h"
  13#include <subcmd/exec-cmd.h>
  14#include "util/config.h"
  15#include "util/quote.h"
  16#include <subcmd/run-command.h>
  17#include "util/parse-events.h"
  18#include <subcmd/parse-options.h>
  19#include "util/bpf-loader.h"
  20#include "util/debug.h"
  21#include "util/event.h"
  22#include <api/fs/fs.h>
  23#include <api/fs/tracing_path.h>
  24#include <errno.h>
  25#include <pthread.h>
  26#include <signal.h>
  27#include <stdlib.h>
  28#include <time.h>
  29#include <sys/types.h>
  30#include <sys/stat.h>
  31#include <unistd.h>
  32#include <linux/kernel.h>
  33
  34const char perf_usage_string[] =
  35        "perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
  36
  37const char perf_more_info_string[] =
  38        "See 'perf help COMMAND' for more information on a specific command.";
  39
  40static int use_pager = -1;
  41const char *input_name;
  42
  43struct cmd_struct {
  44        const char *cmd;
  45        int (*fn)(int, const char **);
  46        int option;
  47};
  48
  49static struct cmd_struct commands[] = {
  50        { "buildid-cache", cmd_buildid_cache, 0 },
  51        { "buildid-list", cmd_buildid_list, 0 },
  52        { "config",     cmd_config,     0 },
  53        { "c2c",        cmd_c2c,        0 },
  54        { "diff",       cmd_diff,       0 },
  55        { "evlist",     cmd_evlist,     0 },
  56        { "help",       cmd_help,       0 },
  57        { "kallsyms",   cmd_kallsyms,   0 },
  58        { "list",       cmd_list,       0 },
  59        { "record",     cmd_record,     0 },
  60        { "report",     cmd_report,     0 },
  61        { "bench",      cmd_bench,      0 },
  62        { "stat",       cmd_stat,       0 },
  63        { "timechart",  cmd_timechart,  0 },
  64        { "top",        cmd_top,        0 },
  65        { "annotate",   cmd_annotate,   0 },
  66        { "version",    cmd_version,    0 },
  67        { "script",     cmd_script,     0 },
  68        { "sched",      cmd_sched,      0 },
  69#ifdef HAVE_LIBELF_SUPPORT
  70        { "probe",      cmd_probe,      0 },
  71#endif
  72        { "kmem",       cmd_kmem,       0 },
  73        { "lock",       cmd_lock,       0 },
  74        { "kvm",        cmd_kvm,        0 },
  75        { "test",       cmd_test,       0 },
  76#if defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT)
  77        { "trace",      cmd_trace,      0 },
  78#endif
  79        { "inject",     cmd_inject,     0 },
  80        { "mem",        cmd_mem,        0 },
  81        { "data",       cmd_data,       0 },
  82        { "ftrace",     cmd_ftrace,     0 },
  83};
  84
  85struct pager_config {
  86        const char *cmd;
  87        int val;
  88};
  89
  90static int pager_command_config(const char *var, const char *value, void *data)
  91{
  92        struct pager_config *c = data;
  93        if (strstarts(var, "pager.") && !strcmp(var + 6, c->cmd))
  94                c->val = perf_config_bool(var, value);
  95        return 0;
  96}
  97
  98/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
  99static int check_pager_config(const char *cmd)
 100{
 101        int err;
 102        struct pager_config c;
 103        c.cmd = cmd;
 104        c.val = -1;
 105        err = perf_config(pager_command_config, &c);
 106        return err ?: c.val;
 107}
 108
 109static int browser_command_config(const char *var, const char *value, void *data)
 110{
 111        struct pager_config *c = data;
 112        if (strstarts(var, "tui.") && !strcmp(var + 4, c->cmd))
 113                c->val = perf_config_bool(var, value);
 114        if (strstarts(var, "gtk.") && !strcmp(var + 4, c->cmd))
 115                c->val = perf_config_bool(var, value) ? 2 : 0;
 116        return 0;
 117}
 118
 119/*
 120 * returns 0 for "no tui", 1 for "use tui", 2 for "use gtk",
 121 * and -1 for "not specified"
 122 */
 123static int check_browser_config(const char *cmd)
 124{
 125        int err;
 126        struct pager_config c;
 127        c.cmd = cmd;
 128        c.val = -1;
 129        err = perf_config(browser_command_config, &c);
 130        return err ?: c.val;
 131}
 132
 133static void commit_pager_choice(void)
 134{
 135        switch (use_pager) {
 136        case 0:
 137                setenv(PERF_PAGER_ENVIRONMENT, "cat", 1);
 138                break;
 139        case 1:
 140                /* setup_pager(); */
 141                break;
 142        default:
 143                break;
 144        }
 145}
 146
 147struct option options[] = {
 148        OPT_ARGUMENT("help", "help"),
 149        OPT_ARGUMENT("version", "version"),
 150        OPT_ARGUMENT("exec-path", "exec-path"),
 151        OPT_ARGUMENT("html-path", "html-path"),
 152        OPT_ARGUMENT("paginate", "paginate"),
 153        OPT_ARGUMENT("no-pager", "no-pager"),
 154        OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
 155        OPT_ARGUMENT("buildid-dir", "buildid-dir"),
 156        OPT_ARGUMENT("list-cmds", "list-cmds"),
 157        OPT_ARGUMENT("list-opts", "list-opts"),
 158        OPT_ARGUMENT("debug", "debug"),
 159        OPT_END()
 160};
 161
 162static int handle_options(const char ***argv, int *argc, int *envchanged)
 163{
 164        int handled = 0;
 165
 166        while (*argc > 0) {
 167                const char *cmd = (*argv)[0];
 168                if (cmd[0] != '-')
 169                        break;
 170
 171                /*
 172                 * For legacy reasons, the "version" and "help"
 173                 * commands can be written with "--" prepended
 174                 * to make them look like flags.
 175                 */
 176                if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
 177                        break;
 178
 179                /*
 180                 * Shortcut for '-h' and '-v' options to invoke help
 181                 * and version command.
 182                 */
 183                if (!strcmp(cmd, "-h")) {
 184                        (*argv)[0] = "--help";
 185                        break;
 186                }
 187
 188                if (!strcmp(cmd, "-v")) {
 189                        (*argv)[0] = "--version";
 190                        break;
 191                }
 192
 193                if (!strcmp(cmd, "-vv")) {
 194                        (*argv)[0] = "version";
 195                        version_verbose = 1;
 196                        break;
 197                }
 198
 199                /*
 200                 * Check remaining flags.
 201                 */
 202                if (strstarts(cmd, CMD_EXEC_PATH)) {
 203                        cmd += strlen(CMD_EXEC_PATH);
 204                        if (*cmd == '=')
 205                                set_argv_exec_path(cmd + 1);
 206                        else {
 207                                puts(get_argv_exec_path());
 208                                exit(0);
 209                        }
 210                } else if (!strcmp(cmd, "--html-path")) {
 211                        puts(system_path(PERF_HTML_PATH));
 212                        exit(0);
 213                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
 214                        use_pager = 1;
 215                } else if (!strcmp(cmd, "--no-pager")) {
 216                        use_pager = 0;
 217                        if (envchanged)
 218                                *envchanged = 1;
 219                } else if (!strcmp(cmd, "--debugfs-dir")) {
 220                        if (*argc < 2) {
 221                                fprintf(stderr, "No directory given for --debugfs-dir.\n");
 222                                usage(perf_usage_string);
 223                        }
 224                        tracing_path_set((*argv)[1]);
 225                        if (envchanged)
 226                                *envchanged = 1;
 227                        (*argv)++;
 228                        (*argc)--;
 229                } else if (!strcmp(cmd, "--buildid-dir")) {
 230                        if (*argc < 2) {
 231                                fprintf(stderr, "No directory given for --buildid-dir.\n");
 232                                usage(perf_usage_string);
 233                        }
 234                        set_buildid_dir((*argv)[1]);
 235                        if (envchanged)
 236                                *envchanged = 1;
 237                        (*argv)++;
 238                        (*argc)--;
 239                } else if (strstarts(cmd, CMD_DEBUGFS_DIR)) {
 240                        tracing_path_set(cmd + strlen(CMD_DEBUGFS_DIR));
 241                        fprintf(stderr, "dir: %s\n", tracing_path);
 242                        if (envchanged)
 243                                *envchanged = 1;
 244                } else if (!strcmp(cmd, "--list-cmds")) {
 245                        unsigned int i;
 246
 247                        for (i = 0; i < ARRAY_SIZE(commands); i++) {
 248                                struct cmd_struct *p = commands+i;
 249                                printf("%s ", p->cmd);
 250                        }
 251                        putchar('\n');
 252                        exit(0);
 253                } else if (!strcmp(cmd, "--list-opts")) {
 254                        unsigned int i;
 255
 256                        for (i = 0; i < ARRAY_SIZE(options)-1; i++) {
 257                                struct option *p = options+i;
 258                                printf("--%s ", p->long_name);
 259                        }
 260                        putchar('\n');
 261                        exit(0);
 262                } else if (!strcmp(cmd, "--debug")) {
 263                        if (*argc < 2) {
 264                                fprintf(stderr, "No variable specified for --debug.\n");
 265                                usage(perf_usage_string);
 266                        }
 267                        if (perf_debug_option((*argv)[1]))
 268                                usage(perf_usage_string);
 269
 270                        (*argv)++;
 271                        (*argc)--;
 272                } else {
 273                        fprintf(stderr, "Unknown option: %s\n", cmd);
 274                        usage(perf_usage_string);
 275                }
 276
 277                (*argv)++;
 278                (*argc)--;
 279                handled++;
 280        }
 281        return handled;
 282}
 283
 284#define RUN_SETUP       (1<<0)
 285#define USE_PAGER       (1<<1)
 286
 287static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 288{
 289        int status;
 290        struct stat st;
 291        char sbuf[STRERR_BUFSIZE];
 292
 293        if (use_browser == -1)
 294                use_browser = check_browser_config(p->cmd);
 295
 296        if (use_pager == -1 && p->option & RUN_SETUP)
 297                use_pager = check_pager_config(p->cmd);
 298        if (use_pager == -1 && p->option & USE_PAGER)
 299                use_pager = 1;
 300        commit_pager_choice();
 301
 302        perf_env__set_cmdline(&perf_env, argc, argv);
 303        status = p->fn(argc, argv);
 304        perf_config__exit();
 305        exit_browser(status);
 306        perf_env__exit(&perf_env);
 307        bpf__clear();
 308
 309        if (status)
 310                return status & 0xff;
 311
 312        /* Somebody closed stdout? */
 313        if (fstat(fileno(stdout), &st))
 314                return 0;
 315        /* Ignore write errors for pipes and sockets.. */
 316        if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
 317                return 0;
 318
 319        status = 1;
 320        /* Check for ENOSPC and EIO errors.. */
 321        if (fflush(stdout)) {
 322                fprintf(stderr, "write failure on standard output: %s",
 323                        str_error_r(errno, sbuf, sizeof(sbuf)));
 324                goto out;
 325        }
 326        if (ferror(stdout)) {
 327                fprintf(stderr, "unknown write failure on standard output");
 328                goto out;
 329        }
 330        if (fclose(stdout)) {
 331                fprintf(stderr, "close failed on standard output: %s",
 332                        str_error_r(errno, sbuf, sizeof(sbuf)));
 333                goto out;
 334        }
 335        status = 0;
 336out:
 337        return status;
 338}
 339
 340static void handle_internal_command(int argc, const char **argv)
 341{
 342        const char *cmd = argv[0];
 343        unsigned int i;
 344
 345        /* Turn "perf cmd --help" into "perf help cmd" */
 346        if (argc > 1 && !strcmp(argv[1], "--help")) {
 347                argv[1] = argv[0];
 348                argv[0] = cmd = "help";
 349        }
 350
 351        for (i = 0; i < ARRAY_SIZE(commands); i++) {
 352                struct cmd_struct *p = commands+i;
 353                if (strcmp(p->cmd, cmd))
 354                        continue;
 355                exit(run_builtin(p, argc, argv));
 356        }
 357}
 358
 359static void execv_dashed_external(const char **argv)
 360{
 361        char *cmd;
 362        const char *tmp;
 363        int status;
 364
 365        if (asprintf(&cmd, "perf-%s", argv[0]) < 0)
 366                goto do_die;
 367
 368        /*
 369         * argv[0] must be the perf command, but the argv array
 370         * belongs to the caller, and may be reused in
 371         * subsequent loop iterations. Save argv[0] and
 372         * restore it on error.
 373         */
 374        tmp = argv[0];
 375        argv[0] = cmd;
 376
 377        /*
 378         * if we fail because the command is not found, it is
 379         * OK to return. Otherwise, we just pass along the status code.
 380         */
 381        status = run_command_v_opt(argv, 0);
 382        if (status != -ERR_RUN_COMMAND_EXEC) {
 383                if (IS_RUN_COMMAND_ERR(status)) {
 384do_die:
 385                        pr_err("FATAL: unable to run '%s'", argv[0]);
 386                        status = -128;
 387                }
 388                exit(-status);
 389        }
 390        errno = ENOENT; /* as if we called execvp */
 391
 392        argv[0] = tmp;
 393        zfree(&cmd);
 394}
 395
 396static int run_argv(int *argcp, const char ***argv)
 397{
 398        /* See if it's an internal command */
 399        handle_internal_command(*argcp, *argv);
 400
 401        /* .. then try the external ones */
 402        execv_dashed_external(*argv);
 403        return 0;
 404}
 405
 406static void pthread__block_sigwinch(void)
 407{
 408        sigset_t set;
 409
 410        sigemptyset(&set);
 411        sigaddset(&set, SIGWINCH);
 412        pthread_sigmask(SIG_BLOCK, &set, NULL);
 413}
 414
 415void pthread__unblock_sigwinch(void)
 416{
 417        sigset_t set;
 418
 419        sigemptyset(&set);
 420        sigaddset(&set, SIGWINCH);
 421        pthread_sigmask(SIG_UNBLOCK, &set, NULL);
 422}
 423
 424#ifdef _SC_LEVEL1_DCACHE_LINESIZE
 425#define cache_line_size(cacheline_sizep) *cacheline_sizep = sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
 426#else
 427static void cache_line_size(int *cacheline_sizep)
 428{
 429        if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep))
 430                pr_debug("cannot determine cache line size");
 431}
 432#endif
 433
 434int main(int argc, const char **argv)
 435{
 436        int err;
 437        const char *cmd;
 438        char sbuf[STRERR_BUFSIZE];
 439        int value;
 440
 441        /* libsubcmd init */
 442        exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT);
 443        pager_init(PERF_PAGER_ENVIRONMENT);
 444
 445        /* The page_size is placed in util object. */
 446        page_size = sysconf(_SC_PAGE_SIZE);
 447        cache_line_size(&cacheline_size);
 448
 449        if (sysctl__read_int("kernel/perf_event_max_stack", &value) == 0)
 450                sysctl_perf_event_max_stack = value;
 451
 452        if (sysctl__read_int("kernel/perf_event_max_contexts_per_stack", &value) == 0)
 453                sysctl_perf_event_max_contexts_per_stack = value;
 454
 455        cmd = extract_argv0_path(argv[0]);
 456        if (!cmd)
 457                cmd = "perf-help";
 458
 459        srandom(time(NULL));
 460
 461        perf_config__init();
 462        err = perf_config(perf_default_config, NULL);
 463        if (err)
 464                return err;
 465        set_buildid_dir(NULL);
 466
 467        /* get debugfs/tracefs mount point from /proc/mounts */
 468        tracing_path_mount();
 469
 470        /*
 471         * "perf-xxxx" is the same as "perf xxxx", but we obviously:
 472         *
 473         *  - cannot take flags in between the "perf" and the "xxxx".
 474         *  - cannot execute it externally (since it would just do
 475         *    the same thing over again)
 476         *
 477         * So we just directly call the internal command handler. If that one
 478         * fails to handle this, then maybe we just run a renamed perf binary
 479         * that contains a dash in its name. To handle this scenario, we just
 480         * fall through and ignore the "xxxx" part of the command string.
 481         */
 482        if (strstarts(cmd, "perf-")) {
 483                cmd += 5;
 484                argv[0] = cmd;
 485                handle_internal_command(argc, argv);
 486                /*
 487                 * If the command is handled, the above function does not
 488                 * return undo changes and fall through in such a case.
 489                 */
 490                cmd -= 5;
 491                argv[0] = cmd;
 492        }
 493        if (strstarts(cmd, "trace")) {
 494#if defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT)
 495                setup_path();
 496                argv[0] = "trace";
 497                return cmd_trace(argc, argv);
 498#else
 499                fprintf(stderr,
 500                        "trace command not available: missing audit-libs devel package at build time.\n");
 501                goto out;
 502#endif
 503        }
 504        /* Look for flags.. */
 505        argv++;
 506        argc--;
 507        handle_options(&argv, &argc, NULL);
 508        commit_pager_choice();
 509
 510        if (argc > 0) {
 511                if (strstarts(argv[0], "--"))
 512                        argv[0] += 2;
 513        } else {
 514                /* The user didn't specify a command; give them help */
 515                printf("\n usage: %s\n\n", perf_usage_string);
 516                list_common_cmds_help();
 517                printf("\n %s\n\n", perf_more_info_string);
 518                goto out;
 519        }
 520        cmd = argv[0];
 521
 522        test_attr__init();
 523
 524        /*
 525         * We use PATH to find perf commands, but we prepend some higher
 526         * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
 527         * environment, and the $(perfexecdir) from the Makefile at build
 528         * time.
 529         */
 530        setup_path();
 531        /*
 532         * Block SIGWINCH notifications so that the thread that wants it can
 533         * unblock and get syscalls like select interrupted instead of waiting
 534         * forever while the signal goes to some other non interested thread.
 535         */
 536        pthread__block_sigwinch();
 537
 538        perf_debug_setup();
 539
 540        while (1) {
 541                static int done_help;
 542
 543                run_argv(&argc, &argv);
 544
 545                if (errno != ENOENT)
 546                        break;
 547
 548                if (!done_help) {
 549                        cmd = argv[0] = help_unknown_cmd(cmd);
 550                        done_help = 1;
 551                } else
 552                        break;
 553        }
 554
 555        fprintf(stderr, "Failed to run command '%s': %s\n",
 556                cmd, str_error_r(errno, sbuf, sizeof(sbuf)));
 557out:
 558        return 1;
 559}
 560