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 { "evlist", cmd_evlist, 0 }, 317 { "help", cmd_help, 0 }, 318 { "list", cmd_list, 0 }, 319 { "record", cmd_record, 0 }, 320 { "report", cmd_report, 0 }, 321 /* ANDROID_CHANGE_BEGIN */ 322#if 0 323 { "bench", cmd_bench, 0 }, 324#endif 325 /* ANDROID_CHANGE_END */ 326 { "stat", cmd_stat, 0 }, 327 { "timechart", cmd_timechart, 0 }, 328 { "top", cmd_top, 0 }, 329 { "annotate", cmd_annotate, 0 }, 330 { "version", cmd_version, 0 }, 331 { "script", cmd_script, 0 }, 332 { "sched", cmd_sched, 0 }, 333 { "probe", cmd_probe, 0 }, 334 { "kmem", cmd_kmem, 0 }, 335 { "lock", cmd_lock, 0 }, 336 { "kvm", cmd_kvm, 0 }, 337 /* ANDROID_CHANGE_BEGIN */ 338#if 0 339 { "test", cmd_test, 0 }, 340#endif 341 /* ANDROID_CHANGE_END */ 342 { "inject", cmd_inject, 0 }, 343 }; 344 unsigned int i; 345 static const char ext[] = STRIP_EXTENSION; 346 347 if (sizeof(ext) > 1) { 348 i = strlen(argv[0]) - strlen(ext); 349 if (i > 0 && !strcmp(argv[0] + i, ext)) { 350 char *argv0 = strdup(argv[0]); 351 argv[0] = cmd = argv0; 352 argv0[i] = '\0'; 353 } 354 } 355 356 /* Turn "perf cmd --help" into "perf help cmd" */ 357 if (argc > 1 && !strcmp(argv[1], "--help")) { 358 argv[1] = argv[0]; 359 argv[0] = cmd = "help"; 360 } 361 362 for (i = 0; i < ARRAY_SIZE(commands); i++) { 363 struct cmd_struct *p = commands+i; 364 if (strcmp(p->cmd, cmd)) 365 continue; 366 exit(run_builtin(p, argc, argv)); 367 } 368} 369 370static void execv_dashed_external(const char **argv) 371{ 372 struct strbuf cmd = STRBUF_INIT; 373 const char *tmp; 374 int status; 375 376 strbuf_addf(&cmd, "perf-%s", argv[0]); 377 378 /* 379 * argv[0] must be the perf command, but the argv array 380 * belongs to the caller, and may be reused in 381 * subsequent loop iterations. Save argv[0] and 382 * restore it on error. 383 */ 384 tmp = argv[0]; 385 argv[0] = cmd.buf; 386 387 /* 388 * if we fail because the command is not found, it is 389 * OK to return. Otherwise, we just pass along the status code. 390 */ 391 status = run_command_v_opt(argv, 0); 392 if (status != -ERR_RUN_COMMAND_EXEC) { 393 if (IS_RUN_COMMAND_ERR(status)) 394 die("unable to run '%s'", argv[0]); 395 exit(-status); 396 } 397 errno = ENOENT; /* as if we called execvp */ 398 399 argv[0] = tmp; 400 401 strbuf_release(&cmd); 402} 403 404static int run_argv(int *argcp, const char ***argv) 405{ 406 int done_alias = 0; 407 408 while (1) { 409 /* See if it's an internal command */ 410 handle_internal_command(*argcp, *argv); 411 412 /* .. then try the external ones */ 413 execv_dashed_external(*argv); 414 415 /* It could be an alias -- this works around the insanity 416 * of overriding "perf log" with "perf show" by having 417 * alias.log = show 418 */ 419 if (done_alias || !handle_alias(argcp, argv)) 420 break; 421 done_alias = 1; 422 } 423 424 return done_alias; 425} 426 427/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ 428static void get_debugfs_mntpt(void) 429{ 430 const char *path = debugfs_mount(NULL); 431 432 if (path) 433 strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); 434 else 435 debugfs_mntpt[0] = '\0'; 436} 437 438int main(int argc, const char **argv) 439{ 440 const char *cmd; 441 442 cmd = perf_extract_argv0_path(argv[0]); 443 if (!cmd) 444 cmd = "perf-help"; 445 /* get debugfs mount point from /proc/mounts */ 446 get_debugfs_mntpt(); 447 /* 448 * "perf-xxxx" is the same as "perf xxxx", but we obviously: 449 * 450 * - cannot take flags in between the "perf" and the "xxxx". 451 * - cannot execute it externally (since it would just do 452 * the same thing over again) 453 * 454 * So we just directly call the internal command handler, and 455 * die if that one cannot handle it. 456 */ 457 if (!prefixcmp(cmd, "perf-")) { 458 cmd += 5; 459 argv[0] = cmd; 460 handle_internal_command(argc, argv); 461 die("cannot handle %s internally", cmd); 462 } 463 464 /* Look for flags.. */ 465 argv++; 466 argc--; 467 handle_options(&argv, &argc, NULL); 468 commit_pager_choice(); 469 set_debugfs_path(); 470 set_buildid_dir(); 471 472 if (argc > 0) { 473 if (!prefixcmp(argv[0], "--")) 474 argv[0] += 2; 475 } else { 476 /* The user didn't specify a command; give them help */ 477 printf("\n usage: %s\n\n", perf_usage_string); 478 list_common_cmds_help(); 479 printf("\n %s\n\n", perf_more_info_string); 480 exit(1); 481 } 482 cmd = argv[0]; 483 484 /* 485 * We use PATH to find perf commands, but we prepend some higher 486 * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH 487 * environment, and the $(perfexecdir) from the Makefile at build 488 * time. 489 */ 490 setup_path(); 491 492 while (1) { 493 static int done_help; 494 static int was_alias; 495 496 was_alias = run_argv(&argc, &argv); 497 if (errno != ENOENT) 498 break; 499 500 if (was_alias) { 501 fprintf(stderr, "Expansion of alias '%s' failed; " 502 "'%s' is not a perf-command\n", 503 cmd, argv[0]); 504 exit(1); 505 } 506 if (!done_help) { 507 cmd = argv[0] = help_unknown_cmd(cmd); 508 done_help = 1; 509 } else 510 break; 511 } 512 513 fprintf(stderr, "Failed to run command '%s': %s\n", 514 cmd, strerror(errno)); 515 516 return 1; 517} 518