diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 81 |
1 files changed, 48 insertions, 33 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5352b5e352ed..95d5c0ae375a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include "util/symbol.h" | 23 | #include "util/symbol.h" |
24 | #include "util/color.h" | 24 | #include "util/color.h" |
25 | #include "util/util.h" | 25 | #include "util/util.h" |
26 | #include "util/rbtree.h" | 26 | #include <linux/rbtree.h> |
27 | #include "util/parse-options.h" | 27 | #include "util/parse-options.h" |
28 | #include "util/parse-events.h" | 28 | #include "util/parse-events.h" |
29 | 29 | ||
@@ -66,6 +66,7 @@ static unsigned int page_size; | |||
66 | static unsigned int mmap_pages = 16; | 66 | static unsigned int mmap_pages = 16; |
67 | static int freq = 0; | 67 | static int freq = 0; |
68 | static int verbose = 0; | 68 | static int verbose = 0; |
69 | static char *vmlinux = NULL; | ||
69 | 70 | ||
70 | static char *sym_filter; | 71 | static char *sym_filter; |
71 | static unsigned long filter_start; | 72 | static unsigned long filter_start; |
@@ -238,7 +239,6 @@ static void print_sym_table(void) | |||
238 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 239 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
239 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | 240 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); |
240 | struct symbol *sym = (struct symbol *)(syme + 1); | 241 | struct symbol *sym = (struct symbol *)(syme + 1); |
241 | char *color = PERF_COLOR_NORMAL; | ||
242 | double pcnt; | 242 | double pcnt; |
243 | 243 | ||
244 | if (++printed > print_entries || syme->snap_count < count_filter) | 244 | if (++printed > print_entries || syme->snap_count < count_filter) |
@@ -247,29 +247,20 @@ static void print_sym_table(void) | |||
247 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 247 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
248 | sum_ksamples)); | 248 | sum_ksamples)); |
249 | 249 | ||
250 | /* | ||
251 | * We color high-overhead entries in red, mid-overhead | ||
252 | * entries in green - and keep the low overhead places | ||
253 | * normal: | ||
254 | */ | ||
255 | if (pcnt >= 5.0) { | ||
256 | color = PERF_COLOR_RED; | ||
257 | } else { | ||
258 | if (pcnt >= 0.5) | ||
259 | color = PERF_COLOR_GREEN; | ||
260 | } | ||
261 | |||
262 | if (nr_counters == 1) | 250 | if (nr_counters == 1) |
263 | printf("%20.2f - ", syme->weight); | 251 | printf("%20.2f - ", syme->weight); |
264 | else | 252 | else |
265 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 253 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); |
266 | 254 | ||
267 | color_fprintf(stdout, color, "%4.1f%%", pcnt); | 255 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
268 | printf(" - %016llx : %s\n", sym->start, sym->name); | 256 | printf(" - %016llx : %s", sym->start, sym->name); |
257 | if (sym->module) | ||
258 | printf("\t[%s]", sym->module->name); | ||
259 | printf("\n"); | ||
269 | } | 260 | } |
270 | } | 261 | } |
271 | 262 | ||
272 | static void *display_thread(void *arg) | 263 | static void *display_thread(void *arg __used) |
273 | { | 264 | { |
274 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 265 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
275 | int delay_msecs = delay_secs * 1000; | 266 | int delay_msecs = delay_secs * 1000; |
@@ -286,11 +277,31 @@ static void *display_thread(void *arg) | |||
286 | return NULL; | 277 | return NULL; |
287 | } | 278 | } |
288 | 279 | ||
280 | /* Tag samples to be skipped. */ | ||
281 | static const char *skip_symbols[] = { | ||
282 | "default_idle", | ||
283 | "cpu_idle", | ||
284 | "enter_idle", | ||
285 | "exit_idle", | ||
286 | "mwait_idle", | ||
287 | "ppc64_runlatch_off", | ||
288 | "pseries_dedicated_idle_sleep", | ||
289 | NULL | ||
290 | }; | ||
291 | |||
289 | static int symbol_filter(struct dso *self, struct symbol *sym) | 292 | static int symbol_filter(struct dso *self, struct symbol *sym) |
290 | { | 293 | { |
291 | static int filter_match; | 294 | static int filter_match; |
292 | struct sym_entry *syme; | 295 | struct sym_entry *syme; |
293 | const char *name = sym->name; | 296 | const char *name = sym->name; |
297 | int i; | ||
298 | |||
299 | /* | ||
300 | * ppc64 uses function descriptors and appends a '.' to the | ||
301 | * start of every instruction address. Remove it. | ||
302 | */ | ||
303 | if (name[0] == '.') | ||
304 | name++; | ||
294 | 305 | ||
295 | if (!strcmp(name, "_text") || | 306 | if (!strcmp(name, "_text") || |
296 | !strcmp(name, "_etext") || | 307 | !strcmp(name, "_etext") || |
@@ -302,13 +313,12 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
302 | return 1; | 313 | return 1; |
303 | 314 | ||
304 | syme = dso__sym_priv(self, sym); | 315 | syme = dso__sym_priv(self, sym); |
305 | /* Tag samples to be skipped. */ | 316 | for (i = 0; skip_symbols[i]; i++) { |
306 | if (!strcmp("default_idle", name) || | 317 | if (!strcmp(skip_symbols[i], name)) { |
307 | !strcmp("cpu_idle", name) || | 318 | syme->skip = 1; |
308 | !strcmp("enter_idle", name) || | 319 | break; |
309 | !strcmp("exit_idle", name) || | 320 | } |
310 | !strcmp("mwait_idle", name)) | 321 | } |
311 | syme->skip = 1; | ||
312 | 322 | ||
313 | if (filter_match == 1) { | 323 | if (filter_match == 1) { |
314 | filter_end = sym->start; | 324 | filter_end = sym->start; |
@@ -340,12 +350,13 @@ static int parse_symbols(void) | |||
340 | { | 350 | { |
341 | struct rb_node *node; | 351 | struct rb_node *node; |
342 | struct symbol *sym; | 352 | struct symbol *sym; |
353 | int modules = vmlinux ? 1 : 0; | ||
343 | 354 | ||
344 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); | 355 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); |
345 | if (kernel_dso == NULL) | 356 | if (kernel_dso == NULL) |
346 | return -1; | 357 | return -1; |
347 | 358 | ||
348 | if (dso__load_kernel(kernel_dso, NULL, symbol_filter, 1) != 0) | 359 | if (dso__load_kernel(kernel_dso, vmlinux, symbol_filter, verbose, modules) <= 0) |
349 | goto out_delete_dso; | 360 | goto out_delete_dso; |
350 | 361 | ||
351 | node = rb_first(&kernel_dso->syms); | 362 | node = rb_first(&kernel_dso->syms); |
@@ -392,11 +403,11 @@ static void record_ip(u64 ip, int counter) | |||
392 | samples--; | 403 | samples--; |
393 | } | 404 | } |
394 | 405 | ||
395 | static void process_event(u64 ip, int counter) | 406 | static void process_event(u64 ip, int counter, int user) |
396 | { | 407 | { |
397 | samples++; | 408 | samples++; |
398 | 409 | ||
399 | if (ip < min_ip || ip > max_ip) { | 410 | if (user) { |
400 | userspace_samples++; | 411 | userspace_samples++; |
401 | return; | 412 | return; |
402 | } | 413 | } |
@@ -407,7 +418,7 @@ static void process_event(u64 ip, int counter) | |||
407 | struct mmap_data { | 418 | struct mmap_data { |
408 | int counter; | 419 | int counter; |
409 | void *base; | 420 | void *base; |
410 | unsigned int mask; | 421 | int mask; |
411 | unsigned int prev; | 422 | unsigned int prev; |
412 | }; | 423 | }; |
413 | 424 | ||
@@ -509,9 +520,10 @@ static void mmap_read_counter(struct mmap_data *md) | |||
509 | 520 | ||
510 | old += size; | 521 | old += size; |
511 | 522 | ||
512 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { | 523 | if (event->header.type == PERF_EVENT_SAMPLE) { |
513 | if (event->header.type & PERF_SAMPLE_IP) | 524 | int user = |
514 | process_event(event->ip.ip, md->counter); | 525 | (event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK) == PERF_EVENT_MISC_USER; |
526 | process_event(event->ip.ip, md->counter, user); | ||
515 | } | 527 | } |
516 | } | 528 | } |
517 | 529 | ||
@@ -660,6 +672,7 @@ static const struct option options[] = { | |||
660 | "system-wide collection from all CPUs"), | 672 | "system-wide collection from all CPUs"), |
661 | OPT_INTEGER('C', "CPU", &profile_cpu, | 673 | OPT_INTEGER('C', "CPU", &profile_cpu, |
662 | "CPU to profile on"), | 674 | "CPU to profile on"), |
675 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | ||
663 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 676 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
664 | "number of mmap data pages"), | 677 | "number of mmap data pages"), |
665 | OPT_INTEGER('r', "realtime", &realtime_prio, | 678 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -674,7 +687,7 @@ static const struct option options[] = { | |||
674 | "put the counters into a counter group"), | 687 | "put the counters into a counter group"), |
675 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", | 688 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", |
676 | "only display symbols matchig this pattern"), | 689 | "only display symbols matchig this pattern"), |
677 | OPT_BOOLEAN('z', "zero", &group, | 690 | OPT_BOOLEAN('z', "zero", &zero, |
678 | "zero history across updates"), | 691 | "zero history across updates"), |
679 | OPT_INTEGER('F', "freq", &freq, | 692 | OPT_INTEGER('F', "freq", &freq, |
680 | "profile at this frequency"), | 693 | "profile at this frequency"), |
@@ -685,10 +698,12 @@ static const struct option options[] = { | |||
685 | OPT_END() | 698 | OPT_END() |
686 | }; | 699 | }; |
687 | 700 | ||
688 | int cmd_top(int argc, const char **argv, const char *prefix) | 701 | int cmd_top(int argc, const char **argv, const char *prefix __used) |
689 | { | 702 | { |
690 | int counter; | 703 | int counter; |
691 | 704 | ||
705 | symbol__init(); | ||
706 | |||
692 | page_size = sysconf(_SC_PAGE_SIZE); | 707 | page_size = sysconf(_SC_PAGE_SIZE); |
693 | 708 | ||
694 | argc = parse_options(argc, argv, options, top_usage, 0); | 709 | argc = parse_options(argc, argv, options, top_usage, 0); |