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