diff options
author | Namhyung Kim <namhyung@kernel.org> | 2015-04-21 00:55:04 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-05-04 12:34:47 -0400 |
commit | 2a7ef02c9ca0172cd48945407893f38c2438e754 (patch) | |
tree | 628fd9ac7c2432298f199e3ef65a052812c148a2 /tools/perf/builtin-kmem.c | |
parent | fb4f313d304b0a5120e870a6cd9ecf90c1023037 (diff) |
perf kmem: Add --live option for current allocation stat
Currently 'perf kmem stat --page' shows total (page) allocation stat by
default, but sometimes one might want to see live (total alloc-only)
requests/pages only. The new --live option does this by subtracting freed
allocation from the stat.
E.g.:
# perf kmem stat --page
SUMMARY (page allocator)
========================
Total allocation requests : 988,858 [ 4,045,368 KB ]
Total free requests : 886,484 [ 3,624,996 KB ]
Total alloc+freed requests : 885,969 [ 3,622,628 KB ]
Total alloc-only requests : 102,889 [ 422,740 KB ]
Total free-only requests : 515 [ 2,368 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 172,173 3,083 806,686 . .
1 284 . . . .
2 6,124 58 . . .
3 114 335 . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . 1 . .
10 . . . . .
# perf kmem stat --page --live
SUMMARY (page allocator)
========================
Total allocation requests : 988,858 [ 4,045,368 KB ]
Total free requests : 886,484 [ 3,624,996 KB ]
Total alloc+freed requests : 885,969 [ 3,622,628 KB ]
Total alloc-only requests : 102,889 [ 422,740 KB ]
Total free-only requests : 515 [ 2,368 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 2,214 3,025 97,156 . .
1 59 . . . .
2 19 58 . . .
3 23 335 . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .
#
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Pekka Enberg <penberg@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joonsoo Kim <js1304@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-mm@kvack.org
Link: http://lkml.kernel.org/r/1429592107-1807-4-git-send-email-namhyung@kernel.org
[ Added examples to the changeset log ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-kmem.c')
-rw-r--r-- | tools/perf/builtin-kmem.c | 110 |
1 files changed, 68 insertions, 42 deletions
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 0393a7f3fa35..7ead9423fd7a 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -244,6 +244,7 @@ static unsigned long nr_page_fails; | |||
244 | static unsigned long nr_page_nomatch; | 244 | static unsigned long nr_page_nomatch; |
245 | 245 | ||
246 | static bool use_pfn; | 246 | static bool use_pfn; |
247 | static bool live_page; | ||
247 | static struct perf_session *kmem_session; | 248 | static struct perf_session *kmem_session; |
248 | 249 | ||
249 | #define MAX_MIGRATE_TYPES 6 | 250 | #define MAX_MIGRATE_TYPES 6 |
@@ -264,7 +265,7 @@ struct page_stat { | |||
264 | int nr_free; | 265 | int nr_free; |
265 | }; | 266 | }; |
266 | 267 | ||
267 | static struct rb_root page_tree; | 268 | static struct rb_root page_live_tree; |
268 | static struct rb_root page_alloc_tree; | 269 | static struct rb_root page_alloc_tree; |
269 | static struct rb_root page_alloc_sorted; | 270 | static struct rb_root page_alloc_sorted; |
270 | static struct rb_root page_caller_tree; | 271 | static struct rb_root page_caller_tree; |
@@ -403,10 +404,19 @@ out: | |||
403 | return sample->ip; | 404 | return sample->ip; |
404 | } | 405 | } |
405 | 406 | ||
407 | struct sort_dimension { | ||
408 | const char name[20]; | ||
409 | sort_fn_t cmp; | ||
410 | struct list_head list; | ||
411 | }; | ||
412 | |||
413 | static LIST_HEAD(page_alloc_sort_input); | ||
414 | static LIST_HEAD(page_caller_sort_input); | ||
415 | |||
406 | static struct page_stat * | 416 | static struct page_stat * |
407 | __page_stat__findnew_page(u64 page, bool create) | 417 | __page_stat__findnew_page(struct page_stat *pstat, bool create) |
408 | { | 418 | { |
409 | struct rb_node **node = &page_tree.rb_node; | 419 | struct rb_node **node = &page_live_tree.rb_node; |
410 | struct rb_node *parent = NULL; | 420 | struct rb_node *parent = NULL; |
411 | struct page_stat *data; | 421 | struct page_stat *data; |
412 | 422 | ||
@@ -416,7 +426,7 @@ __page_stat__findnew_page(u64 page, bool create) | |||
416 | parent = *node; | 426 | parent = *node; |
417 | data = rb_entry(*node, struct page_stat, node); | 427 | data = rb_entry(*node, struct page_stat, node); |
418 | 428 | ||
419 | cmp = data->page - page; | 429 | cmp = data->page - pstat->page; |
420 | if (cmp < 0) | 430 | if (cmp < 0) |
421 | node = &parent->rb_left; | 431 | node = &parent->rb_left; |
422 | else if (cmp > 0) | 432 | else if (cmp > 0) |
@@ -430,34 +440,28 @@ __page_stat__findnew_page(u64 page, bool create) | |||
430 | 440 | ||
431 | data = zalloc(sizeof(*data)); | 441 | data = zalloc(sizeof(*data)); |
432 | if (data != NULL) { | 442 | if (data != NULL) { |
433 | data->page = page; | 443 | data->page = pstat->page; |
444 | data->order = pstat->order; | ||
445 | data->gfp_flags = pstat->gfp_flags; | ||
446 | data->migrate_type = pstat->migrate_type; | ||
434 | 447 | ||
435 | rb_link_node(&data->node, parent, node); | 448 | rb_link_node(&data->node, parent, node); |
436 | rb_insert_color(&data->node, &page_tree); | 449 | rb_insert_color(&data->node, &page_live_tree); |
437 | } | 450 | } |
438 | 451 | ||
439 | return data; | 452 | return data; |
440 | } | 453 | } |
441 | 454 | ||
442 | static struct page_stat *page_stat__find_page(u64 page) | 455 | static struct page_stat *page_stat__find_page(struct page_stat *pstat) |
443 | { | 456 | { |
444 | return __page_stat__findnew_page(page, false); | 457 | return __page_stat__findnew_page(pstat, false); |
445 | } | 458 | } |
446 | 459 | ||
447 | static struct page_stat *page_stat__findnew_page(u64 page) | 460 | static struct page_stat *page_stat__findnew_page(struct page_stat *pstat) |
448 | { | 461 | { |
449 | return __page_stat__findnew_page(page, true); | 462 | return __page_stat__findnew_page(pstat, true); |
450 | } | 463 | } |
451 | 464 | ||
452 | struct sort_dimension { | ||
453 | const char name[20]; | ||
454 | sort_fn_t cmp; | ||
455 | struct list_head list; | ||
456 | }; | ||
457 | |||
458 | static LIST_HEAD(page_alloc_sort_input); | ||
459 | static LIST_HEAD(page_caller_sort_input); | ||
460 | |||
461 | static struct page_stat * | 465 | static struct page_stat * |
462 | __page_stat__findnew_alloc(struct page_stat *pstat, bool create) | 466 | __page_stat__findnew_alloc(struct page_stat *pstat, bool create) |
463 | { | 467 | { |
@@ -615,17 +619,8 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, | |||
615 | * This is to find the current page (with correct gfp flags and | 619 | * This is to find the current page (with correct gfp flags and |
616 | * migrate type) at free event. | 620 | * migrate type) at free event. |
617 | */ | 621 | */ |
618 | pstat = page_stat__findnew_page(page); | ||
619 | if (pstat == NULL) | ||
620 | return -ENOMEM; | ||
621 | |||
622 | pstat->order = order; | ||
623 | pstat->gfp_flags = gfp_flags; | ||
624 | pstat->migrate_type = migrate_type; | ||
625 | pstat->callsite = callsite; | ||
626 | |||
627 | this.page = page; | 622 | this.page = page; |
628 | pstat = page_stat__findnew_alloc(&this); | 623 | pstat = page_stat__findnew_page(&this); |
629 | if (pstat == NULL) | 624 | if (pstat == NULL) |
630 | return -ENOMEM; | 625 | return -ENOMEM; |
631 | 626 | ||
@@ -633,6 +628,16 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, | |||
633 | pstat->alloc_bytes += bytes; | 628 | pstat->alloc_bytes += bytes; |
634 | pstat->callsite = callsite; | 629 | pstat->callsite = callsite; |
635 | 630 | ||
631 | if (!live_page) { | ||
632 | pstat = page_stat__findnew_alloc(&this); | ||
633 | if (pstat == NULL) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | pstat->nr_alloc++; | ||
637 | pstat->alloc_bytes += bytes; | ||
638 | pstat->callsite = callsite; | ||
639 | } | ||
640 | |||
636 | this.callsite = callsite; | 641 | this.callsite = callsite; |
637 | pstat = page_stat__findnew_caller(&this); | 642 | pstat = page_stat__findnew_caller(&this); |
638 | if (pstat == NULL) | 643 | if (pstat == NULL) |
@@ -665,7 +670,8 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
665 | nr_page_frees++; | 670 | nr_page_frees++; |
666 | total_page_free_bytes += bytes; | 671 | total_page_free_bytes += bytes; |
667 | 672 | ||
668 | pstat = page_stat__find_page(page); | 673 | this.page = page; |
674 | pstat = page_stat__find_page(&this); | ||
669 | if (pstat == NULL) { | 675 | if (pstat == NULL) { |
670 | pr_debug2("missing free at page %"PRIx64" (order: %d)\n", | 676 | pr_debug2("missing free at page %"PRIx64" (order: %d)\n", |
671 | page, order); | 677 | page, order); |
@@ -676,20 +682,23 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
676 | return 0; | 682 | return 0; |
677 | } | 683 | } |
678 | 684 | ||
679 | this.page = page; | ||
680 | this.gfp_flags = pstat->gfp_flags; | 685 | this.gfp_flags = pstat->gfp_flags; |
681 | this.migrate_type = pstat->migrate_type; | 686 | this.migrate_type = pstat->migrate_type; |
682 | this.callsite = pstat->callsite; | 687 | this.callsite = pstat->callsite; |
683 | 688 | ||
684 | rb_erase(&pstat->node, &page_tree); | 689 | rb_erase(&pstat->node, &page_live_tree); |
685 | free(pstat); | 690 | free(pstat); |
686 | 691 | ||
687 | pstat = page_stat__find_alloc(&this); | 692 | if (live_page) { |
688 | if (pstat == NULL) | 693 | order_stats[this.order][this.migrate_type]--; |
689 | return -ENOENT; | 694 | } else { |
695 | pstat = page_stat__find_alloc(&this); | ||
696 | if (pstat == NULL) | ||
697 | return -ENOMEM; | ||
690 | 698 | ||
691 | pstat->nr_free++; | 699 | pstat->nr_free++; |
692 | pstat->free_bytes += bytes; | 700 | pstat->free_bytes += bytes; |
701 | } | ||
693 | 702 | ||
694 | pstat = page_stat__find_caller(&this); | 703 | pstat = page_stat__find_caller(&this); |
695 | if (pstat == NULL) | 704 | if (pstat == NULL) |
@@ -698,6 +707,16 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
698 | pstat->nr_free++; | 707 | pstat->nr_free++; |
699 | pstat->free_bytes += bytes; | 708 | pstat->free_bytes += bytes; |
700 | 709 | ||
710 | if (live_page) { | ||
711 | pstat->nr_alloc--; | ||
712 | pstat->alloc_bytes -= bytes; | ||
713 | |||
714 | if (pstat->nr_alloc == 0) { | ||
715 | rb_erase(&pstat->node, &page_caller_tree); | ||
716 | free(pstat); | ||
717 | } | ||
718 | } | ||
719 | |||
701 | return 0; | 720 | return 0; |
702 | } | 721 | } |
703 | 722 | ||
@@ -815,8 +834,8 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines) | |||
815 | const char *format; | 834 | const char *format; |
816 | 835 | ||
817 | printf("\n%.105s\n", graph_dotted_line); | 836 | printf("\n%.105s\n", graph_dotted_line); |
818 | printf(" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n", | 837 | printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n", |
819 | use_pfn ? "PFN" : "Page"); | 838 | use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total"); |
820 | printf("%.105s\n", graph_dotted_line); | 839 | printf("%.105s\n", graph_dotted_line); |
821 | 840 | ||
822 | if (use_pfn) | 841 | if (use_pfn) |
@@ -860,7 +879,8 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines | |||
860 | struct machine *machine = &session->machines.host; | 879 | struct machine *machine = &session->machines.host; |
861 | 880 | ||
862 | printf("\n%.105s\n", graph_dotted_line); | 881 | printf("\n%.105s\n", graph_dotted_line); |
863 | printf(" Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n"); | 882 | printf(" %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n", |
883 | live_page ? "Live" : "Total"); | ||
864 | printf("%.105s\n", graph_dotted_line); | 884 | printf("%.105s\n", graph_dotted_line); |
865 | 885 | ||
866 | while (next && n_lines--) { | 886 | while (next && n_lines--) { |
@@ -1085,8 +1105,13 @@ static void sort_result(void) | |||
1085 | &slab_caller_sort); | 1105 | &slab_caller_sort); |
1086 | } | 1106 | } |
1087 | if (kmem_page) { | 1107 | if (kmem_page) { |
1088 | __sort_page_result(&page_alloc_tree, &page_alloc_sorted, | 1108 | if (live_page) |
1089 | &page_alloc_sort); | 1109 | __sort_page_result(&page_live_tree, &page_alloc_sorted, |
1110 | &page_alloc_sort); | ||
1111 | else | ||
1112 | __sort_page_result(&page_alloc_tree, &page_alloc_sorted, | ||
1113 | &page_alloc_sort); | ||
1114 | |||
1090 | __sort_page_result(&page_caller_tree, &page_caller_sorted, | 1115 | __sort_page_result(&page_caller_tree, &page_caller_sorted, |
1091 | &page_caller_sort); | 1116 | &page_caller_sort); |
1092 | } | 1117 | } |
@@ -1630,6 +1655,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1630 | parse_slab_opt), | 1655 | parse_slab_opt), |
1631 | OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", | 1656 | OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", |
1632 | parse_page_opt), | 1657 | parse_page_opt), |
1658 | OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"), | ||
1633 | OPT_END() | 1659 | OPT_END() |
1634 | }; | 1660 | }; |
1635 | const char *const kmem_subcommands[] = { "record", "stat", NULL }; | 1661 | const char *const kmem_subcommands[] = { "record", "stat", NULL }; |