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