diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 597 |
1 files changed, 373 insertions, 224 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e23bc74e734f..31f2e597800c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -20,8 +20,10 @@ | |||
20 | 20 | ||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/symbol.h" | ||
24 | #include "util/color.h" | 23 | #include "util/color.h" |
24 | #include "util/session.h" | ||
25 | #include "util/symbol.h" | ||
26 | #include "util/thread.h" | ||
25 | #include "util/util.h" | 27 | #include "util/util.h" |
26 | #include <linux/rbtree.h> | 28 | #include <linux/rbtree.h> |
27 | #include "util/parse-options.h" | 29 | #include "util/parse-options.h" |
@@ -54,26 +56,30 @@ | |||
54 | 56 | ||
55 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 57 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; |
56 | 58 | ||
57 | static int system_wide = 0; | 59 | static int system_wide = 0; |
58 | 60 | ||
59 | static int default_interval = 100000; | 61 | static int default_interval = 0; |
60 | 62 | ||
61 | static int count_filter = 5; | 63 | static int count_filter = 5; |
62 | static int print_entries = 15; | 64 | static int print_entries; |
63 | 65 | ||
64 | static int target_pid = -1; | 66 | static int target_pid = -1; |
65 | static int inherit = 0; | 67 | static int inherit = 0; |
66 | static int profile_cpu = -1; | 68 | static int profile_cpu = -1; |
67 | static int nr_cpus = 0; | 69 | static int nr_cpus = 0; |
68 | static unsigned int realtime_prio = 0; | 70 | static unsigned int realtime_prio = 0; |
69 | static int group = 0; | 71 | static int group = 0; |
70 | static unsigned int page_size; | 72 | static unsigned int page_size; |
71 | static unsigned int mmap_pages = 16; | 73 | static unsigned int mmap_pages = 16; |
72 | static int freq = 0; | 74 | static int freq = 1000; /* 1 KHz */ |
73 | 75 | ||
74 | static int delay_secs = 2; | 76 | static int delay_secs = 2; |
75 | static int zero; | 77 | static int zero = 0; |
76 | static int dump_symtab; | 78 | static int dump_symtab = 0; |
79 | |||
80 | static bool hide_kernel_symbols = false; | ||
81 | static bool hide_user_symbols = false; | ||
82 | static struct winsize winsize; | ||
77 | 83 | ||
78 | /* | 84 | /* |
79 | * Source | 85 | * Source |
@@ -86,87 +92,130 @@ struct source_line { | |||
86 | struct source_line *next; | 92 | struct source_line *next; |
87 | }; | 93 | }; |
88 | 94 | ||
89 | static char *sym_filter = NULL; | 95 | static char *sym_filter = NULL; |
90 | struct sym_entry *sym_filter_entry = NULL; | 96 | struct sym_entry *sym_filter_entry = NULL; |
91 | static int sym_pcnt_filter = 5; | 97 | struct sym_entry *sym_filter_entry_sched = NULL; |
92 | static int sym_counter = 0; | 98 | static int sym_pcnt_filter = 5; |
93 | static int display_weighted = -1; | 99 | static int sym_counter = 0; |
100 | static int display_weighted = -1; | ||
94 | 101 | ||
95 | /* | 102 | /* |
96 | * Symbols | 103 | * Symbols |
97 | */ | 104 | */ |
98 | 105 | ||
99 | static u64 min_ip; | 106 | struct sym_entry_source { |
100 | static u64 max_ip = -1ll; | 107 | struct source_line *source; |
108 | struct source_line *lines; | ||
109 | struct source_line **lines_tail; | ||
110 | pthread_mutex_t lock; | ||
111 | }; | ||
101 | 112 | ||
102 | struct sym_entry { | 113 | struct sym_entry { |
103 | struct rb_node rb_node; | 114 | struct rb_node rb_node; |
104 | struct list_head node; | 115 | struct list_head node; |
105 | unsigned long count[MAX_COUNTERS]; | ||
106 | unsigned long snap_count; | 116 | unsigned long snap_count; |
107 | double weight; | 117 | double weight; |
108 | int skip; | 118 | int skip; |
109 | struct source_line *source; | 119 | u16 name_len; |
110 | struct source_line *lines; | 120 | u8 origin; |
111 | struct source_line **lines_tail; | 121 | struct map *map; |
112 | pthread_mutex_t source_lock; | 122 | struct sym_entry_source *src; |
123 | unsigned long count[0]; | ||
113 | }; | 124 | }; |
114 | 125 | ||
115 | /* | 126 | /* |
116 | * Source functions | 127 | * Source functions |
117 | */ | 128 | */ |
118 | 129 | ||
130 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
131 | { | ||
132 | return ((void *)self) + symbol_conf.priv_size; | ||
133 | } | ||
134 | |||
135 | static void get_term_dimensions(struct winsize *ws) | ||
136 | { | ||
137 | char *s = getenv("LINES"); | ||
138 | |||
139 | if (s != NULL) { | ||
140 | ws->ws_row = atoi(s); | ||
141 | s = getenv("COLUMNS"); | ||
142 | if (s != NULL) { | ||
143 | ws->ws_col = atoi(s); | ||
144 | if (ws->ws_row && ws->ws_col) | ||
145 | return; | ||
146 | } | ||
147 | } | ||
148 | #ifdef TIOCGWINSZ | ||
149 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
150 | ws->ws_row && ws->ws_col) | ||
151 | return; | ||
152 | #endif | ||
153 | ws->ws_row = 25; | ||
154 | ws->ws_col = 80; | ||
155 | } | ||
156 | |||
157 | static void update_print_entries(struct winsize *ws) | ||
158 | { | ||
159 | print_entries = ws->ws_row; | ||
160 | |||
161 | if (print_entries > 9) | ||
162 | print_entries -= 9; | ||
163 | } | ||
164 | |||
165 | static void sig_winch_handler(int sig __used) | ||
166 | { | ||
167 | get_term_dimensions(&winsize); | ||
168 | update_print_entries(&winsize); | ||
169 | } | ||
170 | |||
119 | static void parse_source(struct sym_entry *syme) | 171 | static void parse_source(struct sym_entry *syme) |
120 | { | 172 | { |
121 | struct symbol *sym; | 173 | struct symbol *sym; |
122 | struct module *module; | 174 | struct sym_entry_source *source; |
123 | struct section *section = NULL; | 175 | struct map *map; |
124 | FILE *file; | 176 | FILE *file; |
125 | char command[PATH_MAX*2]; | 177 | char command[PATH_MAX*2]; |
126 | const char *path = vmlinux_name; | 178 | const char *path; |
127 | u64 start, end, len; | 179 | u64 len; |
128 | 180 | ||
129 | if (!syme) | 181 | if (!syme) |
130 | return; | 182 | return; |
131 | 183 | ||
132 | if (syme->lines) { | 184 | if (syme->src == NULL) { |
133 | pthread_mutex_lock(&syme->source_lock); | 185 | syme->src = zalloc(sizeof(*source)); |
134 | goto out_assign; | 186 | if (syme->src == NULL) |
187 | return; | ||
188 | pthread_mutex_init(&syme->src->lock, NULL); | ||
135 | } | 189 | } |
136 | 190 | ||
137 | sym = (struct symbol *)(syme + 1); | 191 | source = syme->src; |
138 | module = sym->module; | ||
139 | |||
140 | if (module) | ||
141 | path = module->path; | ||
142 | if (!path) | ||
143 | return; | ||
144 | 192 | ||
145 | start = sym->obj_start; | 193 | if (source->lines) { |
146 | if (!start) | 194 | pthread_mutex_lock(&source->lock); |
147 | start = sym->start; | 195 | goto out_assign; |
148 | |||
149 | if (module) { | ||
150 | section = module->sections->find_section(module->sections, ".text"); | ||
151 | if (section) | ||
152 | start -= section->vma; | ||
153 | } | 196 | } |
154 | 197 | ||
155 | end = start + sym->end - sym->start + 1; | 198 | sym = sym_entry__symbol(syme); |
199 | map = syme->map; | ||
200 | path = map->dso->long_name; | ||
201 | |||
156 | len = sym->end - sym->start; | 202 | len = sym->end - sym->start; |
157 | 203 | ||
158 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); | 204 | sprintf(command, |
205 | "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s", | ||
206 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), | ||
207 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); | ||
159 | 208 | ||
160 | file = popen(command, "r"); | 209 | file = popen(command, "r"); |
161 | if (!file) | 210 | if (!file) |
162 | return; | 211 | return; |
163 | 212 | ||
164 | pthread_mutex_lock(&syme->source_lock); | 213 | pthread_mutex_lock(&source->lock); |
165 | syme->lines_tail = &syme->lines; | 214 | source->lines_tail = &source->lines; |
166 | while (!feof(file)) { | 215 | while (!feof(file)) { |
167 | struct source_line *src; | 216 | struct source_line *src; |
168 | size_t dummy = 0; | 217 | size_t dummy = 0; |
169 | char *c; | 218 | char *c, *sep; |
170 | 219 | ||
171 | src = malloc(sizeof(struct source_line)); | 220 | src = malloc(sizeof(struct source_line)); |
172 | assert(src != NULL); | 221 | assert(src != NULL); |
@@ -182,24 +231,19 @@ static void parse_source(struct sym_entry *syme) | |||
182 | *c = 0; | 231 | *c = 0; |
183 | 232 | ||
184 | src->next = NULL; | 233 | src->next = NULL; |
185 | *syme->lines_tail = src; | 234 | *source->lines_tail = src; |
186 | syme->lines_tail = &src->next; | 235 | source->lines_tail = &src->next; |
187 | 236 | ||
188 | if (strlen(src->line)>8 && src->line[8] == ':') { | 237 | src->eip = strtoull(src->line, &sep, 16); |
189 | src->eip = strtoull(src->line, NULL, 16); | 238 | if (*sep == ':') |
190 | if (section) | 239 | src->eip = map__objdump_2ip(map, src->eip); |
191 | src->eip += section->vma; | 240 | else /* this line has no ip info (e.g. source line) */ |
192 | } | 241 | src->eip = 0; |
193 | if (strlen(src->line)>8 && src->line[16] == ':') { | ||
194 | src->eip = strtoull(src->line, NULL, 16); | ||
195 | if (section) | ||
196 | src->eip += section->vma; | ||
197 | } | ||
198 | } | 242 | } |
199 | pclose(file); | 243 | pclose(file); |
200 | out_assign: | 244 | out_assign: |
201 | sym_filter_entry = syme; | 245 | sym_filter_entry = syme; |
202 | pthread_mutex_unlock(&syme->source_lock); | 246 | pthread_mutex_unlock(&source->lock); |
203 | } | 247 | } |
204 | 248 | ||
205 | static void __zero_source_counters(struct sym_entry *syme) | 249 | static void __zero_source_counters(struct sym_entry *syme) |
@@ -207,7 +251,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
207 | int i; | 251 | int i; |
208 | struct source_line *line; | 252 | struct source_line *line; |
209 | 253 | ||
210 | line = syme->lines; | 254 | line = syme->src->lines; |
211 | while (line) { | 255 | while (line) { |
212 | for (i = 0; i < nr_counters; i++) | 256 | for (i = 0; i < nr_counters; i++) |
213 | line->count[i] = 0; | 257 | line->count[i] = 0; |
@@ -222,13 +266,16 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
222 | if (syme != sym_filter_entry) | 266 | if (syme != sym_filter_entry) |
223 | return; | 267 | return; |
224 | 268 | ||
225 | if (pthread_mutex_trylock(&syme->source_lock)) | 269 | if (pthread_mutex_trylock(&syme->src->lock)) |
226 | return; | 270 | return; |
227 | 271 | ||
228 | if (!syme->source) | 272 | if (syme->src == NULL || syme->src->source == NULL) |
229 | goto out_unlock; | 273 | goto out_unlock; |
230 | 274 | ||
231 | for (line = syme->lines; line; line = line->next) { | 275 | for (line = syme->src->lines; line; line = line->next) { |
276 | /* skip lines without IP info */ | ||
277 | if (line->eip == 0) | ||
278 | continue; | ||
232 | if (line->eip == ip) { | 279 | if (line->eip == ip) { |
233 | line->count[counter]++; | 280 | line->count[counter]++; |
234 | break; | 281 | break; |
@@ -237,32 +284,28 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
237 | break; | 284 | break; |
238 | } | 285 | } |
239 | out_unlock: | 286 | out_unlock: |
240 | pthread_mutex_unlock(&syme->source_lock); | 287 | pthread_mutex_unlock(&syme->src->lock); |
241 | } | 288 | } |
242 | 289 | ||
290 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) | ||
291 | |||
243 | static void lookup_sym_source(struct sym_entry *syme) | 292 | static void lookup_sym_source(struct sym_entry *syme) |
244 | { | 293 | { |
245 | struct symbol *symbol = (struct symbol *)(syme + 1); | 294 | struct symbol *symbol = sym_entry__symbol(syme); |
246 | struct source_line *line; | 295 | struct source_line *line; |
247 | char pattern[PATH_MAX]; | 296 | char pattern[PATTERN_LEN + 1]; |
248 | char *idx; | ||
249 | 297 | ||
250 | sprintf(pattern, "<%s>:", symbol->name); | 298 | sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4, |
299 | map__rip_2objdump(syme->map, symbol->start)); | ||
251 | 300 | ||
252 | if (symbol->module) { | 301 | pthread_mutex_lock(&syme->src->lock); |
253 | idx = strstr(pattern, "\t"); | 302 | for (line = syme->src->lines; line; line = line->next) { |
254 | if (idx) | 303 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { |
255 | *idx = 0; | 304 | syme->src->source = line; |
256 | } | ||
257 | |||
258 | pthread_mutex_lock(&syme->source_lock); | ||
259 | for (line = syme->lines; line; line = line->next) { | ||
260 | if (strstr(line->line, pattern)) { | ||
261 | syme->source = line; | ||
262 | break; | 305 | break; |
263 | } | 306 | } |
264 | } | 307 | } |
265 | pthread_mutex_unlock(&syme->source_lock); | 308 | pthread_mutex_unlock(&syme->src->lock); |
266 | } | 309 | } |
267 | 310 | ||
268 | static void show_lines(struct source_line *queue, int count, int total) | 311 | static void show_lines(struct source_line *queue, int count, int total) |
@@ -292,24 +335,24 @@ static void show_details(struct sym_entry *syme) | |||
292 | if (!syme) | 335 | if (!syme) |
293 | return; | 336 | return; |
294 | 337 | ||
295 | if (!syme->source) | 338 | if (!syme->src->source) |
296 | lookup_sym_source(syme); | 339 | lookup_sym_source(syme); |
297 | 340 | ||
298 | if (!syme->source) | 341 | if (!syme->src->source) |
299 | return; | 342 | return; |
300 | 343 | ||
301 | symbol = (struct symbol *)(syme + 1); | 344 | symbol = sym_entry__symbol(syme); |
302 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | 345 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); |
303 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 346 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
304 | 347 | ||
305 | pthread_mutex_lock(&syme->source_lock); | 348 | pthread_mutex_lock(&syme->src->lock); |
306 | line = syme->source; | 349 | line = syme->src->source; |
307 | while (line) { | 350 | while (line) { |
308 | total += line->count[sym_counter]; | 351 | total += line->count[sym_counter]; |
309 | line = line->next; | 352 | line = line->next; |
310 | } | 353 | } |
311 | 354 | ||
312 | line = syme->source; | 355 | line = syme->src->source; |
313 | while (line) { | 356 | while (line) { |
314 | float pcnt = 0.0; | 357 | float pcnt = 0.0; |
315 | 358 | ||
@@ -334,13 +377,13 @@ static void show_details(struct sym_entry *syme) | |||
334 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | 377 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; |
335 | line = line->next; | 378 | line = line->next; |
336 | } | 379 | } |
337 | pthread_mutex_unlock(&syme->source_lock); | 380 | pthread_mutex_unlock(&syme->src->lock); |
338 | if (more) | 381 | if (more) |
339 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 382 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
340 | } | 383 | } |
341 | 384 | ||
342 | /* | 385 | /* |
343 | * Symbols will be added here in record_ip and will get out | 386 | * Symbols will be added here in event__process_sample and will get out |
344 | * after decayed. | 387 | * after decayed. |
345 | */ | 388 | */ |
346 | static LIST_HEAD(active_symbols); | 389 | static LIST_HEAD(active_symbols); |
@@ -411,6 +454,8 @@ static void print_sym_table(void) | |||
411 | struct sym_entry *syme, *n; | 454 | struct sym_entry *syme, *n; |
412 | struct rb_root tmp = RB_ROOT; | 455 | struct rb_root tmp = RB_ROOT; |
413 | struct rb_node *nd; | 456 | struct rb_node *nd; |
457 | int sym_width = 0, dso_width = 0, max_dso_width; | ||
458 | const int win_width = winsize.ws_col - 1; | ||
414 | 459 | ||
415 | samples = userspace_samples = 0; | 460 | samples = userspace_samples = 0; |
416 | 461 | ||
@@ -422,6 +467,14 @@ static void print_sym_table(void) | |||
422 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 467 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
423 | syme->snap_count = syme->count[snap]; | 468 | syme->snap_count = syme->count[snap]; |
424 | if (syme->snap_count != 0) { | 469 | if (syme->snap_count != 0) { |
470 | |||
471 | if ((hide_user_symbols && | ||
472 | syme->origin == PERF_RECORD_MISC_USER) || | ||
473 | (hide_kernel_symbols && | ||
474 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
475 | list_remove_active_sym(syme); | ||
476 | continue; | ||
477 | } | ||
425 | syme->weight = sym_weight(syme); | 478 | syme->weight = sym_weight(syme); |
426 | rb_insert_active_sym(&tmp, syme); | 479 | rb_insert_active_sym(&tmp, syme); |
427 | sum_ksamples += syme->snap_count; | 480 | sum_ksamples += syme->snap_count; |
@@ -434,8 +487,7 @@ static void print_sym_table(void) | |||
434 | 487 | ||
435 | puts(CONSOLE_CLEAR); | 488 | puts(CONSOLE_CLEAR); |
436 | 489 | ||
437 | printf( | 490 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
438 | "------------------------------------------------------------------------------\n"); | ||
439 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", | 491 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", |
440 | samples_per_sec, | 492 | samples_per_sec, |
441 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 493 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
@@ -473,33 +525,57 @@ static void print_sym_table(void) | |||
473 | printf(", %d CPUs)\n", nr_cpus); | 525 | printf(", %d CPUs)\n", nr_cpus); |
474 | } | 526 | } |
475 | 527 | ||
476 | printf("------------------------------------------------------------------------------\n\n"); | 528 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
477 | 529 | ||
478 | if (sym_filter_entry) { | 530 | if (sym_filter_entry) { |
479 | show_details(sym_filter_entry); | 531 | show_details(sym_filter_entry); |
480 | return; | 532 | return; |
481 | } | 533 | } |
482 | 534 | ||
535 | /* | ||
536 | * Find the longest symbol name that will be displayed | ||
537 | */ | ||
538 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
539 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
540 | if (++printed > print_entries || | ||
541 | (int)syme->snap_count < count_filter) | ||
542 | continue; | ||
543 | |||
544 | if (syme->map->dso->long_name_len > dso_width) | ||
545 | dso_width = syme->map->dso->long_name_len; | ||
546 | |||
547 | if (syme->name_len > sym_width) | ||
548 | sym_width = syme->name_len; | ||
549 | } | ||
550 | |||
551 | printed = 0; | ||
552 | |||
553 | max_dso_width = winsize.ws_col - sym_width - 29; | ||
554 | if (dso_width > max_dso_width) | ||
555 | dso_width = max_dso_width; | ||
556 | putchar('\n'); | ||
483 | if (nr_counters == 1) | 557 | if (nr_counters == 1) |
484 | printf(" samples pcnt"); | 558 | printf(" samples pcnt"); |
485 | else | 559 | else |
486 | printf(" weight samples pcnt"); | 560 | printf(" weight samples pcnt"); |
487 | 561 | ||
488 | if (verbose) | 562 | if (verbose) |
489 | printf(" RIP "); | 563 | printf(" RIP "); |
490 | printf(" kernel function\n"); | 564 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
491 | printf(" %s _______ _____", | 565 | printf(" %s _______ _____", |
492 | nr_counters == 1 ? " " : "______"); | 566 | nr_counters == 1 ? " " : "______"); |
493 | if (verbose) | 567 | if (verbose) |
494 | printf(" ________________"); | 568 | printf(" ________________"); |
495 | printf(" _______________\n\n"); | 569 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
570 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
571 | puts("\n"); | ||
496 | 572 | ||
497 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 573 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
498 | struct symbol *sym; | 574 | struct symbol *sym; |
499 | double pcnt; | 575 | double pcnt; |
500 | 576 | ||
501 | syme = rb_entry(nd, struct sym_entry, rb_node); | 577 | syme = rb_entry(nd, struct sym_entry, rb_node); |
502 | sym = (struct symbol *)(syme + 1); | 578 | sym = sym_entry__symbol(syme); |
503 | 579 | ||
504 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 580 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
505 | continue; | 581 | continue; |
@@ -508,17 +584,18 @@ static void print_sym_table(void) | |||
508 | sum_ksamples)); | 584 | sum_ksamples)); |
509 | 585 | ||
510 | if (nr_counters == 1 || !display_weighted) | 586 | if (nr_counters == 1 || !display_weighted) |
511 | printf("%20.2f - ", syme->weight); | 587 | printf("%20.2f ", syme->weight); |
512 | else | 588 | else |
513 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 589 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
514 | 590 | ||
515 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | 591 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
516 | if (verbose) | 592 | if (verbose) |
517 | printf(" - %016llx", sym->start); | 593 | printf(" %016llx", sym->start); |
518 | printf(" : %s", sym->name); | 594 | printf(" %-*.*s", sym_width, sym_width, sym->name); |
519 | if (sym->module) | 595 | printf(" %-*.*s\n", dso_width, dso_width, |
520 | printf("\t[%s]", sym->module->name); | 596 | dso_width >= syme->map->dso->long_name_len ? |
521 | printf("\n"); | 597 | syme->map->dso->long_name : |
598 | syme->map->dso->short_name); | ||
522 | } | 599 | } |
523 | } | 600 | } |
524 | 601 | ||
@@ -565,10 +642,10 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
565 | 642 | ||
566 | /* zero counters of active symbol */ | 643 | /* zero counters of active symbol */ |
567 | if (syme) { | 644 | if (syme) { |
568 | pthread_mutex_lock(&syme->source_lock); | 645 | pthread_mutex_lock(&syme->src->lock); |
569 | __zero_source_counters(syme); | 646 | __zero_source_counters(syme); |
570 | *target = NULL; | 647 | *target = NULL; |
571 | pthread_mutex_unlock(&syme->source_lock); | 648 | pthread_mutex_unlock(&syme->src->lock); |
572 | } | 649 | } |
573 | 650 | ||
574 | fprintf(stdout, "\n%s: ", msg); | 651 | fprintf(stdout, "\n%s: ", msg); |
@@ -584,7 +661,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
584 | pthread_mutex_unlock(&active_symbols_lock); | 661 | pthread_mutex_unlock(&active_symbols_lock); |
585 | 662 | ||
586 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 663 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
587 | struct symbol *sym = (struct symbol *)(syme + 1); | 664 | struct symbol *sym = sym_entry__symbol(syme); |
588 | 665 | ||
589 | if (!strcmp(buf, sym->name)) { | 666 | if (!strcmp(buf, sym->name)) { |
590 | found = syme; | 667 | found = syme; |
@@ -593,7 +670,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
593 | } | 670 | } |
594 | 671 | ||
595 | if (!found) { | 672 | if (!found) { |
596 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | 673 | fprintf(stderr, "Sorry, %s is not active.\n", buf); |
597 | sleep(1); | 674 | sleep(1); |
598 | return; | 675 | return; |
599 | } else | 676 | } else |
@@ -608,7 +685,7 @@ static void print_mapped_keys(void) | |||
608 | char *name = NULL; | 685 | char *name = NULL; |
609 | 686 | ||
610 | if (sym_filter_entry) { | 687 | if (sym_filter_entry) { |
611 | struct symbol *sym = (struct symbol *)(sym_filter_entry+1); | 688 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); |
612 | name = sym->name; | 689 | name = sym->name; |
613 | } | 690 | } |
614 | 691 | ||
@@ -621,15 +698,19 @@ static void print_mapped_keys(void) | |||
621 | 698 | ||
622 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 699 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
623 | 700 | ||
624 | if (vmlinux_name) { | 701 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
625 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 702 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
626 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 703 | fprintf(stdout, "\t[S] stop annotation.\n"); |
627 | fprintf(stdout, "\t[S] stop annotation.\n"); | ||
628 | } | ||
629 | 704 | ||
630 | if (nr_counters > 1) | 705 | if (nr_counters > 1) |
631 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 706 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
632 | 707 | ||
708 | fprintf(stdout, | ||
709 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | ||
710 | hide_kernel_symbols ? "yes" : "no"); | ||
711 | fprintf(stdout, | ||
712 | "\t[U] hide user symbols. \t(%s)\n", | ||
713 | hide_user_symbols ? "yes" : "no"); | ||
633 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 714 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
634 | fprintf(stdout, "\t[qQ] quit.\n"); | 715 | fprintf(stdout, "\t[qQ] quit.\n"); |
635 | } | 716 | } |
@@ -643,14 +724,15 @@ static int key_mapped(int c) | |||
643 | case 'z': | 724 | case 'z': |
644 | case 'q': | 725 | case 'q': |
645 | case 'Q': | 726 | case 'Q': |
727 | case 'K': | ||
728 | case 'U': | ||
729 | case 'F': | ||
730 | case 's': | ||
731 | case 'S': | ||
646 | return 1; | 732 | return 1; |
647 | case 'E': | 733 | case 'E': |
648 | case 'w': | 734 | case 'w': |
649 | return nr_counters > 1 ? 1 : 0; | 735 | return nr_counters > 1 ? 1 : 0; |
650 | case 'F': | ||
651 | case 's': | ||
652 | case 'S': | ||
653 | return vmlinux_name ? 1 : 0; | ||
654 | default: | 736 | default: |
655 | break; | 737 | break; |
656 | } | 738 | } |
@@ -691,6 +773,11 @@ static void handle_keypress(int c) | |||
691 | break; | 773 | break; |
692 | case 'e': | 774 | case 'e': |
693 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 775 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
776 | if (print_entries == 0) { | ||
777 | sig_winch_handler(SIGWINCH); | ||
778 | signal(SIGWINCH, sig_winch_handler); | ||
779 | } else | ||
780 | signal(SIGWINCH, SIG_DFL); | ||
694 | break; | 781 | break; |
695 | case 'E': | 782 | case 'E': |
696 | if (nr_counters > 1) { | 783 | if (nr_counters > 1) { |
@@ -715,9 +802,14 @@ static void handle_keypress(int c) | |||
715 | case 'F': | 802 | case 'F': |
716 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 803 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
717 | break; | 804 | break; |
805 | case 'K': | ||
806 | hide_kernel_symbols = !hide_kernel_symbols; | ||
807 | break; | ||
718 | case 'q': | 808 | case 'q': |
719 | case 'Q': | 809 | case 'Q': |
720 | printf("exiting.\n"); | 810 | printf("exiting.\n"); |
811 | if (dump_symtab) | ||
812 | dsos__fprintf(stderr); | ||
721 | exit(0); | 813 | exit(0); |
722 | case 's': | 814 | case 's': |
723 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 815 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
@@ -728,12 +820,15 @@ static void handle_keypress(int c) | |||
728 | else { | 820 | else { |
729 | struct sym_entry *syme = sym_filter_entry; | 821 | struct sym_entry *syme = sym_filter_entry; |
730 | 822 | ||
731 | pthread_mutex_lock(&syme->source_lock); | 823 | pthread_mutex_lock(&syme->src->lock); |
732 | sym_filter_entry = NULL; | 824 | sym_filter_entry = NULL; |
733 | __zero_source_counters(syme); | 825 | __zero_source_counters(syme); |
734 | pthread_mutex_unlock(&syme->source_lock); | 826 | pthread_mutex_unlock(&syme->src->lock); |
735 | } | 827 | } |
736 | break; | 828 | break; |
829 | case 'U': | ||
830 | hide_user_symbols = !hide_user_symbols; | ||
831 | break; | ||
737 | case 'w': | 832 | case 'w': |
738 | display_weighted = ~display_weighted; | 833 | display_weighted = ~display_weighted; |
739 | break; | 834 | break; |
@@ -790,7 +885,7 @@ static const char *skip_symbols[] = { | |||
790 | NULL | 885 | NULL |
791 | }; | 886 | }; |
792 | 887 | ||
793 | static int symbol_filter(struct dso *self, struct symbol *sym) | 888 | static int symbol_filter(struct map *map, struct symbol *sym) |
794 | { | 889 | { |
795 | struct sym_entry *syme; | 890 | struct sym_entry *syme; |
796 | const char *name = sym->name; | 891 | const char *name = sym->name; |
@@ -812,10 +907,15 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
812 | strstr(name, "_text_end")) | 907 | strstr(name, "_text_end")) |
813 | return 1; | 908 | return 1; |
814 | 909 | ||
815 | syme = dso__sym_priv(self, sym); | 910 | syme = symbol__priv(sym); |
816 | pthread_mutex_init(&syme->source_lock, NULL); | 911 | syme->map = map; |
817 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 912 | syme->src = NULL; |
818 | sym_filter_entry = syme; | 913 | |
914 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { | ||
915 | /* schedule initial sym_filter_entry setup */ | ||
916 | sym_filter_entry_sched = syme; | ||
917 | sym_filter = NULL; | ||
918 | } | ||
819 | 919 | ||
820 | for (i = 0; skip_symbols[i]; i++) { | 920 | for (i = 0; skip_symbols[i]; i++) { |
821 | if (!strcmp(skip_symbols[i], name)) { | 921 | if (!strcmp(skip_symbols[i], name)) { |
@@ -824,75 +924,99 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
824 | } | 924 | } |
825 | } | 925 | } |
826 | 926 | ||
927 | if (!syme->skip) | ||
928 | syme->name_len = strlen(sym->name); | ||
929 | |||
827 | return 0; | 930 | return 0; |
828 | } | 931 | } |
829 | 932 | ||
830 | static int parse_symbols(void) | 933 | static void event__process_sample(const event_t *self, |
934 | struct perf_session *session, int counter) | ||
831 | { | 935 | { |
832 | struct rb_node *node; | 936 | u64 ip = self->ip.ip; |
833 | struct symbol *sym; | 937 | struct sym_entry *syme; |
834 | int use_modules = vmlinux_name ? 1 : 0; | 938 | struct addr_location al; |
835 | 939 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | |
836 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); | ||
837 | if (kernel_dso == NULL) | ||
838 | return -1; | ||
839 | |||
840 | if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0) | ||
841 | goto out_delete_dso; | ||
842 | 940 | ||
843 | node = rb_first(&kernel_dso->syms); | 941 | ++samples; |
844 | sym = rb_entry(node, struct symbol, rb_node); | ||
845 | min_ip = sym->start; | ||
846 | 942 | ||
847 | node = rb_last(&kernel_dso->syms); | 943 | switch (origin) { |
848 | sym = rb_entry(node, struct symbol, rb_node); | 944 | case PERF_RECORD_MISC_USER: |
849 | max_ip = sym->end; | 945 | ++userspace_samples; |
946 | if (hide_user_symbols) | ||
947 | return; | ||
948 | break; | ||
949 | case PERF_RECORD_MISC_KERNEL: | ||
950 | if (hide_kernel_symbols) | ||
951 | return; | ||
952 | break; | ||
953 | default: | ||
954 | return; | ||
955 | } | ||
850 | 956 | ||
851 | if (dump_symtab) | 957 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
852 | dso__fprintf(kernel_dso, stderr); | 958 | al.filtered) |
959 | return; | ||
853 | 960 | ||
854 | return 0; | 961 | if (al.sym == NULL) { |
962 | /* | ||
963 | * As we do lazy loading of symtabs we only will know if the | ||
964 | * specified vmlinux file is invalid when we actually have a | ||
965 | * hit in kernel space and then try to load it. So if we get | ||
966 | * here and there are _no_ symbols in the DSO backing the | ||
967 | * kernel map, bail out. | ||
968 | * | ||
969 | * We may never get here, for instance, if we use -K/ | ||
970 | * --hide-kernel-symbols, even if the user specifies an | ||
971 | * invalid --vmlinux ;-) | ||
972 | */ | ||
973 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | ||
974 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | ||
975 | pr_err("The %s file can't be used\n", | ||
976 | symbol_conf.vmlinux_name); | ||
977 | exit(1); | ||
978 | } | ||
855 | 979 | ||
856 | out_delete_dso: | 980 | return; |
857 | dso__delete(kernel_dso); | 981 | } |
858 | kernel_dso = NULL; | ||
859 | return -1; | ||
860 | } | ||
861 | 982 | ||
862 | /* | 983 | /* let's see, whether we need to install initial sym_filter_entry */ |
863 | * Binary search in the histogram table and record the hit: | 984 | if (sym_filter_entry_sched) { |
864 | */ | 985 | sym_filter_entry = sym_filter_entry_sched; |
865 | static void record_ip(u64 ip, int counter) | 986 | sym_filter_entry_sched = NULL; |
866 | { | 987 | parse_source(sym_filter_entry); |
867 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); | ||
868 | |||
869 | if (sym != NULL) { | ||
870 | struct sym_entry *syme = dso__sym_priv(kernel_dso, sym); | ||
871 | |||
872 | if (!syme->skip) { | ||
873 | syme->count[counter]++; | ||
874 | record_precise_ip(syme, counter, ip); | ||
875 | pthread_mutex_lock(&active_symbols_lock); | ||
876 | if (list_empty(&syme->node) || !syme->node.next) | ||
877 | __list_insert_active_sym(syme); | ||
878 | pthread_mutex_unlock(&active_symbols_lock); | ||
879 | return; | ||
880 | } | ||
881 | } | 988 | } |
882 | 989 | ||
883 | samples--; | 990 | syme = symbol__priv(al.sym); |
991 | if (!syme->skip) { | ||
992 | syme->count[counter]++; | ||
993 | syme->origin = origin; | ||
994 | record_precise_ip(syme, counter, ip); | ||
995 | pthread_mutex_lock(&active_symbols_lock); | ||
996 | if (list_empty(&syme->node) || !syme->node.next) | ||
997 | __list_insert_active_sym(syme); | ||
998 | pthread_mutex_unlock(&active_symbols_lock); | ||
999 | } | ||
884 | } | 1000 | } |
885 | 1001 | ||
886 | static void process_event(u64 ip, int counter, int user) | 1002 | static int event__process(event_t *event, struct perf_session *session) |
887 | { | 1003 | { |
888 | samples++; | 1004 | switch (event->header.type) { |
889 | 1005 | case PERF_RECORD_COMM: | |
890 | if (user) { | 1006 | event__process_comm(event, session); |
891 | userspace_samples++; | 1007 | break; |
892 | return; | 1008 | case PERF_RECORD_MMAP: |
1009 | event__process_mmap(event, session); | ||
1010 | break; | ||
1011 | case PERF_RECORD_FORK: | ||
1012 | case PERF_RECORD_EXIT: | ||
1013 | event__process_task(event, session); | ||
1014 | break; | ||
1015 | default: | ||
1016 | break; | ||
893 | } | 1017 | } |
894 | 1018 | ||
895 | record_ip(ip, counter); | 1019 | return 0; |
896 | } | 1020 | } |
897 | 1021 | ||
898 | struct mmap_data { | 1022 | struct mmap_data { |
@@ -913,17 +1037,14 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
913 | return head; | 1037 | return head; |
914 | } | 1038 | } |
915 | 1039 | ||
916 | struct timeval last_read, this_read; | 1040 | static void perf_session__mmap_read_counter(struct perf_session *self, |
917 | 1041 | struct mmap_data *md) | |
918 | static void mmap_read_counter(struct mmap_data *md) | ||
919 | { | 1042 | { |
920 | unsigned int head = mmap_read_head(md); | 1043 | unsigned int head = mmap_read_head(md); |
921 | unsigned int old = md->prev; | 1044 | unsigned int old = md->prev; |
922 | unsigned char *data = md->base + page_size; | 1045 | unsigned char *data = md->base + page_size; |
923 | int diff; | 1046 | int diff; |
924 | 1047 | ||
925 | gettimeofday(&this_read, NULL); | ||
926 | |||
927 | /* | 1048 | /* |
928 | * If we're further behind than half the buffer, there's a chance | 1049 | * If we're further behind than half the buffer, there's a chance |
929 | * the writer will bite our tail and mess up the samples under us. | 1050 | * the writer will bite our tail and mess up the samples under us. |
@@ -934,14 +1055,7 @@ static void mmap_read_counter(struct mmap_data *md) | |||
934 | */ | 1055 | */ |
935 | diff = head - old; | 1056 | diff = head - old; |
936 | if (diff > md->mask / 2 || diff < 0) { | 1057 | if (diff > md->mask / 2 || diff < 0) { |
937 | struct timeval iv; | 1058 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
938 | unsigned long msecs; | ||
939 | |||
940 | timersub(&this_read, &last_read, &iv); | ||
941 | msecs = iv.tv_sec*1000 + iv.tv_usec/1000; | ||
942 | |||
943 | fprintf(stderr, "WARNING: failed to keep up with mmap data." | ||
944 | " Last read %lu msecs ago.\n", msecs); | ||
945 | 1059 | ||
946 | /* | 1060 | /* |
947 | * head points to a known good entry, start there. | 1061 | * head points to a known good entry, start there. |
@@ -949,8 +1063,6 @@ static void mmap_read_counter(struct mmap_data *md) | |||
949 | old = head; | 1063 | old = head; |
950 | } | 1064 | } |
951 | 1065 | ||
952 | last_read = this_read; | ||
953 | |||
954 | for (; old != head;) { | 1066 | for (; old != head;) { |
955 | event_t *event = (event_t *)&data[old & md->mask]; | 1067 | event_t *event = (event_t *)&data[old & md->mask]; |
956 | 1068 | ||
@@ -978,13 +1090,11 @@ static void mmap_read_counter(struct mmap_data *md) | |||
978 | event = &event_copy; | 1090 | event = &event_copy; |
979 | } | 1091 | } |
980 | 1092 | ||
1093 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
1094 | event__process_sample(event, self, md->counter); | ||
1095 | else | ||
1096 | event__process(event, self); | ||
981 | old += size; | 1097 | old += size; |
982 | |||
983 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
984 | int user = | ||
985 | (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; | ||
986 | process_event(event->ip.ip, md->counter, user); | ||
987 | } | ||
988 | } | 1098 | } |
989 | 1099 | ||
990 | md->prev = old; | 1100 | md->prev = old; |
@@ -993,13 +1103,13 @@ static void mmap_read_counter(struct mmap_data *md) | |||
993 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 1103 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
994 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 1104 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
995 | 1105 | ||
996 | static void mmap_read(void) | 1106 | static void perf_session__mmap_read(struct perf_session *self) |
997 | { | 1107 | { |
998 | int i, counter; | 1108 | int i, counter; |
999 | 1109 | ||
1000 | for (i = 0; i < nr_cpus; i++) { | 1110 | for (i = 0; i < nr_cpus; i++) { |
1001 | for (counter = 0; counter < nr_counters; counter++) | 1111 | for (counter = 0; counter < nr_counters; counter++) |
1002 | mmap_read_counter(&mmap_array[i][counter]); | 1112 | perf_session__mmap_read_counter(self, &mmap_array[i][counter]); |
1003 | } | 1113 | } |
1004 | } | 1114 | } |
1005 | 1115 | ||
@@ -1018,8 +1128,15 @@ static void start_counter(int i, int counter) | |||
1018 | attr = attrs + counter; | 1128 | attr = attrs + counter; |
1019 | 1129 | ||
1020 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1130 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1021 | attr->freq = freq; | 1131 | |
1132 | if (freq) { | ||
1133 | attr->sample_type |= PERF_SAMPLE_PERIOD; | ||
1134 | attr->freq = 1; | ||
1135 | attr->sample_freq = freq; | ||
1136 | } | ||
1137 | |||
1022 | attr->inherit = (cpu < 0) && inherit; | 1138 | attr->inherit = (cpu < 0) && inherit; |
1139 | attr->mmap = 1; | ||
1023 | 1140 | ||
1024 | try_again: | 1141 | try_again: |
1025 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1142 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
@@ -1077,6 +1194,18 @@ static int __cmd_top(void) | |||
1077 | pthread_t thread; | 1194 | pthread_t thread; |
1078 | int i, counter; | 1195 | int i, counter; |
1079 | int ret; | 1196 | int ret; |
1197 | /* | ||
1198 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | ||
1199 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | ||
1200 | */ | ||
1201 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); | ||
1202 | if (session == NULL) | ||
1203 | return -ENOMEM; | ||
1204 | |||
1205 | if (target_pid != -1) | ||
1206 | event__synthesize_thread(target_pid, event__process, session); | ||
1207 | else | ||
1208 | event__synthesize_threads(event__process, session); | ||
1080 | 1209 | ||
1081 | for (i = 0; i < nr_cpus; i++) { | 1210 | for (i = 0; i < nr_cpus; i++) { |
1082 | group_fd = -1; | 1211 | group_fd = -1; |
@@ -1087,7 +1216,7 @@ static int __cmd_top(void) | |||
1087 | /* Wait for a minimal set of events before starting the snapshot */ | 1216 | /* Wait for a minimal set of events before starting the snapshot */ |
1088 | poll(event_array, nr_poll, 100); | 1217 | poll(event_array, nr_poll, 100); |
1089 | 1218 | ||
1090 | mmap_read(); | 1219 | perf_session__mmap_read(session); |
1091 | 1220 | ||
1092 | if (pthread_create(&thread, NULL, display_thread, NULL)) { | 1221 | if (pthread_create(&thread, NULL, display_thread, NULL)) { |
1093 | printf("Could not create display thread.\n"); | 1222 | printf("Could not create display thread.\n"); |
@@ -1107,7 +1236,7 @@ static int __cmd_top(void) | |||
1107 | while (1) { | 1236 | while (1) { |
1108 | int hits = samples; | 1237 | int hits = samples; |
1109 | 1238 | ||
1110 | mmap_read(); | 1239 | perf_session__mmap_read(session); |
1111 | 1240 | ||
1112 | if (hits == samples) | 1241 | if (hits == samples) |
1113 | ret = poll(event_array, nr_poll, 100); | 1242 | ret = poll(event_array, nr_poll, 100); |
@@ -1133,7 +1262,10 @@ static const struct option options[] = { | |||
1133 | "system-wide collection from all CPUs"), | 1262 | "system-wide collection from all CPUs"), |
1134 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1263 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1135 | "CPU to profile on"), | 1264 | "CPU to profile on"), |
1136 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 1265 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1266 | "file", "vmlinux pathname"), | ||
1267 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | ||
1268 | "hide kernel symbols"), | ||
1137 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1269 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1138 | "number of mmap data pages"), | 1270 | "number of mmap data pages"), |
1139 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1271 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -1149,13 +1281,15 @@ static const struct option options[] = { | |||
1149 | OPT_BOOLEAN('i', "inherit", &inherit, | 1281 | OPT_BOOLEAN('i', "inherit", &inherit, |
1150 | "child tasks inherit counters"), | 1282 | "child tasks inherit counters"), |
1151 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1283 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1152 | "symbol to annotate - requires -k option"), | 1284 | "symbol to annotate"), |
1153 | OPT_BOOLEAN('z', "zero", &zero, | 1285 | OPT_BOOLEAN('z', "zero", &zero, |
1154 | "zero history across updates"), | 1286 | "zero history across updates"), |
1155 | OPT_INTEGER('F', "freq", &freq, | 1287 | OPT_INTEGER('F', "freq", &freq, |
1156 | "profile at this frequency"), | 1288 | "profile at this frequency"), |
1157 | OPT_INTEGER('E', "entries", &print_entries, | 1289 | OPT_INTEGER('E', "entries", &print_entries, |
1158 | "display this many functions"), | 1290 | "display this many functions"), |
1291 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | ||
1292 | "hide user symbols"), | ||
1159 | OPT_BOOLEAN('v', "verbose", &verbose, | 1293 | OPT_BOOLEAN('v', "verbose", &verbose, |
1160 | "be more verbose (show counter open errors, etc)"), | 1294 | "be more verbose (show counter open errors, etc)"), |
1161 | OPT_END() | 1295 | OPT_END() |
@@ -1165,19 +1299,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1165 | { | 1299 | { |
1166 | int counter; | 1300 | int counter; |
1167 | 1301 | ||
1168 | symbol__init(); | ||
1169 | |||
1170 | page_size = sysconf(_SC_PAGE_SIZE); | 1302 | page_size = sysconf(_SC_PAGE_SIZE); |
1171 | 1303 | ||
1172 | argc = parse_options(argc, argv, options, top_usage, 0); | 1304 | argc = parse_options(argc, argv, options, top_usage, 0); |
1173 | if (argc) | 1305 | if (argc) |
1174 | usage_with_options(top_usage, options); | 1306 | usage_with_options(top_usage, options); |
1175 | 1307 | ||
1176 | if (freq) { | ||
1177 | default_interval = freq; | ||
1178 | freq = 1; | ||
1179 | } | ||
1180 | |||
1181 | /* CPU and PID are mutually exclusive */ | 1308 | /* CPU and PID are mutually exclusive */ |
1182 | if (target_pid != -1 && profile_cpu != -1) { | 1309 | if (target_pid != -1 && profile_cpu != -1) { |
1183 | printf("WARNING: PID switch overriding CPU\n"); | 1310 | printf("WARNING: PID switch overriding CPU\n"); |
@@ -1188,11 +1315,27 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1188 | if (!nr_counters) | 1315 | if (!nr_counters) |
1189 | nr_counters = 1; | 1316 | nr_counters = 1; |
1190 | 1317 | ||
1318 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | ||
1319 | (nr_counters + 1) * sizeof(unsigned long)); | ||
1320 | |||
1321 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | ||
1322 | if (symbol__init() < 0) | ||
1323 | return -1; | ||
1324 | |||
1191 | if (delay_secs < 1) | 1325 | if (delay_secs < 1) |
1192 | delay_secs = 1; | 1326 | delay_secs = 1; |
1193 | 1327 | ||
1194 | parse_symbols(); | 1328 | /* |
1195 | parse_source(sym_filter_entry); | 1329 | * User specified count overrides default frequency. |
1330 | */ | ||
1331 | if (default_interval) | ||
1332 | freq = 0; | ||
1333 | else if (freq) { | ||
1334 | default_interval = freq; | ||
1335 | } else { | ||
1336 | fprintf(stderr, "frequency and count are zero, aborting\n"); | ||
1337 | exit(EXIT_FAILURE); | ||
1338 | } | ||
1196 | 1339 | ||
1197 | /* | 1340 | /* |
1198 | * Fill in the ones not specifically initialized via -c: | 1341 | * Fill in the ones not specifically initialized via -c: |
@@ -1211,5 +1354,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1211 | if (target_pid != -1 || profile_cpu != -1) | 1354 | if (target_pid != -1 || profile_cpu != -1) |
1212 | nr_cpus = 1; | 1355 | nr_cpus = 1; |
1213 | 1356 | ||
1357 | get_term_dimensions(&winsize); | ||
1358 | if (print_entries == 0) { | ||
1359 | update_print_entries(&winsize); | ||
1360 | signal(SIGWINCH, sig_winch_handler); | ||
1361 | } | ||
1362 | |||
1214 | return __cmd_top(); | 1363 | return __cmd_top(); |
1215 | } | 1364 | } |