Home | History | Annotate | Download | only in linux-tools-perf
      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 
     18 const char perf_usage_string[] =
     19 	"perf [--version] [--help] COMMAND [ARGS]";
     20 
     21 const char perf_more_info_string[] =
     22 	"See 'perf help COMMAND' for more information on a specific command.";
     23 
     24 int use_browser = -1;
     25 static int use_pager = -1;
     26 
     27 struct pager_config {
     28 	const char *cmd;
     29 	int val;
     30 };
     31 
     32 static char debugfs_mntpt[MAXPATHLEN];
     33 
     34 static 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" */
     43 int 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 
     52 static 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" */
     61 static 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 
     70 static 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 
     84 static 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 
     93 static 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 
    187 static 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 
    250 const 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 
    260 struct cmd_struct {
    261 	const char *cmd;
    262 	int (*fn)(int, const char **, const char *);
    263 	int option;
    264 };
    265 
    266 static 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 
    309 static 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 
    370 static 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 
    404 static 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" */
    428 static 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 
    438 int 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