Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581/* * perf.c * * Performance analysis utility. * * This is the main hub from which the sub-commands (perf stat, * perf top, perf record, perf report, etc.) are started. */#include "builtin.h"#include "perf.h"
#include "util/build-id.h"#include "util/cache.h"#include "util/env.h"#include <internal/lib.h> // page_size#include <subcmd/exec-cmd.h>#include "util/config.h"#include <subcmd/run-command.h>#include "util/parse-events.h"#include <subcmd/parse-options.h>#include <subcmd/help.h>#include "util/debug.h"#include "util/event.h"#include "util/util.h" // usage()#include "ui/ui.h"#include "perf-sys.h"#include <api/fs/fs.h>#include <api/fs/tracing_path.h>#include <perf/core.h>#include <errno.h>#include <pthread.h>#include <signal.h>#include <stdlib.h>#include <time.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/zalloc.h>
static int use_pager = -1;static FILE *debug_fp = NULL;
struct cmd_struct { const char *cmd; int (*fn)(int, const char **); int option;};
static const struct cmd_struct commands[] = { { "archive", NULL, 0 }, { "buildid-cache", cmd_buildid_cache, 0 }, { "buildid-list", cmd_buildid_list, 0 }, { "check", cmd_check, 0 }, { "config", cmd_config, 0 }, { "c2c", cmd_c2c, 0 }, { "diff", cmd_diff, 0 }, { "evlist", cmd_evlist, 0 }, { "help", cmd_help, 0 }, { "iostat", NULL, 0 }, { "kallsyms", cmd_kallsyms, 0 }, { "list", cmd_list, 0 }, { "record", cmd_record, 0 }, { "report", cmd_report, 0 }, { "bench", cmd_bench, 0 }, { "stat", cmd_stat, 0 },#ifdef HAVE_LIBTRACEEVENT { "timechart", cmd_timechart, 0 },#endif { "top", cmd_top, 0 }, { "annotate", cmd_annotate, 0 }, { "version", cmd_version, 0 }, { "script", cmd_script, 0 },#ifdef HAVE_LIBTRACEEVENT { "sched", cmd_sched, 0 },#endif#ifdef HAVE_LIBELF_SUPPORT { "probe", cmd_probe, 0 },#endif#ifdef HAVE_LIBTRACEEVENT { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 },#endif { "kvm", cmd_kvm, 0 }, { "test", cmd_test, 0 },#if defined(HAVE_LIBTRACEEVENT) { "trace", cmd_trace, 0 },#endif { "inject", cmd_inject, 0 }, { "mem", cmd_mem, 0 }, { "data", cmd_data, 0 }, { "ftrace", cmd_ftrace, 0 }, { "daemon", cmd_daemon, 0 },#ifdef HAVE_LIBTRACEEVENT { "kwork", cmd_kwork, 0 },#endif};
struct pager_config { const char *cmd; int val;};
static bool same_cmd_with_prefix(const char *var, struct pager_config *c, const char *header){ return (strstarts(var, header) && !strcmp(var + strlen(header), c->cmd));}
static int pager_command_config(const char *var, const char *value, void *data){ struct pager_config *c = data; if (same_cmd_with_prefix(var, c, "pager.")) c->val = perf_config_bool(var, value); return 0;}
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */static int check_pager_config(const char *cmd){ int err; struct pager_config c; c.cmd = cmd; c.val = -1; err = perf_config(pager_command_config, &c); return err ?: c.val;}
static int browser_command_config(const char *var, const char *value, void *data){ struct pager_config *c = data; if (same_cmd_with_prefix(var, c, "tui.")) c->val = perf_config_bool(var, value); if (same_cmd_with_prefix(var, c, "gtk.")) c->val = perf_config_bool(var, value) ? 2 : 0; return 0;}
/* * returns 0 for "no tui", 1 for "use tui", 2 for "use gtk", * and -1 for "not specified" */static int check_browser_config(const char *cmd){ int err; struct pager_config c; c.cmd = cmd; c.val = -1; err = perf_config(browser_command_config, &c); return err ?: c.val;}
static void commit_pager_choice(void){ switch (use_pager) { case 0: setenv(PERF_PAGER_ENVIRONMENT, "cat", 1); break; case 1: /* setup_pager(); */ break; default: break; }}
static int set_debug_file(const char *path){ debug_fp = fopen(path, "w"); if (!debug_fp) { fprintf(stderr, "Open debug file '%s' failed: %m\n", path); return -1; }
debug_set_file(debug_fp); return 0;}
static const struct option options[] = { OPT_ARGUMENT("help", "help"), OPT_ARGUMENT("version", "version"), OPT_ARGUMENT("exec-path", "exec-path"), OPT_ARGUMENT("html-path", "html-path"), OPT_ARGUMENT("paginate", "paginate"), OPT_ARGUMENT("no-pager", "no-pager"), OPT_ARGUMENT("debugfs-dir", "debugfs-dir"), OPT_ARGUMENT("buildid-dir", "buildid-dir"), OPT_ARGUMENT("list-cmds", "list-cmds"), OPT_ARGUMENT("list-opts", "list-opts"), OPT_ARGUMENT("debug", "debug"), OPT_ARGUMENT("debug-file", "debug-file"), OPT_END()};
static int handle_options(const char ***argv, int *argc, int *envchanged){ int handled = 0;
while (*argc > 0) { const char *cmd = (*argv)[0]; if (cmd[0] != '-') break;
/* * For legacy reasons, the "version" and "help" * commands can be written with "--" prepended * to make them look like flags. */ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version")) break;
/* * Shortcut for '-h' and '-v' options to invoke help * and version command. */ if (!strcmp(cmd, "-h")) { (*argv)[0] = "--help"; break; }
if (!strcmp(cmd, "-v")) { (*argv)[0] = "--version"; break; }
if (!strcmp(cmd, "-vv")) { (*argv)[0] = "version"; verbose = 1; break; }
/* * Check remaining flags. */ if (strstarts(cmd, CMD_EXEC_PATH)) { cmd += strlen(CMD_EXEC_PATH); if (*cmd == '=') set_argv_exec_path(cmd + 1); else { puts(get_argv_exec_path()); exit(0); } } else if (!strcmp(cmd, "--html-path")) { puts(system_path(PERF_HTML_PATH)); exit(0); } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { use_pager = 1; } else if (!strcmp(cmd, "--no-pager")) { use_pager = 0; if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--debugfs-dir")) { if (*argc < 2) { fprintf(stderr, "No directory given for --debugfs-dir.\n"); usage(perf_usage_string); } tracing_path_set((*argv)[1]); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (!strcmp(cmd, "--buildid-dir")) { if (*argc < 2) { fprintf(stderr, "No directory given for --buildid-dir.\n"); usage(perf_usage_string); } set_buildid_dir((*argv)[1]); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (strstarts(cmd, CMD_DEBUGFS_DIR)) { tracing_path_set(cmd + strlen(CMD_DEBUGFS_DIR)); fprintf(stderr, "dir: %s\n", tracing_path_mount()); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--list-cmds")) { unsigned int i;
for (i = 0; i < ARRAY_SIZE(commands); i++) { const struct cmd_struct *p = commands + i; printf("%s ", p->cmd); } putchar('\n'); exit(0); } else if (!strcmp(cmd, "--list-opts")) { unsigned int i;
for (i = 0; i < ARRAY_SIZE(options)-1; i++) { const struct option *p = options + i; printf("--%s ", p->long_name); } putchar('\n'); exit(0); } else if (!strcmp(cmd, "--debug")) { if (*argc < 2) { fprintf(stderr, "No variable specified for --debug.\n"); usage(perf_usage_string); } if (perf_debug_option((*argv)[1])) usage(perf_usage_string);
(*argv)++; (*argc)--; } else if (!strcmp(cmd, "--debug-file")) { if (*argc < 2) { fprintf(stderr, "No path given for --debug-file.\n"); usage(perf_usage_string); }
if (set_debug_file((*argv)[1])) usage(perf_usage_string);
(*argv)++; (*argc)--;
} else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(perf_usage_string); }
(*argv)++; (*argc)--; handled++; } return handled;}
#define RUN_SETUP (1<<0)#define USE_PAGER (1<<1)
static int run_builtin(const struct cmd_struct *p, int argc, const char **argv){ int status; struct stat st;
if (use_browser == -1) use_browser = check_browser_config(p->cmd);
if (use_pager == -1 && p->option & RUN_SETUP) use_pager = check_pager_config(p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; commit_pager_choice();
status = p->fn(argc, argv); perf_config__exit(); exit_browser(status);
if (status) return status & 0xff;
/* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; /* Ignore write errors for pipes and sockets.. */ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) return 0;
status = 1; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) { fprintf(stderr, "write failure on standard output: %m\n"); goto out; } if (ferror(stdout)) { fprintf(stderr, "unknown write failure on standard output\n"); goto out; } if (fclose(stdout)) { fprintf(stderr, "close failed on standard output: %m\n"); goto out; } status = 0;out: return status;}
static void handle_internal_command(int argc, const char **argv){ const char *cmd = argv[0]; unsigned int i;
/* Turn "perf cmd --help" into "perf help cmd" */ if (argc > 1 && !strcmp(argv[1], "--help")) { argv[1] = argv[0]; argv[0] = cmd = "help"; }
for (i = 0; i < ARRAY_SIZE(commands); i++) { const struct cmd_struct *p = commands+i; if (p->fn == NULL) continue; if (strcmp(p->cmd, cmd)) continue; exit(run_builtin(p, argc, argv)); }}
static void execv_dashed_external(const char **argv){ char *cmd; const char *tmp; int status;
if (asprintf(&cmd, "perf-%s", argv[0]) < 0) goto do_die;
/* * argv[0] must be the perf command, but the argv array * belongs to the caller, and may be reused in * subsequent loop iterations. Save argv[0] and * restore it on error. */ tmp = argv[0]; argv[0] = cmd;
/* * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ status = run_command_v_opt(argv, 0); if (status != -ERR_RUN_COMMAND_EXEC) { if (IS_RUN_COMMAND_ERR(status)) {do_die: pr_err("FATAL: unable to run '%s'", argv[0]); status = -128; } exit(-status); } errno = ENOENT; /* as if we called execvp */
argv[0] = tmp; zfree(&cmd);}
static int run_argv(int *argcp, const char ***argv){ /* See if it's an internal command */ handle_internal_command(*argcp, *argv);
/* .. then try the external ones */ execv_dashed_external(*argv); return 0;}
static int libperf_print(enum libperf_print_level level, const char *fmt, va_list ap){ return veprintf(level, verbose, fmt, ap);}
int main(int argc, const char **argv){ int err, done_help = 0; const char *cmd;
perf_debug_setup();
/* libsubcmd init */ exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT); pager_init(PERF_PAGER_ENVIRONMENT);
libperf_init(libperf_print);
cmd = extract_argv0_path(argv[0]); if (!cmd) cmd = "perf-help";
srandom(time(NULL));
/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ config_exclusive_filename = getenv("PERF_CONFIG");
err = perf_config(perf_default_config, NULL); if (err) return err; set_buildid_dir(NULL);
/* * "perf-xxxx" is the same as "perf xxxx", but we obviously: * * - cannot take flags in between the "perf" and the "xxxx". * - cannot execute it externally (since it would just do * the same thing over again) * * So we just directly call the internal command handler. If that one * fails to handle this, then maybe we just run a renamed perf binary * that contains a dash in its name. To handle this scenario, we just * fall through and ignore the "xxxx" part of the command string. */ if (strstarts(cmd, "perf-")) { cmd += 5; argv[0] = cmd; handle_internal_command(argc, argv); /* * If the command is handled, the above function does not * return undo changes and fall through in such a case. */ cmd -= 5; argv[0] = cmd; } if (strstarts(cmd, "trace")) {#ifndef HAVE_LIBTRACEEVENT fprintf(stderr, "trace command not available: missing libtraceevent devel package at build time.\n"); goto out;#else setup_path(); argv[0] = "trace"; return cmd_trace(argc, argv);#endif } /* Look for flags.. */ argv++; argc--; handle_options(&argv, &argc, NULL); commit_pager_choice();
if (argc > 0) { if (strstarts(argv[0], "--")) argv[0] += 2; } else { /* The user didn't specify a command; give them help */ printf("\n usage: %s\n\n", perf_usage_string); list_common_cmds_help(); printf("\n %s\n\n", perf_more_info_string); goto out; } cmd = argv[0];
/* * We use PATH to find perf commands, but we prepend some higher * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH * environment, and the $(perfexecdir) from the Makefile at build * time. */ setup_path(); /* * Block SIGWINCH notifications so that the thread that wants it can * unblock and get syscalls like select interrupted instead of waiting * forever while the signal goes to some other non interested thread. */ pthread__block_sigwinch();
while (1) { run_argv(&argc, &argv);
if (errno != ENOENT) break;
if (!done_help) { struct cmdnames main_cmds = {};
for (unsigned int i = 0; i < ARRAY_SIZE(commands); i++) { add_cmdname(&main_cmds, commands[i].cmd, strlen(commands[i].cmd)); } cmd = argv[0] = help_unknown_cmd(cmd, &main_cmds); clean_cmdnames(&main_cmds); done_help = 1; if (!cmd) break; } else break; }
if (cmd) { fprintf(stderr, "Failed to run command '%s': %m\n", cmd); }out: if (debug_fp) fclose(debug_fp);
return 1;}