perf kmem: Implement page allocation analysis (v1)
Namhyung Kim
maintainercover perf kmem: Implement page allocation analysis (v1)
Hello,
Currently perf kmem command only analyzes SLAB memory allocation. And I'd like to introduce page allocation analysis also. Users can use
--slab and/or --page option to select it. If none of these options
are used, it does slab allocation analysis for backward compatibility.
The patch 1-3 are bugfix and cleanups. Patch 4 implements basic support for page allocation analysis, patch 5 deals with the callsite and finally patch 6 implements sorting.
In this patchset, I used two kmem events: kmem:mm_page_alloc and kmem_page_free for analysis as they can track every memory allocation/free path AFAIK. However, unlike slab tracepoint events, those page allocation events don't provide callsite info directly. So I recorded callchains and extracted callsites like below:
Normal page allocation callchains look like this:
360a7e __alloc_pages_nodemask
3a711c alloc_pages_current
357bc7 __page_cache_alloc <-- callsite
357cf6 pagecache_get_page
48b0a prepare_pages
494d3 __btrfs_buffered_write
49cdf btrfs_file_write_iter
3ceb6e new_sync_write
3cf447 vfs_write
3cff99 sys_write
7556e9 system_call
f880 __write_nocancel
33eb9 cmd_record
4b38e cmd_kmem
7aa23 run_builtin
27a9a main
20800 __libc_start_main
But first two are internal page allocation functions so it should be skipped. To determine such allocation functions, I used following regex:
^_?_?(alloc|get_free|get_zeroed)_pages?
This gave me a following list of functions (you can see this with -v):
alloc func: __get_free_pages
alloc func: get_zeroed_page
alloc func: alloc_pages_exact
alloc func: __alloc_pages_direct_compact
alloc func: __alloc_pages_nodemask
alloc func: alloc_page_interleave
alloc func: alloc_pages_current
alloc func: alloc_pages_vma
alloc func: alloc_page_buffers
alloc func: alloc_pages_exact_nid
After skipping those function, it got '__page_cache_alloc'.
Other information such as allocation order, migration type and gfp flags are provided by tracepoint events.
Basically the output will be sorted by total allocation bytes, but you can change it by using -s/--sort option. The following sort keys are added to support page analysis: page, order, mtype, gfp. Existing 'callsite', 'bytes' and 'hit' sort keys also can be used.
An example follows:
# perf kmem record --slab --page sleep 1 [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]
# perf kmem stat --page --caller -l 10 -s order,hit
--------------------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite
--------------------------------------------------------------------------------------------
65536/16384 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab
51347456/4096 | 12536 | 0 | MOVABLE | 0102005a | __page_cache_alloc
53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
40960/4096 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault
28672/4096 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
8192/4096 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total alloc requested: 12593
Total alloc failure : 0
Total bytes allocated: 51630080
Total free requested: 115
Total free unmatched: 67
Total bytes freed : 471040
Order UNMOVABLE RECLAIMABLE MOVABLE RESERVED CMA/ISOLATE
----- ------------ ------------ ------------ ------------ ------------
0 32 0 12557 0 0
1 0 0 0 0 0
2 0 4 0 0 0
3 0 0 0 0 0
4 0 0 0 0 0
5 0 0 0 0 0
6 0 0 0 0 0
7 0 0 0 0 0
8 0 0 0 0 0
9 0 0 0 0 0
10 0 0 0 0 0I have some idea how to improve it. But I'd also like to hear other idea, suggestion, feedback and so on.
This is available at perf/kmem-page-v1 branch on my tree:
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Thanks,
Namhyung
Namhyung Kim (6):
perf kmem: Fix segfault when invalid sort key is given perf kmem: Allow -v option
perf kmem: Fix alignment of slab result table perf kmem: Analyze page allocator events also perf kmem: Implement stat --page --caller perf kmem: Support sort keys on page analysis
tools/perf/Documentation/perf-kmem.txt | 18 +- tools/perf/builtin-kmem.c | 961 ++++++++++++++++++++++++++++++--- 2 files changed, 915 insertions(+), 64 deletions(-)
Signature · 1 line
2.3.1
Namhyung Kim
maintainerPATCH 1/6 perf kmem: Fix segfault when invalid sort key is given
When it tries to free 'str', it was already updated by strsep() - so it needs to save the original pointer.
# perf kmem stat -s xxx,hit
Error: Unknown --sort key: 'xxx'
*** Error in `perf': free(): invalid pointer: 0x0000000000e9e7b6 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x7198e)[0x7fc7e6e0d98e]
/usr/lib/libc.so.6(+0x76dee)[0x7fc7e6e12dee]
/usr/lib/libc.so.6(+0x775cb)[0x7fc7e6e135cb]
./perf[0x44a1b5]
./perf[0x490b20]
./perf(parse_options_step+0x173)[0x491773]
./perf(parse_options_subcommand+0xa7)[0x491fb7]
./perf(cmd_kmem+0x2bc)[0x44ae4c]
./perf[0x47aa13]
./perf(main+0x60a)[0x427a9a]
/usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fc7e6dbc800]
./perf(_start+0x29)[0x427bb9]--- tools/perf/builtin-kmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/builtin-kmem.c +2 -1
@@ -559,6 +559,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) {char *tok;char *str =strdup (arg); +char *pos = str;if (!str) {pr_err ("%s: strdup failed\n" , __func__);
@@ -566,7 +567,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) }while (true) { - tok =strsep (&str,"," ); + tok =strsep (&pos,"," );if (!tok)break ;if (sort_dimension__add (tok, sort_list) <0 ) {
Signature · 1 line
2.3.1
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf kmem: Fix segfault when invalid sort key is given
perf kmem: Fix segfault when invalid sort key is given
Commit-ID: 405f87557da35a03ba4663eca971ffac58b0a818 Gitweb: http://git.kernel.org/tip/405f87557da35a03ba4663eca971ffac58b0a818 Author: Namhyung Kim <[email protected]> AuthorDate: Thu, 12 Mar 2015 16:32:46 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Fri, 13 Mar 2015 07:47:47 -0300
perf kmem: Fix segfault when invalid sort key is given
When it tries to free 'str', it was already updated by strsep() - so it needs to save the original pointer.
# perf kmem stat -s xxx,hit
Error: Unknown --sort key: 'xxx'
*** Error in `perf': free(): invalid pointer: 0x0000000000e9e7b6 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x7198e)[0x7fc7e6e0d98e]
/usr/lib/libc.so.6(+0x76dee)[0x7fc7e6e12dee]
/usr/lib/libc.so.6(+0x775cb)[0x7fc7e6e135cb]
./perf[0x44a1b5]
./perf[0x490b20]
./perf(parse_options_step+0x173)[0x491773]
./perf(parse_options_subcommand+0xa7)[0x491fb7]
./perf(cmd_kmem+0x2bc)[0x44ae4c]
./perf[0x47aa13]
./perf(main+0x60a)[0x427a9a]
/usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fc7e6dbc800]
./perf(_start+0x29)[0x427bb9]--- tools/perf/builtin-kmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/builtin-kmem.c +2 -1
@@ -559,6 +559,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) {char *tok;char *str =strdup (arg); +char *pos = str;if (!str) {pr_err ("%s: strdup failed\n" , __func__);
@@ -566,7 +567,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) }while (true) { - tok =strsep (&str,"," ); + tok =strsep (&pos,"," );if (!tok)break ;if (sort_dimension__add (tok, sort_list) <0 ) {
Namhyung Kim
maintainerPATCH 2/6 perf kmem: Allow -v option
In reply to Namhyung KimCurrent perf kmem fails when -v option is used. As it's very useful for debugging, let's allow it.
--- tools/perf/Documentation/perf-kmem.txt | 4 ++++ tools/perf/builtin-kmem.c | 2 ++ 2 files changed, 6 insertions(+)
tools/perf/Documentation/perf-kmem.txt +4 -0
@@ -25,6 +25,10 @@ OPTIONS --input=<file>:: Select the input file (default: perf.data unless stdin is a fifo) +-v:: +--verbose:: + Be more verbose. (show symbol address, etc) + --caller:: Show per-callsite statistics
tools/perf/builtin-kmem.c +2 -0
@@ -663,6 +663,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)const char *const default_sort_order ="frag,hit,bytes" ;const struct option kmem_options[] = {OPT_STRING ('i' ,"input" , &input_name,"file" ,"input file name" ), +OPT_INCR ('v' ,"verbose" , &verbose, +"be more verbose (show symbol address, etc)" ),OPT_CALLBACK_NOOPT (0 ,"caller" ,NULL ,NULL ,"show per-callsite statistics" , parse_caller_opt), OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
Signature · 1 line
2.3.1
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf kmem: Allow -v option
perf kmem: Allow -v option
Commit-ID: bd72a33ebae8b4d37e3d2a3f0f3f3333ac9654dd Gitweb: http://git.kernel.org/tip/bd72a33ebae8b4d37e3d2a3f0f3f3333ac9654dd Author: Namhyung Kim <[email protected]> AuthorDate: Thu, 12 Mar 2015 16:32:47 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Fri, 13 Mar 2015 07:47:48 -0300
perf kmem: Allow -v option
Current perf kmem fails when -v option is used. As it's very useful for debugging, let's allow it.
--- tools/perf/Documentation/perf-kmem.txt | 4 ++++ tools/perf/builtin-kmem.c | 2 ++ 2 files changed, 6 insertions(+)
tools/perf/Documentation/perf-kmem.txt +4 -0
@@ -25,6 +25,10 @@ OPTIONS --input=<file>:: Select the input file (default: perf.data unless stdin is a fifo) +-v:: +--verbose:: + Be more verbose. (show symbol address, etc) + --caller:: Show per-callsite statistics
tools/perf/builtin-kmem.c +2 -0
@@ -663,6 +663,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)const char *const default_sort_order ="frag,hit,bytes" ;const struct option kmem_options[] = {OPT_STRING ('i' ,"input" , &input_name,"file" ,"input file name" ), +OPT_INCR ('v' ,"verbose" , &verbose, +"be more verbose (show symbol address, etc)" ),OPT_CALLBACK_NOOPT (0 ,"caller" ,NULL ,NULL ,"show per-callsite statistics" , parse_caller_opt), OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
Namhyung Kim
maintainerPATCH 3/6 perf kmem: Fix alignment of slab result table
In reply to Namhyung KimIts table was a bit misaligned. Fix it.
Before:
# perf kmem stat --caller -l 10 ------------------------------------------------------------------------------------------------------ Callsite | Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag ------------------------------------------------------------------------------------------------------ radeon_cs_parser_init.part.1+11a | 2080/260 | 1504/188 | 8 | 0 | 27.692% radeon_cs_parser_init.part.1+e1 | 384/96 | 288/72 | 4 | 0 | 25.000% radeon_cs_parser_init.part.1+93 | 128/32 | 96/24 | 4 | 0 | 25.000% load_elf_binary+a39 | 512/512 | 392/392 | 1 | 0 | 23.438% __alloc_skb+89 | 6144/877 | 4800/685 | 7 | 6 | 21.875% radeon_fence_emit+5c | 1152/192 | 912/152 | 6 | 0 | 20.833% radeon_cs_parser_relocs+ad | 8192/2048 | 6624/1656 | 4 | 0 | 19.141% radeon_sa_bo_new+78 | 1280/64 | 1120/56 | 20 | 0 | 12.500% load_elf_binary+2c4 | 32/32 | 28/28 | 1 | 0 | 12.500% anon_vma_prepare+101 | 576/72 | 512/64 | 8 | 0 | 11.111% ... | ... | ... | ... | ... | ... ------------------------------------------------------------------------------------------------------
After:
---------------------------------------------------------------------------------------------------------
Callsite | Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag
---------------------------------------------------------------------------------------------------------
radeon_cs_parser_init.part.1+11a | 2080/260 | 1504/188 | 8 | 0 | 27.692% radeon_cs_parser_init.part.1+e1 | 384/96 | 288/72 | 4 | 0 | 25.000% radeon_cs_parser_init.part.1+93 | 128/32 | 96/24 | 4 | 0 | 25.000% load_elf_binary+a39 | 512/512 | 392/392 | 1 | 0 | 23.438% __alloc_skb+89 | 6144/877 | 4800/685 | 7 | 6 | 21.875% radeon_fence_emit+5c | 1152/192 | 912/152 | 6 | 0 | 20.833% radeon_cs_parser_relocs+ad | 8192/2048 | 6624/1656 | 4 | 0 | 19.141% radeon_sa_bo_new+78 | 1280/64 | 1120/56 | 20 | 0 | 12.500% load_elf_binary+2c4 | 32/32 | 28/28 | 1 | 0 | 12.500% anon_vma_prepare+101 | 576/72 | 512/64 | 8 | 0 | 11.111% ... | ... | ... | ... | ... | ...
---------------------------------------------------------------------------------------------------------
--- tools/perf/builtin-kmem.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
tools/perf/builtin-kmem.c +5 -5
@@ -275,10 +275,10 @@ static void __print_result(struct rb_root *root, struct perf_session *session,struct rb_node *next;struct machine *machine = &session->machines.host; -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );printf (" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n" ); -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); next =rb_first (root);
@@ -304,7 +304,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,snprintf (buf,sizeof (buf),"%#" PRIx64"" , addr);printf (" %-34s |" , buf); - printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n", + printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n", (unsigned long long)data->bytes_alloc, (unsigned long)data->bytes_alloc / data->hit, (unsigned long long)data->bytes_req,
@@ -317,9 +317,9 @@ static void __print_result(struct rb_root *root, struct perf_session *session, }if (n_lines ==-1 ) -printf (" ... | ... | ... | ... | ... | ... \n" ); +printf (" ... | ... | ... | ... | ... | ... \n" ); -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); }static void print_summary (void )
Signature · 1 line
2.3.1
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf kmem: Fix alignment of slab result table
perf kmem: Fix alignment of slab result table
Commit-ID: 65f46e0214c64198a0266c37181a7776e16b7e53 Gitweb: http://git.kernel.org/tip/65f46e0214c64198a0266c37181a7776e16b7e53 Author: Namhyung Kim <[email protected]> AuthorDate: Thu, 12 Mar 2015 16:32:48 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Fri, 13 Mar 2015 07:47:48 -0300
perf kmem: Fix alignment of slab result table
Its table was a bit misaligned. Fix it.
Before:
# perf kmem stat --caller -l 10 ------------------------------------------------------------------------------------------------------ Callsite | Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag ------------------------------------------------------------------------------------------------------ radeon_cs_parser_init.part.1+11a | 2080/260 | 1504/188 | 8 | 0 | 27.692% radeon_cs_parser_init.part.1+e1 | 384/96 | 288/72 | 4 | 0 | 25.000% radeon_cs_parser_init.part.1+93 | 128/32 | 96/24 | 4 | 0 | 25.000% load_elf_binary+a39 | 512/512 | 392/392 | 1 | 0 | 23.438% __alloc_skb+89 | 6144/877 | 4800/685 | 7 | 6 | 21.875% radeon_fence_emit+5c | 1152/192 | 912/152 | 6 | 0 | 20.833% radeon_cs_parser_relocs+ad | 8192/2048 | 6624/1656 | 4 | 0 | 19.141% radeon_sa_bo_new+78 | 1280/64 | 1120/56 | 20 | 0 | 12.500% load_elf_binary+2c4 | 32/32 | 28/28 | 1 | 0 | 12.500% anon_vma_prepare+101 | 576/72 | 512/64 | 8 | 0 | 11.111% ... | ... | ... | ... | ... | ... ------------------------------------------------------------------------------------------------------
After:
---------------------------------------------------------------------------------------------------------
Callsite | Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag
---------------------------------------------------------------------------------------------------------
radeon_cs_parser_init.part.1+11a | 2080/260 | 1504/188 | 8 | 0 | 27.692% radeon_cs_parser_init.part.1+e1 | 384/96 | 288/72 | 4 | 0 | 25.000% radeon_cs_parser_init.part.1+93 | 128/32 | 96/24 | 4 | 0 | 25.000% load_elf_binary+a39 | 512/512 | 392/392 | 1 | 0 | 23.438% __alloc_skb+89 | 6144/877 | 4800/685 | 7 | 6 | 21.875% radeon_fence_emit+5c | 1152/192 | 912/152 | 6 | 0 | 20.833% radeon_cs_parser_relocs+ad | 8192/2048 | 6624/1656 | 4 | 0 | 19.141% radeon_sa_bo_new+78 | 1280/64 | 1120/56 | 20 | 0 | 12.500% load_elf_binary+2c4 | 32/32 | 28/28 | 1 | 0 | 12.500% anon_vma_prepare+101 | 576/72 | 512/64 | 8 | 0 | 11.111% ... | ... | ... | ... | ... | ...
---------------------------------------------------------------------------------------------------------
--- tools/perf/builtin-kmem.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
tools/perf/builtin-kmem.c +5 -5
@@ -275,10 +275,10 @@ static void __print_result(struct rb_root *root, struct perf_session *session,struct rb_node *next;struct machine *machine = &session->machines.host; -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );printf (" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n" ); -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); next =rb_first (root);
@@ -304,7 +304,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,snprintf (buf,sizeof (buf),"%#" PRIx64"" , addr);printf (" %-34s |" , buf); - printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n", + printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n", (unsigned long long)data->bytes_alloc, (unsigned long)data->bytes_alloc / data->hit, (unsigned long long)data->bytes_req,
@@ -317,9 +317,9 @@ static void __print_result(struct rb_root *root, struct perf_session *session, }if (n_lines ==-1 ) -printf (" ... | ... | ... | ... | ... | ... \n" ); +printf (" ... | ... | ... | ... | ... | ... \n" ); -printf ("%.102s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); }static void print_summary (void )
Namhyung Kim
maintainerPATCH 4/6 perf kmem: Analyze page allocator events also
In reply to Namhyung KimThe perf kmem command records and analyze kernel memory allocation only for SLAB objects. This patch implement a simple page allocator analyzer using kmem:mm_page_alloc and kmem_mm_page_free events.
It adds two new options of --slab and --page. The --slab option is for analyzing SLAB allocator and that's what perf kmem currently does.
The new --page option enables page allocator events and analyze kernel memory usage in page unit. Currently, 'stat --alloc' subcommand is implemented only.
If none of these --slab nor --page is specified, --slab is implied.
# perf kmem stat --page --alloc --line 10
--------------------------------------------------------------------------------
Page | Total_alloc/Per | Hit | Order | Migrate type | GFP flag
--------------------------------------------------------------------------------
ffffea0015e48e00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16384/16384 | 1 | 2 | RECLAIMABLE | 00285250
... | ... | ... | ... | ... | ...
--------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total alloc requested: 44260
Total alloc failure : 0
Total bytes allocated: 181510144
Total free requested: 49
Total free unmatched: 68
Total bytes freed : 200704
Order UNMOVABLE RECLAIMABLE MOVABLE RESERVED CMA/ISOLATE
----- ------------ ------------ ------------ ------------ ------------
0 32 0 44210 0 0
1 0 0 0 0 0
2 0 18 0 0 0
3 0 0 0 0 0
4 0 0 0 0 0
5 0 0 0 0 0
6 0 0 0 0 0
7 0 0 0 0 0
8 0 0 0 0 0
9 0 0 0 0 0
10 0 0 0 0 0--- tools/perf/Documentation/perf-kmem.txt | 9 +- tools/perf/builtin-kmem.c | 353 +++++++++++++++++++++++++++++++-- 2 files changed, 346 insertions(+), 16 deletions(-)
tools/perf/Documentation/perf-kmem.txt +8 -1
@@ -3,7 +3,7 @@ perf-kmem(1) NAME ---- -perf-kmem - Tool to trace/measure kernel memory(slab) properties +perf-kmem - Tool to trace/measure kernel memory properties SYNOPSIS --------
@@ -46,6 +46,13 @@ OPTIONS --raw-ip:: Print raw ip instead of symbol +--slab:: + Analyze SLAB allocator events. This option is enabled by default. + To disable it, use --no-slab. + +--page:: + Analyze page allocator events + SEE ALSO -------- linkperf:perf-record[1]
tools/perf/builtin-kmem.c +338 -15
468 lines not on this page · raw message
Signature · 1 line
2.3.1
Re: [PATCH 4/6] perf kmem: Analyze page allocator events also
On Thu, Mar 12, 2015 at 04:32:49PM +0900, Namhyung Kim wrote:
SNIP
Quoted 5 lines
tools/perf/builtin-kmem.c +5
-0+static int parse_slab_opt (const struct option *opt__maybe_unused , +const char *arg__maybe_unused , +int unset__maybe_unused ) +{ + kmem_slab = (kmem_page +1 );
tools/perf/builtin-kmem.c static int parse_alloc_opt(const struct option *opt __maybe_unused, · line 897
Quoted 10 lines
tools/perf/builtin-kmem.c +10
-0+return 0 ; +} + +static int parse_page_opt (const struct option *opt__maybe_unused , +const char *arg__maybe_unused , +int unset__maybe_unused ) +{ + kmem_page = (kmem_slab +1 ); +return 0 ; +}
hum, just curious.. why not just assign 1, I'm missing the magic ;-)
thanks,
jirk
Namhyung Kim
maintainerRe: [PATCH 4/6] perf kmem: Analyze page allocator events also
Hi Jiri,
Quoted 13 lines · Jiri Olsa
On Thu, Mar 12, 2015 at 04:32:49PM +0900, Namhyung Kim wrote:
SNIP
Quoted 9 lines
tools/perf/builtin-kmem.c +9
-0+static int parse_slab_opt (const struct option *opt__maybe_unused , +const char *arg__maybe_unused , +int unset__maybe_unused ) +{ + kmem_slab = (kmem_page +1 ); +return 0 ; +} + +static int parse_page_opt(const struct option *opt __maybe_unused,
Quoted 8 lines
tools/perf/builtin-kmem.c static int parse_alloc_opt(const struct option *opt __maybe_unused, · line 897
Quoted 6 lines
tools/perf/builtin-kmem.c +6
-0+const char *arg__maybe_unused , + int unset __maybe_unused) +{ + kmem_page = (kmem_slab +1 ); +return 0 ; +}hum, just curious.. why not just assign 1, I'm missing the magic ;-)
Well, basically I just followed the existing convention. :)
Anyway it controls the effect of -s/--sort option. The single -s option will be used to set sort keys for both of --alloc and --caller stat. So it needs to known currently selected mode at option parsing time. I extended it to page/slab selection also.
For example,
# perf kmem --page --alloc -s bytes --slab --caller -s hit
The first -s option will set sort keys for page/alloc stat and the second -s option will set sort keys for slab/caller stat. So the -s option should be preceded by at least one of the mode selection options (i.e. --page/slab/alloc/caller). I'll add it to the man page also.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 5/6 perf kmem: Implement stat --page --caller
In reply to Namhyung KimIt perf kmem support caller statistics for page. Unlike slab case, the tracepoints in page allocator don't provide callsite info. So it records with callchain and extracts callsite info.
Note that the callchain contains several memory allocation functions which has no meaning for users. So skip those functions to get proper callsites. I used following regex pattern to skip the allocator functions:
^_?_?(alloc|get_free|get_zeroed)_pages?
This gave me a following list of functions:
# perf kmem record --page sleep 3 # perf kmem stat --page -v ... alloc func: __get_free_pages alloc func: get_zeroed_page alloc func: alloc_pages_exact alloc func: __alloc_pages_direct_compact alloc func: __alloc_pages_nodemask alloc func: alloc_page_interleave alloc func: alloc_pages_current alloc func: alloc_pages_vma alloc func: alloc_page_buffers alloc func: alloc_pages_exact_nid ...
The output looks mostly same as --alloc (I also added callsite column to that) but groups entries by callsite. Currently, the order, migrate type and GFP flag info is for the last allocation and not guaranteed to be same for all allocations from the callsite.
--------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite
--------------------------------------------------------------------------------
1089536/4096 | 266 | 0 | UNMOVABLE | 000000d0 | __pollwait
53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
45056/4096 | 11 | 0 | MOVABLE | 000280da | handle_mm_fault
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page
16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
12288/4096 | 3 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
8192/4096 | 2 | 0 | UNMOVABLE | 00000010 | bio_copy_user_iov
4096/4096 | 1 | 0 | UNMOVABLE | 000200d2 | pipe_write
4096/4096 | 1 | 0 | MOVABLE | 000280da | do_wp_page
4096/4096 | 1 | 0 | UNMOVABLE | 002084d0 | pgd_alloc--------------------------------------------------------------------------------
--- tools/perf/builtin-kmem.c | 299 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 275 insertions(+), 24 deletions(-)
tools/perf/builtin-kmem.c +275 -24
@@ -10,6 +10,7 @@#include "util/header.h" #include "util/session.h" #include "util/tool.h" +#include "util/callchain.h" #include "util/parse-options.h" #include "util/trace-event.h"
@@ -20,6 +21,7 @@#include <linux/rbtree.h> #include <linux/string.h> +#include <regex.h> static int kmem_slab;static int kmem_page;
@@ -237,6 +239,8 @@ static unsigned long nr_page_frees;static unsigned long nr_page_fails;static unsigned long nr_page_nomatch; +static struct perf_session *kmem_session; +#define MAX_MIGRATE_TYPES 6#define MAX_PAGE_ORDER 11
@@ -245,6 +249,7 @@ static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];struct page_stat {struct rb_node node;u64 page; +u64 callsite;int order;unsigned gfp_flags;unsigned migrate_type;
@@ -254,12 +259,142 @@ struct page_stat {int nr_free; }; -static struct rb_root page_tree; +static struct rb_root page_alloc_tree;static struct rb_root page_alloc_sorted; +static struct rb_root page_caller_tree; +static struct rb_root page_caller_sorted; + +struct alloc_func { +u64 start; +u64 end; +char *name; +}; + +static int nr_alloc_funcs; +static struct alloc_func *alloc_func_list; + +static int funcmp (const void *a,const void *b) +{ +const struct alloc_func *fa = a; +const struct alloc_func *fb = b; + +if (fa->start > fb->start) +return 1 ; +else +return -1 ; +} + +static int callcmp (const void *a,const void *b) +{ +const struct alloc_func *fa = a; +const struct alloc_func *fb = b; + +if (fb->start <= fa->start && fa->end < fb->end) +return 0 ; + +if (fa->start > fb->start) +return 1 ; +else +return -1 ; +} + +static int build_alloc_func_list (void ) +{ +int ret; +struct map *kernel_map; +struct symbol *sym; +struct rb_node *node; +struct alloc_func *func; +struct machine *machine = &kmem_session->machines.host; + +regex_t alloc_func_regex; +const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ; + + ret =regcomp (&alloc_func_regex, pattern,REG_EXTENDED ); +if (ret) { +char err[BUFSIZ ]; + +regerror (ret, &alloc_func_regex, err,sizeof (err)); +pr_err ("Invalid regex: %s\n%s" , pattern, err); +return -EINVAL ; + } + + kernel_map = machine->vmlinux_maps[MAP__FUNCTION ]; +map__load (kernel_map,NULL ); + +map__for_each_symbol (kernel_map, sym, node) { +if (regexec (&alloc_func_regex, sym->name,0 ,NULL ,0 )) +continue ; + + func =realloc (alloc_func_list, + (nr_alloc_funcs +1 ) *sizeof (*func)); +if (func ==NULL ) +return -ENOMEM ; + +pr_debug ("alloc func: %s\n" , sym->name); + func[nr_alloc_funcs].start = sym->start; + func[nr_alloc_funcs].end = sym->end; + func[nr_alloc_funcs].name = sym->name; + + alloc_func_list = func; + nr_alloc_funcs++; + } + +qsort (alloc_func_list, nr_alloc_funcs,sizeof (*func), funcmp); + +regfree (&alloc_func_regex); +return 0 ; +} + +/* +* Find first non-memory allocation function from callchain. +* The allocation functions are in the 'alloc_func_list'. +*/ +static u64 find_callsite (struct perf_evsel *evsel,struct perf_sample *sample) +{ +struct addr_location al; +struct machine *machine = &kmem_session->machines.host; +struct callchain_cursor_node *node; + +if (alloc_func_list ==NULL ) +build_alloc_func_list (); + + al.thread =machine__findnew_thread (machine, sample->pid, sample->tid); +sample__resolve_callchain (sample,NULL , evsel, &al,16 ); + +callchain_cursor_commit (&callchain_cursor); +while (true) { +struct alloc_func key, *caller; +u64 addr; + + node =callchain_cursor_current (&callchain_cursor); +if (node ==NULL ) +break ; + + key.start = key.end = node->ip; + caller =bsearch (&key, alloc_func_list, nr_alloc_funcs, +sizeof (key), callcmp); +if (!caller) { +/* found */ +if (node->map) + addr =map__unmap_ip (node->map, node->ip); +else + addr = node->ip; + +return addr; + }else +pr_debug3 ("skipping alloc function: %s\n" , caller->name); -static struct page_stat *search_page_stat (unsigned long page,bool create) +callchain_cursor_advance (&callchain_cursor); + } + +pr_debug2 ("unknown callsite: %" PRIx64, sample->ip); +return sample->ip; +} + +static struct page_stat *search_page_alloc_stat (u64 page,bool create) { -struct rb_node **node = &page_tree.rb_node; +struct rb_node **node = &page_alloc_tree.rb_node;struct rb_node *parent =NULL ;struct page_stat *data;
@@ -286,7 +421,42 @@ static struct page_stat *search_page_stat(unsigned long page, bool create) data->page = page;rb_link_node (&data->node, parent, node); -rb_insert_color (&data->node, &page_tree); +rb_insert_color (&data->node, &page_alloc_tree); + } + +return data; +} + +static struct page_stat *search_page_caller_stat (u64 callsite,bool create) +{ +struct rb_node **node = &page_caller_tree.rb_node; +struct rb_node *parent =NULL ; +struct page_stat *data; + +while (*node) { +s64 cmp; + + parent = *node; + data = rb_entry(*node, struct page_stat, node); + + cmp = data->callsite - callsite; +if (cmp <0 ) + node = &parent->rb_left; +else if (cmp >0 ) + node = &parent->rb_right; +else +return data; + } + +if (!create) +return NULL ; + + data =zalloc (sizeof (*data)); +if (data !=NULL ) { + data->callsite = callsite; + +rb_link_node (&data->node, parent, node); +rb_insert_color (&data->node, &page_caller_tree); }return data;
@@ -301,6 +471,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,unsigned int migrate_type =perf_evsel__intval (evsel, sample,"migratetype" );u64 bytes = kmem_page_size << order; +u64 callsite;struct page_stat *stat;if (page ==0 ) {
@@ -308,22 +479,39 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,return 0 ; } + callsite =find_callsite (evsel, sample); +/* * XXX: We'd better to use PFN instead of page pointer to deal * with things like partial freeing. But AFAIK there's no way * to convert a pointer to struct page into PFN in userspace. */ - stat =search_page_stat (page, true); -if (stat ==NULL ) + stat =search_page_alloc_stat (page, true); +if (stat ==NULL ) { +pr_err ("cannot create page alloc stat\n" );return -1 ; + } stat->order = order; stat->gfp_flags = gfp_flags; stat->migrate_type = migrate_type; + stat->callsite = callsite; + stat->nr_alloc++; + stat->alloc_bytes += bytes; + + stat =search_page_caller_stat (callsite, true); +if (stat ==NULL ) { +pr_err ("cannot create page caller stat\n" ); +return -1 ; + } + stat->order = order; + stat->gfp_flags = gfp_flags; + stat->migrate_type = migrate_type; stat->nr_alloc++; - nr_page_allocs++; stat->alloc_bytes += bytes; + + nr_page_allocs++; total_page_alloc_bytes += bytes; order_stats[order][migrate_type]++;
@@ -342,7 +530,7 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, nr_page_frees++; total_page_free_bytes += bytes; - stat =search_page_stat (page, false); + stat =search_page_alloc_stat (page, false);if (stat ==NULL ) {pr_debug2 ("missing free at page %" PRIx64" (order: %d)\n" , page, order);
@@ -353,6 +541,12 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, stat->nr_free++; stat->free_bytes += bytes; + stat =search_page_caller_stat (stat->callsite, false); +if (stat !=NULL ) { + stat->nr_free++; + stat->free_bytes += bytes; + } +return 0 ; }
@@ -463,36 +657,85 @@ static const char * const migrate_type_str[] = { "UNKNOWN", }; -static void __print_page_result (struct rb_root *root, -struct perf_session *session__maybe_unused , -int n_lines) +static void __print_page_alloc_result (struct perf_session *session,int n_lines) { -struct rb_node *next =rb_first (root); +struct rb_node *next =rb_first (&page_alloc_sorted); +struct machine *machine = &session->machines.host; -printf ("\n%.80s\n" , graph_dotted_line); -printf (" Page | Total_alloc/Per | Hit | Order | Migrate type | GFP flag\n" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("\n%.92s\n" , graph_dotted_line); +printf (" Page | Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite\n" ); +printf ("%.92s\n" , graph_dotted_line);while (next && n_lines--) {struct page_stat *data; +struct symbol *sym; +struct map *map; +char buf[32 ]; +char *caller = buf; data = rb_entry(next, struct page_stat, node); + sym =machine__find_kernel_function (machine, data->callsite, + &map,NULL ); +if (sym && sym->name) + caller = sym->name; +else +scnprintf (buf,sizeof (buf),"%" PRIx64, data->callsite); -printf (" %016llx | %9llu/%-5lu | %8d | %5d | %12s | %08lx\n" , +printf (" %016llx | %9llu/%-5lu | %8d | %5d | %12s | %08lx | %s\n" , (unsigned long long )data->page, (unsigned long long )data->alloc_bytes, (unsigned long )data->alloc_bytes / data->nr_alloc, data->nr_alloc, data->order, migrate_type_str[data->migrate_type], - (unsigned long )data->gfp_flags); + (unsigned long )data->gfp_flags, caller); + + next =rb_next (next); + } + +if (n_lines ==-1 ) +printf (" ... | ... | ... | ... | ... | ... | ...\n" ); + +printf ("%.92s\n" , graph_dotted_line); +} + +static void __print_page_caller_result (struct perf_session *session,int n_lines) +{ +struct rb_node *next =rb_first (&page_caller_sorted); +struct machine *machine = &session->machines.host; + +printf ("\n%.92s\n" , graph_dotted_line); +printf (" Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite\n" ); +printf ("%.92s\n" , graph_dotted_line); + +while (next && n_lines--) { +struct page_stat *data; +struct symbol *sym; +struct map *map; +char buf[32 ]; +char *caller = buf; + + data = rb_entry(next, struct page_stat, node); + sym =machine__find_kernel_function (machine, data->callsite, + &map,NULL ); +if (sym && sym->name) + caller = sym->name; +else +scnprintf (buf,sizeof (buf),"%" PRIx64, data->callsite); + +printf (" %9llu/%-5lu | %8d | %5d | %12s | %08lx | %s\n" , + (unsigned long long )data->alloc_bytes, + (unsigned long )data->alloc_bytes / data->nr_alloc, + data->nr_alloc, data->order, + migrate_type_str[data->migrate_type], + (unsigned long )data->gfp_flags, caller); next =rb_next (next); }if (n_lines ==-1 ) -printf (" ... | ... | ... | ... | ... | ... \n" ); +printf (" ... | ... | ... | ... | ... | ...\n" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("%.92s\n" , graph_dotted_line); }static void print_slab_summary (void )
@@ -547,8 +790,10 @@ static void print_slab_result(struct perf_session *session)static void print_page_result (struct perf_session *session) { +if (caller_flag) +__print_page_caller_result (session, caller_lines);if (alloc_flag) -__print_page_result (&page_alloc_sorted, session, alloc_lines); +__print_page_alloc_result (session, alloc_lines);print_page_summary (); }
@@ -666,7 +911,8 @@ static void sort_result(void) &caller_sort); }if (kmem_page) { -__sort_page_result (&page_tree, &page_alloc_sorted); +__sort_page_result (&page_alloc_tree, &page_alloc_sorted); +__sort_page_result (&page_caller_tree, &page_caller_sorted); } }
@@ -696,8 +942,10 @@ static int __cmd_kmem(struct perf_session *session)setup_pager (); err =perf_session__process_events (session, &perf_kmem); -if (err !=0 ) +if (err !=0 ) { +pr_err ("error during process events: %d\n" , err); goto out; + }sort_result ();print_result (session); out:
@@ -938,7 +1186,7 @@ static int __cmd_record(int argc, const char **argv)if (kmem_slab) rec_argc +=ARRAY_SIZE (slab_events);if (kmem_page) - rec_argc +=ARRAY_SIZE (page_events); + rec_argc +=ARRAY_SIZE (page_events) +1 ;/* for -g */ rec_argv =calloc (rec_argc +1 ,sizeof (char *));
@@ -953,6 +1201,8 @@ static int __cmd_record(int argc, const char **argv) rec_argv[i] =strdup (slab_events[j]); }if (kmem_page) { + rec_argv[i++] =strdup ("-g" ); +for (j =0 ; j <ARRAY_SIZE (page_events); j++, i++) rec_argv[i] =strdup (page_events[j]); }
@@ -1011,7 +1261,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)return __cmd_record (argc, argv); } - session =perf_session__new (&file, false, &perf_kmem); + kmem_session = session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;
@@ -1024,6 +1274,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) } kmem_page_size =pevent_get_page_size (evsel->tp_format->pevent); + symbol_conf.use_callchain = true; }symbol__init (&session->header.env);
Signature · 1 line
2.3.1
Namhyung Kim
maintainerPATCH 6/6 perf kmem: Support sort keys on page analysis
In reply to Namhyung KimAdd new sort keys for page: page, order, mtype, gfp - existing 'bytes', 'hit' and 'callsite' sort keys also work for page. Note that -s/--sort option should be preceded by either of --slab or --page option to determine where the sort keys applies.
Now it properly groups and sorts allocation stats - so same page/caller with different order/mtype/gfp will be printed on a different line.
# perf kmem stat --page --caller -l 10 -s order,hit
--------------------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite
--------------------------------------------------------------------------------------------
65536/16384 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab
51347456/4096 | 12536 | 0 | MOVABLE | 0102005a | __page_cache_alloc
53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
40960/4096 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault
28672/4096 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page
20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
8192/4096 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
--- tools/perf/Documentation/perf-kmem.txt | 5 +- tools/perf/builtin-kmem.c | 396 +++++++++++++++++++++++++++------ 2 files changed, 332 insertions(+), 69 deletions(-)
tools/perf/Documentation/perf-kmem.txt +4 -1
@@ -37,7 +37,10 @@ OPTIONS -s <key[,key2...]>:: --sort=<key[,key2...]>:: - Sort the output (default: frag,hit,bytes) + Sort the output (default: 'frag,hit,bytes' for slab and 'bytes,hit' + for page). Available sort keys are 'ptr, callsite, bytes, hit, + pingpong, frag' for slab and 'page, callsite, bytes, hit, order, + mtype, gfp' for page. -l <num>:: --line=<num>::
tools/perf/builtin-kmem.c +328 -68
639 lines not on this page · raw message
Signature · 1 line
2.3.1
Quoted 11 lines · Namhyung Kim
Hello,
Currently perf kmem command only analyzes SLAB memory allocation. And I'd like to introduce page allocation analysis also. Users can use
--slab and/or --page option to select it. If none of these options
are used, it does slab allocation analysis for backward compatibility.The patch 1-3 are bugfix and cleanups. Patch 4 implements basic support for page allocation analysis, patch 5 deals with the callsite and finally patch 6 implements sorting.
Quoted 5 lines
In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_freefor analysis as they can track every memory allocation/free path AFAIK. However, unlike slab tracepoint events, those page allocation events don't provide callsite info directly. So I recorded callchains and extracted callsites like below:
Really cool features!
I have a couple of output typography observations:
Quoted 50 lines
Normal page allocation callchains look like this:
360a7e
__alloc_pages_nodemask
3a711calloc_pages_current
357bc7__page_cache_alloc<-- callsite
357cf6pagecache_get_page
48b0aprepare_pages
494d3__btrfs_buffered_write
49cdfbtrfs_file_write_iter
3ceb6enew_sync_write
3cf447vfs_write
3cff99sys_write
7556e9system_callf880 __write_nocancel33eb9
cmd_record
4b38ecmd_kmem
7aa23run_builtin
27a9a main
20800__libc_start_mainBut first two are internal page allocation functions so it should be skipped. To determine such allocation functions, I used following regex:
^_?_?(alloc|get_free|get_zeroed)_pages?
This gave me a following list of functions (you can see this with -v):
alloc func:
__get_free_pages
alloc func:get_zeroed_page
alloc func:alloc_pages_exact
alloc func:__alloc_pages_direct_compact
alloc func:__alloc_pages_nodemask
alloc func:alloc_page_interleave
alloc func:alloc_pages_current
alloc func:alloc_pages_vma
alloc func:alloc_page_buffers
alloc func:alloc_pages_exact_nidAfter skipping those function, it got '
__page_cache_alloc'.Other information such as allocation order, migration type and gfp flags are provided by tracepoint events.
Basically the output will be sorted by total allocation bytes, but you can change it by using -s/--sort option. The following sort keys are added to support page analysis: page, order, mtype, gfp. Existing 'callsite', 'bytes' and 'hit' sort keys also can be used.
An example follows:
Quoted 8 lines
# perf kmem record --slab --page sleep 1 [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]# perf kmem stat --page --caller -l 10 -s order,hit--------------------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite
s/Per/Size
s/Hit/Hits
s/Migrate type/Migration type
s/GFP flag/GFP flags
?
Quoted 14 lines
--------------------------------------------------------------------------------------------
65536/16384 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab 51347456/4096 | 12536 | 0 | MOVABLE | 0102005a | __page_cache_alloc 53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one 40960/4096 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault 28672/4096 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault 16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page 16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc 8192/4096 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc ... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
Quoted 8 lines
SUMMARY (page allocator)
========================
Total alloc requested: 12593
Total alloc failure : 0
Total bytes allocated: 51630080
Total free requested: 115
Total free unmatched: 67
Total bytes freed : 471040
I'd suggest the following changes to the format:
- Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 'freed only', depending on how much of their lifetime we've managed to trace. These groups are really distinct and it makes little sense to mix up their stats.
- Add commas to the numbers, to make it easier to read and compare larger numbers.
- Right-align the numbers, to make them easy to compare when they are placed under each other.
- Merge the 'count' and 'bytes' stats into a single line, so that it's more compact, easier to navigate, but also only comparable type numbers are placed under each other.
I.e. something like this (mockup) output:
SUMMARY (page allocator)
========================
Pages allocated+freed: 12,593 [ 51,630,080 bytes ]
Pages allocated-only: 2,342 [ 1,235,010 bytes ]
Pages freed-only: 67 [ 135,311 bytes ]
Page allocation failures : 0
Quoted 13 lines
Order UNMOVABLE RECLAIMABLE MOVABLE RESERVED CMA/ISOLATE
----- ------------ ------------ ------------ ------------ ------------0 32 0 12557 0 0 1 0 0 0 0 0 2 0 4 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 0 6 0 0 0 0 0 7 0 0 0 0 0 8 0 0 0 0 0 9 0 0 0 0 0 10 0 0 0 0 0
Here I'd suggest the following refinements:
- Use '.' instead of '0', to make actual nonzero values stand out visually, while still keeping a tabular format
- Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special'
colum: this will be zero in 99.9% of the cases, as those pages mostly deal with driver interfaces, mostly used during init/deinit.
- Capitalize less.
- Use comma-separated numbers for better readability.
So something like this:
Order Unmovable Reclaimable Movable Special
----- ------------ ------------ ------------ ------------
0 32 . 12,557 .
1 . . . .
2 . 4 . .
3 . . . .
4 . . . .
5 . . . .
6 . . . .
7 . . . .
8 . . . .
9 . . . .
10 . . . .Look for example how easily noticeable the '4' value is now, while it was pretty easy to miss in the original table.
Quoted 2 lines
I have some idea how to improve it. But I'd also like to hear other idea, suggestion, feedback and so on.
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
Thanks,
Ingo
Namhyung Kim
maintainerHi Ingo,
Quoted 13 lines · Ingo Molnar
Quoted 11 lines · Namhyung Kim
Hello,
Currently perf kmem command only analyzes SLAB memory allocation. And I'd like to introduce page allocation analysis also. Users can use
--slab and/or --page option to select it. If none of these options
are used, it does slab allocation analysis for backward compatibility.The patch 1-3 are bugfix and cleanups. Patch 4 implements basic support for page allocation analysis, patch 5 deals with the callsite and finally patch 6 implements sorting.
Quoted 7 lines
Quoted 5 lines
In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_freefor analysis as they can track every memory allocation/free path AFAIK. However, unlike slab tracepoint events, those page allocation events don't provide callsite info directly. So I recorded callchains and extracted callsites like below:Really cool features!
Thanks!
Quoted 62 lines
I have a couple of output typography observations:
Quoted 50 lines
Normal page allocation callchains look like this:
360a7e
__alloc_pages_nodemask
3a711calloc_pages_current
357bc7__page_cache_alloc<-- callsite
357cf6pagecache_get_page
48b0aprepare_pages
494d3__btrfs_buffered_write
49cdfbtrfs_file_write_iter
3ceb6enew_sync_write
3cf447vfs_write
3cff99sys_write
7556e9system_callf880 __write_nocancel33eb9
cmd_record
4b38ecmd_kmem
7aa23run_builtin
27a9a main
20800__libc_start_mainBut first two are internal page allocation functions so it should be skipped. To determine such allocation functions, I used following regex:
^_?_?(alloc|get_free|get_zeroed)_pages?
This gave me a following list of functions (you can see this with -v):
alloc func:
__get_free_pages
alloc func:get_zeroed_page
alloc func:alloc_pages_exact
alloc func:__alloc_pages_direct_compact
alloc func:__alloc_pages_nodemask
alloc func:alloc_page_interleave
alloc func:alloc_pages_current
alloc func:alloc_pages_vma
alloc func:alloc_page_buffers
alloc func:alloc_pages_exact_nidAfter skipping those function, it got '
__page_cache_alloc'.Other information such as allocation order, migration type and gfp flags are provided by tracepoint events.
Basically the output will be sorted by total allocation bytes, but you can change it by using -s/--sort option. The following sort keys are added to support page analysis: page, order, mtype, gfp. Existing 'callsite', 'bytes' and 'hit' sort keys also can be used.
An example follows:
Quoted 8 lines
# perf kmem record --slab --page sleep 1 [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]# perf kmem stat --page --caller -l 10 -s order,hit--------------------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsite
Quoted 6 lines
s/Per/Size
s/Hit/Hits
s/Migrate type/Migration type
s/GFP flag/GFP flags?
OK, will change. (They'll spend a bit more column spaces though.)
Quoted 24 lines
Quoted 15 lines
--------------------------------------------------------------------------------------------
65536/16384 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab 51347456/4096 | 12536 | 0 | MOVABLE | 0102005a | __page_cache_alloc 53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one 40960/4096 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault 28672/4096 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault 16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page 16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc 8192/4096 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc ... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
Quoted 8 lines
SUMMARY (page allocator)
========================
Total alloc requested: 12593
Total alloc failure : 0
Total bytes allocated: 51630080
Total free requested: 115
Total free unmatched: 67
Total bytes freed : 471040
Quoted 6 lines
I'd suggest the following changes to the format:
- Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 'freed only', depending on how much of their lifetime we've managed to trace. These groups are really distinct and it makes little sense to mix up their stats.
Good idea. Actually I'm thinking about a new option that shows only lively allocated memory (excluding freed page) in the table. FYI current number is total allocated memory (including freed page).
Quoted 3 lines
- Add commas to the numbers, to make it easier to read and compare larger numbers.
OK
Quoted 3 lines
- Right-align the numbers, to make them easy to compare when they are placed under each other.
OK
Quoted 4 lines
- Merge the 'count' and 'bytes' stats into a single line, so that it's more compact, easier to navigate, but also only comparable type numbers are placed under each other.
OK
Quoted 6 lines
I.e. something like this (mockup) output:
SUMMARY (page allocator)
========================
Quoted 6 lines
Pages allocated+freed: 12,593 [ 51,630,080 bytes ]
Pages allocated-only: 2,342 [ 1,235,010 bytes ]
Pages freed-only: 67 [ 135,311 bytes ]Page allocation failures : 0
Looks a lot better!
One thing I need to tell you is that the numbers are not pages but requests.
Quoted 16 lines
Quoted 4 lines
Order UNMOVABLE RECLAIMABLE MOVABLE RESERVED CMA/ISOLATE
----- ------------ ------------ ------------ ------------ ------------Quoted 11 lines
0 32 0 12557 0 0 1 0 0 0 0 0 2 0 4 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 0 6 0 0 0 0 0 7 0 0 0 0 0 8 0 0 0 0 0 9 0 0 0 0 0 10 0 0 0 0 0
Quoted 4 lines
Here I'd suggest the following refinements:
- Use '.' instead of '0', to make actual nonzero values stand out visually, while still keeping a tabular format
OK
Quoted 4 lines
- Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special'
colum: this will be zero in 99.9% of the cases, as those pages mostly deal with driver interfaces, mostly used during init/deinit.
I'm not sure about the CMA pages..
Quoted 2 lines
- Capitalize less.
OK
Quoted 2 lines
- Use comma-separated numbers for better readability.
OK
Quoted 19 lines
So something like this:
Order Unmovable Reclaimable Movable Special
----- ------------ ------------ ------------ ------------0 32 . 12,557 . 1 . . . . 2 . 4 . . 3 . . . . 4 . . . . 5 . . . . 6 . . . . 7 . . . . 8 . . . . 9 . . . . 10 . . . .
Quoted 2 lines
Look for example how easily noticeable the '4' value is now, while it was pretty easy to miss in the original table.
Indeed!
Quoted 7 lines
Quoted 3 lines
I have some idea how to improve it. But I'd also like to hear other idea, suggestion, feedback and so on.
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
I think it can be done easily as slab analysis already contains the info.
Thanks for your useful feedbacks!
Namhyung
Quoted 10 lines · Namhyung Kim
Quoted 10 lines
I.e. something like this (mockup) output:
SUMMARY (page allocator) ======================== Pages allocated+freed: 12,593 [ 51,630,080 bytes ] Pages allocated-only: 2,342 [ 1,235,010 bytes ] Pages freed-only: 67 [ 135,311 bytes ]
Quoted 6 lines
Quoted 1 line
Page allocation failures : 0
Looks a lot better!
One thing I need to tell you is that the numbers are not pages but requests.
Yes, but in the MM code we tend to call larger order allocations 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K order-1 page. So at least in MM-speak it should be OK to call them 'pages'.
But your call!
Thanks,
Ingo
Namhyung Kim
maintainerHi Ingo,
Quoted 20 lines · Ingo Molnar
Quoted 10 lines · Namhyung Kim
Quoted 10 lines
I.e. something like this (mockup) output:
SUMMARY (page allocator) ======================== Pages allocated+freed: 12,593 [ 51,630,080 bytes ] Pages allocated-only: 2,342 [ 1,235,010 bytes ] Pages freed-only: 67 [ 135,311 bytes ]Quoted 6 lines
Quoted 1 line
Page allocation failures : 0
Looks a lot better!
One thing I need to tell you is that the numbers are not pages but requests.
Quoted 6 lines
Yes, but in the MM code we tend to call larger order allocations 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K order-1 page. So at least in MM-speak it should be OK to call them 'pages'.
But your call!
How about this?
SUMMARY (page allocator)
========================
Total allocation requests : 9,015 [ 37,200 Kbytes ] (A) Total free requests : 8,093 [ 33,176 Kbytes ] (B)
Total alloc+freed requests : 7,985 [ 32,732 Kbytes ] (C) Total alloc-only requests : 1,030 [ 4,468 Kbytes ] (D) Total free-only requests : 108 [ 444 Kbytes ] (E)
Total allocation failure : 0 [ 0 Kbytes ]
(A) = (C) + (D)
(B) = (C) + (E)
Thanks,
Namhyung
Quoted 34 lines · Namhyung Kim
Hi Ingo,
Quoted 20 lines · Ingo Molnar
Quoted 10 lines · Namhyung Kim
Quoted 10 lines
I.e. something like this (mockup) output:
SUMMARY (page allocator) ======================== Pages allocated+freed: 12,593 [ 51,630,080 bytes ] Pages allocated-only: 2,342 [ 1,235,010 bytes ] Pages freed-only: 67 [ 135,311 bytes ]Quoted 6 lines
Quoted 1 line
Page allocation failures : 0
Looks a lot better!
One thing I need to tell you is that the numbers are not pages but requests.
Quoted 6 lines
Yes, but in the MM code we tend to call larger order allocations 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K order-1 page. So at least in MM-speak it should be OK to call them 'pages'.
But your call!
How about this?
SUMMARY (page allocator)
========================
Quoted 8 lines
Total allocation requests : 9,015 [ 37,200 Kbytes ] (A) Total free requests : 8,093 [ 33,176 Kbytes ] (B)Total alloc+freed requests : 7,985 [ 32,732 Kbytes ] (C) Total alloc-only requests : 1,030 [ 4,468 Kbytes ] (D) Total free-only requests : 108 [ 444 Kbytes ] (E)Total allocation failure : 0 [ 0 Kbytes ]
s/failure/failures
s/Kbytes/KB
I'd leave a bit more space for the numbers, for up into billions of requests and terabytes of data. Other than that, sounds good to me!
Thanks,
Ingo
Namhyung Kim
maintainerHi Ingo,
Quoted 40 lines · Ingo Molnar
Quoted 37 lines · Namhyung Kim
Hi Ingo,
Quoted 20 lines · Ingo Molnar
Quoted 10 lines · Namhyung Kim
Quoted 10 lines
I.e. something like this (mockup) output:
SUMMARY (page allocator) ======================== Pages allocated+freed: 12,593 [ 51,630,080 bytes ] Pages allocated-only: 2,342 [ 1,235,010 bytes ] Pages freed-only: 67 [ 135,311 bytes ]Quoted 6 lines
Quoted 1 line
Page allocation failures : 0
Looks a lot better!
One thing I need to tell you is that the numbers are not pages but requests.
Quoted 6 lines
Yes, but in the MM code we tend to call larger order allocations 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K order-1 page. So at least in MM-speak it should be OK to call them 'pages'.
But your call!
How about this?
SUMMARY (page allocator)
========================Total allocation requests : 9,015 [ 37,200 Kbytes ] (A) Total free requests : 8,093 [ 33,176 Kbytes ] (B)
Quoted 8 lines
Quoted 5 lines
Total alloc+freed requests : 7,985 [ 32,732 Kbytes ] (C) Total alloc-only requests : 1,030 [ 4,468 Kbytes ] (D) Total free-only requests : 108 [ 444 Kbytes ] (E)Total allocation failure : 0 [ 0 Kbytes ]
s/failure/failures
s/Kbytes/KB
OK
Quoted 3 lines
I'd leave a bit more space for the numbers, for up into billions of requests and terabytes of data. Other than that, sounds good to me!
I reserved 16 character space for each!
Thanks,
Namhyung
Namhyung Kim
maintainerQuoted 6 lines · Namhyung Kim
Quoted 3 lines · Ingo Molnar
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
I think it can be done easily as slab analysis already contains the info.
Hmm.. it seems slab events provide the node info but page events don't. Without it, I don't know which node a page is in so cannot determine such cross-node alloc+free patterns.
Thanks,
Namhyung
Quoted 11 lines · Namhyung Kim
Quoted 6 lines · Namhyung Kim
Quoted 3 lines · Ingo Molnar
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
I think it can be done easily as slab analysis already contains the info.
Hmm.. it seems slab events provide the node info but page events don't. Without it, I don't know which node a page is in so cannot determine such cross-node alloc+free patterns.
Well, but we know the CPU that the allocation occured on, and can map the CPU to the node.
libnuma can do the mappings, 'man numa' will show you the interfaces, I think you'll need numa_node_of_cpu().
numalib needs no initialization, and 'perf bench numa' already links to it - so I think you should be fine just doing:
#include <numa.h> #include <numaif.h>
and using the API straight away.
Thanks,
Ingo
Namhyung Kim
maintainerHi Ingo,
Quoted 9 lines · Ingo Molnar
Quoted 6 lines · Namhyung Kim
Quoted 5 lines · Namhyung Kim
Quoted 3 lines · Ingo Molnar
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
I think it can be done easily as slab analysis already contains the info.
Hmm.. it seems slab events provide the node info but page events don't. Without it, I don't know which node a page is in so cannot determine such cross-node alloc+free patterns.
Well, but we know the CPU that the allocation occured on, and can map the CPU to the node.
Does it gaurantee that the page always belongs to a same node allocating cpu is in? I can simply map the page with an node using cpu info but was not sure whether that's always correct.
Thanks,
Namhyung
Quoted 18 lines · Namhyung Kim
Hi Ingo,
Quoted 15 lines · Ingo Molnar
Quoted 11 lines · Namhyung Kim
Quoted 6 lines · Namhyung Kim
Quoted 3 lines · Ingo Molnar
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
I think it can be done easily as slab analysis already contains the info.
Hmm.. it seems slab events provide the node info but page events don't. Without it, I don't know which node a page is in so cannot determine such cross-node alloc+free patterns.
Quoted 6 lines
Quoted 2 lines
Well, but we know the CPU that the allocation occured on, and can map the CPU to the node.
Does it gaurantee that the page always belongs to a same node allocating cpu is in? I can simply map the page with an node using cpu info but was not sure whether that's always correct.
There's no such guarantee :-(
We try to (and generally we succeed in) allocating on the same node, but tooling really needs to know about the memory pressure and other corner cases as well to be dependable.
I suspect we need a new trace_mm_page_alloc tracepoint, and a CONFIG_DEBUG_MM_TRACE_PAGE_COMPAT setting to phase out the old tracepoint on new distros?
Then we'd also have to track page migration to be able to track the full lifetime of a node in tooling: I think most of the tracepoints are in place, but it has to be checked whether coverage is comprehensive.
Thanks,
Ingo
Arnaldo Carvalho de Melo
maintainerEm Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar escreveu:
Quoted 13 lines · Namhyung Kim
Quoted 12 lines
Hello,
Currently perf kmem command only analyzes SLAB memory allocation. And I'd like to introduce page allocation analysis also. Users can use
--slab and/or --page option to select it. If none of these options
are used, it does slab allocation analysis for backward compatibility.The patch 1-3 are bugfix and cleanups. Patch 4 implements basic support for page allocation analysis, patch 5 deals with the callsite and finally patch 6 implements sorting.
Quoted 7 lines
Quoted 5 lines
In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_freefor analysis as they can track every memory allocation/free path AFAIK. However, unlike slab tracepoint events, those page allocation events don't provide callsite info directly. So I recorded callchains and extracted callsites like below:Really cool features!
Yeah, indeed.
I cherry picked the first three, will push to Ingo soon.
- Arnaldo
Quoted 179 lines
I have a couple of output typography observations:
Quoted 50 lines
Normal page allocation callchains look like this:
360a7e
__alloc_pages_nodemask
3a711calloc_pages_current
357bc7__page_cache_alloc<-- callsite
357cf6pagecache_get_page
48b0aprepare_pages
494d3__btrfs_buffered_write
49cdfbtrfs_file_write_iter
3ceb6enew_sync_write
3cf447vfs_write
3cff99sys_write
7556e9system_callf880 __write_nocancel33eb9
cmd_record
4b38ecmd_kmem
7aa23run_builtin
27a9a main
20800__libc_start_mainBut first two are internal page allocation functions so it should be skipped. To determine such allocation functions, I used following regex:
^_?_?(alloc|get_free|get_zeroed)_pages?
This gave me a following list of functions (you can see this with -v):
alloc func:
__get_free_pages
alloc func:get_zeroed_page
alloc func:alloc_pages_exact
alloc func:__alloc_pages_direct_compact
alloc func:__alloc_pages_nodemask
alloc func:alloc_page_interleave
alloc func:alloc_pages_current
alloc func:alloc_pages_vma
alloc func:alloc_page_buffers
alloc func:alloc_pages_exact_nidAfter skipping those function, it got '
__page_cache_alloc'.Other information such as allocation order, migration type and gfp flags are provided by tracepoint events.
Basically the output will be sorted by total allocation bytes, but you can change it by using -s/--sort option. The following sort keys are added to support page analysis: page, order, mtype, gfp. Existing 'callsite', 'bytes' and 'hit' sort keys also can be used.
An example follows:
Quoted 8 lines
# perf kmem record --slab --page sleep 1 [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]# perf kmem stat --page --caller -l 10 -s order,hit--------------------------------------------------------------------------------------------
Total_alloc/Per | Hit | Order | Migrate type | GFP flag | Callsites/Per/Size
s/Hit/Hits
s/Migrate type/Migration type
s/GFP flag/GFP flags?
Quoted 14 lines
--------------------------------------------------------------------------------------------
65536/16384 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab 51347456/4096 | 12536 | 0 | MOVABLE | 0102005a | __page_cache_alloc 53248/4096 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one 40960/4096 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault 28672/4096 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_wp_page 20480/4096 | 5 | 0 | MOVABLE | 000200da | do_cow_fault 16384/4096 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page 16384/4096 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc 8192/4096 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc ... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
Quoted 8 lines
SUMMARY (page allocator)
========================
Total alloc requested: 12593
Total alloc failure : 0
Total bytes allocated: 51630080
Total free requested: 115
Total free unmatched: 67
Total bytes freed : 471040I'd suggest the following changes to the format:
- Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 'freed only', depending on how much of their lifetime we've managed to trace. These groups are really distinct and it makes little sense to mix up their stats.
- Add commas to the numbers, to make it easier to read and compare larger numbers.
- Right-align the numbers, to make them easy to compare when they are placed under each other.
- Merge the 'count' and 'bytes' stats into a single line, so that it's more compact, easier to navigate, but also only comparable type numbers are placed under each other.
I.e. something like this (mockup) output:
SUMMARY (page allocator)
========================Pages allocated+freed: 12,593 [ 51,630,080 bytes ]
Pages allocated-only: 2,342 [ 1,235,010 bytes ]
Pages freed-only: 67 [ 135,311 bytes ]Page allocation failures : 0
Quoted 13 lines
Order UNMOVABLE RECLAIMABLE MOVABLE RESERVED CMA/ISOLATE
----- ------------ ------------ ------------ ------------ ------------0 32 0 12557 0 0 1 0 0 0 0 0 2 0 4 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 0 6 0 0 0 0 0 7 0 0 0 0 0 8 0 0 0 0 0 9 0 0 0 0 0 10 0 0 0 0 0Here I'd suggest the following refinements:
- Use '.' instead of '0', to make actual nonzero values stand out visually, while still keeping a tabular format
- Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special'
colum: this will be zero in 99.9% of the cases, as those pages mostly deal with driver interfaces, mostly used during init/deinit.- Capitalize less.
- Use comma-separated numbers for better readability.
So something like this:
Order Unmovable Reclaimable Movable Special
----- ------------ ------------ ------------ ------------0 32 . 12,557 . 1 . . . . 2 . 4 . . 3 . . . . 4 . . . . 5 . . . . 6 . . . . 7 . . . . 8 . . . . 9 . . . . 10 . . . .Look for example how easily noticeable the '4' value is now, while it was pretty easy to miss in the original table.
Quoted 2 lines
I have some idea how to improve it. But I'd also like to hear other idea, suggestion, feedback and so on.
So there's one thing that would be useful: to track pages allocated on one node, but freed on another. Those kinds of allocation/free patterns are especially expensive and might make sense to visualize.
Thanks,
Ingo