162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * perf.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Performance analysis utility. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This is the main hub from which the sub-commands (perf stat, 762306a36Sopenharmony_ci * perf top, perf record, perf report, etc.) are started. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include "builtin.h" 1062306a36Sopenharmony_ci#include "perf.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "util/build-id.h" 1362306a36Sopenharmony_ci#include "util/cache.h" 1462306a36Sopenharmony_ci#include "util/env.h" 1562306a36Sopenharmony_ci#include <internal/lib.h> // page_size 1662306a36Sopenharmony_ci#include <subcmd/exec-cmd.h> 1762306a36Sopenharmony_ci#include "util/config.h" 1862306a36Sopenharmony_ci#include <subcmd/run-command.h> 1962306a36Sopenharmony_ci#include "util/parse-events.h" 2062306a36Sopenharmony_ci#include <subcmd/parse-options.h> 2162306a36Sopenharmony_ci#include "util/debug.h" 2262306a36Sopenharmony_ci#include "util/event.h" 2362306a36Sopenharmony_ci#include "util/util.h" // usage() 2462306a36Sopenharmony_ci#include "ui/ui.h" 2562306a36Sopenharmony_ci#include "perf-sys.h" 2662306a36Sopenharmony_ci#include <api/fs/fs.h> 2762306a36Sopenharmony_ci#include <api/fs/tracing_path.h> 2862306a36Sopenharmony_ci#include <perf/core.h> 2962306a36Sopenharmony_ci#include <errno.h> 3062306a36Sopenharmony_ci#include <pthread.h> 3162306a36Sopenharmony_ci#include <signal.h> 3262306a36Sopenharmony_ci#include <stdlib.h> 3362306a36Sopenharmony_ci#include <time.h> 3462306a36Sopenharmony_ci#include <sys/types.h> 3562306a36Sopenharmony_ci#include <sys/stat.h> 3662306a36Sopenharmony_ci#include <unistd.h> 3762306a36Sopenharmony_ci#include <linux/kernel.h> 3862306a36Sopenharmony_ci#include <linux/string.h> 3962306a36Sopenharmony_ci#include <linux/zalloc.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int use_pager = -1; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct cmd_struct { 4462306a36Sopenharmony_ci const char *cmd; 4562306a36Sopenharmony_ci int (*fn)(int, const char **); 4662306a36Sopenharmony_ci int option; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct cmd_struct commands[] = { 5062306a36Sopenharmony_ci { "archive", NULL, 0 }, 5162306a36Sopenharmony_ci { "buildid-cache", cmd_buildid_cache, 0 }, 5262306a36Sopenharmony_ci { "buildid-list", cmd_buildid_list, 0 }, 5362306a36Sopenharmony_ci { "config", cmd_config, 0 }, 5462306a36Sopenharmony_ci { "c2c", cmd_c2c, 0 }, 5562306a36Sopenharmony_ci { "diff", cmd_diff, 0 }, 5662306a36Sopenharmony_ci { "evlist", cmd_evlist, 0 }, 5762306a36Sopenharmony_ci { "help", cmd_help, 0 }, 5862306a36Sopenharmony_ci { "iostat", NULL, 0 }, 5962306a36Sopenharmony_ci { "kallsyms", cmd_kallsyms, 0 }, 6062306a36Sopenharmony_ci { "list", cmd_list, 0 }, 6162306a36Sopenharmony_ci { "record", cmd_record, 0 }, 6262306a36Sopenharmony_ci { "report", cmd_report, 0 }, 6362306a36Sopenharmony_ci { "bench", cmd_bench, 0 }, 6462306a36Sopenharmony_ci { "stat", cmd_stat, 0 }, 6562306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 6662306a36Sopenharmony_ci { "timechart", cmd_timechart, 0 }, 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci { "top", cmd_top, 0 }, 6962306a36Sopenharmony_ci { "annotate", cmd_annotate, 0 }, 7062306a36Sopenharmony_ci { "version", cmd_version, 0 }, 7162306a36Sopenharmony_ci { "script", cmd_script, 0 }, 7262306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 7362306a36Sopenharmony_ci { "sched", cmd_sched, 0 }, 7462306a36Sopenharmony_ci#endif 7562306a36Sopenharmony_ci#ifdef HAVE_LIBELF_SUPPORT 7662306a36Sopenharmony_ci { "probe", cmd_probe, 0 }, 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 7962306a36Sopenharmony_ci { "kmem", cmd_kmem, 0 }, 8062306a36Sopenharmony_ci { "lock", cmd_lock, 0 }, 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci { "kvm", cmd_kvm, 0 }, 8362306a36Sopenharmony_ci { "test", cmd_test, 0 }, 8462306a36Sopenharmony_ci#if defined(HAVE_LIBTRACEEVENT) && (defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT)) 8562306a36Sopenharmony_ci { "trace", cmd_trace, 0 }, 8662306a36Sopenharmony_ci#endif 8762306a36Sopenharmony_ci { "inject", cmd_inject, 0 }, 8862306a36Sopenharmony_ci { "mem", cmd_mem, 0 }, 8962306a36Sopenharmony_ci { "data", cmd_data, 0 }, 9062306a36Sopenharmony_ci { "ftrace", cmd_ftrace, 0 }, 9162306a36Sopenharmony_ci { "daemon", cmd_daemon, 0 }, 9262306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 9362306a36Sopenharmony_ci { "kwork", cmd_kwork, 0 }, 9462306a36Sopenharmony_ci#endif 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct pager_config { 9862306a36Sopenharmony_ci const char *cmd; 9962306a36Sopenharmony_ci int val; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic bool same_cmd_with_prefix(const char *var, struct pager_config *c, 10362306a36Sopenharmony_ci const char *header) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return (strstarts(var, header) && !strcmp(var + strlen(header), c->cmd)); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int pager_command_config(const char *var, const char *value, void *data) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct pager_config *c = data; 11162306a36Sopenharmony_ci if (same_cmd_with_prefix(var, c, "pager.")) 11262306a36Sopenharmony_ci c->val = perf_config_bool(var, value); 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ 11762306a36Sopenharmony_cistatic int check_pager_config(const char *cmd) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci struct pager_config c; 12162306a36Sopenharmony_ci c.cmd = cmd; 12262306a36Sopenharmony_ci c.val = -1; 12362306a36Sopenharmony_ci err = perf_config(pager_command_config, &c); 12462306a36Sopenharmony_ci return err ?: c.val; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int browser_command_config(const char *var, const char *value, void *data) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct pager_config *c = data; 13062306a36Sopenharmony_ci if (same_cmd_with_prefix(var, c, "tui.")) 13162306a36Sopenharmony_ci c->val = perf_config_bool(var, value); 13262306a36Sopenharmony_ci if (same_cmd_with_prefix(var, c, "gtk.")) 13362306a36Sopenharmony_ci c->val = perf_config_bool(var, value) ? 2 : 0; 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * returns 0 for "no tui", 1 for "use tui", 2 for "use gtk", 13962306a36Sopenharmony_ci * and -1 for "not specified" 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic int check_browser_config(const char *cmd) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int err; 14462306a36Sopenharmony_ci struct pager_config c; 14562306a36Sopenharmony_ci c.cmd = cmd; 14662306a36Sopenharmony_ci c.val = -1; 14762306a36Sopenharmony_ci err = perf_config(browser_command_config, &c); 14862306a36Sopenharmony_ci return err ?: c.val; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void commit_pager_choice(void) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci switch (use_pager) { 15462306a36Sopenharmony_ci case 0: 15562306a36Sopenharmony_ci setenv(PERF_PAGER_ENVIRONMENT, "cat", 1); 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case 1: 15862306a36Sopenharmony_ci /* setup_pager(); */ 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistruct option options[] = { 16662306a36Sopenharmony_ci OPT_ARGUMENT("help", "help"), 16762306a36Sopenharmony_ci OPT_ARGUMENT("version", "version"), 16862306a36Sopenharmony_ci OPT_ARGUMENT("exec-path", "exec-path"), 16962306a36Sopenharmony_ci OPT_ARGUMENT("html-path", "html-path"), 17062306a36Sopenharmony_ci OPT_ARGUMENT("paginate", "paginate"), 17162306a36Sopenharmony_ci OPT_ARGUMENT("no-pager", "no-pager"), 17262306a36Sopenharmony_ci OPT_ARGUMENT("debugfs-dir", "debugfs-dir"), 17362306a36Sopenharmony_ci OPT_ARGUMENT("buildid-dir", "buildid-dir"), 17462306a36Sopenharmony_ci OPT_ARGUMENT("list-cmds", "list-cmds"), 17562306a36Sopenharmony_ci OPT_ARGUMENT("list-opts", "list-opts"), 17662306a36Sopenharmony_ci OPT_ARGUMENT("debug", "debug"), 17762306a36Sopenharmony_ci OPT_END() 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int handle_options(const char ***argv, int *argc, int *envchanged) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int handled = 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while (*argc > 0) { 18562306a36Sopenharmony_ci const char *cmd = (*argv)[0]; 18662306a36Sopenharmony_ci if (cmd[0] != '-') 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * For legacy reasons, the "version" and "help" 19162306a36Sopenharmony_ci * commands can be written with "--" prepended 19262306a36Sopenharmony_ci * to make them look like flags. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version")) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Shortcut for '-h' and '-v' options to invoke help 19962306a36Sopenharmony_ci * and version command. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci if (!strcmp(cmd, "-h")) { 20262306a36Sopenharmony_ci (*argv)[0] = "--help"; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!strcmp(cmd, "-v")) { 20762306a36Sopenharmony_ci (*argv)[0] = "--version"; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!strcmp(cmd, "-vv")) { 21262306a36Sopenharmony_ci (*argv)[0] = "version"; 21362306a36Sopenharmony_ci verbose = 1; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * Check remaining flags. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci if (strstarts(cmd, CMD_EXEC_PATH)) { 22162306a36Sopenharmony_ci cmd += strlen(CMD_EXEC_PATH); 22262306a36Sopenharmony_ci if (*cmd == '=') 22362306a36Sopenharmony_ci set_argv_exec_path(cmd + 1); 22462306a36Sopenharmony_ci else { 22562306a36Sopenharmony_ci puts(get_argv_exec_path()); 22662306a36Sopenharmony_ci exit(0); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } else if (!strcmp(cmd, "--html-path")) { 22962306a36Sopenharmony_ci puts(system_path(PERF_HTML_PATH)); 23062306a36Sopenharmony_ci exit(0); 23162306a36Sopenharmony_ci } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { 23262306a36Sopenharmony_ci use_pager = 1; 23362306a36Sopenharmony_ci } else if (!strcmp(cmd, "--no-pager")) { 23462306a36Sopenharmony_ci use_pager = 0; 23562306a36Sopenharmony_ci if (envchanged) 23662306a36Sopenharmony_ci *envchanged = 1; 23762306a36Sopenharmony_ci } else if (!strcmp(cmd, "--debugfs-dir")) { 23862306a36Sopenharmony_ci if (*argc < 2) { 23962306a36Sopenharmony_ci fprintf(stderr, "No directory given for --debugfs-dir.\n"); 24062306a36Sopenharmony_ci usage(perf_usage_string); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci tracing_path_set((*argv)[1]); 24362306a36Sopenharmony_ci if (envchanged) 24462306a36Sopenharmony_ci *envchanged = 1; 24562306a36Sopenharmony_ci (*argv)++; 24662306a36Sopenharmony_ci (*argc)--; 24762306a36Sopenharmony_ci } else if (!strcmp(cmd, "--buildid-dir")) { 24862306a36Sopenharmony_ci if (*argc < 2) { 24962306a36Sopenharmony_ci fprintf(stderr, "No directory given for --buildid-dir.\n"); 25062306a36Sopenharmony_ci usage(perf_usage_string); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci set_buildid_dir((*argv)[1]); 25362306a36Sopenharmony_ci if (envchanged) 25462306a36Sopenharmony_ci *envchanged = 1; 25562306a36Sopenharmony_ci (*argv)++; 25662306a36Sopenharmony_ci (*argc)--; 25762306a36Sopenharmony_ci } else if (strstarts(cmd, CMD_DEBUGFS_DIR)) { 25862306a36Sopenharmony_ci tracing_path_set(cmd + strlen(CMD_DEBUGFS_DIR)); 25962306a36Sopenharmony_ci fprintf(stderr, "dir: %s\n", tracing_path_mount()); 26062306a36Sopenharmony_ci if (envchanged) 26162306a36Sopenharmony_ci *envchanged = 1; 26262306a36Sopenharmony_ci } else if (!strcmp(cmd, "--list-cmds")) { 26362306a36Sopenharmony_ci unsigned int i; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(commands); i++) { 26662306a36Sopenharmony_ci struct cmd_struct *p = commands+i; 26762306a36Sopenharmony_ci printf("%s ", p->cmd); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci putchar('\n'); 27062306a36Sopenharmony_ci exit(0); 27162306a36Sopenharmony_ci } else if (!strcmp(cmd, "--list-opts")) { 27262306a36Sopenharmony_ci unsigned int i; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(options)-1; i++) { 27562306a36Sopenharmony_ci struct option *p = options+i; 27662306a36Sopenharmony_ci printf("--%s ", p->long_name); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci putchar('\n'); 27962306a36Sopenharmony_ci exit(0); 28062306a36Sopenharmony_ci } else if (!strcmp(cmd, "--debug")) { 28162306a36Sopenharmony_ci if (*argc < 2) { 28262306a36Sopenharmony_ci fprintf(stderr, "No variable specified for --debug.\n"); 28362306a36Sopenharmony_ci usage(perf_usage_string); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci if (perf_debug_option((*argv)[1])) 28662306a36Sopenharmony_ci usage(perf_usage_string); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci (*argv)++; 28962306a36Sopenharmony_ci (*argc)--; 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci fprintf(stderr, "Unknown option: %s\n", cmd); 29262306a36Sopenharmony_ci usage(perf_usage_string); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci (*argv)++; 29662306a36Sopenharmony_ci (*argc)--; 29762306a36Sopenharmony_ci handled++; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci return handled; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci#define RUN_SETUP (1<<0) 30362306a36Sopenharmony_ci#define USE_PAGER (1<<1) 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int run_builtin(struct cmd_struct *p, int argc, const char **argv) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int status; 30862306a36Sopenharmony_ci struct stat st; 30962306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (use_browser == -1) 31262306a36Sopenharmony_ci use_browser = check_browser_config(p->cmd); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (use_pager == -1 && p->option & RUN_SETUP) 31562306a36Sopenharmony_ci use_pager = check_pager_config(p->cmd); 31662306a36Sopenharmony_ci if (use_pager == -1 && p->option & USE_PAGER) 31762306a36Sopenharmony_ci use_pager = 1; 31862306a36Sopenharmony_ci commit_pager_choice(); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci perf_env__init(&perf_env); 32162306a36Sopenharmony_ci perf_env__set_cmdline(&perf_env, argc, argv); 32262306a36Sopenharmony_ci status = p->fn(argc, argv); 32362306a36Sopenharmony_ci perf_config__exit(); 32462306a36Sopenharmony_ci exit_browser(status); 32562306a36Sopenharmony_ci perf_env__exit(&perf_env); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (status) 32862306a36Sopenharmony_ci return status & 0xff; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Somebody closed stdout? */ 33162306a36Sopenharmony_ci if (fstat(fileno(stdout), &st)) 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci /* Ignore write errors for pipes and sockets.. */ 33462306a36Sopenharmony_ci if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci status = 1; 33862306a36Sopenharmony_ci /* Check for ENOSPC and EIO errors.. */ 33962306a36Sopenharmony_ci if (fflush(stdout)) { 34062306a36Sopenharmony_ci fprintf(stderr, "write failure on standard output: %s", 34162306a36Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 34262306a36Sopenharmony_ci goto out; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci if (ferror(stdout)) { 34562306a36Sopenharmony_ci fprintf(stderr, "unknown write failure on standard output"); 34662306a36Sopenharmony_ci goto out; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci if (fclose(stdout)) { 34962306a36Sopenharmony_ci fprintf(stderr, "close failed on standard output: %s", 35062306a36Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 35162306a36Sopenharmony_ci goto out; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci status = 0; 35462306a36Sopenharmony_ciout: 35562306a36Sopenharmony_ci return status; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void handle_internal_command(int argc, const char **argv) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci const char *cmd = argv[0]; 36162306a36Sopenharmony_ci unsigned int i; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Turn "perf cmd --help" into "perf help cmd" */ 36462306a36Sopenharmony_ci if (argc > 1 && !strcmp(argv[1], "--help")) { 36562306a36Sopenharmony_ci argv[1] = argv[0]; 36662306a36Sopenharmony_ci argv[0] = cmd = "help"; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(commands); i++) { 37062306a36Sopenharmony_ci struct cmd_struct *p = commands+i; 37162306a36Sopenharmony_ci if (p->fn == NULL) 37262306a36Sopenharmony_ci continue; 37362306a36Sopenharmony_ci if (strcmp(p->cmd, cmd)) 37462306a36Sopenharmony_ci continue; 37562306a36Sopenharmony_ci exit(run_builtin(p, argc, argv)); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void execv_dashed_external(const char **argv) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci char *cmd; 38262306a36Sopenharmony_ci const char *tmp; 38362306a36Sopenharmony_ci int status; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (asprintf(&cmd, "perf-%s", argv[0]) < 0) 38662306a36Sopenharmony_ci goto do_die; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * argv[0] must be the perf command, but the argv array 39062306a36Sopenharmony_ci * belongs to the caller, and may be reused in 39162306a36Sopenharmony_ci * subsequent loop iterations. Save argv[0] and 39262306a36Sopenharmony_ci * restore it on error. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci tmp = argv[0]; 39562306a36Sopenharmony_ci argv[0] = cmd; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * if we fail because the command is not found, it is 39962306a36Sopenharmony_ci * OK to return. Otherwise, we just pass along the status code. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci status = run_command_v_opt(argv, 0); 40262306a36Sopenharmony_ci if (status != -ERR_RUN_COMMAND_EXEC) { 40362306a36Sopenharmony_ci if (IS_RUN_COMMAND_ERR(status)) { 40462306a36Sopenharmony_cido_die: 40562306a36Sopenharmony_ci pr_err("FATAL: unable to run '%s'", argv[0]); 40662306a36Sopenharmony_ci status = -128; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci exit(-status); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci errno = ENOENT; /* as if we called execvp */ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci argv[0] = tmp; 41362306a36Sopenharmony_ci zfree(&cmd); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int run_argv(int *argcp, const char ***argv) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci /* See if it's an internal command */ 41962306a36Sopenharmony_ci handle_internal_command(*argcp, *argv); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* .. then try the external ones */ 42262306a36Sopenharmony_ci execv_dashed_external(*argv); 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int libperf_print(enum libperf_print_level level, 42762306a36Sopenharmony_ci const char *fmt, va_list ap) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci return veprintf(level, verbose, fmt, ap); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciint main(int argc, const char **argv) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci int err; 43562306a36Sopenharmony_ci const char *cmd; 43662306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci perf_debug_setup(); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* libsubcmd init */ 44162306a36Sopenharmony_ci exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT); 44262306a36Sopenharmony_ci pager_init(PERF_PAGER_ENVIRONMENT); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci libperf_init(libperf_print); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci cmd = extract_argv0_path(argv[0]); 44762306a36Sopenharmony_ci if (!cmd) 44862306a36Sopenharmony_ci cmd = "perf-help"; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci srandom(time(NULL)); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 45362306a36Sopenharmony_ci config_exclusive_filename = getenv("PERF_CONFIG"); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = perf_config(perf_default_config, NULL); 45662306a36Sopenharmony_ci if (err) 45762306a36Sopenharmony_ci return err; 45862306a36Sopenharmony_ci set_buildid_dir(NULL); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * "perf-xxxx" is the same as "perf xxxx", but we obviously: 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * - cannot take flags in between the "perf" and the "xxxx". 46462306a36Sopenharmony_ci * - cannot execute it externally (since it would just do 46562306a36Sopenharmony_ci * the same thing over again) 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * So we just directly call the internal command handler. If that one 46862306a36Sopenharmony_ci * fails to handle this, then maybe we just run a renamed perf binary 46962306a36Sopenharmony_ci * that contains a dash in its name. To handle this scenario, we just 47062306a36Sopenharmony_ci * fall through and ignore the "xxxx" part of the command string. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (strstarts(cmd, "perf-")) { 47362306a36Sopenharmony_ci cmd += 5; 47462306a36Sopenharmony_ci argv[0] = cmd; 47562306a36Sopenharmony_ci handle_internal_command(argc, argv); 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * If the command is handled, the above function does not 47862306a36Sopenharmony_ci * return undo changes and fall through in such a case. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci cmd -= 5; 48162306a36Sopenharmony_ci argv[0] = cmd; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci if (strstarts(cmd, "trace")) { 48462306a36Sopenharmony_ci#ifndef HAVE_LIBTRACEEVENT 48562306a36Sopenharmony_ci fprintf(stderr, 48662306a36Sopenharmony_ci "trace command not available: missing libtraceevent devel package at build time.\n"); 48762306a36Sopenharmony_ci goto out; 48862306a36Sopenharmony_ci#elif !defined(HAVE_LIBAUDIT_SUPPORT) && !defined(HAVE_SYSCALL_TABLE_SUPPORT) 48962306a36Sopenharmony_ci fprintf(stderr, 49062306a36Sopenharmony_ci "trace command not available: missing audit-libs devel package at build time.\n"); 49162306a36Sopenharmony_ci goto out; 49262306a36Sopenharmony_ci#else 49362306a36Sopenharmony_ci setup_path(); 49462306a36Sopenharmony_ci argv[0] = "trace"; 49562306a36Sopenharmony_ci return cmd_trace(argc, argv); 49662306a36Sopenharmony_ci#endif 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci /* Look for flags.. */ 49962306a36Sopenharmony_ci argv++; 50062306a36Sopenharmony_ci argc--; 50162306a36Sopenharmony_ci handle_options(&argv, &argc, NULL); 50262306a36Sopenharmony_ci commit_pager_choice(); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (argc > 0) { 50562306a36Sopenharmony_ci if (strstarts(argv[0], "--")) 50662306a36Sopenharmony_ci argv[0] += 2; 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci /* The user didn't specify a command; give them help */ 50962306a36Sopenharmony_ci printf("\n usage: %s\n\n", perf_usage_string); 51062306a36Sopenharmony_ci list_common_cmds_help(); 51162306a36Sopenharmony_ci printf("\n %s\n\n", perf_more_info_string); 51262306a36Sopenharmony_ci goto out; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci cmd = argv[0]; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci test_attr__init(); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * We use PATH to find perf commands, but we prepend some higher 52062306a36Sopenharmony_ci * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH 52162306a36Sopenharmony_ci * environment, and the $(perfexecdir) from the Makefile at build 52262306a36Sopenharmony_ci * time. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci setup_path(); 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * Block SIGWINCH notifications so that the thread that wants it can 52762306a36Sopenharmony_ci * unblock and get syscalls like select interrupted instead of waiting 52862306a36Sopenharmony_ci * forever while the signal goes to some other non interested thread. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci pthread__block_sigwinch(); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci while (1) { 53362306a36Sopenharmony_ci static int done_help; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci run_argv(&argc, &argv); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (errno != ENOENT) 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!done_help) { 54162306a36Sopenharmony_ci cmd = argv[0] = help_unknown_cmd(cmd); 54262306a36Sopenharmony_ci done_help = 1; 54362306a36Sopenharmony_ci } else 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci fprintf(stderr, "Failed to run command '%s': %s\n", 54862306a36Sopenharmony_ci cmd, str_error_r(errno, sbuf, sizeof(sbuf))); 54962306a36Sopenharmony_ciout: 55062306a36Sopenharmony_ci return 1; 55162306a36Sopenharmony_ci} 552