diff options
-rw-r--r-- | tools/perf/Makefile | 2 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 482 | ||||
-rw-r--r-- | tools/perf/util/top.c | 212 | ||||
-rw-r--r-- | tools/perf/util/top.h | 67 |
4 files changed, 418 insertions, 345 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 36ff73c0af94..edc660e0bc2f 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -437,6 +437,7 @@ LIB_H += util/probe-finder.h | |||
437 | LIB_H += util/probe-event.h | 437 | LIB_H += util/probe-event.h |
438 | LIB_H += util/pstack.h | 438 | LIB_H += util/pstack.h |
439 | LIB_H += util/cpumap.h | 439 | LIB_H += util/cpumap.h |
440 | LIB_H += util/top.h | ||
440 | LIB_H += $(ARCH_INCLUDE) | 441 | LIB_H += $(ARCH_INCLUDE) |
441 | 442 | ||
442 | LIB_OBJS += $(OUTPUT)util/abspath.o | 443 | LIB_OBJS += $(OUTPUT)util/abspath.o |
@@ -464,6 +465,7 @@ LIB_OBJS += $(OUTPUT)util/strbuf.o | |||
464 | LIB_OBJS += $(OUTPUT)util/string.o | 465 | LIB_OBJS += $(OUTPUT)util/string.o |
465 | LIB_OBJS += $(OUTPUT)util/strlist.o | 466 | LIB_OBJS += $(OUTPUT)util/strlist.o |
466 | LIB_OBJS += $(OUTPUT)util/strfilter.o | 467 | LIB_OBJS += $(OUTPUT)util/strfilter.o |
468 | LIB_OBJS += $(OUTPUT)util/top.o | ||
467 | LIB_OBJS += $(OUTPUT)util/usage.o | 469 | LIB_OBJS += $(OUTPUT)util/usage.o |
468 | LIB_OBJS += $(OUTPUT)util/wrapper.o | 470 | LIB_OBJS += $(OUTPUT)util/wrapper.o |
469 | LIB_OBJS += $(OUTPUT)util/sigchain.o | 471 | LIB_OBJS += $(OUTPUT)util/sigchain.o |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 599036b06730..3c9ba943aa48 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "util/symbol.h" | 27 | #include "util/symbol.h" |
28 | #include "util/thread.h" | 28 | #include "util/thread.h" |
29 | #include "util/thread_map.h" | 29 | #include "util/thread_map.h" |
30 | #include "util/top.h" | ||
30 | #include "util/util.h" | 31 | #include "util/util.h" |
31 | #include <linux/rbtree.h> | 32 | #include <linux/rbtree.h> |
32 | #include "util/parse-options.h" | 33 | #include "util/parse-options.h" |
@@ -47,7 +48,6 @@ | |||
47 | #include <errno.h> | 48 | #include <errno.h> |
48 | #include <time.h> | 49 | #include <time.h> |
49 | #include <sched.h> | 50 | #include <sched.h> |
50 | #include <pthread.h> | ||
51 | 51 | ||
52 | #include <sys/syscall.h> | 52 | #include <sys/syscall.h> |
53 | #include <sys/ioctl.h> | 53 | #include <sys/ioctl.h> |
@@ -62,75 +62,35 @@ | |||
62 | 62 | ||
63 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 63 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
64 | 64 | ||
65 | struct perf_evlist *evsel_list; | 65 | static struct perf_top top = { |
66 | .count_filter = 5, | ||
67 | .delay_secs = 2, | ||
68 | .display_weighted = -1, | ||
69 | .target_pid = -1, | ||
70 | .target_tid = -1, | ||
71 | .active_symbols = LIST_HEAD_INIT(top.active_symbols), | ||
72 | .active_symbols_lock = PTHREAD_MUTEX_INITIALIZER, | ||
73 | .freq = 1000, /* 1 KHz */ | ||
74 | }; | ||
66 | 75 | ||
67 | static bool system_wide = false; | 76 | static bool system_wide = false; |
68 | 77 | ||
69 | static int default_interval = 0; | 78 | static int default_interval = 0; |
70 | 79 | ||
71 | static int count_filter = 5; | ||
72 | static int print_entries; | ||
73 | |||
74 | static int target_pid = -1; | ||
75 | static int target_tid = -1; | ||
76 | static bool inherit = false; | 80 | static bool inherit = false; |
77 | static int realtime_prio = 0; | 81 | static int realtime_prio = 0; |
78 | static bool group = false; | 82 | static bool group = false; |
79 | static unsigned int page_size; | 83 | static unsigned int page_size; |
80 | static unsigned int mmap_pages = 128; | 84 | static unsigned int mmap_pages = 128; |
81 | static int freq = 1000; /* 1 KHz */ | ||
82 | 85 | ||
83 | static int delay_secs = 2; | ||
84 | static bool zero = false; | ||
85 | static bool dump_symtab = false; | 86 | static bool dump_symtab = false; |
86 | 87 | ||
87 | static bool hide_kernel_symbols = false; | ||
88 | static bool hide_user_symbols = false; | ||
89 | static struct winsize winsize; | 88 | static struct winsize winsize; |
90 | 89 | ||
91 | /* | ||
92 | * Source | ||
93 | */ | ||
94 | |||
95 | struct source_line { | ||
96 | u64 eip; | ||
97 | unsigned long count[MAX_COUNTERS]; | ||
98 | char *line; | ||
99 | struct source_line *next; | ||
100 | }; | ||
101 | |||
102 | static const char *sym_filter = NULL; | 90 | static const char *sym_filter = NULL; |
103 | struct sym_entry *sym_filter_entry = NULL; | 91 | struct sym_entry *sym_filter_entry = NULL; |
104 | struct sym_entry *sym_filter_entry_sched = NULL; | 92 | struct sym_entry *sym_filter_entry_sched = NULL; |
105 | static int sym_pcnt_filter = 5; | 93 | static int sym_pcnt_filter = 5; |
106 | static int sym_counter = 0; | ||
107 | static struct perf_evsel *sym_evsel = NULL; | ||
108 | static int display_weighted = -1; | ||
109 | static const char *cpu_list; | ||
110 | |||
111 | /* | ||
112 | * Symbols | ||
113 | */ | ||
114 | |||
115 | struct sym_entry_source { | ||
116 | struct source_line *source; | ||
117 | struct source_line *lines; | ||
118 | struct source_line **lines_tail; | ||
119 | pthread_mutex_t lock; | ||
120 | }; | ||
121 | |||
122 | struct sym_entry { | ||
123 | struct rb_node rb_node; | ||
124 | struct list_head node; | ||
125 | unsigned long snap_count; | ||
126 | double weight; | ||
127 | int skip; | ||
128 | u16 name_len; | ||
129 | u8 origin; | ||
130 | struct map *map; | ||
131 | struct sym_entry_source *src; | ||
132 | unsigned long count[0]; | ||
133 | }; | ||
134 | 94 | ||
135 | /* | 95 | /* |
136 | * Source functions | 96 | * Source functions |
@@ -165,10 +125,10 @@ void get_term_dimensions(struct winsize *ws) | |||
165 | 125 | ||
166 | static void update_print_entries(struct winsize *ws) | 126 | static void update_print_entries(struct winsize *ws) |
167 | { | 127 | { |
168 | print_entries = ws->ws_row; | 128 | top.print_entries = ws->ws_row; |
169 | 129 | ||
170 | if (print_entries > 9) | 130 | if (top.print_entries > 9) |
171 | print_entries -= 9; | 131 | top.print_entries -= 9; |
172 | } | 132 | } |
173 | 133 | ||
174 | static void sig_winch_handler(int sig __used) | 134 | static void sig_winch_handler(int sig __used) |
@@ -269,7 +229,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
269 | 229 | ||
270 | line = syme->src->lines; | 230 | line = syme->src->lines; |
271 | while (line) { | 231 | while (line) { |
272 | for (i = 0; i < evsel_list->nr_entries; i++) | 232 | for (i = 0; i < top.evlist->nr_entries; i++) |
273 | line->count[i] = 0; | 233 | line->count[i] = 0; |
274 | line = line->next; | 234 | line = line->next; |
275 | } | 235 | } |
@@ -331,9 +291,9 @@ static void show_lines(struct source_line *queue, int count, int total) | |||
331 | 291 | ||
332 | line = queue; | 292 | line = queue; |
333 | for (i = 0; i < count; i++) { | 293 | for (i = 0; i < count; i++) { |
334 | float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; | 294 | float pcnt = 100.0*(float)line->count[top.sym_counter]/(float)total; |
335 | 295 | ||
336 | printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); | 296 | printf("%8li %4.1f%%\t%s\n", line->count[top.sym_counter], pcnt, line->line); |
337 | line = line->next; | 297 | line = line->next; |
338 | } | 298 | } |
339 | } | 299 | } |
@@ -358,13 +318,13 @@ static void show_details(struct sym_entry *syme) | |||
358 | return; | 318 | return; |
359 | 319 | ||
360 | symbol = sym_entry__symbol(syme); | 320 | symbol = sym_entry__symbol(syme); |
361 | printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name); | 321 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
362 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 322 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
363 | 323 | ||
364 | pthread_mutex_lock(&syme->src->lock); | 324 | pthread_mutex_lock(&syme->src->lock); |
365 | line = syme->src->source; | 325 | line = syme->src->source; |
366 | while (line) { | 326 | while (line) { |
367 | total += line->count[sym_counter]; | 327 | total += line->count[top.sym_counter]; |
368 | line = line->next; | 328 | line = line->next; |
369 | } | 329 | } |
370 | 330 | ||
@@ -376,10 +336,10 @@ static void show_details(struct sym_entry *syme) | |||
376 | line_queue = line; | 336 | line_queue = line; |
377 | line_queue_count++; | 337 | line_queue_count++; |
378 | 338 | ||
379 | if (line->count[sym_counter]) | 339 | if (line->count[top.sym_counter]) |
380 | pcnt = 100.0 * line->count[sym_counter] / (float)total; | 340 | pcnt = 100.0 * line->count[top.sym_counter] / (float)total; |
381 | if (pcnt >= (float)sym_pcnt_filter) { | 341 | if (pcnt >= (float)sym_pcnt_filter) { |
382 | if (displayed <= print_entries) | 342 | if (displayed <= top.print_entries) |
383 | show_lines(line_queue, line_queue_count, total); | 343 | show_lines(line_queue, line_queue_count, total); |
384 | else more++; | 344 | else more++; |
385 | displayed += line_queue_count; | 345 | displayed += line_queue_count; |
@@ -390,7 +350,7 @@ static void show_details(struct sym_entry *syme) | |||
390 | line_queue_count--; | 350 | line_queue_count--; |
391 | } | 351 | } |
392 | 352 | ||
393 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | 353 | line->count[top.sym_counter] = top.zero ? 0 : line->count[top.sym_counter] * 7 / 8; |
394 | line = line->next; | 354 | line = line->next; |
395 | } | 355 | } |
396 | pthread_mutex_unlock(&syme->src->lock); | 356 | pthread_mutex_unlock(&syme->src->lock); |
@@ -398,181 +358,30 @@ static void show_details(struct sym_entry *syme) | |||
398 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 358 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
399 | } | 359 | } |
400 | 360 | ||
401 | /* | ||
402 | * Symbols will be added here in perf_event__process_sample and will get out | ||
403 | * after decayed. | ||
404 | */ | ||
405 | static LIST_HEAD(active_symbols); | ||
406 | static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER; | ||
407 | |||
408 | /* | ||
409 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
410 | */ | ||
411 | static double sym_weight(const struct sym_entry *sym) | ||
412 | { | ||
413 | double weight = sym->snap_count; | ||
414 | int counter; | ||
415 | |||
416 | if (!display_weighted) | ||
417 | return weight; | ||
418 | |||
419 | for (counter = 1; counter < evsel_list->nr_entries - 1; counter++) | ||
420 | weight *= sym->count[counter]; | ||
421 | |||
422 | weight /= (sym->count[counter] + 1); | ||
423 | |||
424 | return weight; | ||
425 | } | ||
426 | |||
427 | static long samples; | ||
428 | static long kernel_samples, us_samples; | ||
429 | static long exact_samples; | ||
430 | static long guest_us_samples, guest_kernel_samples; | ||
431 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 361 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
432 | 362 | ||
433 | static void __list_insert_active_sym(struct sym_entry *syme) | 363 | static void __list_insert_active_sym(struct sym_entry *syme) |
434 | { | 364 | { |
435 | list_add(&syme->node, &active_symbols); | 365 | list_add(&syme->node, &top.active_symbols); |
436 | } | ||
437 | |||
438 | static void list_remove_active_sym(struct sym_entry *syme) | ||
439 | { | ||
440 | pthread_mutex_lock(&active_symbols_lock); | ||
441 | list_del_init(&syme->node); | ||
442 | pthread_mutex_unlock(&active_symbols_lock); | ||
443 | } | ||
444 | |||
445 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
446 | { | ||
447 | struct rb_node **p = &tree->rb_node; | ||
448 | struct rb_node *parent = NULL; | ||
449 | struct sym_entry *iter; | ||
450 | |||
451 | while (*p != NULL) { | ||
452 | parent = *p; | ||
453 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
454 | |||
455 | if (se->weight > iter->weight) | ||
456 | p = &(*p)->rb_left; | ||
457 | else | ||
458 | p = &(*p)->rb_right; | ||
459 | } | ||
460 | |||
461 | rb_link_node(&se->rb_node, parent, p); | ||
462 | rb_insert_color(&se->rb_node, tree); | ||
463 | } | 366 | } |
464 | 367 | ||
465 | static void print_sym_table(struct perf_session *session) | 368 | static void print_sym_table(struct perf_session *session) |
466 | { | 369 | { |
467 | int printed = 0, j; | 370 | char bf[160]; |
468 | struct perf_evsel *counter; | 371 | int printed = 0; |
469 | int snap = !display_weighted ? sym_counter : 0; | ||
470 | float samples_per_sec = samples/delay_secs; | ||
471 | float ksamples_per_sec = kernel_samples/delay_secs; | ||
472 | float us_samples_per_sec = (us_samples)/delay_secs; | ||
473 | float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; | ||
474 | float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; | ||
475 | float esamples_percent = (100.0*exact_samples)/samples; | ||
476 | float sum_ksamples = 0.0; | ||
477 | struct sym_entry *syme, *n; | ||
478 | struct rb_root tmp = RB_ROOT; | ||
479 | struct rb_node *nd; | 372 | struct rb_node *nd; |
480 | int sym_width = 0, dso_width = 0, dso_short_width = 0; | 373 | struct sym_entry *syme; |
374 | struct rb_root tmp = RB_ROOT; | ||
481 | const int win_width = winsize.ws_col - 1; | 375 | const int win_width = winsize.ws_col - 1; |
482 | 376 | int sym_width, dso_width, dso_short_width; | |
483 | samples = us_samples = kernel_samples = exact_samples = 0; | 377 | float sum_ksamples = perf_top__decay_samples(&top, &tmp); |
484 | guest_kernel_samples = guest_us_samples = 0; | ||
485 | |||
486 | /* Sort the active symbols */ | ||
487 | pthread_mutex_lock(&active_symbols_lock); | ||
488 | syme = list_entry(active_symbols.next, struct sym_entry, node); | ||
489 | pthread_mutex_unlock(&active_symbols_lock); | ||
490 | |||
491 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | ||
492 | syme->snap_count = syme->count[snap]; | ||
493 | if (syme->snap_count != 0) { | ||
494 | |||
495 | if ((hide_user_symbols && | ||
496 | syme->origin == PERF_RECORD_MISC_USER) || | ||
497 | (hide_kernel_symbols && | ||
498 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
499 | list_remove_active_sym(syme); | ||
500 | continue; | ||
501 | } | ||
502 | syme->weight = sym_weight(syme); | ||
503 | rb_insert_active_sym(&tmp, syme); | ||
504 | sum_ksamples += syme->snap_count; | ||
505 | |||
506 | for (j = 0; j < evsel_list->nr_entries; j++) | ||
507 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; | ||
508 | } else | ||
509 | list_remove_active_sym(syme); | ||
510 | } | ||
511 | 378 | ||
512 | puts(CONSOLE_CLEAR); | 379 | puts(CONSOLE_CLEAR); |
513 | 380 | ||
514 | if (!perf_guest) { | 381 | perf_top__header_snprintf(&top, bf, sizeof(bf)); |
515 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | 382 | printf("%s\n", bf); |
516 | " exact: %4.1f%% [", | ||
517 | samples_per_sec, | ||
518 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
519 | samples_per_sec)), | ||
520 | esamples_percent); | ||
521 | } else { | ||
522 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
523 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
524 | " exact: %4.1f%% [", | ||
525 | samples_per_sec, | ||
526 | 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / | ||
527 | samples_per_sec)), | ||
528 | 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / | ||
529 | samples_per_sec)), | ||
530 | 100.0 - (100.0 * ((samples_per_sec - | ||
531 | guest_kernel_samples_per_sec) / | ||
532 | samples_per_sec)), | ||
533 | 100.0 - (100.0 * ((samples_per_sec - | ||
534 | guest_us_samples_per_sec) / | ||
535 | samples_per_sec)), | ||
536 | esamples_percent); | ||
537 | } | ||
538 | 383 | ||
539 | if (evsel_list->nr_entries == 1 || !display_weighted) { | 384 | perf_top__reset_sample_counters(&top); |
540 | struct perf_evsel *first; | ||
541 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); | ||
542 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); | ||
543 | if (freq) | ||
544 | printf("Hz "); | ||
545 | else | ||
546 | printf(" "); | ||
547 | } | ||
548 | |||
549 | if (!display_weighted) | ||
550 | printf("%s", event_name(sym_evsel)); | ||
551 | else list_for_each_entry(counter, &evsel_list->entries, node) { | ||
552 | if (counter->idx) | ||
553 | printf("/"); | ||
554 | |||
555 | printf("%s", event_name(counter)); | ||
556 | } | ||
557 | |||
558 | printf( "], "); | ||
559 | |||
560 | if (target_pid != -1) | ||
561 | printf(" (target_pid: %d", target_pid); | ||
562 | else if (target_tid != -1) | ||
563 | printf(" (target_tid: %d", target_tid); | ||
564 | else | ||
565 | printf(" (all"); | ||
566 | |||
567 | if (cpu_list) | ||
568 | printf(", CPU%s: %s)\n", evsel_list->cpus->nr > 1 ? "s" : "", cpu_list); | ||
569 | else { | ||
570 | if (target_tid != -1) | ||
571 | printf(")\n"); | ||
572 | else | ||
573 | printf(", %d CPU%s)\n", evsel_list->cpus->nr, | ||
574 | evsel_list->cpus->nr > 1 ? "s" : ""); | ||
575 | } | ||
576 | 385 | ||
577 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 386 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
578 | 387 | ||
@@ -587,26 +396,8 @@ static void print_sym_table(struct perf_session *session) | |||
587 | return; | 396 | return; |
588 | } | 397 | } |
589 | 398 | ||
590 | /* | 399 | perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, |
591 | * Find the longest symbol name that will be displayed | 400 | &sym_width); |
592 | */ | ||
593 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
594 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
595 | if (++printed > print_entries || | ||
596 | (int)syme->snap_count < count_filter) | ||
597 | continue; | ||
598 | |||
599 | if (syme->map->dso->long_name_len > dso_width) | ||
600 | dso_width = syme->map->dso->long_name_len; | ||
601 | |||
602 | if (syme->map->dso->short_name_len > dso_short_width) | ||
603 | dso_short_width = syme->map->dso->short_name_len; | ||
604 | |||
605 | if (syme->name_len > sym_width) | ||
606 | sym_width = syme->name_len; | ||
607 | } | ||
608 | |||
609 | printed = 0; | ||
610 | 401 | ||
611 | if (sym_width + dso_width > winsize.ws_col - 29) { | 402 | if (sym_width + dso_width > winsize.ws_col - 29) { |
612 | dso_width = dso_short_width; | 403 | dso_width = dso_short_width; |
@@ -614,7 +405,7 @@ static void print_sym_table(struct perf_session *session) | |||
614 | sym_width = winsize.ws_col - dso_width - 29; | 405 | sym_width = winsize.ws_col - dso_width - 29; |
615 | } | 406 | } |
616 | putchar('\n'); | 407 | putchar('\n'); |
617 | if (evsel_list->nr_entries == 1) | 408 | if (top.evlist->nr_entries == 1) |
618 | printf(" samples pcnt"); | 409 | printf(" samples pcnt"); |
619 | else | 410 | else |
620 | printf(" weight samples pcnt"); | 411 | printf(" weight samples pcnt"); |
@@ -623,7 +414,7 @@ static void print_sym_table(struct perf_session *session) | |||
623 | printf(" RIP "); | 414 | printf(" RIP "); |
624 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | 415 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
625 | printf(" %s _______ _____", | 416 | printf(" %s _______ _____", |
626 | evsel_list->nr_entries == 1 ? " " : "______"); | 417 | top.evlist->nr_entries == 1 ? " " : "______"); |
627 | if (verbose) | 418 | if (verbose) |
628 | printf(" ________________"); | 419 | printf(" ________________"); |
629 | printf(" %-*.*s", sym_width, sym_width, graph_line); | 420 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
@@ -636,13 +427,14 @@ static void print_sym_table(struct perf_session *session) | |||
636 | 427 | ||
637 | syme = rb_entry(nd, struct sym_entry, rb_node); | 428 | syme = rb_entry(nd, struct sym_entry, rb_node); |
638 | sym = sym_entry__symbol(syme); | 429 | sym = sym_entry__symbol(syme); |
639 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 430 | if (++printed > top.print_entries || |
431 | (int)syme->snap_count < top.count_filter) | ||
640 | continue; | 432 | continue; |
641 | 433 | ||
642 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 434 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
643 | sum_ksamples)); | 435 | sum_ksamples)); |
644 | 436 | ||
645 | if (evsel_list->nr_entries == 1 || !display_weighted) | 437 | if (top.evlist->nr_entries == 1 || !top.display_weighted) |
646 | printf("%20.2f ", syme->weight); | 438 | printf("%20.2f ", syme->weight); |
647 | else | 439 | else |
648 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | 440 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
@@ -715,11 +507,11 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
715 | if (p) | 507 | if (p) |
716 | *p = 0; | 508 | *p = 0; |
717 | 509 | ||
718 | pthread_mutex_lock(&active_symbols_lock); | 510 | pthread_mutex_lock(&top.active_symbols_lock); |
719 | syme = list_entry(active_symbols.next, struct sym_entry, node); | 511 | syme = list_entry(top.active_symbols.next, struct sym_entry, node); |
720 | pthread_mutex_unlock(&active_symbols_lock); | 512 | pthread_mutex_unlock(&top.active_symbols_lock); |
721 | 513 | ||
722 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 514 | list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { |
723 | struct symbol *sym = sym_entry__symbol(syme); | 515 | struct symbol *sym = sym_entry__symbol(syme); |
724 | 516 | ||
725 | if (!strcmp(buf, sym->name)) { | 517 | if (!strcmp(buf, sym->name)) { |
@@ -749,28 +541,28 @@ static void print_mapped_keys(void) | |||
749 | } | 541 | } |
750 | 542 | ||
751 | fprintf(stdout, "\nMapped keys:\n"); | 543 | fprintf(stdout, "\nMapped keys:\n"); |
752 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); | 544 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top.delay_secs); |
753 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | 545 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top.print_entries); |
754 | 546 | ||
755 | if (evsel_list->nr_entries > 1) | 547 | if (top.evlist->nr_entries > 1) |
756 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); | 548 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top.sym_evsel)); |
757 | 549 | ||
758 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 550 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top.count_filter); |
759 | 551 | ||
760 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 552 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
761 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 553 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
762 | fprintf(stdout, "\t[S] stop annotation.\n"); | 554 | fprintf(stdout, "\t[S] stop annotation.\n"); |
763 | 555 | ||
764 | if (evsel_list->nr_entries > 1) | 556 | if (top.evlist->nr_entries > 1) |
765 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 557 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); |
766 | 558 | ||
767 | fprintf(stdout, | 559 | fprintf(stdout, |
768 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 560 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
769 | hide_kernel_symbols ? "yes" : "no"); | 561 | top.hide_kernel_symbols ? "yes" : "no"); |
770 | fprintf(stdout, | 562 | fprintf(stdout, |
771 | "\t[U] hide user symbols. \t(%s)\n", | 563 | "\t[U] hide user symbols. \t(%s)\n", |
772 | hide_user_symbols ? "yes" : "no"); | 564 | top.hide_user_symbols ? "yes" : "no"); |
773 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 565 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top.zero ? 1 : 0); |
774 | fprintf(stdout, "\t[qQ] quit.\n"); | 566 | fprintf(stdout, "\t[qQ] quit.\n"); |
775 | } | 567 | } |
776 | 568 | ||
@@ -791,7 +583,7 @@ static int key_mapped(int c) | |||
791 | return 1; | 583 | return 1; |
792 | case 'E': | 584 | case 'E': |
793 | case 'w': | 585 | case 'w': |
794 | return evsel_list->nr_entries > 1 ? 1 : 0; | 586 | return top.evlist->nr_entries > 1 ? 1 : 0; |
795 | default: | 587 | default: |
796 | break; | 588 | break; |
797 | } | 589 | } |
@@ -826,47 +618,47 @@ static void handle_keypress(struct perf_session *session, int c) | |||
826 | 618 | ||
827 | switch (c) { | 619 | switch (c) { |
828 | case 'd': | 620 | case 'd': |
829 | prompt_integer(&delay_secs, "Enter display delay"); | 621 | prompt_integer(&top.delay_secs, "Enter display delay"); |
830 | if (delay_secs < 1) | 622 | if (top.delay_secs < 1) |
831 | delay_secs = 1; | 623 | top.delay_secs = 1; |
832 | break; | 624 | break; |
833 | case 'e': | 625 | case 'e': |
834 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 626 | prompt_integer(&top.print_entries, "Enter display entries (lines)"); |
835 | if (print_entries == 0) { | 627 | if (top.print_entries == 0) { |
836 | sig_winch_handler(SIGWINCH); | 628 | sig_winch_handler(SIGWINCH); |
837 | signal(SIGWINCH, sig_winch_handler); | 629 | signal(SIGWINCH, sig_winch_handler); |
838 | } else | 630 | } else |
839 | signal(SIGWINCH, SIG_DFL); | 631 | signal(SIGWINCH, SIG_DFL); |
840 | break; | 632 | break; |
841 | case 'E': | 633 | case 'E': |
842 | if (evsel_list->nr_entries > 1) { | 634 | if (top.evlist->nr_entries > 1) { |
843 | fprintf(stderr, "\nAvailable events:"); | 635 | fprintf(stderr, "\nAvailable events:"); |
844 | 636 | ||
845 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) | 637 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) |
846 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); | 638 | fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel)); |
847 | 639 | ||
848 | prompt_integer(&sym_counter, "Enter details event counter"); | 640 | prompt_integer(&top.sym_counter, "Enter details event counter"); |
849 | 641 | ||
850 | if (sym_counter >= evsel_list->nr_entries) { | 642 | if (top.sym_counter >= top.evlist->nr_entries) { |
851 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); | 643 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
852 | sym_counter = 0; | 644 | top.sym_counter = 0; |
853 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); | 645 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel)); |
854 | sleep(1); | 646 | sleep(1); |
855 | break; | 647 | break; |
856 | } | 648 | } |
857 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) | 649 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) |
858 | if (sym_evsel->idx == sym_counter) | 650 | if (top.sym_evsel->idx == top.sym_counter) |
859 | break; | 651 | break; |
860 | } else sym_counter = 0; | 652 | } else top.sym_counter = 0; |
861 | break; | 653 | break; |
862 | case 'f': | 654 | case 'f': |
863 | prompt_integer(&count_filter, "Enter display event count filter"); | 655 | prompt_integer(&top.count_filter, "Enter display event count filter"); |
864 | break; | 656 | break; |
865 | case 'F': | 657 | case 'F': |
866 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 658 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
867 | break; | 659 | break; |
868 | case 'K': | 660 | case 'K': |
869 | hide_kernel_symbols = !hide_kernel_symbols; | 661 | top.hide_kernel_symbols = !top.hide_kernel_symbols; |
870 | break; | 662 | break; |
871 | case 'q': | 663 | case 'q': |
872 | case 'Q': | 664 | case 'Q': |
@@ -890,13 +682,13 @@ static void handle_keypress(struct perf_session *session, int c) | |||
890 | } | 682 | } |
891 | break; | 683 | break; |
892 | case 'U': | 684 | case 'U': |
893 | hide_user_symbols = !hide_user_symbols; | 685 | top.hide_user_symbols = !top.hide_user_symbols; |
894 | break; | 686 | break; |
895 | case 'w': | 687 | case 'w': |
896 | display_weighted = ~display_weighted; | 688 | top.display_weighted = ~top.display_weighted; |
897 | break; | 689 | break; |
898 | case 'z': | 690 | case 'z': |
899 | zero = !zero; | 691 | top.zero = !top.zero; |
900 | break; | 692 | break; |
901 | default: | 693 | default: |
902 | break; | 694 | break; |
@@ -917,7 +709,7 @@ static void *display_thread(void *arg __used) | |||
917 | tc.c_cc[VTIME] = 0; | 709 | tc.c_cc[VTIME] = 0; |
918 | 710 | ||
919 | repeat: | 711 | repeat: |
920 | delay_msecs = delay_secs * 1000; | 712 | delay_msecs = top.delay_secs * 1000; |
921 | tcsetattr(0, TCSANOW, &tc); | 713 | tcsetattr(0, TCSANOW, &tc); |
922 | /* trash return*/ | 714 | /* trash return*/ |
923 | getc(stdin); | 715 | getc(stdin); |
@@ -1005,27 +797,27 @@ static void perf_event__process_sample(const union perf_event *event, | |||
1005 | struct machine *machine; | 797 | struct machine *machine; |
1006 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 798 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
1007 | 799 | ||
1008 | ++samples; | 800 | ++top.samples; |
1009 | 801 | ||
1010 | switch (origin) { | 802 | switch (origin) { |
1011 | case PERF_RECORD_MISC_USER: | 803 | case PERF_RECORD_MISC_USER: |
1012 | ++us_samples; | 804 | ++top.us_samples; |
1013 | if (hide_user_symbols) | 805 | if (top.hide_user_symbols) |
1014 | return; | 806 | return; |
1015 | machine = perf_session__find_host_machine(session); | 807 | machine = perf_session__find_host_machine(session); |
1016 | break; | 808 | break; |
1017 | case PERF_RECORD_MISC_KERNEL: | 809 | case PERF_RECORD_MISC_KERNEL: |
1018 | ++kernel_samples; | 810 | ++top.kernel_samples; |
1019 | if (hide_kernel_symbols) | 811 | if (top.hide_kernel_symbols) |
1020 | return; | 812 | return; |
1021 | machine = perf_session__find_host_machine(session); | 813 | machine = perf_session__find_host_machine(session); |
1022 | break; | 814 | break; |
1023 | case PERF_RECORD_MISC_GUEST_KERNEL: | 815 | case PERF_RECORD_MISC_GUEST_KERNEL: |
1024 | ++guest_kernel_samples; | 816 | ++top.guest_kernel_samples; |
1025 | machine = perf_session__find_machine(session, event->ip.pid); | 817 | machine = perf_session__find_machine(session, event->ip.pid); |
1026 | break; | 818 | break; |
1027 | case PERF_RECORD_MISC_GUEST_USER: | 819 | case PERF_RECORD_MISC_GUEST_USER: |
1028 | ++guest_us_samples; | 820 | ++top.guest_us_samples; |
1029 | /* | 821 | /* |
1030 | * TODO: we don't process guest user from host side | 822 | * TODO: we don't process guest user from host side |
1031 | * except simple counting. | 823 | * except simple counting. |
@@ -1042,7 +834,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
1042 | } | 834 | } |
1043 | 835 | ||
1044 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 836 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
1045 | exact_samples++; | 837 | top.exact_samples++; |
1046 | 838 | ||
1047 | if (perf_event__preprocess_sample(event, session, &al, sample, | 839 | if (perf_event__preprocess_sample(event, session, &al, sample, |
1048 | symbol_filter) < 0 || | 840 | symbol_filter) < 0 || |
@@ -1093,14 +885,14 @@ static void perf_event__process_sample(const union perf_event *event, | |||
1093 | struct perf_evsel *evsel; | 885 | struct perf_evsel *evsel; |
1094 | 886 | ||
1095 | syme->origin = origin; | 887 | syme->origin = origin; |
1096 | evsel = perf_evlist__id2evsel(evsel_list, sample->id); | 888 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
1097 | assert(evsel != NULL); | 889 | assert(evsel != NULL); |
1098 | syme->count[evsel->idx]++; | 890 | syme->count[evsel->idx]++; |
1099 | record_precise_ip(syme, evsel->idx, ip); | 891 | record_precise_ip(syme, evsel->idx, ip); |
1100 | pthread_mutex_lock(&active_symbols_lock); | 892 | pthread_mutex_lock(&top.active_symbols_lock); |
1101 | if (list_empty(&syme->node) || !syme->node.next) | 893 | if (list_empty(&syme->node) || !syme->node.next) |
1102 | __list_insert_active_sym(syme); | 894 | __list_insert_active_sym(syme); |
1103 | pthread_mutex_unlock(&active_symbols_lock); | 895 | pthread_mutex_unlock(&top.active_symbols_lock); |
1104 | } | 896 | } |
1105 | } | 897 | } |
1106 | 898 | ||
@@ -1109,7 +901,7 @@ static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) | |||
1109 | struct perf_sample sample; | 901 | struct perf_sample sample; |
1110 | union perf_event *event; | 902 | union perf_event *event; |
1111 | 903 | ||
1112 | while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) { | 904 | while ((event = perf_evlist__read_on_cpu(top.evlist, cpu)) != NULL) { |
1113 | perf_session__parse_sample(self, event, &sample); | 905 | perf_session__parse_sample(self, event, &sample); |
1114 | 906 | ||
1115 | if (event->header.type == PERF_RECORD_SAMPLE) | 907 | if (event->header.type == PERF_RECORD_SAMPLE) |
@@ -1123,7 +915,7 @@ static void perf_session__mmap_read(struct perf_session *self) | |||
1123 | { | 915 | { |
1124 | int i; | 916 | int i; |
1125 | 917 | ||
1126 | for (i = 0; i < evsel_list->cpus->nr; i++) | 918 | for (i = 0; i < top.evlist->cpus->nr; i++) |
1127 | perf_session__mmap_read_cpu(self, i); | 919 | perf_session__mmap_read_cpu(self, i); |
1128 | } | 920 | } |
1129 | 921 | ||
@@ -1136,10 +928,10 @@ static void start_counters(struct perf_evlist *evlist) | |||
1136 | 928 | ||
1137 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 929 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1138 | 930 | ||
1139 | if (freq) { | 931 | if (top.freq) { |
1140 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 932 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
1141 | attr->freq = 1; | 933 | attr->freq = 1; |
1142 | attr->sample_freq = freq; | 934 | attr->sample_freq = top.freq; |
1143 | } | 935 | } |
1144 | 936 | ||
1145 | if (evlist->nr_entries > 1) { | 937 | if (evlist->nr_entries > 1) { |
@@ -1149,8 +941,8 @@ static void start_counters(struct perf_evlist *evlist) | |||
1149 | 941 | ||
1150 | attr->mmap = 1; | 942 | attr->mmap = 1; |
1151 | try_again: | 943 | try_again: |
1152 | if (perf_evsel__open(counter, evsel_list->cpus, | 944 | if (perf_evsel__open(counter, top.evlist->cpus, |
1153 | evsel_list->threads, group, inherit) < 0) { | 945 | top.evlist->threads, group, inherit) < 0) { |
1154 | int err = errno; | 946 | int err = errno; |
1155 | 947 | ||
1156 | if (err == EPERM || err == EACCES) | 948 | if (err == EPERM || err == EACCES) |
@@ -1198,18 +990,18 @@ static int __cmd_top(void) | |||
1198 | if (session == NULL) | 990 | if (session == NULL) |
1199 | return -ENOMEM; | 991 | return -ENOMEM; |
1200 | 992 | ||
1201 | if (target_tid != -1) | 993 | if (top.target_tid != -1) |
1202 | perf_event__synthesize_thread(target_tid, perf_event__process, | 994 | perf_event__synthesize_thread(top.target_tid, perf_event__process, |
1203 | session); | 995 | session); |
1204 | else | 996 | else |
1205 | perf_event__synthesize_threads(perf_event__process, session); | 997 | perf_event__synthesize_threads(perf_event__process, session); |
1206 | 998 | ||
1207 | start_counters(evsel_list); | 999 | start_counters(top.evlist); |
1208 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); | 1000 | first = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1209 | perf_session__set_sample_type(session, first->attr.sample_type); | 1001 | perf_session__set_sample_type(session, first->attr.sample_type); |
1210 | 1002 | ||
1211 | /* Wait for a minimal set of events before starting the snapshot */ | 1003 | /* Wait for a minimal set of events before starting the snapshot */ |
1212 | poll(evsel_list->pollfd, evsel_list->nr_fds, 100); | 1004 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
1213 | 1005 | ||
1214 | perf_session__mmap_read(session); | 1006 | perf_session__mmap_read(session); |
1215 | 1007 | ||
@@ -1229,12 +1021,12 @@ static int __cmd_top(void) | |||
1229 | } | 1021 | } |
1230 | 1022 | ||
1231 | while (1) { | 1023 | while (1) { |
1232 | int hits = samples; | 1024 | u64 hits = top.samples; |
1233 | 1025 | ||
1234 | perf_session__mmap_read(session); | 1026 | perf_session__mmap_read(session); |
1235 | 1027 | ||
1236 | if (hits == samples) | 1028 | if (hits == top.samples) |
1237 | ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100); | 1029 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
1238 | } | 1030 | } |
1239 | 1031 | ||
1240 | return 0; | 1032 | return 0; |
@@ -1246,31 +1038,31 @@ static const char * const top_usage[] = { | |||
1246 | }; | 1038 | }; |
1247 | 1039 | ||
1248 | static const struct option options[] = { | 1040 | static const struct option options[] = { |
1249 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 1041 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1250 | "event selector. use 'perf list' to list available events", | 1042 | "event selector. use 'perf list' to list available events", |
1251 | parse_events), | 1043 | parse_events), |
1252 | OPT_INTEGER('c', "count", &default_interval, | 1044 | OPT_INTEGER('c', "count", &default_interval, |
1253 | "event period to sample"), | 1045 | "event period to sample"), |
1254 | OPT_INTEGER('p', "pid", &target_pid, | 1046 | OPT_INTEGER('p', "pid", &top.target_pid, |
1255 | "profile events on existing process id"), | 1047 | "profile events on existing process id"), |
1256 | OPT_INTEGER('t', "tid", &target_tid, | 1048 | OPT_INTEGER('t', "tid", &top.target_tid, |
1257 | "profile events on existing thread id"), | 1049 | "profile events on existing thread id"), |
1258 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1050 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1259 | "system-wide collection from all CPUs"), | 1051 | "system-wide collection from all CPUs"), |
1260 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | 1052 | OPT_STRING('C', "cpu", &top.cpu_list, "cpu", |
1261 | "list of cpus to monitor"), | 1053 | "list of cpus to monitor"), |
1262 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 1054 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1263 | "file", "vmlinux pathname"), | 1055 | "file", "vmlinux pathname"), |
1264 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | 1056 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, |
1265 | "hide kernel symbols"), | 1057 | "hide kernel symbols"), |
1266 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), | 1058 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), |
1267 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1059 | OPT_INTEGER('r', "realtime", &realtime_prio, |
1268 | "collect data with this RT SCHED_FIFO priority"), | 1060 | "collect data with this RT SCHED_FIFO priority"), |
1269 | OPT_INTEGER('d', "delay", &delay_secs, | 1061 | OPT_INTEGER('d', "delay", &top.delay_secs, |
1270 | "number of seconds to delay between refreshes"), | 1062 | "number of seconds to delay between refreshes"), |
1271 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, | 1063 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, |
1272 | "dump the symbol table used for profiling"), | 1064 | "dump the symbol table used for profiling"), |
1273 | OPT_INTEGER('f', "count-filter", &count_filter, | 1065 | OPT_INTEGER('f', "count-filter", &top.count_filter, |
1274 | "only display functions with more events than this"), | 1066 | "only display functions with more events than this"), |
1275 | OPT_BOOLEAN('g', "group", &group, | 1067 | OPT_BOOLEAN('g', "group", &group, |
1276 | "put the counters into a counter group"), | 1068 | "put the counters into a counter group"), |
@@ -1278,13 +1070,13 @@ static const struct option options[] = { | |||
1278 | "child tasks inherit counters"), | 1070 | "child tasks inherit counters"), |
1279 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1071 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1280 | "symbol to annotate"), | 1072 | "symbol to annotate"), |
1281 | OPT_BOOLEAN('z', "zero", &zero, | 1073 | OPT_BOOLEAN('z', "zero", &top.zero, |
1282 | "zero history across updates"), | 1074 | "zero history across updates"), |
1283 | OPT_INTEGER('F', "freq", &freq, | 1075 | OPT_INTEGER('F', "freq", &top.freq, |
1284 | "profile at this frequency"), | 1076 | "profile at this frequency"), |
1285 | OPT_INTEGER('E', "entries", &print_entries, | 1077 | OPT_INTEGER('E', "entries", &top.print_entries, |
1286 | "display this many functions"), | 1078 | "display this many functions"), |
1287 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | 1079 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, |
1288 | "hide user symbols"), | 1080 | "hide user symbols"), |
1289 | OPT_INCR('v', "verbose", &verbose, | 1081 | OPT_INCR('v', "verbose", &verbose, |
1290 | "be more verbose (show counter open errors, etc)"), | 1082 | "be more verbose (show counter open errors, etc)"), |
@@ -1296,8 +1088,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1296 | struct perf_evsel *pos; | 1088 | struct perf_evsel *pos; |
1297 | int status = -ENOMEM; | 1089 | int status = -ENOMEM; |
1298 | 1090 | ||
1299 | evsel_list = perf_evlist__new(NULL, NULL); | 1091 | top.evlist = perf_evlist__new(NULL, NULL); |
1300 | if (evsel_list == NULL) | 1092 | if (top.evlist == NULL) |
1301 | return -ENOMEM; | 1093 | return -ENOMEM; |
1302 | 1094 | ||
1303 | page_size = sysconf(_SC_PAGE_SIZE); | 1095 | page_size = sysconf(_SC_PAGE_SIZE); |
@@ -1307,43 +1099,43 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1307 | usage_with_options(top_usage, options); | 1099 | usage_with_options(top_usage, options); |
1308 | 1100 | ||
1309 | /* CPU and PID are mutually exclusive */ | 1101 | /* CPU and PID are mutually exclusive */ |
1310 | if (target_tid > 0 && cpu_list) { | 1102 | if (top.target_tid > 0 && top.cpu_list) { |
1311 | printf("WARNING: PID switch overriding CPU\n"); | 1103 | printf("WARNING: PID switch overriding CPU\n"); |
1312 | sleep(1); | 1104 | sleep(1); |
1313 | cpu_list = NULL; | 1105 | top.cpu_list = NULL; |
1314 | } | 1106 | } |
1315 | 1107 | ||
1316 | if (target_pid != -1) | 1108 | if (top.target_pid != -1) |
1317 | target_tid = target_pid; | 1109 | top.target_tid = top.target_pid; |
1318 | 1110 | ||
1319 | if (perf_evlist__create_maps(evsel_list, target_pid, | 1111 | if (perf_evlist__create_maps(top.evlist, top.target_pid, |
1320 | target_tid, cpu_list) < 0) | 1112 | top.target_tid, top.cpu_list) < 0) |
1321 | usage_with_options(top_usage, options); | 1113 | usage_with_options(top_usage, options); |
1322 | 1114 | ||
1323 | if (!evsel_list->nr_entries && | 1115 | if (!top.evlist->nr_entries && |
1324 | perf_evlist__add_default(evsel_list) < 0) { | 1116 | perf_evlist__add_default(top.evlist) < 0) { |
1325 | pr_err("Not enough memory for event selector list\n"); | 1117 | pr_err("Not enough memory for event selector list\n"); |
1326 | return -ENOMEM; | 1118 | return -ENOMEM; |
1327 | } | 1119 | } |
1328 | 1120 | ||
1329 | if (delay_secs < 1) | 1121 | if (top.delay_secs < 1) |
1330 | delay_secs = 1; | 1122 | top.delay_secs = 1; |
1331 | 1123 | ||
1332 | /* | 1124 | /* |
1333 | * User specified count overrides default frequency. | 1125 | * User specified count overrides default frequency. |
1334 | */ | 1126 | */ |
1335 | if (default_interval) | 1127 | if (default_interval) |
1336 | freq = 0; | 1128 | top.freq = 0; |
1337 | else if (freq) { | 1129 | else if (top.freq) { |
1338 | default_interval = freq; | 1130 | default_interval = top.freq; |
1339 | } else { | 1131 | } else { |
1340 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 1132 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
1341 | exit(EXIT_FAILURE); | 1133 | exit(EXIT_FAILURE); |
1342 | } | 1134 | } |
1343 | 1135 | ||
1344 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1136 | list_for_each_entry(pos, &top.evlist->entries, node) { |
1345 | if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, | 1137 | if (perf_evsel__alloc_fd(pos, top.evlist->cpus->nr, |
1346 | evsel_list->threads->nr) < 0) | 1138 | top.evlist->threads->nr) < 0) |
1347 | goto out_free_fd; | 1139 | goto out_free_fd; |
1348 | /* | 1140 | /* |
1349 | * Fill in the ones not specifically initialized via -c: | 1141 | * Fill in the ones not specifically initialized via -c: |
@@ -1354,28 +1146,28 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1354 | pos->attr.sample_period = default_interval; | 1146 | pos->attr.sample_period = default_interval; |
1355 | } | 1147 | } |
1356 | 1148 | ||
1357 | if (perf_evlist__alloc_pollfd(evsel_list) < 0 || | 1149 | if (perf_evlist__alloc_pollfd(top.evlist) < 0 || |
1358 | perf_evlist__alloc_mmap(evsel_list) < 0) | 1150 | perf_evlist__alloc_mmap(top.evlist) < 0) |
1359 | goto out_free_fd; | 1151 | goto out_free_fd; |
1360 | 1152 | ||
1361 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); | 1153 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1362 | 1154 | ||
1363 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1155 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
1364 | (evsel_list->nr_entries + 1) * sizeof(unsigned long)); | 1156 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); |
1365 | 1157 | ||
1366 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1158 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1367 | if (symbol__init() < 0) | 1159 | if (symbol__init() < 0) |
1368 | return -1; | 1160 | return -1; |
1369 | 1161 | ||
1370 | get_term_dimensions(&winsize); | 1162 | get_term_dimensions(&winsize); |
1371 | if (print_entries == 0) { | 1163 | if (top.print_entries == 0) { |
1372 | update_print_entries(&winsize); | 1164 | update_print_entries(&winsize); |
1373 | signal(SIGWINCH, sig_winch_handler); | 1165 | signal(SIGWINCH, sig_winch_handler); |
1374 | } | 1166 | } |
1375 | 1167 | ||
1376 | status = __cmd_top(); | 1168 | status = __cmd_top(); |
1377 | out_free_fd: | 1169 | out_free_fd: |
1378 | perf_evlist__delete(evsel_list); | 1170 | perf_evlist__delete(top.evlist); |
1379 | 1171 | ||
1380 | return status; | 1172 | return status; |
1381 | } | 1173 | } |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c new file mode 100644 index 000000000000..c06cc5386e7e --- /dev/null +++ b/tools/perf/util/top.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Refactored from builtin-top.c, see that files for further copyright notes. | ||
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | ||
8 | |||
9 | #include "cpumap.h" | ||
10 | #include "event.h" | ||
11 | #include "evlist.h" | ||
12 | #include "evsel.h" | ||
13 | #include "parse-events.h" | ||
14 | #include "symbol.h" | ||
15 | #include "top.h" | ||
16 | #include <inttypes.h> | ||
17 | |||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | ||
65 | { | ||
66 | struct perf_evsel *counter; | ||
67 | float samples_per_sec = top->samples / top->delay_secs; | ||
68 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | ||
69 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | ||
70 | size_t ret = 0; | ||
71 | |||
72 | if (!perf_guest) { | ||
73 | ret = snprintf(bf, size, | ||
74 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | ||
75 | " exact: %4.1f%% [", samples_per_sec, | ||
76 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
77 | samples_per_sec)), | ||
78 | esamples_percent); | ||
79 | } else { | ||
80 | float us_samples_per_sec = top->us_samples / top->delay_secs; | ||
81 | float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; | ||
82 | float guest_us_samples_per_sec = top->guest_us_samples / top->delay_secs; | ||
83 | |||
84 | ret = snprintf(bf, size, | ||
85 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
86 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
87 | " exact: %4.1f%% [", samples_per_sec, | ||
88 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
89 | samples_per_sec)), | ||
90 | 100.0 - (100.0 * ((samples_per_sec - us_samples_per_sec) / | ||
91 | samples_per_sec)), | ||
92 | 100.0 - (100.0 * ((samples_per_sec - | ||
93 | guest_kernel_samples_per_sec) / | ||
94 | samples_per_sec)), | ||
95 | 100.0 - (100.0 * ((samples_per_sec - | ||
96 | guest_us_samples_per_sec) / | ||
97 | samples_per_sec)), | ||
98 | esamples_percent); | ||
99 | } | ||
100 | |||
101 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
102 | struct perf_evsel *first; | ||
103 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | ||
104 | ret += snprintf(bf + ret, size - ret, "%" PRIu64 "%s ", | ||
105 | (uint64_t)first->attr.sample_period, | ||
106 | top->freq ? "Hz" : ""); | ||
107 | } | ||
108 | |||
109 | if (!top->display_weighted) { | ||
110 | ret += snprintf(bf + ret, size - ret, "%s", | ||
111 | event_name(top->sym_evsel)); | ||
112 | } else list_for_each_entry(counter, &top->evlist->entries, node) { | ||
113 | ret += snprintf(bf + ret, size - ret, "%s%s", | ||
114 | counter->idx ? "/" : "", event_name(counter)); | ||
115 | } | ||
116 | |||
117 | ret += snprintf(bf + ret, size - ret, "], "); | ||
118 | |||
119 | if (top->target_pid != -1) | ||
120 | ret += snprintf(bf + ret, size - ret, " (target_pid: %d", | ||
121 | top->target_pid); | ||
122 | else if (top->target_tid != -1) | ||
123 | ret += snprintf(bf + ret, size - ret, " (target_tid: %d", | ||
124 | top->target_tid); | ||
125 | else | ||
126 | ret += snprintf(bf + ret, size - ret, " (all"); | ||
127 | |||
128 | if (top->cpu_list) | ||
129 | ret += snprintf(bf + ret, size - ret, ", CPU%s: %s)", | ||
130 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | ||
131 | else { | ||
132 | if (top->target_tid != -1) | ||
133 | ret += snprintf(bf + ret, size - ret, ")"); | ||
134 | else | ||
135 | ret += snprintf(bf + ret, size - ret, ", %d CPU%s)", | ||
136 | top->evlist->cpus->nr, | ||
137 | top->evlist->cpus->nr > 1 ? "s" : ""); | ||
138 | } | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | void perf_top__reset_sample_counters(struct perf_top *top) | ||
144 | { | ||
145 | top->samples = top->us_samples = top->kernel_samples = | ||
146 | top->exact_samples = top->guest_kernel_samples = | ||
147 | top->guest_us_samples = 0; | ||
148 | } | ||
149 | |||
150 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
151 | { | ||
152 | struct sym_entry *syme, *n; | ||
153 | float sum_ksamples = 0.0; | ||
154 | int snap = !top->display_weighted ? top->sym_counter : 0, j; | ||
155 | |||
156 | /* Sort the active symbols */ | ||
157 | pthread_mutex_lock(&top->active_symbols_lock); | ||
158 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
159 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
160 | |||
161 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
162 | syme->snap_count = syme->count[snap]; | ||
163 | if (syme->snap_count != 0) { | ||
164 | |||
165 | if ((top->hide_user_symbols && | ||
166 | syme->origin == PERF_RECORD_MISC_USER) || | ||
167 | (top->hide_kernel_symbols && | ||
168 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
169 | perf_top__remove_active_sym(top, syme); | ||
170 | continue; | ||
171 | } | ||
172 | syme->weight = sym_weight(syme, top); | ||
173 | rb_insert_active_sym(root, syme); | ||
174 | sum_ksamples += syme->snap_count; | ||
175 | |||
176 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
177 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
178 | } else | ||
179 | perf_top__remove_active_sym(top, syme); | ||
180 | } | ||
181 | |||
182 | return sum_ksamples; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * Find the longest symbol name that will be displayed | ||
187 | */ | ||
188 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
189 | int *dso_width, int *dso_short_width, int *sym_width) | ||
190 | { | ||
191 | struct rb_node *nd; | ||
192 | int printed = 0; | ||
193 | |||
194 | *sym_width = *dso_width = *dso_short_width = 0; | ||
195 | |||
196 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
197 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
198 | |||
199 | if (++printed > top->print_entries || | ||
200 | (int)syme->snap_count < top->count_filter) | ||
201 | continue; | ||
202 | |||
203 | if (syme->map->dso->long_name_len > *dso_width) | ||
204 | *dso_width = syme->map->dso->long_name_len; | ||
205 | |||
206 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
207 | *dso_short_width = syme->map->dso->short_name_len; | ||
208 | |||
209 | if (syme->name_len > *sym_width) | ||
210 | *sym_width = syme->name_len; | ||
211 | } | ||
212 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h new file mode 100644 index 000000000000..0467b26dd8d8 --- /dev/null +++ b/tools/perf/util/top.h | |||
@@ -0,0 +1,67 @@ | |||
1 | #ifndef __PERF_TOP_H | ||
2 | #define __PERF_TOP_H 1 | ||
3 | |||
4 | #include "types.h" | ||
5 | #include "../perf.h" | ||
6 | #include <stddef.h> | ||
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | |||
11 | struct perf_evlist; | ||
12 | struct perf_evsel; | ||
13 | |||
14 | struct source_line { | ||
15 | u64 eip; | ||
16 | unsigned long count[MAX_COUNTERS]; /* FIXME */ | ||
17 | char *line; | ||
18 | struct source_line *next; | ||
19 | }; | ||
20 | |||
21 | struct sym_entry_source { | ||
22 | struct source_line *source; | ||
23 | struct source_line *lines; | ||
24 | struct source_line **lines_tail; | ||
25 | pthread_mutex_t lock; | ||
26 | }; | ||
27 | |||
28 | struct sym_entry { | ||
29 | struct rb_node rb_node; | ||
30 | struct list_head node; | ||
31 | unsigned long snap_count; | ||
32 | double weight; | ||
33 | int skip; | ||
34 | u16 name_len; | ||
35 | u8 origin; | ||
36 | struct map *map; | ||
37 | struct sym_entry_source *src; | ||
38 | unsigned long count[0]; | ||
39 | }; | ||
40 | |||
41 | struct perf_top { | ||
42 | struct perf_evlist *evlist; | ||
43 | /* | ||
44 | * Symbols will be added here in perf_event__process_sample and will | ||
45 | * get out after decayed. | ||
46 | */ | ||
47 | struct list_head active_symbols; | ||
48 | pthread_mutex_t active_symbols_lock; | ||
49 | u64 samples; | ||
50 | u64 kernel_samples, us_samples; | ||
51 | u64 exact_samples; | ||
52 | u64 guest_us_samples, guest_kernel_samples; | ||
53 | int print_entries, count_filter, delay_secs; | ||
54 | int display_weighted, freq; | ||
55 | int sym_counter, target_pid, target_tid; | ||
56 | bool hide_kernel_symbols, hide_user_symbols, zero; | ||
57 | const char *cpu_list; | ||
58 | struct perf_evsel *sym_evsel; | ||
59 | }; | ||
60 | |||
61 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | ||
62 | void perf_top__reset_sample_counters(struct perf_top *top); | ||
63 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
64 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
65 | int *dso_width, int *dso_short_width, int *sym_width); | ||
66 | |||
67 | #endif /* __PERF_TOP_H */ | ||