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>
This commit is contained in:
Namhyung Kim 2015-04-21 13:55:04 +09:00 committed by Arnaldo Carvalho de Melo
parent fb4f313d30
commit 2a7ef02c9c
2 changed files with 73 additions and 42 deletions

View File

@ -56,6 +56,11 @@ OPTIONS
--page:: --page::
Analyze page allocator events Analyze page allocator events
--live::
Show live page stat. The perf kmem shows total allocation stat by
default, but this option shows live (currently allocated) pages
instead. (This option works with --page option only)
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-record[1] linkperf:perf-record[1]

View File

@ -244,6 +244,7 @@ static unsigned long nr_page_fails;
static unsigned long nr_page_nomatch; static unsigned long nr_page_nomatch;
static bool use_pfn; static bool use_pfn;
static bool live_page;
static struct perf_session *kmem_session; static struct perf_session *kmem_session;
#define MAX_MIGRATE_TYPES 6 #define MAX_MIGRATE_TYPES 6
@ -264,7 +265,7 @@ struct page_stat {
int nr_free; int nr_free;
}; };
static struct rb_root page_tree; static struct rb_root page_live_tree;
static struct rb_root page_alloc_tree; static struct rb_root page_alloc_tree;
static struct rb_root page_alloc_sorted; static struct rb_root page_alloc_sorted;
static struct rb_root page_caller_tree; static struct rb_root page_caller_tree;
@ -403,10 +404,19 @@ out:
return sample->ip; return sample->ip;
} }
struct sort_dimension {
const char name[20];
sort_fn_t cmp;
struct list_head list;
};
static LIST_HEAD(page_alloc_sort_input);
static LIST_HEAD(page_caller_sort_input);
static struct page_stat * static struct page_stat *
__page_stat__findnew_page(u64 page, bool create) __page_stat__findnew_page(struct page_stat *pstat, bool create)
{ {
struct rb_node **node = &page_tree.rb_node; struct rb_node **node = &page_live_tree.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct page_stat *data; struct page_stat *data;
@ -416,7 +426,7 @@ __page_stat__findnew_page(u64 page, bool create)
parent = *node; parent = *node;
data = rb_entry(*node, struct page_stat, node); data = rb_entry(*node, struct page_stat, node);
cmp = data->page - page; cmp = data->page - pstat->page;
if (cmp < 0) if (cmp < 0)
node = &parent->rb_left; node = &parent->rb_left;
else if (cmp > 0) else if (cmp > 0)
@ -430,34 +440,28 @@ __page_stat__findnew_page(u64 page, bool create)
data = zalloc(sizeof(*data)); data = zalloc(sizeof(*data));
if (data != NULL) { if (data != NULL) {
data->page = page; data->page = pstat->page;
data->order = pstat->order;
data->gfp_flags = pstat->gfp_flags;
data->migrate_type = pstat->migrate_type;
rb_link_node(&data->node, parent, node); rb_link_node(&data->node, parent, node);
rb_insert_color(&data->node, &page_tree); rb_insert_color(&data->node, &page_live_tree);
} }
return data; return data;
} }
static struct page_stat *page_stat__find_page(u64 page) static struct page_stat *page_stat__find_page(struct page_stat *pstat)
{ {
return __page_stat__findnew_page(page, false); return __page_stat__findnew_page(pstat, false);
} }
static struct page_stat *page_stat__findnew_page(u64 page) static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
{ {
return __page_stat__findnew_page(page, true); return __page_stat__findnew_page(pstat, true);
} }
struct sort_dimension {
const char name[20];
sort_fn_t cmp;
struct list_head list;
};
static LIST_HEAD(page_alloc_sort_input);
static LIST_HEAD(page_caller_sort_input);
static struct page_stat * static struct page_stat *
__page_stat__findnew_alloc(struct page_stat *pstat, bool create) __page_stat__findnew_alloc(struct page_stat *pstat, bool create)
{ {
@ -615,17 +619,8 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
* This is to find the current page (with correct gfp flags and * This is to find the current page (with correct gfp flags and
* migrate type) at free event. * migrate type) at free event.
*/ */
pstat = page_stat__findnew_page(page);
if (pstat == NULL)
return -ENOMEM;
pstat->order = order;
pstat->gfp_flags = gfp_flags;
pstat->migrate_type = migrate_type;
pstat->callsite = callsite;
this.page = page; this.page = page;
pstat = page_stat__findnew_alloc(&this); pstat = page_stat__findnew_page(&this);
if (pstat == NULL) if (pstat == NULL)
return -ENOMEM; return -ENOMEM;
@ -633,6 +628,16 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
pstat->alloc_bytes += bytes; pstat->alloc_bytes += bytes;
pstat->callsite = callsite; pstat->callsite = callsite;
if (!live_page) {
pstat = page_stat__findnew_alloc(&this);
if (pstat == NULL)
return -ENOMEM;
pstat->nr_alloc++;
pstat->alloc_bytes += bytes;
pstat->callsite = callsite;
}
this.callsite = callsite; this.callsite = callsite;
pstat = page_stat__findnew_caller(&this); pstat = page_stat__findnew_caller(&this);
if (pstat == NULL) if (pstat == NULL)
@ -665,7 +670,8 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
nr_page_frees++; nr_page_frees++;
total_page_free_bytes += bytes; total_page_free_bytes += bytes;
pstat = page_stat__find_page(page); this.page = page;
pstat = page_stat__find_page(&this);
if (pstat == NULL) { if (pstat == NULL) {
pr_debug2("missing free at page %"PRIx64" (order: %d)\n", pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
page, order); page, order);
@ -676,20 +682,23 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
return 0; return 0;
} }
this.page = page;
this.gfp_flags = pstat->gfp_flags; this.gfp_flags = pstat->gfp_flags;
this.migrate_type = pstat->migrate_type; this.migrate_type = pstat->migrate_type;
this.callsite = pstat->callsite; this.callsite = pstat->callsite;
rb_erase(&pstat->node, &page_tree); rb_erase(&pstat->node, &page_live_tree);
free(pstat); free(pstat);
pstat = page_stat__find_alloc(&this); if (live_page) {
if (pstat == NULL) order_stats[this.order][this.migrate_type]--;
return -ENOENT; } else {
pstat = page_stat__find_alloc(&this);
if (pstat == NULL)
return -ENOMEM;
pstat->nr_free++; pstat->nr_free++;
pstat->free_bytes += bytes; pstat->free_bytes += bytes;
}
pstat = page_stat__find_caller(&this); pstat = page_stat__find_caller(&this);
if (pstat == NULL) if (pstat == NULL)
@ -698,6 +707,16 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
pstat->nr_free++; pstat->nr_free++;
pstat->free_bytes += bytes; pstat->free_bytes += bytes;
if (live_page) {
pstat->nr_alloc--;
pstat->alloc_bytes -= bytes;
if (pstat->nr_alloc == 0) {
rb_erase(&pstat->node, &page_caller_tree);
free(pstat);
}
}
return 0; return 0;
} }
@ -815,8 +834,8 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
const char *format; const char *format;
printf("\n%.105s\n", graph_dotted_line); printf("\n%.105s\n", graph_dotted_line);
printf(" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n", printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n",
use_pfn ? "PFN" : "Page"); use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total");
printf("%.105s\n", graph_dotted_line); printf("%.105s\n", graph_dotted_line);
if (use_pfn) if (use_pfn)
@ -860,7 +879,8 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
struct machine *machine = &session->machines.host; struct machine *machine = &session->machines.host;
printf("\n%.105s\n", graph_dotted_line); printf("\n%.105s\n", graph_dotted_line);
printf(" Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n"); printf(" %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n",
live_page ? "Live" : "Total");
printf("%.105s\n", graph_dotted_line); printf("%.105s\n", graph_dotted_line);
while (next && n_lines--) { while (next && n_lines--) {
@ -1085,8 +1105,13 @@ static void sort_result(void)
&slab_caller_sort); &slab_caller_sort);
} }
if (kmem_page) { if (kmem_page) {
__sort_page_result(&page_alloc_tree, &page_alloc_sorted, if (live_page)
&page_alloc_sort); __sort_page_result(&page_live_tree, &page_alloc_sorted,
&page_alloc_sort);
else
__sort_page_result(&page_alloc_tree, &page_alloc_sorted,
&page_alloc_sort);
__sort_page_result(&page_caller_tree, &page_caller_sorted, __sort_page_result(&page_caller_tree, &page_caller_sorted,
&page_caller_sort); &page_caller_sort);
} }
@ -1630,6 +1655,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
parse_slab_opt), parse_slab_opt),
OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
parse_page_opt), parse_page_opt),
OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
OPT_END() OPT_END()
}; };
const char *const kmem_subcommands[] = { "record", "stat", NULL }; const char *const kmem_subcommands[] = { "record", "stat", NULL };