diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 496 |
1 files changed, 456 insertions, 40 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f139f1ab9333..d58701346b1e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <fcntl.h> | 31 | #include <fcntl.h> |
32 | 32 | ||
33 | #include <stdio.h> | 33 | #include <stdio.h> |
34 | #include <termios.h> | ||
35 | #include <unistd.h> | ||
34 | 36 | ||
35 | #include <errno.h> | 37 | #include <errno.h> |
36 | #include <time.h> | 38 | #include <time.h> |
@@ -54,7 +56,7 @@ static int system_wide = 0; | |||
54 | 56 | ||
55 | static int default_interval = 100000; | 57 | static int default_interval = 100000; |
56 | 58 | ||
57 | static u64 count_filter = 5; | 59 | static int count_filter = 5; |
58 | static int print_entries = 15; | 60 | static int print_entries = 15; |
59 | 61 | ||
60 | static int target_pid = -1; | 62 | static int target_pid = -1; |
@@ -69,15 +71,27 @@ static int freq = 0; | |||
69 | static int verbose = 0; | 71 | static int verbose = 0; |
70 | static char *vmlinux = NULL; | 72 | static char *vmlinux = NULL; |
71 | 73 | ||
72 | static char *sym_filter; | ||
73 | static unsigned long filter_start; | ||
74 | static unsigned long filter_end; | ||
75 | |||
76 | static int delay_secs = 2; | 74 | static int delay_secs = 2; |
77 | static int zero; | 75 | static int zero; |
78 | static int dump_symtab; | 76 | static int dump_symtab; |
79 | 77 | ||
80 | /* | 78 | /* |
79 | * Source | ||
80 | */ | ||
81 | |||
82 | struct source_line { | ||
83 | u64 eip; | ||
84 | unsigned long count[MAX_COUNTERS]; | ||
85 | char *line; | ||
86 | struct source_line *next; | ||
87 | }; | ||
88 | |||
89 | static char *sym_filter = NULL; | ||
90 | struct sym_entry *sym_filter_entry = NULL; | ||
91 | static int sym_pcnt_filter = 5; | ||
92 | static int sym_counter = 0; | ||
93 | |||
94 | /* | ||
81 | * Symbols | 95 | * Symbols |
82 | */ | 96 | */ |
83 | 97 | ||
@@ -91,9 +105,237 @@ struct sym_entry { | |||
91 | unsigned long snap_count; | 105 | unsigned long snap_count; |
92 | double weight; | 106 | double weight; |
93 | int skip; | 107 | int skip; |
108 | struct source_line *source; | ||
109 | struct source_line *lines; | ||
110 | struct source_line **lines_tail; | ||
111 | pthread_mutex_t source_lock; | ||
94 | }; | 112 | }; |
95 | 113 | ||
96 | struct sym_entry *sym_filter_entry; | 114 | /* |
115 | * Source functions | ||
116 | */ | ||
117 | |||
118 | static void parse_source(struct sym_entry *syme) | ||
119 | { | ||
120 | struct symbol *sym; | ||
121 | struct module *module; | ||
122 | struct section *section = NULL; | ||
123 | FILE *file; | ||
124 | char command[PATH_MAX*2], *path = vmlinux; | ||
125 | u64 start, end, len; | ||
126 | |||
127 | if (!syme) | ||
128 | return; | ||
129 | |||
130 | if (syme->lines) { | ||
131 | pthread_mutex_lock(&syme->source_lock); | ||
132 | goto out_assign; | ||
133 | } | ||
134 | |||
135 | sym = (struct symbol *)(syme + 1); | ||
136 | module = sym->module; | ||
137 | |||
138 | if (module) | ||
139 | path = module->path; | ||
140 | if (!path) | ||
141 | return; | ||
142 | |||
143 | start = sym->obj_start; | ||
144 | if (!start) | ||
145 | start = sym->start; | ||
146 | |||
147 | if (module) { | ||
148 | section = module->sections->find_section(module->sections, ".text"); | ||
149 | if (section) | ||
150 | start -= section->vma; | ||
151 | } | ||
152 | |||
153 | end = start + sym->end - sym->start + 1; | ||
154 | len = sym->end - sym->start; | ||
155 | |||
156 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); | ||
157 | |||
158 | file = popen(command, "r"); | ||
159 | if (!file) | ||
160 | return; | ||
161 | |||
162 | pthread_mutex_lock(&syme->source_lock); | ||
163 | syme->lines_tail = &syme->lines; | ||
164 | while (!feof(file)) { | ||
165 | struct source_line *src; | ||
166 | size_t dummy = 0; | ||
167 | char *c; | ||
168 | |||
169 | src = malloc(sizeof(struct source_line)); | ||
170 | assert(src != NULL); | ||
171 | memset(src, 0, sizeof(struct source_line)); | ||
172 | |||
173 | if (getline(&src->line, &dummy, file) < 0) | ||
174 | break; | ||
175 | if (!src->line) | ||
176 | break; | ||
177 | |||
178 | c = strchr(src->line, '\n'); | ||
179 | if (c) | ||
180 | *c = 0; | ||
181 | |||
182 | src->next = NULL; | ||
183 | *syme->lines_tail = src; | ||
184 | syme->lines_tail = &src->next; | ||
185 | |||
186 | if (strlen(src->line)>8 && src->line[8] == ':') { | ||
187 | src->eip = strtoull(src->line, NULL, 16); | ||
188 | if (section) | ||
189 | src->eip += section->vma; | ||
190 | } | ||
191 | if (strlen(src->line)>8 && src->line[16] == ':') { | ||
192 | src->eip = strtoull(src->line, NULL, 16); | ||
193 | if (section) | ||
194 | src->eip += section->vma; | ||
195 | } | ||
196 | } | ||
197 | pclose(file); | ||
198 | out_assign: | ||
199 | sym_filter_entry = syme; | ||
200 | pthread_mutex_unlock(&syme->source_lock); | ||
201 | } | ||
202 | |||
203 | static void __zero_source_counters(struct sym_entry *syme) | ||
204 | { | ||
205 | int i; | ||
206 | struct source_line *line; | ||
207 | |||
208 | line = syme->lines; | ||
209 | while (line) { | ||
210 | for (i = 0; i < nr_counters; i++) | ||
211 | line->count[i] = 0; | ||
212 | line = line->next; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | ||
217 | { | ||
218 | struct source_line *line; | ||
219 | |||
220 | if (syme != sym_filter_entry) | ||
221 | return; | ||
222 | |||
223 | if (pthread_mutex_trylock(&syme->source_lock)) | ||
224 | return; | ||
225 | |||
226 | if (!syme->source) | ||
227 | goto out_unlock; | ||
228 | |||
229 | for (line = syme->lines; line; line = line->next) { | ||
230 | if (line->eip == ip) { | ||
231 | line->count[counter]++; | ||
232 | break; | ||
233 | } | ||
234 | if (line->eip > ip) | ||
235 | break; | ||
236 | } | ||
237 | out_unlock: | ||
238 | pthread_mutex_unlock(&syme->source_lock); | ||
239 | } | ||
240 | |||
241 | static void lookup_sym_source(struct sym_entry *syme) | ||
242 | { | ||
243 | struct symbol *symbol = (struct symbol *)(syme + 1); | ||
244 | struct source_line *line; | ||
245 | char pattern[PATH_MAX]; | ||
246 | char *idx; | ||
247 | |||
248 | sprintf(pattern, "<%s>:", symbol->name); | ||
249 | |||
250 | if (symbol->module) { | ||
251 | idx = strstr(pattern, "\t"); | ||
252 | if (idx) | ||
253 | *idx = 0; | ||
254 | } | ||
255 | |||
256 | pthread_mutex_lock(&syme->source_lock); | ||
257 | for (line = syme->lines; line; line = line->next) { | ||
258 | if (strstr(line->line, pattern)) { | ||
259 | syme->source = line; | ||
260 | break; | ||
261 | } | ||
262 | } | ||
263 | pthread_mutex_unlock(&syme->source_lock); | ||
264 | } | ||
265 | |||
266 | static void show_lines(struct source_line *queue, int count, int total) | ||
267 | { | ||
268 | int i; | ||
269 | struct source_line *line; | ||
270 | |||
271 | line = queue; | ||
272 | for (i = 0; i < count; i++) { | ||
273 | float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; | ||
274 | |||
275 | printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); | ||
276 | line = line->next; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | #define TRACE_COUNT 3 | ||
281 | |||
282 | static void show_details(struct sym_entry *syme) | ||
283 | { | ||
284 | struct symbol *symbol; | ||
285 | struct source_line *line; | ||
286 | struct source_line *line_queue = NULL; | ||
287 | int displayed = 0; | ||
288 | int line_queue_count = 0, total = 0, more = 0; | ||
289 | |||
290 | if (!syme) | ||
291 | return; | ||
292 | |||
293 | if (!syme->source) | ||
294 | lookup_sym_source(syme); | ||
295 | |||
296 | if (!syme->source) | ||
297 | return; | ||
298 | |||
299 | symbol = (struct symbol *)(syme + 1); | ||
300 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | ||
301 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | ||
302 | |||
303 | pthread_mutex_lock(&syme->source_lock); | ||
304 | line = syme->source; | ||
305 | while (line) { | ||
306 | total += line->count[sym_counter]; | ||
307 | line = line->next; | ||
308 | } | ||
309 | |||
310 | line = syme->source; | ||
311 | while (line) { | ||
312 | float pcnt = 0.0; | ||
313 | |||
314 | if (!line_queue_count) | ||
315 | line_queue = line; | ||
316 | line_queue_count++; | ||
317 | |||
318 | if (line->count[sym_counter]) | ||
319 | pcnt = 100.0 * line->count[sym_counter] / (float)total; | ||
320 | if (pcnt >= (float)sym_pcnt_filter) { | ||
321 | if (displayed <= print_entries) | ||
322 | show_lines(line_queue, line_queue_count, total); | ||
323 | else more++; | ||
324 | displayed += line_queue_count; | ||
325 | line_queue_count = 0; | ||
326 | line_queue = NULL; | ||
327 | } else if (line_queue_count > TRACE_COUNT) { | ||
328 | line_queue = line_queue->next; | ||
329 | line_queue_count--; | ||
330 | } | ||
331 | |||
332 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | ||
333 | line = line->next; | ||
334 | } | ||
335 | pthread_mutex_unlock(&syme->source_lock); | ||
336 | if (more) | ||
337 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | ||
338 | } | ||
97 | 339 | ||
98 | struct dso *kernel_dso; | 340 | struct dso *kernel_dso; |
99 | 341 | ||
@@ -228,6 +470,11 @@ static void print_sym_table(void) | |||
228 | 470 | ||
229 | printf("------------------------------------------------------------------------------\n\n"); | 471 | printf("------------------------------------------------------------------------------\n\n"); |
230 | 472 | ||
473 | if (sym_filter_entry) { | ||
474 | show_details(sym_filter_entry); | ||
475 | return; | ||
476 | } | ||
477 | |||
231 | if (nr_counters == 1) | 478 | if (nr_counters == 1) |
232 | printf(" samples pcnt"); | 479 | printf(" samples pcnt"); |
233 | else | 480 | else |
@@ -242,7 +489,7 @@ static void print_sym_table(void) | |||
242 | struct symbol *sym = (struct symbol *)(syme + 1); | 489 | struct symbol *sym = (struct symbol *)(syme + 1); |
243 | double pcnt; | 490 | double pcnt; |
244 | 491 | ||
245 | if (++printed > print_entries || syme->snap_count < count_filter) | 492 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
246 | continue; | 493 | continue; |
247 | 494 | ||
248 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 495 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
@@ -261,19 +508,208 @@ static void print_sym_table(void) | |||
261 | } | 508 | } |
262 | } | 509 | } |
263 | 510 | ||
511 | static void prompt_integer(int *target, const char *msg) | ||
512 | { | ||
513 | char *buf = malloc(0), *p; | ||
514 | size_t dummy = 0; | ||
515 | int tmp; | ||
516 | |||
517 | fprintf(stdout, "\n%s: ", msg); | ||
518 | if (getline(&buf, &dummy, stdin) < 0) | ||
519 | return; | ||
520 | |||
521 | p = strchr(buf, '\n'); | ||
522 | if (p) | ||
523 | *p = 0; | ||
524 | |||
525 | p = buf; | ||
526 | while(*p) { | ||
527 | if (!isdigit(*p)) | ||
528 | goto out_free; | ||
529 | p++; | ||
530 | } | ||
531 | tmp = strtoul(buf, NULL, 10); | ||
532 | *target = tmp; | ||
533 | out_free: | ||
534 | free(buf); | ||
535 | } | ||
536 | |||
537 | static void prompt_percent(int *target, const char *msg) | ||
538 | { | ||
539 | int tmp = 0; | ||
540 | |||
541 | prompt_integer(&tmp, msg); | ||
542 | if (tmp >= 0 && tmp <= 100) | ||
543 | *target = tmp; | ||
544 | } | ||
545 | |||
546 | static void prompt_symbol(struct sym_entry **target, const char *msg) | ||
547 | { | ||
548 | char *buf = malloc(0), *p; | ||
549 | struct sym_entry *syme = *target, *n, *found = NULL; | ||
550 | size_t dummy = 0; | ||
551 | |||
552 | /* zero counters of active symbol */ | ||
553 | if (syme) { | ||
554 | pthread_mutex_lock(&syme->source_lock); | ||
555 | __zero_source_counters(syme); | ||
556 | *target = NULL; | ||
557 | pthread_mutex_unlock(&syme->source_lock); | ||
558 | } | ||
559 | |||
560 | fprintf(stdout, "\n%s: ", msg); | ||
561 | if (getline(&buf, &dummy, stdin) < 0) | ||
562 | goto out_free; | ||
563 | |||
564 | p = strchr(buf, '\n'); | ||
565 | if (p) | ||
566 | *p = 0; | ||
567 | |||
568 | pthread_mutex_lock(&active_symbols_lock); | ||
569 | syme = list_entry(active_symbols.next, struct sym_entry, node); | ||
570 | pthread_mutex_unlock(&active_symbols_lock); | ||
571 | |||
572 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | ||
573 | struct symbol *sym = (struct symbol *)(syme + 1); | ||
574 | |||
575 | if (!strcmp(buf, sym->name)) { | ||
576 | found = syme; | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | |||
581 | if (!found) { | ||
582 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | ||
583 | sleep(1); | ||
584 | return; | ||
585 | } else | ||
586 | parse_source(found); | ||
587 | |||
588 | out_free: | ||
589 | free(buf); | ||
590 | } | ||
591 | |||
592 | static void print_known_keys(void) | ||
593 | { | ||
594 | fprintf(stdout, "\nknown keys:\n"); | ||
595 | fprintf(stdout, "\t[d] select display delay.\n"); | ||
596 | fprintf(stdout, "\t[e] select display entries (lines).\n"); | ||
597 | fprintf(stdout, "\t[E] select annotation event counter.\n"); | ||
598 | fprintf(stdout, "\t[f] select normal display count filter.\n"); | ||
599 | fprintf(stdout, "\t[F] select annotation display count filter (percentage).\n"); | ||
600 | fprintf(stdout, "\t[qQ] quit.\n"); | ||
601 | fprintf(stdout, "\t[s] select annotation symbol and start annotation.\n"); | ||
602 | fprintf(stdout, "\t[S] stop annotation, revert to normal display.\n"); | ||
603 | fprintf(stdout, "\t[z] toggle event count zeroing.\n"); | ||
604 | } | ||
605 | |||
606 | static void handle_keypress(int c) | ||
607 | { | ||
608 | int once = 0; | ||
609 | repeat: | ||
610 | switch (c) { | ||
611 | case 'd': | ||
612 | prompt_integer(&delay_secs, "Enter display delay"); | ||
613 | break; | ||
614 | case 'e': | ||
615 | prompt_integer(&print_entries, "Enter display entries (lines)"); | ||
616 | break; | ||
617 | case 'E': | ||
618 | if (nr_counters > 1) { | ||
619 | int i; | ||
620 | |||
621 | fprintf(stderr, "\nAvailable events:"); | ||
622 | for (i = 0; i < nr_counters; i++) | ||
623 | fprintf(stderr, "\n\t%d %s", i, event_name(i)); | ||
624 | |||
625 | prompt_integer(&sym_counter, "Enter details event counter"); | ||
626 | |||
627 | if (sym_counter >= nr_counters) { | ||
628 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0)); | ||
629 | sym_counter = 0; | ||
630 | sleep(1); | ||
631 | } | ||
632 | } else sym_counter = 0; | ||
633 | break; | ||
634 | case 'f': | ||
635 | prompt_integer(&count_filter, "Enter display event count filter"); | ||
636 | break; | ||
637 | case 'F': | ||
638 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | ||
639 | break; | ||
640 | case 'q': | ||
641 | case 'Q': | ||
642 | printf("exiting.\n"); | ||
643 | exit(0); | ||
644 | case 's': | ||
645 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | ||
646 | break; | ||
647 | case 'S': | ||
648 | if (!sym_filter_entry) | ||
649 | break; | ||
650 | else { | ||
651 | struct sym_entry *syme = sym_filter_entry; | ||
652 | |||
653 | pthread_mutex_lock(&syme->source_lock); | ||
654 | sym_filter_entry = NULL; | ||
655 | __zero_source_counters(syme); | ||
656 | pthread_mutex_unlock(&syme->source_lock); | ||
657 | } | ||
658 | break; | ||
659 | case 'z': | ||
660 | zero = ~zero; | ||
661 | break; | ||
662 | default: { | ||
663 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | ||
664 | struct termios tc, save; | ||
665 | |||
666 | if (!once) { | ||
667 | print_known_keys(); | ||
668 | once++; | ||
669 | } | ||
670 | |||
671 | tcgetattr(0, &save); | ||
672 | tc = save; | ||
673 | tc.c_lflag &= ~(ICANON | ECHO); | ||
674 | tc.c_cc[VMIN] = 0; | ||
675 | tc.c_cc[VTIME] = 0; | ||
676 | tcsetattr(0, TCSANOW, &tc); | ||
677 | |||
678 | poll(&stdin_poll, 1, -1); | ||
679 | c = getc(stdin); | ||
680 | |||
681 | tcsetattr(0, TCSAFLUSH, &save); | ||
682 | goto repeat; | ||
683 | } | ||
684 | } | ||
685 | } | ||
686 | |||
264 | static void *display_thread(void *arg __used) | 687 | static void *display_thread(void *arg __used) |
265 | { | 688 | { |
266 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 689 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
267 | int delay_msecs = delay_secs * 1000; | 690 | struct termios tc, save; |
268 | 691 | int delay_msecs, c; | |
269 | printf("PerfTop refresh period: %d seconds\n", delay_secs); | 692 | |
693 | tcgetattr(0, &save); | ||
694 | tc = save; | ||
695 | tc.c_lflag &= ~(ICANON | ECHO); | ||
696 | tc.c_cc[VMIN] = 0; | ||
697 | tc.c_cc[VTIME] = 0; | ||
698 | repeat: | ||
699 | delay_msecs = delay_secs * 1000; | ||
700 | tcsetattr(0, TCSANOW, &tc); | ||
701 | /* trash return*/ | ||
702 | getc(stdin); | ||
270 | 703 | ||
271 | do { | 704 | do { |
272 | print_sym_table(); | 705 | print_sym_table(); |
273 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); | 706 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); |
274 | 707 | ||
275 | printf("key pressed - exiting.\n"); | 708 | c = getc(stdin); |
276 | exit(0); | 709 | tcsetattr(0, TCSAFLUSH, &save); |
710 | |||
711 | handle_keypress(c); | ||
712 | goto repeat; | ||
277 | 713 | ||
278 | return NULL; | 714 | return NULL; |
279 | } | 715 | } |
@@ -293,7 +729,6 @@ static const char *skip_symbols[] = { | |||
293 | 729 | ||
294 | static int symbol_filter(struct dso *self, struct symbol *sym) | 730 | static int symbol_filter(struct dso *self, struct symbol *sym) |
295 | { | 731 | { |
296 | static int filter_match; | ||
297 | struct sym_entry *syme; | 732 | struct sym_entry *syme; |
298 | const char *name = sym->name; | 733 | const char *name = sym->name; |
299 | int i; | 734 | int i; |
@@ -315,6 +750,10 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
315 | return 1; | 750 | return 1; |
316 | 751 | ||
317 | syme = dso__sym_priv(self, sym); | 752 | syme = dso__sym_priv(self, sym); |
753 | pthread_mutex_init(&syme->source_lock, NULL); | ||
754 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | ||
755 | sym_filter_entry = syme; | ||
756 | |||
318 | for (i = 0; skip_symbols[i]; i++) { | 757 | for (i = 0; skip_symbols[i]; i++) { |
319 | if (!strcmp(skip_symbols[i], name)) { | 758 | if (!strcmp(skip_symbols[i], name)) { |
320 | syme->skip = 1; | 759 | syme->skip = 1; |
@@ -322,29 +761,6 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
322 | } | 761 | } |
323 | } | 762 | } |
324 | 763 | ||
325 | if (filter_match == 1) { | ||
326 | filter_end = sym->start; | ||
327 | filter_match = -1; | ||
328 | if (filter_end - filter_start > 10000) { | ||
329 | fprintf(stderr, | ||
330 | "hm, too large filter symbol <%s> - skipping.\n", | ||
331 | sym_filter); | ||
332 | fprintf(stderr, "symbol filter start: %016lx\n", | ||
333 | filter_start); | ||
334 | fprintf(stderr, " end: %016lx\n", | ||
335 | filter_end); | ||
336 | filter_end = filter_start = 0; | ||
337 | sym_filter = NULL; | ||
338 | sleep(1); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) { | ||
343 | filter_match = 1; | ||
344 | filter_start = sym->start; | ||
345 | } | ||
346 | |||
347 | |||
348 | return 0; | 764 | return 0; |
349 | } | 765 | } |
350 | 766 | ||
@@ -380,8 +796,6 @@ out_delete_dso: | |||
380 | return -1; | 796 | return -1; |
381 | } | 797 | } |
382 | 798 | ||
383 | #define TRACE_COUNT 3 | ||
384 | |||
385 | /* | 799 | /* |
386 | * Binary search in the histogram table and record the hit: | 800 | * Binary search in the histogram table and record the hit: |
387 | */ | 801 | */ |
@@ -394,6 +808,7 @@ static void record_ip(u64 ip, int counter) | |||
394 | 808 | ||
395 | if (!syme->skip) { | 809 | if (!syme->skip) { |
396 | syme->count[counter]++; | 810 | syme->count[counter]++; |
811 | record_precise_ip(syme, counter, ip); | ||
397 | pthread_mutex_lock(&active_symbols_lock); | 812 | pthread_mutex_lock(&active_symbols_lock); |
398 | if (list_empty(&syme->node) || !syme->node.next) | 813 | if (list_empty(&syme->node) || !syme->node.next) |
399 | __list_insert_active_sym(syme); | 814 | __list_insert_active_sym(syme); |
@@ -690,8 +1105,8 @@ static const struct option options[] = { | |||
690 | "put the counters into a counter group"), | 1105 | "put the counters into a counter group"), |
691 | OPT_BOOLEAN('i', "inherit", &inherit, | 1106 | OPT_BOOLEAN('i', "inherit", &inherit, |
692 | "child tasks inherit counters"), | 1107 | "child tasks inherit counters"), |
693 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", | 1108 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
694 | "only display symbols matchig this pattern"), | 1109 | "symbol to annotate - requires -k option"), |
695 | OPT_BOOLEAN('z', "zero", &zero, | 1110 | OPT_BOOLEAN('z', "zero", &zero, |
696 | "zero history across updates"), | 1111 | "zero history across updates"), |
697 | OPT_INTEGER('F', "freq", &freq, | 1112 | OPT_INTEGER('F', "freq", &freq, |
@@ -734,6 +1149,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
734 | delay_secs = 1; | 1149 | delay_secs = 1; |
735 | 1150 | ||
736 | parse_symbols(); | 1151 | parse_symbols(); |
1152 | parse_source(sym_filter_entry); | ||
737 | 1153 | ||
738 | /* | 1154 | /* |
739 | * Fill in the ones not specifically initialized via -c: | 1155 | * Fill in the ones not specifically initialized via -c: |