Root/tools/perf/perf.c

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

Archive Download this file



interactive