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