diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-05-26 03:17:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-26 06:33:04 -0400 |
commit | b456bae0ff4f3cf91639dd32b2bfc49b1c30b4b0 (patch) | |
tree | 9f1843233e526764f6d6e7d63b1fc7119bd0660f /Documentation/perf_counter/builtin-top.c | |
parent | 5242519b0296d128425368fc6ab17f541d5fa775 (diff) |
perf top: Convert to Git option parsing
Remove getopt usage and use Git's much more advanced and more compact
command option library.
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-top.c')
-rw-r--r-- | Documentation/perf_counter/builtin-top.c | 559 |
1 files changed, 105 insertions, 454 deletions
diff --git a/Documentation/perf_counter/builtin-top.c b/Documentation/perf_counter/builtin-top.c index 626b32076499..87b925c8f8e8 100644 --- a/Documentation/perf_counter/builtin-top.c +++ b/Documentation/perf_counter/builtin-top.c | |||
@@ -45,8 +45,10 @@ | |||
45 | 45 | ||
46 | #include "perf.h" | 46 | #include "perf.h" |
47 | #include "util/util.h" | 47 | #include "util/util.h" |
48 | #include "util/util.h" | ||
49 | #include "util/parse-options.h" | ||
50 | #include "util/parse-events.h" | ||
48 | 51 | ||
49 | #include <getopt.h> | ||
50 | #include <assert.h> | 52 | #include <assert.h> |
51 | #include <fcntl.h> | 53 | #include <fcntl.h> |
52 | 54 | ||
@@ -70,8 +72,7 @@ | |||
70 | 72 | ||
71 | static int system_wide = 0; | 73 | static int system_wide = 0; |
72 | 74 | ||
73 | static int nr_counters = 0; | 75 | static __u64 default_event_id[MAX_COUNTERS] = { |
74 | static __u64 event_id[MAX_COUNTERS] = { | ||
75 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), | 76 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), |
76 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), | 77 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), |
77 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), | 78 | EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), |
@@ -88,7 +89,7 @@ static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | |||
88 | 89 | ||
89 | static __u64 count_filter = 100; | 90 | static __u64 count_filter = 100; |
90 | 91 | ||
91 | static int tid = -1; | 92 | static int target_pid = -1; |
92 | static int profile_cpu = -1; | 93 | static int profile_cpu = -1; |
93 | static int nr_cpus = 0; | 94 | static int nr_cpus = 0; |
94 | static int nmi = 1; | 95 | static int nmi = 1; |
@@ -100,8 +101,6 @@ static int use_mmap = 0; | |||
100 | static int use_munmap = 0; | 101 | static int use_munmap = 0; |
101 | static int freq = 0; | 102 | static int freq = 0; |
102 | 103 | ||
103 | static char *vmlinux; | ||
104 | |||
105 | static char *sym_filter; | 104 | static char *sym_filter; |
106 | static unsigned long filter_start; | 105 | static unsigned long filter_start; |
107 | static unsigned long filter_end; | 106 | static unsigned long filter_end; |
@@ -110,18 +109,6 @@ static int delay_secs = 2; | |||
110 | static int zero; | 109 | static int zero; |
111 | static int dump_symtab; | 110 | static int dump_symtab; |
112 | 111 | ||
113 | static int scale; | ||
114 | |||
115 | struct source_line { | ||
116 | uint64_t EIP; | ||
117 | unsigned long count; | ||
118 | char *line; | ||
119 | struct source_line *next; | ||
120 | }; | ||
121 | |||
122 | static struct source_line *lines; | ||
123 | static struct source_line **lines_tail; | ||
124 | |||
125 | static const unsigned int default_count[] = { | 112 | static const unsigned int default_count[] = { |
126 | 1000000, | 113 | 1000000, |
127 | 1000000, | 114 | 1000000, |
@@ -131,194 +118,6 @@ static const unsigned int default_count[] = { | |||
131 | 10000, | 118 | 10000, |
132 | }; | 119 | }; |
133 | 120 | ||
134 | static char *hw_event_names[] = { | ||
135 | "CPU cycles", | ||
136 | "instructions", | ||
137 | "cache references", | ||
138 | "cache misses", | ||
139 | "branches", | ||
140 | "branch misses", | ||
141 | "bus cycles", | ||
142 | }; | ||
143 | |||
144 | static char *sw_event_names[] = { | ||
145 | "cpu clock ticks", | ||
146 | "task clock ticks", | ||
147 | "pagefaults", | ||
148 | "context switches", | ||
149 | "CPU migrations", | ||
150 | "minor faults", | ||
151 | "major faults", | ||
152 | }; | ||
153 | |||
154 | struct event_symbol { | ||
155 | __u64 event; | ||
156 | char *symbol; | ||
157 | }; | ||
158 | |||
159 | static struct event_symbol event_symbols[] = { | ||
160 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cpu-cycles", }, | ||
161 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES), "cycles", }, | ||
162 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS), "instructions", }, | ||
163 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES), "cache-references", }, | ||
164 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES), "cache-misses", }, | ||
165 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branch-instructions", }, | ||
166 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS), "branches", }, | ||
167 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES), "branch-misses", }, | ||
168 | {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES), "bus-cycles", }, | ||
169 | |||
170 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK), "cpu-clock", }, | ||
171 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK), "task-clock", }, | ||
172 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "page-faults", }, | ||
173 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS), "faults", }, | ||
174 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN), "minor-faults", }, | ||
175 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ), "major-faults", }, | ||
176 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "context-switches", }, | ||
177 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES), "cs", }, | ||
178 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "cpu-migrations", }, | ||
179 | {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS), "migrations", }, | ||
180 | }; | ||
181 | |||
182 | #define __PERF_COUNTER_FIELD(config, name) \ | ||
183 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | ||
184 | |||
185 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | ||
186 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | ||
187 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | ||
188 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | ||
189 | |||
190 | static void display_events_help(void) | ||
191 | { | ||
192 | unsigned int i; | ||
193 | __u64 e; | ||
194 | |||
195 | printf( | ||
196 | " -e EVENT --event=EVENT # symbolic-name abbreviations"); | ||
197 | |||
198 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
199 | int type, id; | ||
200 | |||
201 | e = event_symbols[i].event; | ||
202 | type = PERF_COUNTER_TYPE(e); | ||
203 | id = PERF_COUNTER_ID(e); | ||
204 | |||
205 | printf("\n %d:%d: %-20s", | ||
206 | type, id, event_symbols[i].symbol); | ||
207 | } | ||
208 | |||
209 | printf("\n" | ||
210 | " rNNN: raw PMU events (eventsel+umask)\n\n"); | ||
211 | } | ||
212 | |||
213 | static void display_help(void) | ||
214 | { | ||
215 | printf( | ||
216 | "Usage: kerneltop [<options>]\n" | ||
217 | " Or: kerneltop -S [<options>] COMMAND [ARGS]\n\n" | ||
218 | "KernelTop Options (up to %d event types can be specified at once):\n\n", | ||
219 | MAX_COUNTERS); | ||
220 | |||
221 | display_events_help(); | ||
222 | |||
223 | printf( | ||
224 | " -c CNT --count=CNT # event period to sample\n\n" | ||
225 | " -C CPU --cpu=CPU # CPU (-1 for all) [default: -1]\n" | ||
226 | " -p PID --pid=PID # PID of sampled task (-1 for all) [default: -1]\n\n" | ||
227 | " -l # show scale factor for RR events\n" | ||
228 | " -d delay --delay=<seconds> # sampling/display delay [default: 2]\n" | ||
229 | " -f CNT --filter=CNT # min-event-count filter [default: 100]\n\n" | ||
230 | " -r prio --realtime=<prio> # event acquisition runs with SCHED_FIFO policy\n" | ||
231 | " -s symbol --symbol=<symbol> # function to be showed annotated one-shot\n" | ||
232 | " -x path --vmlinux=<path> # the vmlinux binary, required for -s use\n" | ||
233 | " -z --zero # zero counts after display\n" | ||
234 | " -D --dump_symtab # dump symbol table to stderr on startup\n" | ||
235 | " -m pages --mmap_pages=<pages> # number of mmap data pages\n" | ||
236 | " -M --mmap_info # print mmap info stream\n" | ||
237 | " -U --munmap_info # print munmap info stream\n" | ||
238 | ); | ||
239 | |||
240 | exit(0); | ||
241 | } | ||
242 | |||
243 | static char *event_name(int ctr) | ||
244 | { | ||
245 | __u64 config = event_id[ctr]; | ||
246 | int type = PERF_COUNTER_TYPE(config); | ||
247 | int id = PERF_COUNTER_ID(config); | ||
248 | static char buf[32]; | ||
249 | |||
250 | if (PERF_COUNTER_RAW(config)) { | ||
251 | sprintf(buf, "raw 0x%llx", PERF_COUNTER_CONFIG(config)); | ||
252 | return buf; | ||
253 | } | ||
254 | |||
255 | switch (type) { | ||
256 | case PERF_TYPE_HARDWARE: | ||
257 | if (id < PERF_HW_EVENTS_MAX) | ||
258 | return hw_event_names[id]; | ||
259 | return "unknown-hardware"; | ||
260 | |||
261 | case PERF_TYPE_SOFTWARE: | ||
262 | if (id < PERF_SW_EVENTS_MAX) | ||
263 | return sw_event_names[id]; | ||
264 | return "unknown-software"; | ||
265 | |||
266 | default: | ||
267 | break; | ||
268 | } | ||
269 | |||
270 | return "unknown"; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Each event can have multiple symbolic names. | ||
275 | * Symbolic names are (almost) exactly matched. | ||
276 | */ | ||
277 | static __u64 match_event_symbols(char *str) | ||
278 | { | ||
279 | __u64 config, id; | ||
280 | int type; | ||
281 | unsigned int i; | ||
282 | |||
283 | if (sscanf(str, "r%llx", &config) == 1) | ||
284 | return config | PERF_COUNTER_RAW_MASK; | ||
285 | |||
286 | if (sscanf(str, "%d:%llu", &type, &id) == 2) | ||
287 | return EID(type, id); | ||
288 | |||
289 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
290 | if (!strncmp(str, event_symbols[i].symbol, | ||
291 | strlen(event_symbols[i].symbol))) | ||
292 | return event_symbols[i].event; | ||
293 | } | ||
294 | |||
295 | return ~0ULL; | ||
296 | } | ||
297 | |||
298 | static int parse_events(char *str) | ||
299 | { | ||
300 | __u64 config; | ||
301 | |||
302 | again: | ||
303 | if (nr_counters == MAX_COUNTERS) | ||
304 | return -1; | ||
305 | |||
306 | config = match_event_symbols(str); | ||
307 | if (config == ~0ULL) | ||
308 | return -1; | ||
309 | |||
310 | event_id[nr_counters] = config; | ||
311 | nr_counters++; | ||
312 | |||
313 | str = strstr(str, ","); | ||
314 | if (str) { | ||
315 | str++; | ||
316 | goto again; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /* | 121 | /* |
323 | * Symbols | 122 | * Symbols |
324 | */ | 123 | */ |
@@ -331,7 +130,6 @@ struct sym_entry { | |||
331 | char *sym; | 130 | char *sym; |
332 | unsigned long count[MAX_COUNTERS]; | 131 | unsigned long count[MAX_COUNTERS]; |
333 | int skip; | 132 | int skip; |
334 | struct source_line *source; | ||
335 | }; | 133 | }; |
336 | 134 | ||
337 | #define MAX_SYMS 100000 | 135 | #define MAX_SYMS 100000 |
@@ -342,8 +140,6 @@ struct sym_entry *sym_filter_entry; | |||
342 | 140 | ||
343 | static struct sym_entry sym_table[MAX_SYMS]; | 141 | static struct sym_entry sym_table[MAX_SYMS]; |
344 | 142 | ||
345 | static void show_details(struct sym_entry *sym); | ||
346 | |||
347 | /* | 143 | /* |
348 | * Ordering weight: count-1 * count-2 * ... / count-n | 144 | * Ordering weight: count-1 * count-2 * ... / count-n |
349 | */ | 145 | */ |
@@ -419,15 +215,15 @@ static void print_sym_table(void) | |||
419 | 215 | ||
420 | printf( "], "); | 216 | printf( "], "); |
421 | 217 | ||
422 | if (tid != -1) | 218 | if (target_pid != -1) |
423 | printf(" (tid: %d", tid); | 219 | printf(" (target_pid: %d", target_pid); |
424 | else | 220 | else |
425 | printf(" (all"); | 221 | printf(" (all"); |
426 | 222 | ||
427 | if (profile_cpu != -1) | 223 | if (profile_cpu != -1) |
428 | printf(", cpu: %d)\n", profile_cpu); | 224 | printf(", cpu: %d)\n", profile_cpu); |
429 | else { | 225 | else { |
430 | if (tid != -1) | 226 | if (target_pid != -1) |
431 | printf(")\n"); | 227 | printf(")\n"); |
432 | else | 228 | else |
433 | printf(", %d CPUs)\n", nr_cpus); | 229 | printf(", %d CPUs)\n", nr_cpus); |
@@ -463,9 +259,6 @@ static void print_sym_table(void) | |||
463 | pcnt, tmp[i].addr, tmp[i].sym); | 259 | pcnt, tmp[i].addr, tmp[i].sym); |
464 | } | 260 | } |
465 | 261 | ||
466 | if (sym_filter_entry) | ||
467 | show_details(sym_filter_entry); | ||
468 | |||
469 | { | 262 | { |
470 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 263 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
471 | 264 | ||
@@ -628,134 +421,8 @@ static void parse_symbols(void) | |||
628 | } | 421 | } |
629 | } | 422 | } |
630 | 423 | ||
631 | /* | ||
632 | * Source lines | ||
633 | */ | ||
634 | |||
635 | static void parse_vmlinux(char *filename) | ||
636 | { | ||
637 | FILE *file; | ||
638 | char command[PATH_MAX*2]; | ||
639 | if (!filename) | ||
640 | return; | ||
641 | |||
642 | sprintf(command, "objdump --start-address=0x%016lx --stop-address=0x%016lx -dS %s", filter_start, filter_end, filename); | ||
643 | |||
644 | file = popen(command, "r"); | ||
645 | if (!file) | ||
646 | return; | ||
647 | |||
648 | lines_tail = &lines; | ||
649 | while (!feof(file)) { | ||
650 | struct source_line *src; | ||
651 | size_t dummy = 0; | ||
652 | char *c; | ||
653 | |||
654 | src = malloc(sizeof(struct source_line)); | ||
655 | assert(src != NULL); | ||
656 | memset(src, 0, sizeof(struct source_line)); | ||
657 | |||
658 | if (getline(&src->line, &dummy, file) < 0) | ||
659 | break; | ||
660 | if (!src->line) | ||
661 | break; | ||
662 | |||
663 | c = strchr(src->line, '\n'); | ||
664 | if (c) | ||
665 | *c = 0; | ||
666 | |||
667 | src->next = NULL; | ||
668 | *lines_tail = src; | ||
669 | lines_tail = &src->next; | ||
670 | |||
671 | if (strlen(src->line)>8 && src->line[8] == ':') | ||
672 | src->EIP = strtoull(src->line, NULL, 16); | ||
673 | if (strlen(src->line)>8 && src->line[16] == ':') | ||
674 | src->EIP = strtoull(src->line, NULL, 16); | ||
675 | } | ||
676 | pclose(file); | ||
677 | } | ||
678 | |||
679 | static void record_precise_ip(uint64_t ip) | ||
680 | { | ||
681 | struct source_line *line; | ||
682 | |||
683 | for (line = lines; line; line = line->next) { | ||
684 | if (line->EIP == ip) | ||
685 | line->count++; | ||
686 | if (line->EIP > ip) | ||
687 | break; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | static void lookup_sym_in_vmlinux(struct sym_entry *sym) | ||
692 | { | ||
693 | struct source_line *line; | ||
694 | char pattern[PATH_MAX]; | ||
695 | sprintf(pattern, "<%s>:", sym->sym); | ||
696 | |||
697 | for (line = lines; line; line = line->next) { | ||
698 | if (strstr(line->line, pattern)) { | ||
699 | sym->source = line; | ||
700 | break; | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | |||
705 | static void show_lines(struct source_line *line_queue, int line_queue_count) | ||
706 | { | ||
707 | int i; | ||
708 | struct source_line *line; | ||
709 | |||
710 | line = line_queue; | ||
711 | for (i = 0; i < line_queue_count; i++) { | ||
712 | printf("%8li\t%s\n", line->count, line->line); | ||
713 | line = line->next; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | #define TRACE_COUNT 3 | 424 | #define TRACE_COUNT 3 |
718 | 425 | ||
719 | static void show_details(struct sym_entry *sym) | ||
720 | { | ||
721 | struct source_line *line; | ||
722 | struct source_line *line_queue = NULL; | ||
723 | int displayed = 0; | ||
724 | int line_queue_count = 0; | ||
725 | |||
726 | if (!sym->source) | ||
727 | lookup_sym_in_vmlinux(sym); | ||
728 | if (!sym->source) | ||
729 | return; | ||
730 | |||
731 | printf("Showing details for %s\n", sym->sym); | ||
732 | |||
733 | line = sym->source; | ||
734 | while (line) { | ||
735 | if (displayed && strstr(line->line, ">:")) | ||
736 | break; | ||
737 | |||
738 | if (!line_queue_count) | ||
739 | line_queue = line; | ||
740 | line_queue_count ++; | ||
741 | |||
742 | if (line->count >= count_filter) { | ||
743 | show_lines(line_queue, line_queue_count); | ||
744 | line_queue_count = 0; | ||
745 | line_queue = NULL; | ||
746 | } else if (line_queue_count > TRACE_COUNT) { | ||
747 | line_queue = line_queue->next; | ||
748 | line_queue_count --; | ||
749 | } | ||
750 | |||
751 | line->count = 0; | ||
752 | displayed++; | ||
753 | if (displayed > 300) | ||
754 | break; | ||
755 | line = line->next; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | /* | 426 | /* |
760 | * Binary search in the histogram table and record the hit: | 427 | * Binary search in the histogram table and record the hit: |
761 | */ | 428 | */ |
@@ -764,8 +431,6 @@ static void record_ip(uint64_t ip, int counter) | |||
764 | int left_idx, middle_idx, right_idx, idx; | 431 | int left_idx, middle_idx, right_idx, idx; |
765 | unsigned long left, middle, right; | 432 | unsigned long left, middle, right; |
766 | 433 | ||
767 | record_precise_ip(ip); | ||
768 | |||
769 | left_idx = 0; | 434 | left_idx = 0; |
770 | right_idx = sym_table_count-1; | 435 | right_idx = sym_table_count-1; |
771 | assert(ip <= max_ip && ip >= min_ip); | 436 | assert(ip <= max_ip && ip >= min_ip); |
@@ -822,97 +487,6 @@ static void process_event(uint64_t ip, int counter) | |||
822 | record_ip(ip, counter); | 487 | record_ip(ip, counter); |
823 | } | 488 | } |
824 | 489 | ||
825 | static void process_options(int argc, char **argv) | ||
826 | { | ||
827 | int error = 0, counter; | ||
828 | |||
829 | for (;;) { | ||
830 | int option_index = 0; | ||
831 | /** Options for getopt */ | ||
832 | static struct option long_options[] = { | ||
833 | {"count", required_argument, NULL, 'c'}, | ||
834 | {"cpu", required_argument, NULL, 'C'}, | ||
835 | {"delay", required_argument, NULL, 'd'}, | ||
836 | {"dump_symtab", no_argument, NULL, 'D'}, | ||
837 | {"event", required_argument, NULL, 'e'}, | ||
838 | {"filter", required_argument, NULL, 'f'}, | ||
839 | {"group", required_argument, NULL, 'g'}, | ||
840 | {"help", no_argument, NULL, 'h'}, | ||
841 | {"nmi", required_argument, NULL, 'n'}, | ||
842 | {"mmap_info", no_argument, NULL, 'M'}, | ||
843 | {"mmap_pages", required_argument, NULL, 'm'}, | ||
844 | {"munmap_info", no_argument, NULL, 'U'}, | ||
845 | {"pid", required_argument, NULL, 'p'}, | ||
846 | {"realtime", required_argument, NULL, 'r'}, | ||
847 | {"scale", no_argument, NULL, 'l'}, | ||
848 | {"symbol", required_argument, NULL, 's'}, | ||
849 | {"stat", no_argument, NULL, 'S'}, | ||
850 | {"vmlinux", required_argument, NULL, 'x'}, | ||
851 | {"zero", no_argument, NULL, 'z'}, | ||
852 | {"freq", required_argument, NULL, 'F'}, | ||
853 | {NULL, 0, NULL, 0 } | ||
854 | }; | ||
855 | int c = getopt_long(argc, argv, "+:ac:C:d:De:f:g:hln:m:p:r:s:Sx:zMUF:", | ||
856 | long_options, &option_index); | ||
857 | if (c == -1) | ||
858 | break; | ||
859 | |||
860 | switch (c) { | ||
861 | case 'a': system_wide = 1; break; | ||
862 | case 'c': default_interval = atoi(optarg); break; | ||
863 | case 'C': | ||
864 | /* CPU and PID are mutually exclusive */ | ||
865 | if (tid != -1) { | ||
866 | printf("WARNING: CPU switch overriding PID\n"); | ||
867 | sleep(1); | ||
868 | tid = -1; | ||
869 | } | ||
870 | profile_cpu = atoi(optarg); break; | ||
871 | case 'd': delay_secs = atoi(optarg); break; | ||
872 | case 'D': dump_symtab = 1; break; | ||
873 | |||
874 | case 'e': error = parse_events(optarg); break; | ||
875 | |||
876 | case 'f': count_filter = atoi(optarg); break; | ||
877 | case 'g': group = atoi(optarg); break; | ||
878 | case 'h': display_help(); break; | ||
879 | case 'l': scale = 1; break; | ||
880 | case 'n': nmi = atoi(optarg); break; | ||
881 | case 'p': | ||
882 | /* CPU and PID are mutually exclusive */ | ||
883 | if (profile_cpu != -1) { | ||
884 | printf("WARNING: PID switch overriding CPU\n"); | ||
885 | sleep(1); | ||
886 | profile_cpu = -1; | ||
887 | } | ||
888 | tid = atoi(optarg); break; | ||
889 | case 'r': realtime_prio = atoi(optarg); break; | ||
890 | case 's': sym_filter = strdup(optarg); break; | ||
891 | case 'x': vmlinux = strdup(optarg); break; | ||
892 | case 'z': zero = 1; break; | ||
893 | case 'm': mmap_pages = atoi(optarg); break; | ||
894 | case 'M': use_mmap = 1; break; | ||
895 | case 'U': use_munmap = 1; break; | ||
896 | case 'F': freq = 1; default_interval = atoi(optarg); break; | ||
897 | default: error = 1; break; | ||
898 | } | ||
899 | } | ||
900 | if (error) | ||
901 | display_help(); | ||
902 | |||
903 | if (!nr_counters) { | ||
904 | nr_counters = 1; | ||
905 | event_id[0] = 0; | ||
906 | } | ||
907 | |||
908 | for (counter = 0; counter < nr_counters; counter++) { | ||
909 | if (event_count[counter]) | ||
910 | continue; | ||
911 | |||
912 | event_count[counter] = default_interval; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | struct mmap_data { | 490 | struct mmap_data { |
917 | int counter; | 491 | int counter; |
918 | void *base; | 492 | void *base; |
@@ -973,11 +547,11 @@ static void mmap_read(struct mmap_data *md) | |||
973 | struct ip_event { | 547 | struct ip_event { |
974 | struct perf_event_header header; | 548 | struct perf_event_header header; |
975 | __u64 ip; | 549 | __u64 ip; |
976 | __u32 pid, tid; | 550 | __u32 pid, target_pid; |
977 | }; | 551 | }; |
978 | struct mmap_event { | 552 | struct mmap_event { |
979 | struct perf_event_header header; | 553 | struct perf_event_header header; |
980 | __u32 pid, tid; | 554 | __u32 pid, target_pid; |
981 | __u64 start; | 555 | __u64 start; |
982 | __u64 len; | 556 | __u64 len; |
983 | __u64 pgoff; | 557 | __u64 pgoff; |
@@ -1043,7 +617,7 @@ static void mmap_read(struct mmap_data *md) | |||
1043 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 617 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
1044 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 618 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
1045 | 619 | ||
1046 | int cmd_top(int argc, char **argv, const char *prefix) | 620 | static int __cmd_top(void) |
1047 | { | 621 | { |
1048 | struct perf_counter_hw_event hw_event; | 622 | struct perf_counter_hw_event hw_event; |
1049 | pthread_t thread; | 623 | pthread_t thread; |
@@ -1051,27 +625,12 @@ int cmd_top(int argc, char **argv, const char *prefix) | |||
1051 | unsigned int cpu; | 625 | unsigned int cpu; |
1052 | int ret; | 626 | int ret; |
1053 | 627 | ||
1054 | page_size = sysconf(_SC_PAGE_SIZE); | ||
1055 | |||
1056 | process_options(argc, argv); | ||
1057 | |||
1058 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | ||
1059 | assert(nr_cpus <= MAX_NR_CPUS); | ||
1060 | assert(nr_cpus >= 0); | ||
1061 | |||
1062 | if (tid != -1 || profile_cpu != -1) | ||
1063 | nr_cpus = 1; | ||
1064 | |||
1065 | parse_symbols(); | ||
1066 | if (vmlinux && sym_filter_entry) | ||
1067 | parse_vmlinux(vmlinux); | ||
1068 | |||
1069 | for (i = 0; i < nr_cpus; i++) { | 628 | for (i = 0; i < nr_cpus; i++) { |
1070 | group_fd = -1; | 629 | group_fd = -1; |
1071 | for (counter = 0; counter < nr_counters; counter++) { | 630 | for (counter = 0; counter < nr_counters; counter++) { |
1072 | 631 | ||
1073 | cpu = profile_cpu; | 632 | cpu = profile_cpu; |
1074 | if (tid == -1 && profile_cpu == -1) | 633 | if (target_pid == -1 && profile_cpu == -1) |
1075 | cpu = i; | 634 | cpu = i; |
1076 | 635 | ||
1077 | memset(&hw_event, 0, sizeof(hw_event)); | 636 | memset(&hw_event, 0, sizeof(hw_event)); |
@@ -1083,7 +642,7 @@ int cmd_top(int argc, char **argv, const char *prefix) | |||
1083 | hw_event.munmap = use_munmap; | 642 | hw_event.munmap = use_munmap; |
1084 | hw_event.freq = freq; | 643 | hw_event.freq = freq; |
1085 | 644 | ||
1086 | fd[i][counter] = sys_perf_counter_open(&hw_event, tid, cpu, group_fd, 0); | 645 | fd[i][counter] = sys_perf_counter_open(&hw_event, target_pid, cpu, group_fd, 0); |
1087 | if (fd[i][counter] < 0) { | 646 | if (fd[i][counter] < 0) { |
1088 | int err = errno; | 647 | int err = errno; |
1089 | printf("kerneltop error: syscall returned with %d (%s)\n", | 648 | printf("kerneltop error: syscall returned with %d (%s)\n", |
@@ -1147,3 +706,95 @@ int cmd_top(int argc, char **argv, const char *prefix) | |||
1147 | 706 | ||
1148 | return 0; | 707 | return 0; |
1149 | } | 708 | } |
709 | |||
710 | static const char * const top_usage[] = { | ||
711 | "perf top [<options>]", | ||
712 | NULL | ||
713 | }; | ||
714 | |||
715 | static char events_help_msg[EVENTS_HELP_MAX]; | ||
716 | |||
717 | static const struct option options[] = { | ||
718 | OPT_CALLBACK('e', "event", NULL, "event", | ||
719 | events_help_msg, parse_events), | ||
720 | OPT_INTEGER('c', "count", &default_interval, | ||
721 | "event period to sample"), | ||
722 | OPT_INTEGER('p', "pid", &target_pid, | ||
723 | "profile events on existing pid"), | ||
724 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | ||
725 | "system-wide collection from all CPUs"), | ||
726 | OPT_INTEGER('C', "CPU", &profile_cpu, | ||
727 | "CPU to profile on"), | ||
728 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | ||
729 | "number of mmap data pages"), | ||
730 | OPT_INTEGER('r', "realtime", &realtime_prio, | ||
731 | "collect data with this RT SCHED_FIFO priority"), | ||
732 | OPT_INTEGER('d', "delay", &realtime_prio, | ||
733 | "number of seconds to delay between refreshes"), | ||
734 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, | ||
735 | "dump the symbol table used for profiling"), | ||
736 | OPT_INTEGER('f', "--count-filter", &count_filter, | ||
737 | "only display functions with more events than this"), | ||
738 | OPT_BOOLEAN('g', "group", &group, | ||
739 | "put the counters into a counter group"), | ||
740 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", | ||
741 | "only display symbols matchig this pattern"), | ||
742 | OPT_BOOLEAN('z', "zero", &group, | ||
743 | "zero history across updates"), | ||
744 | OPT_BOOLEAN('M', "use-mmap", &use_mmap, | ||
745 | "track mmap events"), | ||
746 | OPT_BOOLEAN('U', "use-munmap", &use_munmap, | ||
747 | "track munmap events"), | ||
748 | OPT_INTEGER('F', "--freq", &freq, | ||
749 | "profile at this frequency"), | ||
750 | OPT_END() | ||
751 | }; | ||
752 | |||
753 | int cmd_top(int argc, const char **argv, const char *prefix) | ||
754 | { | ||
755 | int counter; | ||
756 | |||
757 | page_size = sysconf(_SC_PAGE_SIZE); | ||
758 | |||
759 | create_events_help(events_help_msg); | ||
760 | memcpy(event_id, default_event_id, sizeof(default_event_id)); | ||
761 | |||
762 | argc = parse_options(argc, argv, options, top_usage, 0); | ||
763 | if (argc) | ||
764 | usage_with_options(top_usage, options); | ||
765 | |||
766 | if (freq) { | ||
767 | default_interval = freq; | ||
768 | freq = 1; | ||
769 | } | ||
770 | |||
771 | /* CPU and PID are mutually exclusive */ | ||
772 | if (target_pid != -1 && profile_cpu != -1) { | ||
773 | printf("WARNING: PID switch overriding CPU\n"); | ||
774 | sleep(1); | ||
775 | profile_cpu = -1; | ||
776 | } | ||
777 | |||
778 | if (!nr_counters) { | ||
779 | nr_counters = 1; | ||
780 | event_id[0] = 0; | ||
781 | } | ||
782 | |||
783 | for (counter = 0; counter < nr_counters; counter++) { | ||
784 | if (event_count[counter]) | ||
785 | continue; | ||
786 | |||
787 | event_count[counter] = default_interval; | ||
788 | } | ||
789 | |||
790 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | ||
791 | assert(nr_cpus <= MAX_NR_CPUS); | ||
792 | assert(nr_cpus >= 0); | ||
793 | |||
794 | if (target_pid != -1 || profile_cpu != -1) | ||
795 | nr_cpus = 1; | ||
796 | |||
797 | parse_symbols(); | ||
798 | |||
799 | return __cmd_top(); | ||
800 | } | ||