diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-05-26 03:17:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-26 05:26:32 -0400 |
commit | 0e9b20b8a1cab6c6ab4f98f917a2d98783103969 (patch) | |
tree | fb294181846f82f1cd3dc96bcbd8a8506c3f8b27 /Documentation/perf_counter/builtin-record.c | |
parent | 4e97ddf09ee3ce715fc334399bae4cc0c0a13057 (diff) |
perf record: Convert to Git option parsing
Remove getopt usage and use Git's much more advanced and more compact
command option library.
Git's library (util/parse-options.[ch]) constructs help texts and
error messages automatically, and has a number of other convenience
features as well.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/builtin-record.c')
-rw-r--r-- | Documentation/perf_counter/builtin-record.c | 372 |
1 files changed, 174 insertions, 198 deletions
diff --git a/Documentation/perf_counter/builtin-record.c b/Documentation/perf_counter/builtin-record.c index f225efaff9f5..f12a7822fcf1 100644 --- a/Documentation/perf_counter/builtin-record.c +++ b/Documentation/perf_counter/builtin-record.c | |||
@@ -2,6 +2,8 @@ | |||
2 | 2 | ||
3 | #include "perf.h" | 3 | #include "perf.h" |
4 | #include "util/util.h" | 4 | #include "util/util.h" |
5 | #include "util/parse-options.h" | ||
6 | #include "util/exec_cmd.h" | ||
5 | 7 | ||
6 | #include <sys/types.h> | 8 | #include <sys/types.h> |
7 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
@@ -11,7 +13,6 @@ | |||
11 | #include <stdlib.h> | 13 | #include <stdlib.h> |
12 | #include <string.h> | 14 | #include <string.h> |
13 | #include <limits.h> | 15 | #include <limits.h> |
14 | #include <getopt.h> | ||
15 | #include <assert.h> | 16 | #include <assert.h> |
16 | #include <fcntl.h> | 17 | #include <fcntl.h> |
17 | #include <stdio.h> | 18 | #include <stdio.h> |
@@ -33,8 +34,8 @@ | |||
33 | 34 | ||
34 | 35 | ||
35 | 36 | ||
36 | #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) | 37 | #define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) |
37 | #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) | 38 | #define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) |
38 | 39 | ||
39 | static int nr_counters = 0; | 40 | static int nr_counters = 0; |
40 | static __u64 event_id[MAX_COUNTERS] = { }; | 41 | static __u64 event_id[MAX_COUNTERS] = { }; |
@@ -45,7 +46,7 @@ static int nr_cpus = 0; | |||
45 | static unsigned int page_size; | 46 | static unsigned int page_size; |
46 | static unsigned int mmap_pages = 16; | 47 | static unsigned int mmap_pages = 16; |
47 | static int output; | 48 | static int output; |
48 | static char *output_name = "output.perf"; | 49 | static const char *output_name = "output.perf"; |
49 | static int group = 0; | 50 | static int group = 0; |
50 | static unsigned int realtime_prio = 0; | 51 | static unsigned int realtime_prio = 0; |
51 | static int system_wide = 0; | 52 | static int system_wide = 0; |
@@ -62,192 +63,6 @@ const unsigned int default_count[] = { | |||
62 | 10000, | 63 | 10000, |
63 | }; | 64 | }; |
64 | 65 | ||
65 | struct event_symbol { | ||
66 | __u64 event; | ||
67 | char *symbol; | ||
68 | }; | ||
69 | |||
70 | static struct event_symbol event_symbols[] = { | ||
71 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cpu-cycles", }, | ||
72 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cycles", }, | ||
73 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS), "instructions", }, | ||
74 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES), "cache-references", }, | ||
75 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES), "cache-misses", }, | ||
76 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branch-instructions", }, | ||
77 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branches", }, | ||
78 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES), "branch-misses", }, | ||
79 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES), "bus-cycles", }, | ||
80 | |||
81 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK), "cpu-clock", }, | ||
82 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), "task-clock", }, | ||
83 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "page-faults", }, | ||
84 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "faults", }, | ||
85 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN), "minor-faults", }, | ||
86 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ), "major-faults", }, | ||
87 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "context-switches", }, | ||
88 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "cs", }, | ||
89 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "cpu-migrations", }, | ||
90 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "migrations", }, | ||
91 | }; | ||
92 | |||
93 | /* | ||
94 | * Each event can have multiple symbolic names. | ||
95 | * Symbolic names are (almost) exactly matched. | ||
96 | */ | ||
97 | static __u64 match_event_symbols(char *str) | ||
98 | { | ||
99 | __u64 config, id; | ||
100 | int type; | ||
101 | unsigned int i; | ||
102 | |||
103 | if (sscanf(str, "r%llx", &config) == 1) | ||
104 | return config | PERF_COUNTER_RAW_MASK; | ||
105 | |||
106 | if (sscanf(str, "%d:%llu", &type, &id) == 2) | ||
107 | return EID(type, id); | ||
108 | |||
109 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
110 | if (!strncmp(str, event_symbols[i].symbol, | ||
111 | strlen(event_symbols[i].symbol))) | ||
112 | return event_symbols[i].event; | ||
113 | } | ||
114 | |||
115 | return ~0ULL; | ||
116 | } | ||
117 | |||
118 | static int parse_events(char *str) | ||
119 | { | ||
120 | __u64 config; | ||
121 | |||
122 | again: | ||
123 | if (nr_counters == MAX_COUNTERS) | ||
124 | return -1; | ||
125 | |||
126 | config = match_event_symbols(str); | ||
127 | if (config == ~0ULL) | ||
128 | return -1; | ||
129 | |||
130 | event_id[nr_counters] = config; | ||
131 | nr_counters++; | ||
132 | |||
133 | str = strstr(str, ","); | ||
134 | if (str) { | ||
135 | str++; | ||
136 | goto again; | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | #define __PERF_COUNTER_FIELD(config, name) \ | ||
143 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | ||
144 | |||
145 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | ||
146 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | ||
147 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | ||
148 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | ||
149 | |||
150 | static void display_events_help(void) | ||
151 | { | ||
152 | unsigned int i; | ||
153 | __u64 e; | ||
154 | |||
155 | printf( | ||
156 | " -e EVENT --event=EVENT # symbolic-name abbreviations"); | ||
157 | |||
158 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
159 | int type, id; | ||
160 | |||
161 | e = event_symbols[i].event; | ||
162 | type = PERF_COUNTER_TYPE(e); | ||
163 | id = PERF_COUNTER_ID(e); | ||
164 | |||
165 | printf("\n %d:%d: %-20s", | ||
166 | type, id, event_symbols[i].symbol); | ||
167 | } | ||
168 | |||
169 | printf("\n" | ||
170 | " rNNN: raw PMU events (eventsel+umask)\n\n"); | ||
171 | } | ||
172 | |||
173 | static void display_help(void) | ||
174 | { | ||
175 | printf( | ||
176 | "Usage: perf-record [<options>] <cmd>\n" | ||
177 | "perf-record Options (up to %d event types can be specified at once):\n\n", | ||
178 | MAX_COUNTERS); | ||
179 | |||
180 | display_events_help(); | ||
181 | |||
182 | printf( | ||
183 | " -c CNT --count=CNT # event period to sample\n" | ||
184 | " -m pages --mmap_pages=<pages> # number of mmap data pages\n" | ||
185 | " -o file --output=<file> # output file\n" | ||
186 | " -p pid --pid=<pid> # record events on existing pid\n" | ||
187 | " -r prio --realtime=<prio> # use RT prio\n" | ||
188 | " -s --system # system wide profiling\n" | ||
189 | ); | ||
190 | |||
191 | exit(0); | ||
192 | } | ||
193 | |||
194 | static void process_options(int argc, char * const argv[]) | ||
195 | { | ||
196 | int error = 0, counter; | ||
197 | |||
198 | for (;;) { | ||
199 | int option_index = 0; | ||
200 | /** Options for getopt */ | ||
201 | static struct option long_options[] = { | ||
202 | {"count", required_argument, NULL, 'c'}, | ||
203 | {"event", required_argument, NULL, 'e'}, | ||
204 | {"mmap_pages", required_argument, NULL, 'm'}, | ||
205 | {"output", required_argument, NULL, 'o'}, | ||
206 | {"pid", required_argument, NULL, 'p'}, | ||
207 | {"realtime", required_argument, NULL, 'r'}, | ||
208 | {"system", no_argument, NULL, 's'}, | ||
209 | {"inherit", no_argument, NULL, 'i'}, | ||
210 | {"nmi", no_argument, NULL, 'n'}, | ||
211 | {NULL, 0, NULL, 0 } | ||
212 | }; | ||
213 | int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin", | ||
214 | long_options, &option_index); | ||
215 | if (c == -1) | ||
216 | break; | ||
217 | |||
218 | switch (c) { | ||
219 | case 'c': default_interval = atoi(optarg); break; | ||
220 | case 'e': error = parse_events(optarg); break; | ||
221 | case 'm': mmap_pages = atoi(optarg); break; | ||
222 | case 'o': output_name = strdup(optarg); break; | ||
223 | case 'p': target_pid = atoi(optarg); break; | ||
224 | case 'r': realtime_prio = atoi(optarg); break; | ||
225 | case 's': system_wide ^= 1; break; | ||
226 | case 'i': inherit ^= 1; break; | ||
227 | case 'n': nmi ^= 1; break; | ||
228 | default: error = 1; break; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if (argc - optind == 0 && target_pid == -1) | ||
233 | error = 1; | ||
234 | |||
235 | if (error) | ||
236 | display_help(); | ||
237 | |||
238 | if (!nr_counters) { | ||
239 | nr_counters = 1; | ||
240 | event_id[0] = 0; | ||
241 | } | ||
242 | |||
243 | for (counter = 0; counter < nr_counters; counter++) { | ||
244 | if (event_count[counter]) | ||
245 | continue; | ||
246 | |||
247 | event_count[counter] = default_interval; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | struct mmap_data { | 66 | struct mmap_data { |
252 | int counter; | 67 | int counter; |
253 | void *base; | 68 | void *base; |
@@ -538,16 +353,13 @@ static void open_counters(int cpu, pid_t pid) | |||
538 | nr_cpu++; | 353 | nr_cpu++; |
539 | } | 354 | } |
540 | 355 | ||
541 | int cmd_record(int argc, char * const argv[]) | 356 | static int __cmd_record(int argc, const char **argv) |
542 | { | 357 | { |
543 | int i, counter; | 358 | int i, counter; |
544 | pid_t pid; | 359 | pid_t pid; |
545 | int ret; | 360 | int ret; |
546 | 361 | ||
547 | page_size = sysconf(_SC_PAGE_SIZE); | 362 | page_size = sysconf(_SC_PAGE_SIZE); |
548 | |||
549 | process_options(argc, argv); | ||
550 | |||
551 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 363 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
552 | assert(nr_cpus <= MAX_NR_CPUS); | 364 | assert(nr_cpus <= MAX_NR_CPUS); |
553 | assert(nr_cpus >= 0); | 365 | assert(nr_cpus >= 0); |
@@ -558,9 +370,6 @@ int cmd_record(int argc, char * const argv[]) | |||
558 | exit(-1); | 370 | exit(-1); |
559 | } | 371 | } |
560 | 372 | ||
561 | argc -= optind; | ||
562 | argv += optind; | ||
563 | |||
564 | if (!system_wide) { | 373 | if (!system_wide) { |
565 | open_counters(-1, target_pid != -1 ? target_pid : 0); | 374 | open_counters(-1, target_pid != -1 ? target_pid : 0); |
566 | } else for (i = 0; i < nr_cpus; i++) | 375 | } else for (i = 0; i < nr_cpus; i++) |
@@ -575,7 +384,7 @@ int cmd_record(int argc, char * const argv[]) | |||
575 | perror("failed to fork"); | 384 | perror("failed to fork"); |
576 | 385 | ||
577 | if (!pid) { | 386 | if (!pid) { |
578 | if (execvp(argv[0], argv)) { | 387 | if (execvp(argv[0], (char **)argv)) { |
579 | perror(argv[0]); | 388 | perror(argv[0]); |
580 | exit(-1); | 389 | exit(-1); |
581 | } | 390 | } |
@@ -610,3 +419,170 @@ int cmd_record(int argc, char * const argv[]) | |||
610 | 419 | ||
611 | return 0; | 420 | return 0; |
612 | } | 421 | } |
422 | |||
423 | struct event_symbol { | ||
424 | __u64 event; | ||
425 | char *symbol; | ||
426 | }; | ||
427 | |||
428 | static struct event_symbol event_symbols[] = { | ||
429 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cpu-cycles", }, | ||
430 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cycles", }, | ||
431 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS), "instructions", }, | ||
432 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES), "cache-references", }, | ||
433 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES), "cache-misses", }, | ||
434 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branch-instructions", }, | ||
435 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branches", }, | ||
436 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES), "branch-misses", }, | ||
437 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES), "bus-cycles", }, | ||
438 | |||
439 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK), "cpu-clock", }, | ||
440 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), "task-clock", }, | ||
441 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "page-faults", }, | ||
442 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "faults", }, | ||
443 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN), "minor-faults", }, | ||
444 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ), "major-faults", }, | ||
445 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "context-switches", }, | ||
446 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "cs", }, | ||
447 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "cpu-migrations", }, | ||
448 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "migrations", }, | ||
449 | }; | ||
450 | |||
451 | /* | ||
452 | * Each event can have multiple symbolic names. | ||
453 | * Symbolic names are (almost) exactly matched. | ||
454 | */ | ||
455 | static __u64 match_event_symbols(const char *str) | ||
456 | { | ||
457 | __u64 config, id; | ||
458 | int type; | ||
459 | unsigned int i; | ||
460 | |||
461 | if (sscanf(str, "r%llx", &config) == 1) | ||
462 | return config | PERF_COUNTER_RAW_MASK; | ||
463 | |||
464 | if (sscanf(str, "%d:%llu", &type, &id) == 2) | ||
465 | return EID(type, id); | ||
466 | |||
467 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
468 | if (!strncmp(str, event_symbols[i].symbol, | ||
469 | strlen(event_symbols[i].symbol))) | ||
470 | return event_symbols[i].event; | ||
471 | } | ||
472 | |||
473 | return ~0ULL; | ||
474 | } | ||
475 | |||
476 | static int parse_events(const struct option *opt, const char *str, int unset) | ||
477 | { | ||
478 | __u64 config; | ||
479 | |||
480 | again: | ||
481 | if (nr_counters == MAX_COUNTERS) | ||
482 | return -1; | ||
483 | |||
484 | config = match_event_symbols(str); | ||
485 | if (config == ~0ULL) | ||
486 | return -1; | ||
487 | |||
488 | event_id[nr_counters] = config; | ||
489 | nr_counters++; | ||
490 | |||
491 | str = strstr(str, ","); | ||
492 | if (str) { | ||
493 | str++; | ||
494 | goto again; | ||
495 | } | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static char events_help[100000]; | ||
501 | |||
502 | #define __PERF_COUNTER_FIELD(config, name) \ | ||
503 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | ||
504 | |||
505 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | ||
506 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | ||
507 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | ||
508 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | ||
509 | |||
510 | |||
511 | |||
512 | static void create_events_help(void) | ||
513 | { | ||
514 | unsigned int i; | ||
515 | char *str; | ||
516 | __u64 e; | ||
517 | |||
518 | str = events_help; | ||
519 | |||
520 | str += sprintf(str, | ||
521 | "event name: ["); | ||
522 | |||
523 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
524 | int type, id; | ||
525 | |||
526 | e = event_symbols[i].event; | ||
527 | type = PERF_COUNTER_TYPE(e); | ||
528 | id = PERF_COUNTER_ID(e); | ||
529 | |||
530 | if (i) | ||
531 | str += sprintf(str, "|"); | ||
532 | |||
533 | str += sprintf(str, "%s", | ||
534 | event_symbols[i].symbol); | ||
535 | } | ||
536 | |||
537 | str += sprintf(str, "|rNNN]"); | ||
538 | } | ||
539 | |||
540 | static const char * const record_usage[] = { | ||
541 | "perf record [<options>] <command>", | ||
542 | NULL | ||
543 | }; | ||
544 | |||
545 | const struct option options[] = { | ||
546 | OPT_CALLBACK('e', "event", NULL, "event", | ||
547 | events_help, parse_events), | ||
548 | OPT_INTEGER('c', "count", &default_interval, | ||
549 | "event period to sample"), | ||
550 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | ||
551 | "number of mmap data pages"), | ||
552 | OPT_STRING('o', "output", &output_name, "file", | ||
553 | "output file name"), | ||
554 | OPT_BOOLEAN('i', "inherit", &inherit, | ||
555 | "child tasks inherit counters"), | ||
556 | OPT_INTEGER('p', "pid", &target_pid, | ||
557 | "record events on existing pid"), | ||
558 | OPT_INTEGER('r', "realtime", &realtime_prio, | ||
559 | "collect data with this RT SCHED_FIFO priority"), | ||
560 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | ||
561 | "system-wide collection from all CPUs"), | ||
562 | OPT_END() | ||
563 | }; | ||
564 | |||
565 | int cmd_record(int argc, const char **argv, const char *prefix) | ||
566 | { | ||
567 | int counter; | ||
568 | |||
569 | create_events_help(); | ||
570 | |||
571 | argc = parse_options(argc, argv, options, record_usage, 0); | ||
572 | if (!argc) | ||
573 | usage_with_options(record_usage, options); | ||
574 | |||
575 | if (!nr_counters) { | ||
576 | nr_counters = 1; | ||
577 | event_id[0] = 0; | ||
578 | } | ||
579 | |||
580 | for (counter = 0; counter < nr_counters; counter++) { | ||
581 | if (event_count[counter]) | ||
582 | continue; | ||
583 | |||
584 | event_count[counter] = default_interval; | ||
585 | } | ||
586 | |||
587 | return __cmd_record(argc, argv); | ||
588 | } | ||