linux/tools/perf/perf.c
<<
>>
Prefs
   1/*
   2 * perf.c
   3 *
   4 * Performance analysis utility.
   5 *
   6 * This is the main hub from which the sub-commands (perf stat,
   7 * perf top, perf record, perf report, etc.) are started.
   8 */
   9#include "builtin.h"
  10
  11#include "util/exec_cmd.h"
  12#include "util/cache.h"
  13#include "util/quote.h"
  14#include "util/run-command.h"
  15#include "util/parse-events.h"
  16#include "util/string.h"
  17
  18const char perf_usage_string[] =
  19        "perf [--version] [--help] COMMAND [ARGS]";
  20
  21const char perf_more_info_string[] =
  22        "See 'perf help COMMAND' for more information on a specific command.";
  23
  24static int use_pager = -1;
  25struct pager_config {
  26        const char *cmd;
  27        int val;
  28};
  29
  30static char debugfs_mntpt[MAXPATHLEN];
  31
  32static int pager_command_config(const char *var, const char *value, void *data)
  33{
  34        struct pager_config *c = data;
  35        if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
  36                c->val = perf_config_bool(var, value);
  37        return 0;
  38}
  39
  40/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
  41int check_pager_config(const char *cmd)
  42{
  43        struct pager_config c;
  44        c.cmd = cmd;
  45        c.val = -1;
  46        perf_config(pager_command_config, &c);
  47        return c.val;
  48}
  49
  50static void commit_pager_choice(void) {
  51        switch (use_pager) {
  52        case 0:
  53                setenv("PERF_PAGER", "cat", 1);
  54                break;
  55        case 1:
  56                /* setup_pager(); */
  57                break;
  58        default:
  59                break;
  60        }
  61}
  62
  63static void set_debugfs_path(void)
  64{
  65        char *path;
  66
  67        path = getenv(PERF_DEBUGFS_ENVIRONMENT);
  68        snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
  69                 "tracing/events");
  70}
  71
  72static int handle_options(const char*** argv, int* argc, int* envchanged)
  73{
  74        int handled = 0;
  75
  76        while (*argc > 0) {
  77                const char *cmd = (*argv)[0];
  78                if (cmd[0] != '-')
  79                        break;
  80
  81                /*
  82                 * For legacy reasons, the "version" and "help"
  83                 * commands can be written with "--" prepended
  84                 * to make them look like flags.
  85                 */
  86                if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
  87                        break;
  88
  89                /*
  90                 * Check remaining flags.
  91                 */
  92                if (!prefixcmp(cmd, "--exec-path")) {
  93                        cmd += 11;
  94                        if (*cmd == '=')
  95                                perf_set_argv_exec_path(cmd + 1);
  96                        else {
  97                                puts(perf_exec_path());
  98                                exit(0);
  99                        }
 100                } else if (!strcmp(cmd, "--html-path")) {
 101                        puts(system_path(PERF_HTML_PATH));
 102                        exit(0);
 103                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
 104                        use_pager = 1;
 105                } else if (!strcmp(cmd, "--no-pager")) {
 106                        use_pager = 0;
 107                        if (envchanged)
 108                                *envchanged = 1;
 109                } else if (!strcmp(cmd, "--perf-dir")) {
 110                        if (*argc < 2) {
 111                                fprintf(stderr, "No directory given for --perf-dir.\n" );
 112                                usage(perf_usage_string);
 113                        }
 114                        setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
 115                        if (envchanged)
 116                                *envchanged = 1;
 117                        (*argv)++;
 118                        (*argc)--;
 119                        handled++;
 120                } else if (!prefixcmp(cmd, "--perf-dir=")) {
 121                        setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1);
 122                        if (envchanged)
 123                                *envchanged = 1;
 124                } else if (!strcmp(cmd, "--work-tree")) {
 125                        if (*argc < 2) {
 126                                fprintf(stderr, "No directory given for --work-tree.\n" );
 127                                usage(perf_usage_string);
 128                        }
 129                        setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
 130                        if (envchanged)
 131                                *envchanged = 1;
 132                        (*argv)++;
 133                        (*argc)--;
 134                } else if (!prefixcmp(cmd, "--work-tree=")) {
 135                        setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
 136                        if (envchanged)
 137                                *envchanged = 1;
 138                } else if (!strcmp(cmd, "--debugfs-dir")) {
 139                        if (*argc < 2) {
 140                                fprintf(stderr, "No directory given for --debugfs-dir.\n");
 141                                usage(perf_usage_string);
 142                        }
 143                        strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
 144                        debugfs_mntpt[MAXPATHLEN - 1] = '\0';
 145                        if (envchanged)
 146                                *envchanged = 1;
 147                        (*argv)++;
 148                        (*argc)--;
 149                } else if (!prefixcmp(cmd, "--debugfs-dir=")) {
 150                        strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN);
 151                        debugfs_mntpt[MAXPATHLEN - 1] = '\0';
 152                        if (envchanged)
 153                                *envchanged = 1;
 154                } else {
 155                        fprintf(stderr, "Unknown option: %s\n", cmd);
 156                        usage(perf_usage_string);
 157                }
 158
 159                (*argv)++;
 160                (*argc)--;
 161                handled++;
 162        }
 163        return handled;
 164}
 165
 166static int handle_alias(int *argcp, const char ***argv)
 167{
 168        int envchanged = 0, ret = 0, saved_errno = errno;
 169        int count, option_count;
 170        const char** new_argv;
 171        const char *alias_command;
 172        char *alias_string;
 173
 174        alias_command = (*argv)[0];
 175        alias_string = alias_lookup(alias_command);
 176        if (alias_string) {
 177                if (alias_string[0] == '!') {
 178                        if (*argcp > 1) {
 179                                struct strbuf buf;
 180
 181                                strbuf_init(&buf, PATH_MAX);
 182                                strbuf_addstr(&buf, alias_string);
 183                                sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
 184                                free(alias_string);
 185                                alias_string = buf.buf;
 186                        }
 187                        ret = system(alias_string + 1);
 188                        if (ret >= 0 && WIFEXITED(ret) &&
 189                            WEXITSTATUS(ret) != 127)
 190                                exit(WEXITSTATUS(ret));
 191                        die("Failed to run '%s' when expanding alias '%s'",
 192                            alias_string + 1, alias_command);
 193                }
 194                count = split_cmdline(alias_string, &new_argv);
 195                if (count < 0)
 196                        die("Bad alias.%s string", alias_command);
 197                option_count = handle_options(&new_argv, &count, &envchanged);
 198                if (envchanged)
 199                        die("alias '%s' changes environment variables\n"
 200                                 "You can use '!perf' in the alias to do this.",
 201                                 alias_command);
 202                memmove(new_argv - option_count, new_argv,
 203                                count * sizeof(char *));
 204                new_argv -= option_count;
 205
 206                if (count < 1)
 207                        die("empty alias for %s", alias_command);
 208
 209                if (!strcmp(alias_command, new_argv[0]))
 210                        die("recursive alias: %s", alias_command);
 211
 212                new_argv = realloc(new_argv, sizeof(char*) *
 213                                    (count + *argcp + 1));
 214                /* insert after command name */
 215                memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
 216                new_argv[count+*argcp] = NULL;
 217
 218                *argv = new_argv;
 219                *argcp += count - 1;
 220
 221                ret = 1;
 222        }
 223
 224        errno = saved_errno;
 225
 226        return ret;
 227}
 228
 229const char perf_version_string[] = PERF_VERSION;
 230
 231#define RUN_SETUP       (1<<0)
 232#define USE_PAGER       (1<<1)
 233/*
 234 * require working tree to be present -- anything uses this needs
 235 * RUN_SETUP for reading from the configuration file.
 236 */
 237#define NEED_WORK_TREE  (1<<2)
 238
 239struct cmd_struct {
 240        const char *cmd;
 241        int (*fn)(int, const char **, const char *);
 242        int option;
 243};
 244
 245static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 246{
 247        int status;
 248        struct stat st;
 249        const char *prefix;
 250
 251        prefix = NULL;
 252        if (p->option & RUN_SETUP)
 253                prefix = NULL; /* setup_perf_directory(); */
 254
 255        if (use_pager == -1 && p->option & RUN_SETUP)
 256                use_pager = check_pager_config(p->cmd);
 257        if (use_pager == -1 && p->option & USE_PAGER)
 258                use_pager = 1;
 259        commit_pager_choice();
 260        set_debugfs_path();
 261
 262        status = p->fn(argc, argv, prefix);
 263        if (status)
 264                return status & 0xff;
 265
 266        /* Somebody closed stdout? */
 267        if (fstat(fileno(stdout), &st))
 268                return 0;
 269        /* Ignore write errors for pipes and sockets.. */
 270        if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
 271                return 0;
 272
 273        /* Check for ENOSPC and EIO errors.. */
 274        if (fflush(stdout))
 275                die("write failure on standard output: %s", strerror(errno));
 276        if (ferror(stdout))
 277                die("unknown write failure on standard output");
 278        if (fclose(stdout))
 279                die("close failed on standard output: %s", strerror(errno));
 280        return 0;
 281}
 282
 283static void handle_internal_command(int argc, const char **argv)
 284{
 285        const char *cmd = argv[0];
 286        static struct cmd_struct commands[] = {
 287                { "help", cmd_help, 0 },
 288                { "list", cmd_list, 0 },
 289                { "record", cmd_record, 0 },
 290                { "report", cmd_report, 0 },
 291                { "stat", cmd_stat, 0 },
 292                { "timechart", cmd_timechart, 0 },
 293                { "top", cmd_top, 0 },
 294                { "annotate", cmd_annotate, 0 },
 295                { "version", cmd_version, 0 },
 296                { "trace", cmd_trace, 0 },
 297                { "sched", cmd_sched, 0 },
 298        };
 299        unsigned int i;
 300        static const char ext[] = STRIP_EXTENSION;
 301
 302        if (sizeof(ext) > 1) {
 303                i = strlen(argv[0]) - strlen(ext);
 304                if (i > 0 && !strcmp(argv[0] + i, ext)) {
 305                        char *argv0 = strdup(argv[0]);
 306                        argv[0] = cmd = argv0;
 307                        argv0[i] = '\0';
 308                }
 309        }
 310
 311        /* Turn "perf cmd --help" into "perf help cmd" */
 312        if (argc > 1 && !strcmp(argv[1], "--help")) {
 313                argv[1] = argv[0];
 314                argv[0] = cmd = "help";
 315        }
 316
 317        for (i = 0; i < ARRAY_SIZE(commands); i++) {
 318                struct cmd_struct *p = commands+i;
 319                if (strcmp(p->cmd, cmd))
 320                        continue;
 321                exit(run_builtin(p, argc, argv));
 322        }
 323}
 324
 325static void execv_dashed_external(const char **argv)
 326{
 327        struct strbuf cmd = STRBUF_INIT;
 328        const char *tmp;
 329        int status;
 330
 331        strbuf_addf(&cmd, "perf-%s", argv[0]);
 332
 333        /*
 334         * argv[0] must be the perf command, but the argv array
 335         * belongs to the caller, and may be reused in
 336         * subsequent loop iterations. Save argv[0] and
 337         * restore it on error.
 338         */
 339        tmp = argv[0];
 340        argv[0] = cmd.buf;
 341
 342        /*
 343         * if we fail because the command is not found, it is
 344         * OK to return. Otherwise, we just pass along the status code.
 345         */
 346        status = run_command_v_opt(argv, 0);
 347        if (status != -ERR_RUN_COMMAND_EXEC) {
 348                if (IS_RUN_COMMAND_ERR(status))
 349                        die("unable to run '%s'", argv[0]);
 350                exit(-status);
 351        }
 352        errno = ENOENT; /* as if we called execvp */
 353
 354        argv[0] = tmp;
 355
 356        strbuf_release(&cmd);
 357}
 358
 359static int run_argv(int *argcp, const char ***argv)
 360{
 361        int done_alias = 0;
 362
 363        while (1) {
 364                /* See if it's an internal command */
 365                handle_internal_command(*argcp, *argv);
 366
 367                /* .. then try the external ones */
 368                execv_dashed_external(*argv);
 369
 370                /* It could be an alias -- this works around the insanity
 371                 * of overriding "perf log" with "perf show" by having
 372                 * alias.log = show
 373                 */
 374                if (done_alias || !handle_alias(argcp, argv))
 375                        break;
 376                done_alias = 1;
 377        }
 378
 379        return done_alias;
 380}
 381
 382/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
 383static void get_debugfs_mntpt(void)
 384{
 385        FILE *file;
 386        char fs_type[100];
 387        char debugfs[MAXPATHLEN];
 388
 389        /*
 390         * try the standard location
 391         */
 392        if (valid_debugfs_mount("/sys/kernel/debug/") == 0) {
 393                strcpy(debugfs_mntpt, "/sys/kernel/debug/");
 394                return;
 395        }
 396
 397        /*
 398         * try the sane location
 399         */
 400        if (valid_debugfs_mount("/debug/") == 0) {
 401                strcpy(debugfs_mntpt, "/debug/");
 402                return;
 403        }
 404
 405        /*
 406         * give up and parse /proc/mounts
 407         */
 408        file = fopen("/proc/mounts", "r");
 409        if (file == NULL)
 410                return;
 411
 412        while (fscanf(file, "%*s %"
 413                      STR(MAXPATHLEN)
 414                      "s %99s %*s %*d %*d\n",
 415                      debugfs, fs_type) == 2) {
 416                if (strcmp(fs_type, "debugfs") == 0)
 417                        break;
 418        }
 419        fclose(file);
 420        if (strcmp(fs_type, "debugfs") == 0) {
 421                strncpy(debugfs_mntpt, debugfs, MAXPATHLEN);
 422                debugfs_mntpt[MAXPATHLEN - 1] = '\0';
 423        }
 424}
 425
 426int main(int argc, const char **argv)
 427{
 428        const char *cmd;
 429
 430        cmd = perf_extract_argv0_path(argv[0]);
 431        if (!cmd)
 432                cmd = "perf-help";
 433        /* get debugfs mount point from /proc/mounts */
 434        get_debugfs_mntpt();
 435        /*
 436         * "perf-xxxx" is the same as "perf xxxx", but we obviously:
 437         *
 438         *  - cannot take flags in between the "perf" and the "xxxx".
 439         *  - cannot execute it externally (since it would just do
 440         *    the same thing over again)
 441         *
 442         * So we just directly call the internal command handler, and
 443         * die if that one cannot handle it.
 444         */
 445        if (!prefixcmp(cmd, "perf-")) {
 446                cmd += 5;
 447                argv[0] = cmd;
 448                handle_internal_command(argc, argv);
 449                die("cannot handle %s internally", cmd);
 450        }
 451
 452        /* Look for flags.. */
 453        argv++;
 454        argc--;
 455        handle_options(&argv, &argc, NULL);
 456        commit_pager_choice();
 457        set_debugfs_path();
 458        if (argc > 0) {
 459                if (!prefixcmp(argv[0], "--"))
 460                        argv[0] += 2;
 461        } else {
 462                /* The user didn't specify a command; give them help */
 463                printf("\n usage: %s\n\n", perf_usage_string);
 464                list_common_cmds_help();
 465                printf("\n %s\n\n", perf_more_info_string);
 466                exit(1);
 467        }
 468        cmd = argv[0];
 469
 470        /*
 471         * We use PATH to find perf commands, but we prepend some higher
 472         * precidence paths: the "--exec-path" option, the PERF_EXEC_PATH
 473         * environment, and the $(perfexecdir) from the Makefile at build
 474         * time.
 475         */
 476        setup_path();
 477
 478        while (1) {
 479                static int done_help = 0;
 480                static int was_alias = 0;
 481
 482                was_alias = run_argv(&argc, &argv);
 483                if (errno != ENOENT)
 484                        break;
 485
 486                if (was_alias) {
 487                        fprintf(stderr, "Expansion of alias '%s' failed; "
 488                                "'%s' is not a perf-command\n",
 489                                cmd, argv[0]);
 490                        exit(1);
 491                }
 492                if (!done_help) {
 493                        cmd = argv[0] = help_unknown_cmd(cmd);
 494                        done_help = 1;
 495                } else
 496                        break;
 497        }
 498
 499        fprintf(stderr, "Failed to run command '%s': %s\n",
 500                cmd, strerror(errno));
 501
 502        return 1;
 503}
 504