diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-06 14:33:43 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-06 14:33:43 -0400 |
commit | 864709302a80f26fa9da3be5b47304f0b8bae192 (patch) | |
tree | 8c2bab78f141fe43a38914bd3e3aae0a88f958e5 /tools/perf/perf.c | |
parent | 75b5032212641f6d38ac041416945e70da833b68 (diff) |
perf_counter tools: Move from Documentation/perf_counter/ to tools/perf/
Several people have suggested that 'perf' has become a full-fledged
tool that should be moved out of Documentation/. Move it to the
(new) tools/ directory.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/perf.c')
-rw-r--r-- | tools/perf/perf.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/tools/perf/perf.c b/tools/perf/perf.c new file mode 100644 index 000000000000..4eb725933703 --- /dev/null +++ b/tools/perf/perf.c | |||
@@ -0,0 +1,428 @@ | |||
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 | |||
16 | const char perf_usage_string[] = | ||
17 | "perf [--version] [--help] COMMAND [ARGS]"; | ||
18 | |||
19 | const char perf_more_info_string[] = | ||
20 | "See 'perf help COMMAND' for more information on a specific command."; | ||
21 | |||
22 | static int use_pager = -1; | ||
23 | struct pager_config { | ||
24 | const char *cmd; | ||
25 | int val; | ||
26 | }; | ||
27 | |||
28 | static int pager_command_config(const char *var, const char *value, void *data) | ||
29 | { | ||
30 | struct pager_config *c = data; | ||
31 | if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) | ||
32 | c->val = perf_config_bool(var, value); | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ | ||
37 | int check_pager_config(const char *cmd) | ||
38 | { | ||
39 | struct pager_config c; | ||
40 | c.cmd = cmd; | ||
41 | c.val = -1; | ||
42 | perf_config(pager_command_config, &c); | ||
43 | return c.val; | ||
44 | } | ||
45 | |||
46 | static void commit_pager_choice(void) { | ||
47 | switch (use_pager) { | ||
48 | case 0: | ||
49 | setenv("PERF_PAGER", "cat", 1); | ||
50 | break; | ||
51 | case 1: | ||
52 | /* setup_pager(); */ | ||
53 | break; | ||
54 | default: | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | static int handle_options(const char*** argv, int* argc, int* envchanged) | ||
60 | { | ||
61 | int handled = 0; | ||
62 | |||
63 | while (*argc > 0) { | ||
64 | const char *cmd = (*argv)[0]; | ||
65 | if (cmd[0] != '-') | ||
66 | break; | ||
67 | |||
68 | /* | ||
69 | * For legacy reasons, the "version" and "help" | ||
70 | * commands can be written with "--" prepended | ||
71 | * to make them look like flags. | ||
72 | */ | ||
73 | if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version")) | ||
74 | break; | ||
75 | |||
76 | /* | ||
77 | * Check remaining flags. | ||
78 | */ | ||
79 | if (!prefixcmp(cmd, "--exec-path")) { | ||
80 | cmd += 11; | ||
81 | if (*cmd == '=') | ||
82 | perf_set_argv_exec_path(cmd + 1); | ||
83 | else { | ||
84 | puts(perf_exec_path()); | ||
85 | exit(0); | ||
86 | } | ||
87 | } else if (!strcmp(cmd, "--html-path")) { | ||
88 | puts(system_path(PERF_HTML_PATH)); | ||
89 | exit(0); | ||
90 | } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { | ||
91 | use_pager = 1; | ||
92 | } else if (!strcmp(cmd, "--no-pager")) { | ||
93 | use_pager = 0; | ||
94 | if (envchanged) | ||
95 | *envchanged = 1; | ||
96 | } else if (!strcmp(cmd, "--perf-dir")) { | ||
97 | if (*argc < 2) { | ||
98 | fprintf(stderr, "No directory given for --perf-dir.\n" ); | ||
99 | usage(perf_usage_string); | ||
100 | } | ||
101 | setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1); | ||
102 | if (envchanged) | ||
103 | *envchanged = 1; | ||
104 | (*argv)++; | ||
105 | (*argc)--; | ||
106 | handled++; | ||
107 | } else if (!prefixcmp(cmd, "--perf-dir=")) { | ||
108 | setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1); | ||
109 | if (envchanged) | ||
110 | *envchanged = 1; | ||
111 | } else if (!strcmp(cmd, "--work-tree")) { | ||
112 | if (*argc < 2) { | ||
113 | fprintf(stderr, "No directory given for --work-tree.\n" ); | ||
114 | usage(perf_usage_string); | ||
115 | } | ||
116 | setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); | ||
117 | if (envchanged) | ||
118 | *envchanged = 1; | ||
119 | (*argv)++; | ||
120 | (*argc)--; | ||
121 | } else if (!prefixcmp(cmd, "--work-tree=")) { | ||
122 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); | ||
123 | if (envchanged) | ||
124 | *envchanged = 1; | ||
125 | } else { | ||
126 | fprintf(stderr, "Unknown option: %s\n", cmd); | ||
127 | usage(perf_usage_string); | ||
128 | } | ||
129 | |||
130 | (*argv)++; | ||
131 | (*argc)--; | ||
132 | handled++; | ||
133 | } | ||
134 | return handled; | ||
135 | } | ||
136 | |||
137 | static int handle_alias(int *argcp, const char ***argv) | ||
138 | { | ||
139 | int envchanged = 0, ret = 0, saved_errno = errno; | ||
140 | int count, option_count; | ||
141 | const char** new_argv; | ||
142 | const char *alias_command; | ||
143 | char *alias_string; | ||
144 | |||
145 | alias_command = (*argv)[0]; | ||
146 | alias_string = alias_lookup(alias_command); | ||
147 | if (alias_string) { | ||
148 | if (alias_string[0] == '!') { | ||
149 | if (*argcp > 1) { | ||
150 | struct strbuf buf; | ||
151 | |||
152 | strbuf_init(&buf, PATH_MAX); | ||
153 | strbuf_addstr(&buf, alias_string); | ||
154 | sq_quote_argv(&buf, (*argv) + 1, PATH_MAX); | ||
155 | free(alias_string); | ||
156 | alias_string = buf.buf; | ||
157 | } | ||
158 | ret = system(alias_string + 1); | ||
159 | if (ret >= 0 && WIFEXITED(ret) && | ||
160 | WEXITSTATUS(ret) != 127) | ||
161 | exit(WEXITSTATUS(ret)); | ||
162 | die("Failed to run '%s' when expanding alias '%s'", | ||
163 | alias_string + 1, alias_command); | ||
164 | } | ||
165 | count = split_cmdline(alias_string, &new_argv); | ||
166 | if (count < 0) | ||
167 | die("Bad alias.%s string", alias_command); | ||
168 | option_count = handle_options(&new_argv, &count, &envchanged); | ||
169 | if (envchanged) | ||
170 | die("alias '%s' changes environment variables\n" | ||
171 | "You can use '!perf' in the alias to do this.", | ||
172 | alias_command); | ||
173 | memmove(new_argv - option_count, new_argv, | ||
174 | count * sizeof(char *)); | ||
175 | new_argv -= option_count; | ||
176 | |||
177 | if (count < 1) | ||
178 | die("empty alias for %s", alias_command); | ||
179 | |||
180 | if (!strcmp(alias_command, new_argv[0])) | ||
181 | die("recursive alias: %s", alias_command); | ||
182 | |||
183 | new_argv = realloc(new_argv, sizeof(char*) * | ||
184 | (count + *argcp + 1)); | ||
185 | /* insert after command name */ | ||
186 | memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp); | ||
187 | new_argv[count+*argcp] = NULL; | ||
188 | |||
189 | *argv = new_argv; | ||
190 | *argcp += count - 1; | ||
191 | |||
192 | ret = 1; | ||
193 | } | ||
194 | |||
195 | errno = saved_errno; | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | const char perf_version_string[] = PERF_VERSION; | ||
201 | |||
202 | #define RUN_SETUP (1<<0) | ||
203 | #define USE_PAGER (1<<1) | ||
204 | /* | ||
205 | * require working tree to be present -- anything uses this needs | ||
206 | * RUN_SETUP for reading from the configuration file. | ||
207 | */ | ||
208 | #define NEED_WORK_TREE (1<<2) | ||
209 | |||
210 | struct cmd_struct { | ||
211 | const char *cmd; | ||
212 | int (*fn)(int, const char **, const char *); | ||
213 | int option; | ||
214 | }; | ||
215 | |||
216 | static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | ||
217 | { | ||
218 | int status; | ||
219 | struct stat st; | ||
220 | const char *prefix; | ||
221 | |||
222 | prefix = NULL; | ||
223 | if (p->option & RUN_SETUP) | ||
224 | prefix = NULL; /* setup_perf_directory(); */ | ||
225 | |||
226 | if (use_pager == -1 && p->option & RUN_SETUP) | ||
227 | use_pager = check_pager_config(p->cmd); | ||
228 | if (use_pager == -1 && p->option & USE_PAGER) | ||
229 | use_pager = 1; | ||
230 | commit_pager_choice(); | ||
231 | |||
232 | if (p->option & NEED_WORK_TREE) | ||
233 | /* setup_work_tree() */; | ||
234 | |||
235 | status = p->fn(argc, argv, prefix); | ||
236 | if (status) | ||
237 | return status & 0xff; | ||
238 | |||
239 | /* Somebody closed stdout? */ | ||
240 | if (fstat(fileno(stdout), &st)) | ||
241 | return 0; | ||
242 | /* Ignore write errors for pipes and sockets.. */ | ||
243 | if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) | ||
244 | return 0; | ||
245 | |||
246 | /* Check for ENOSPC and EIO errors.. */ | ||
247 | if (fflush(stdout)) | ||
248 | die("write failure on standard output: %s", strerror(errno)); | ||
249 | if (ferror(stdout)) | ||
250 | die("unknown write failure on standard output"); | ||
251 | if (fclose(stdout)) | ||
252 | die("close failed on standard output: %s", strerror(errno)); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static void handle_internal_command(int argc, const char **argv) | ||
257 | { | ||
258 | const char *cmd = argv[0]; | ||
259 | static struct cmd_struct commands[] = { | ||
260 | { "help", cmd_help, 0 }, | ||
261 | { "list", cmd_list, 0 }, | ||
262 | { "record", cmd_record, 0 }, | ||
263 | { "report", cmd_report, 0 }, | ||
264 | { "stat", cmd_stat, 0 }, | ||
265 | { "top", cmd_top, 0 }, | ||
266 | { "annotate", cmd_annotate, 0 }, | ||
267 | { "version", cmd_version, 0 }, | ||
268 | }; | ||
269 | int i; | ||
270 | static const char ext[] = STRIP_EXTENSION; | ||
271 | |||
272 | if (sizeof(ext) > 1) { | ||
273 | i = strlen(argv[0]) - strlen(ext); | ||
274 | if (i > 0 && !strcmp(argv[0] + i, ext)) { | ||
275 | char *argv0 = strdup(argv[0]); | ||
276 | argv[0] = cmd = argv0; | ||
277 | argv0[i] = '\0'; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | /* Turn "perf cmd --help" into "perf help cmd" */ | ||
282 | if (argc > 1 && !strcmp(argv[1], "--help")) { | ||
283 | argv[1] = argv[0]; | ||
284 | argv[0] = cmd = "help"; | ||
285 | } | ||
286 | |||
287 | for (i = 0; i < ARRAY_SIZE(commands); i++) { | ||
288 | struct cmd_struct *p = commands+i; | ||
289 | if (strcmp(p->cmd, cmd)) | ||
290 | continue; | ||
291 | exit(run_builtin(p, argc, argv)); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | static void execv_dashed_external(const char **argv) | ||
296 | { | ||
297 | struct strbuf cmd = STRBUF_INIT; | ||
298 | const char *tmp; | ||
299 | int status; | ||
300 | |||
301 | strbuf_addf(&cmd, "perf-%s", argv[0]); | ||
302 | |||
303 | /* | ||
304 | * argv[0] must be the perf command, but the argv array | ||
305 | * belongs to the caller, and may be reused in | ||
306 | * subsequent loop iterations. Save argv[0] and | ||
307 | * restore it on error. | ||
308 | */ | ||
309 | tmp = argv[0]; | ||
310 | argv[0] = cmd.buf; | ||
311 | |||
312 | /* | ||
313 | * if we fail because the command is not found, it is | ||
314 | * OK to return. Otherwise, we just pass along the status code. | ||
315 | */ | ||
316 | status = run_command_v_opt(argv, 0); | ||
317 | if (status != -ERR_RUN_COMMAND_EXEC) { | ||
318 | if (IS_RUN_COMMAND_ERR(status)) | ||
319 | die("unable to run '%s'", argv[0]); | ||
320 | exit(-status); | ||
321 | } | ||
322 | errno = ENOENT; /* as if we called execvp */ | ||
323 | |||
324 | argv[0] = tmp; | ||
325 | |||
326 | strbuf_release(&cmd); | ||
327 | } | ||
328 | |||
329 | static int run_argv(int *argcp, const char ***argv) | ||
330 | { | ||
331 | int done_alias = 0; | ||
332 | |||
333 | while (1) { | ||
334 | /* See if it's an internal command */ | ||
335 | handle_internal_command(*argcp, *argv); | ||
336 | |||
337 | /* .. then try the external ones */ | ||
338 | execv_dashed_external(*argv); | ||
339 | |||
340 | /* It could be an alias -- this works around the insanity | ||
341 | * of overriding "perf log" with "perf show" by having | ||
342 | * alias.log = show | ||
343 | */ | ||
344 | if (done_alias || !handle_alias(argcp, argv)) | ||
345 | break; | ||
346 | done_alias = 1; | ||
347 | } | ||
348 | |||
349 | return done_alias; | ||
350 | } | ||
351 | |||
352 | |||
353 | int main(int argc, const char **argv) | ||
354 | { | ||
355 | const char *cmd; | ||
356 | |||
357 | cmd = perf_extract_argv0_path(argv[0]); | ||
358 | if (!cmd) | ||
359 | cmd = "perf-help"; | ||
360 | |||
361 | /* | ||
362 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: | ||
363 | * | ||
364 | * - cannot take flags in between the "perf" and the "xxxx". | ||
365 | * - cannot execute it externally (since it would just do | ||
366 | * the same thing over again) | ||
367 | * | ||
368 | * So we just directly call the internal command handler, and | ||
369 | * die if that one cannot handle it. | ||
370 | */ | ||
371 | if (!prefixcmp(cmd, "perf-")) { | ||
372 | cmd += 5; | ||
373 | argv[0] = cmd; | ||
374 | handle_internal_command(argc, argv); | ||
375 | die("cannot handle %s internally", cmd); | ||
376 | } | ||
377 | |||
378 | /* Look for flags.. */ | ||
379 | argv++; | ||
380 | argc--; | ||
381 | handle_options(&argv, &argc, NULL); | ||
382 | commit_pager_choice(); | ||
383 | if (argc > 0) { | ||
384 | if (!prefixcmp(argv[0], "--")) | ||
385 | argv[0] += 2; | ||
386 | } else { | ||
387 | /* The user didn't specify a command; give them help */ | ||
388 | printf("\n usage: %s\n\n", perf_usage_string); | ||
389 | list_common_cmds_help(); | ||
390 | printf("\n %s\n\n", perf_more_info_string); | ||
391 | exit(1); | ||
392 | } | ||
393 | cmd = argv[0]; | ||
394 | |||
395 | /* | ||
396 | * We use PATH to find perf commands, but we prepend some higher | ||
397 | * precidence paths: the "--exec-path" option, the PERF_EXEC_PATH | ||
398 | * environment, and the $(perfexecdir) from the Makefile at build | ||
399 | * time. | ||
400 | */ | ||
401 | setup_path(); | ||
402 | |||
403 | while (1) { | ||
404 | static int done_help = 0; | ||
405 | static int was_alias = 0; | ||
406 | |||
407 | was_alias = run_argv(&argc, &argv); | ||
408 | if (errno != ENOENT) | ||
409 | break; | ||
410 | |||
411 | if (was_alias) { | ||
412 | fprintf(stderr, "Expansion of alias '%s' failed; " | ||
413 | "'%s' is not a perf-command\n", | ||
414 | cmd, argv[0]); | ||
415 | exit(1); | ||
416 | } | ||
417 | if (!done_help) { | ||
418 | cmd = argv[0] = help_unknown_cmd(cmd); | ||
419 | done_help = 1; | ||
420 | } else | ||
421 | break; | ||
422 | } | ||
423 | |||
424 | fprintf(stderr, "Failed to run command '%s': %s\n", | ||
425 | cmd, strerror(errno)); | ||
426 | |||
427 | return 1; | ||
428 | } | ||