perf kmem: Implement page allocation analysis (v3)
Subsystems (2)
Namhyung Kim
maintainercover perf kmem: Implement page allocation analysis (v3)
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.
* changes in v3)
- add live page statistics
* changes in v2)
- Use thousand grouping for big numbers - i.e. 12345 -> 12,345 (Ingo)
- Improve output stat readability (Ingo)
- Remove alloc size column as it can be calculated from hits and order
Patch 1 is to support thousand grouping on stat output. Patch 2 implements basic support for page allocation analysis, patch 3 deals with the callsite and finally patch 4 implements sorting.
In this patchset, I used two kmem events: kmem:mm_page_alloc and kmem_page_free for analysis as they can track almost all of 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 (KB) | Hits | Order | Migration type | GFP flags | Callsite
--------------------------------------------------------------------------------------------
64 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab
50,144 | 12,536 | 0 | MOVABLE | 0102005a | __page_cache_alloc
52 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
40 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault
28 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait
20 | 5 | 0 | MOVABLE | 000200da | do_wp_page
20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
16 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
8 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total allocation requests : 12,594 [ 50,420 KB ] Total free requests : 182 [ 728 KB ]
Total alloc+freed requests : 115 [ 460 KB ] Total alloc-only requests : 12,479 [ 49,960 KB ] Total free-only requests : 67 [ 268 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 32 . 12,558 . .
1 . . . . .
2 . 4 . . .
3 . . . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .I 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-v3 branch on my tree:
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Thanks,
Namhyung
Namhyung Kim (5):
perf kmem: Print big numbers using thousands' group perf kmem: Analyze page allocator events also perf kmem: Implement stat --page --caller
perf kmem: Support sort keys on page analysis perf kmem: Add --live option for current allocation stat
tools/perf/Documentation/perf-kmem.txt | 19 +- tools/perf/builtin-kmem.c | 1065 ++++++++++++++++++++++++++++++-- 2 files changed, 1022 insertions(+), 62 deletions(-)
Signature · 1 line
2.3.3
Namhyung Kim
maintainerPATCH 1/5 perf kmem: Print big numbers using thousands' group
Like perf stat, this makes easy to read the numbers on stat like below:
# perf kmem stat
SUMMARY
=======
Total bytes requested: 9,770,900
Total bytes allocated: 9,782,712
Total bytes wasted on internal fragmentation: 11,812 Internal fragmentation: 0.120744%
Cross CPU allocations: 74/152,819
--- tools/perf/builtin-kmem.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
tools/perf/builtin-kmem.c +7 -4
@@ -20,6 +20,7 @@#include <linux/rbtree.h> #include <linux/string.h> +#include <locale.h> struct alloc_stat ;typedef int (*sort_fn_t )(struct alloc_stat *,struct alloc_stat *);
@@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session,static void print_summary (void ) {printf ("\nSUMMARY\n=======\n" ); -printf ("Total bytes requested: %lu\n" , total_requested); -printf ("Total bytes allocated: %lu\n" , total_allocated); -printf ("Total bytes wasted on internal fragmentation: %lu\n" , +printf ("Total bytes requested: %'lu\n" , total_requested); +printf ("Total bytes allocated: %'lu\n" , total_allocated); +printf ("Total bytes wasted on internal fragmentation: %'lu\n" , total_allocated - total_requested);printf ("Internal fragmentation: %f%%\n" ,fragmentation (total_requested, total_allocated)); -printf ("Cross CPU allocations: %lu/%lu\n" , nr_cross_allocs, nr_allocs); +printf ("Cross CPU allocations: %'lu/%'lu\n" , nr_cross_allocs, nr_allocs); }static void print_result (struct perf_session *session)
@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)symbol__init (&session->header.env);if (!strcmp (argv[0 ],"stat" )) { +setlocale (LC_ALL ,"" ); +if (cpu__setup_cpunode_map ()) goto out_delete;
Signature · 1 line
2.3.3
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 1/5] perf kmem: Print big numbers using thousands' group
Em Mon, Mar 23, 2015 at 03:30:40PM +0900, Namhyung Kim escreveu:
Quoted 16 lines
Like perf stat, this makes easy to read the numbers on stat like below:
# perf kmem statSUMMARY
=======
Total bytes requested: 9,770,900
Total bytes allocated: 9,782,712
Total bytes wasted on internal fragmentation: 11,812 Internal fragmentation: 0.120744%
Cross CPU allocations: 74/152,819Suggested-by Ingo Molnar <[email protected]>Signed-off-by Namhyung Kim <[email protected]>static void
print_result(struct perf_session *session)
tools/perf/builtin-kmem.c int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) · line 711
Quoted 5 lines
tools/perf/builtin-kmem.c +2
-0@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)symbol__init (&session->header.env);if (!strcmp (argv[0 ],"stat" )) { +setlocale (LC_ALL ,"" ); +
Applying, but I think it is better to have this call in perf's main() routine, to avoid repeating it in each tool, as 'builtin-stat' already does this, but as this affects all tools, some further testing is needed, I think, or does anyone see any problem with that?
- Arnaldo
Namhyung Kim
maintainerRe: [PATCH 1/5] perf kmem: Print big numbers using thousands' group
Hi Arnaldo,
Quoted 19 lines · Arnaldo Carvalho de Melo
Em Mon, Mar 23, 2015 at 03:30:40PM +0900, Namhyung Kim escreveu:
Quoted 18 lines
Like perf stat, this makes easy to read the numbers on stat like below:
# perf kmem statSUMMARY
=======
Total bytes requested: 9,770,900
Total bytes allocated: 9,782,712
Total bytes wasted on internal fragmentation: 11,812 Internal fragmentation: 0.120744%
Cross CPU allocations: 74/152,819Suggested-by Ingo Molnar <[email protected]>Signed-off-by Namhyung Kim <[email protected]>static void
print_result(struct perf_session *session)tools/perf/builtin-kmem.c +0
-0@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)symbol__init (&session->header.env);
Quoted 8 lines
tools/perf/builtin-kmem.c int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) · line 711
Quoted 3 lines
tools/perf/builtin-kmem.c +2
-0if (!strcmp (argv[0 ],"stat" )) { +setlocale (LC_ALL ,"" ); +Applying, but I think it is better to have this call in perf's
main()routine, to avoid repeating it in each tool, as 'builtin-stat' already does this, but as this affects all tools, some further testing is needed, I think, or does anyone see any problem with that?
I don't see any explicit problem with it, but as its effect can be subtle I agree that we need further testing..
Thanks,
Namhyung
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf kmem: Print big numbers using thousands' group
In reply to Namhyung Kimperf kmem: Print big numbers using thousands' group
Commit-ID: 77cfe388767572586b7d4fd533c38f902d020f17 Gitweb: http://git.kernel.org/tip/77cfe388767572586b7d4fd533c38f902d020f17 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 23 Mar 2015 15:30:40 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Tue, 24 Mar 2015 12:07:06 -0300
perf kmem: Print big numbers using thousands' group
Like perf stat, this makes easy to read the numbers on stat like below:
# perf kmem stat
SUMMARY
=======
Total bytes requested: 9,770,900
Total bytes allocated: 9,782,712
Total bytes wasted on internal fragmentation: 11,812 Internal fragmentation: 0.120744%
Cross CPU allocations: 74/152,819
--- tools/perf/builtin-kmem.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
tools/perf/builtin-kmem.c +7 -4
@@ -20,6 +20,7 @@#include <linux/rbtree.h> #include <linux/string.h> +#include <locale.h> struct alloc_stat ;typedef int (*sort_fn_t )(struct alloc_stat *,struct alloc_stat *);
@@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session,static void print_summary (void ) {printf ("\nSUMMARY\n=======\n" ); -printf ("Total bytes requested: %lu\n" , total_requested); -printf ("Total bytes allocated: %lu\n" , total_allocated); -printf ("Total bytes wasted on internal fragmentation: %lu\n" , +printf ("Total bytes requested: %'lu\n" , total_requested); +printf ("Total bytes allocated: %'lu\n" , total_allocated); +printf ("Total bytes wasted on internal fragmentation: %'lu\n" , total_allocated - total_requested);printf ("Internal fragmentation: %f%%\n" ,fragmentation (total_requested, total_allocated)); -printf ("Cross CPU allocations: %lu/%lu\n" , nr_cross_allocs, nr_allocs); +printf ("Cross CPU allocations: %'lu/%'lu\n" , nr_cross_allocs, nr_allocs); }static void print_result (struct perf_session *session)
@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)symbol__init (&session->header.env);if (!strcmp (argv[0 ],"stat" )) { +setlocale (LC_ALL ,"" ); +if (cpu__setup_cpunode_map ()) goto out_delete;
Namhyung Kim
maintainerPATCH 2/5 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------
ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total allocation requests : 44,260 [ 177,256 KB ] Total free requests : 117 [ 468 KB ]
Total alloc+freed requests : 49 [ 196 KB ] Total alloc-only requests : 44,211 [ 177,060 KB ] Total free-only requests : 68 [ 272 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 32 . 44,210 . .
1 . . . . .
2 . 18 . . .
3 . . . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .--- tools/perf/Documentation/perf-kmem.txt | 8 +- tools/perf/builtin-kmem.c | 376 +++++++++++++++++++++++++++++++-- 2 files changed, 368 insertions(+), 16 deletions(-)
tools/perf/Documentation/perf-kmem.txt +7 -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,12 @@ OPTIONS --raw-ip:: Print raw ip instead of symbol +--slab:: + Analyze SLAB allocator events. + +--page:: + Analyze page allocator events + SEE ALSO -------- linkperf:perf-record[1]
tools/perf/builtin-kmem.c +361 -15
491 lines not on this page · raw message
Signature · 1 line
2.3.3
Re: [PATCH 2/5] perf kmem: Analyze page allocator events also
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 28 lines
The 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250
Quoted 2 lines
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------
Tracepoint on mm_page_alloc print out pfn as well as pointer of struct page. How about printing pfn rather than pointer of struct page?
Thanks.
Namhyung Kim
maintainerRe: [PATCH 2/5] perf kmem: Analyze page allocator events also
Quoted 32 lines · Joonsoo Kim
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 28 lines
The 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250Quoted 2 lines
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------
Quoted 2 lines
Tracepoint on
mm_page_allocprint out pfn as well as pointer of struct page. How about printing pfn rather than pointer of struct page?
I'd really like to have pfn rather than struct page. But I don't know how to convert page pointer to pfn in userspace.
The output of tracepoint via $debugfs/tracing/trace file is generated from kernel-side, so it can easily have pfn from page pointer. But tracepoint itself only saves page pointer and we need to convert/print it in userspace.
Yes, perf script (or libtraceevent) shows pfn when printing those events. But that's bogus since it cannot determine the size of the struct page so the pointer arithmetic in open-coded page_to_pfn() which is saved in the print_fmt of the tracepoint will end up with an normal integer arithmatic.
Do I miss something?
Thanks,
Namhyung
Re: [PATCH 2/5] perf kmem: Analyze page allocator events also
2015-03-24 9:18 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 36 lines · Joonsoo Kim
Quoted 32 lines
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 28 lines
The 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250Quoted 2 lines
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------Quoted 2 lines
Tracepoint on
mm_page_allocprint out pfn as well as pointer of struct page. How about printing pfn rather than pointer of struct page?
Quoted 7 lines
I'd really like to have pfn rather than struct page. But I don't know how to convert page pointer to pfn in userspace.
The output of tracepoint via $debugfs/tracing/trace file is generated from kernel-side, so it can easily have pfn from page pointer. But tracepoint itself only saves page pointer and we need to convert/print it in userspace.
Ah...I didn't realize that perf don't use output of $debugfs/tracing/trace file. So, perf just uses raw trace buffer directly? If pfn is saved to the trace buffer, perf can print pfn rather than pointer of struct page?
Quoted 5 lines
Yes, perf script (or libtraceevent) shows pfn when printing those events. But that's bogus since it cannot determine the size of the struct page so the pointer arithmetic in open-coded
page_to_pfn()which is saved in theprint_fmtof the tracepoint will end up with an normal integer arithmatic.
How about following change and making 'perf kmem' print pfn? If we store pfn on the trace buffer, we can print $debugfs/tracing/trace as is and 'perf kmem' can also print pfn.
Thanks.
include/trace/events/kmem.h +4 -4
@@ -199,22 +199,22 @@ TRACE_EVENT(mm_page_alloc, TP_ARGS(page, order, gfp_flags, migratetype),TP_STRUCT__entry ( -__field (struct page *,page ) +__field (unsigned long ,pfn ) __field( unsigned int, order )__field ( gfp_t, gfp_flags )__field ( int, migratetype ) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page ? page_to_pfn(page) : -1; __entry->order = order; __entry->gfp_flags = gfp_flags; __entry->migratetype = migratetype; ),TP_printk ("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s" , - __entry->page, - __entry->page ?page_to_pfn (__entry->page) :0 , + __entry->pfn !=-1 ?pfn_to_page (__entry->pfn) :NULL , + __entry->pfn !=-1 ? __entry->pfn :0 , __entry->order, __entry->migratetype,show_gfp_flags (__entry->gfp_flags))
Namhyung Kim
maintainerRe: [PATCH 2/5] perf kmem: Analyze page allocator events also
Hi Joonsoo,
Quoted 40 lines · Joonsoo Kim
2015-03-24 9:18 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 39 lines · Joonsoo Kim
Quoted 32 lines
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 28 lines
The 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250Quoted 2 lines
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------Quoted 2 lines
Tracepoint on
mm_page_allocprint out pfn as well as pointer of struct page. How about printing pfn rather than pointer of struct page?I'd really like to have pfn rather than struct page. But I don't know how to convert page pointer to pfn in userspace.
Quoted 8 lines
Quoted 4 lines
The output of tracepoint via $debugfs/tracing/trace file is generated from kernel-side, so it can easily have pfn from page pointer. But tracepoint itself only saves page pointer and we need to convert/print it in userspace.
Ah...I didn't realize that perf don't use output of $debugfs/tracing/trace file. So, perf just uses raw trace buffer directly? If pfn is saved to the trace buffer, perf can print pfn rather than pointer of struct page?
Yes, perf uses raw (binary) trace data.. If the tracepoint saves pfn directly it would be nice!
Quoted 10 lines
Quoted 6 lines
Yes, perf script (or libtraceevent) shows pfn when printing those events. But that's bogus since it cannot determine the size of the struct page so the pointer arithmetic in open-coded
page_to_pfn()which is saved in theprint_fmtof the tracepoint will end up with an normal integer arithmatic.How about following change and making 'perf kmem' print pfn? If we store pfn on the trace buffer, we can print $debugfs/tracing/trace as is and 'perf kmem' can also print pfn.
I'm very happy with this change. The textual output via debugfs/tracefs will be same, binary data will be changed but libtraceevent will handle it seamlessly.
Thanks,
Namhyung
Quoted 37 lines
include/trace/events/kmem.h +4
-5@@ -199,22 +199,22 @@ TRACE_EVENT(mm_page_alloc, TP_ARGS(page, order, gfp_flags, migratetype),TP_STRUCT__entry ( - __field( struct page *, page ) +__field (unsigned long ,pfn ) __field( unsigned int, order )__field ( gfp_t, gfp_flags )__field ( int, migratetype ) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page ? page_to_pfn(page) : -1; __entry->order = order; __entry->gfp_flags = gfp_flags; __entry->migratetype = migratetype; ),TP_printk ("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s" , - __entry->page, - __entry->page ?page_to_pfn (__entry->page) :0 , + __entry->pfn !=-1 ?pfn_to_page (__entry->pfn) :NULL , + __entry->pfn !=-1 ? __entry->pfn :0 , __entry->order, __entry->migratetype,show_gfp_flags (__entry->gfp_flags)) --To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Ingo Molnar
Acked-byRe: [PATCH 2/5] perf kmem: Analyze page allocator events also
In reply to Joonsoo KimQuoted 82 lines · Joonsoo Kim
2015-03-24 9:18 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 36 lines · Joonsoo Kim
Quoted 32 lines
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 28 lines
The 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 (KB) | Hits | Order | Migration type | GFP flags
-------------------------------------------------------------------------------------ffffea0015e48e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea0015e47400 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440f600 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea001440cc00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c6300 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5c00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c5000 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4f00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4e00 | 16 | 1 | 2 | RECLAIMABLE | 00285250 ffffea00140c4d00 | 16 | 1 | 2 | RECLAIMABLE | 00285250Quoted 2 lines
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------------Quoted 2 lines
Tracepoint on
mm_page_allocprint out pfn as well as pointer of struct page. How about printing pfn rather than pointer of struct page?Quoted 7 lines
I'd really like to have pfn rather than struct page. But I don't know how to convert page pointer to pfn in userspace.
The output of tracepoint via $debugfs/tracing/trace file is generated from kernel-side, so it can easily have pfn from page pointer. But tracepoint itself only saves page pointer and we need to convert/print it in userspace.
Ah...I didn't realize that perf don't use output of $debugfs/tracing/trace file. So, perf just uses raw trace buffer directly? If pfn is saved to the trace buffer, perf can print pfn rather than pointer of struct page?
Quoted 5 lines
Yes, perf script (or libtraceevent) shows pfn when printing those events. But that's bogus since it cannot determine the size of the struct page so the pointer arithmetic in open-coded
page_to_pfn()which is saved in theprint_fmtof the tracepoint will end up with an normal integer arithmatic.How about following change and making 'perf kmem' print pfn? If we store pfn on the trace buffer, we can print $debugfs/tracing/trace as is and 'perf kmem' can also print pfn.
Thanks.
include/trace/events/kmem.h +2
-2@@ -199,22 +199,22 @@ TRACE_EVENT(mm_page_alloc, TP_ARGS(page, order, gfp_flags, migratetype),TP_STRUCT__entry ( -__field (struct page *,page ) +__field (unsigned long ,pfn ) __field( unsigned int, order )__field ( gfp_t, gfp_flags )__field ( int, migratetype ) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page ? page_to_pfn(page) : -1; __entry->order = order; __entry->gfp_flags = gfp_flags; __entry->migratetype = migratetype;
include/trace/events/kmem.h TRACE_EVENT(mm_page_alloc, · line 220
Quoted 10 lines
include/trace/events/kmem.h +2
-2),TP_printk ("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s" , - __entry->page, - __entry->page ?page_to_pfn (__entry->page) :0 , + __entry->pfn !=-1 ?pfn_to_page (__entry->pfn) :NULL , + __entry->pfn !=-1 ? __entry->pfn :0 , __entry->order, __entry->migratetype,show_gfp_flags (__entry->gfp_flags))
It would be very nice to make all the other page granular tracepoints output pfn (which is a physical address that can be resolved to 'node' and other properties), not 'struct page *' (which is a kernel resource with little meaning to user-space tooling).
I.e. the following tracepoints:
triton:~/tip> git grep -E '__field.*struct page *' include/trace/
include/trace/events/filemap.h: __field(struct page *, page) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/pagemap.h: __field(struct page *, page ) include/trace/events/pagemap.h: __field(struct page *, page ) include/trace/events/vmscan.h: __field(struct page *, page)
there's very little breakage I can imagine: they have traced pointers to 'struct page', which is a pretty opaque page identifier to user-space, and they'll trace pfn's in the future, which still serves as a page identifier.
One thing would be important: to do all these changes at once, to make sure that the various page identifiers can be compared.
Also, we might keep the 'page' field name if anything relies on that - but 'pfn' is even better.
Thanks,
Ingo
Namhyung Kim
maintainerRe: [PATCH 2/5] perf kmem: Analyze page allocator events also
Hi Ingo,
Quoted 49 lines · Ingo Molnar
Quoted 27 lines · Joonsoo Kim
How about following change and making 'perf kmem' print pfn? If we store pfn on the trace buffer, we can print $debugfs/tracing/trace as is and 'perf kmem' can also print pfn.
Thanks.
include/trace/events/kmem.h +2
-2@@ -199,22 +199,22 @@ TRACE_EVENT(mm_page_alloc, TP_ARGS(page, order, gfp_flags, migratetype),TP_STRUCT__entry ( -__field (struct page *,page ) +__field (unsigned long ,pfn ) __field( unsigned int, order )__field ( gfp_t, gfp_flags )__field ( int, migratetype ) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page ? page_to_pfn(page) : -1; __entry->order = order; __entry->gfp_flags = gfp_flags; __entry->migratetype = migratetype;include/trace/events/kmem.h TRACE_EVENT(mm_page_alloc, · line 220
Quoted 10 lines
include/trace/events/kmem.h +2
-2),TP_printk ("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s" , - __entry->page, - __entry->page ?page_to_pfn (__entry->page) :0 , + __entry->pfn !=-1 ?pfn_to_page (__entry->pfn) :NULL , + __entry->pfn !=-1 ? __entry->pfn :0 , __entry->order, __entry->migratetype,show_gfp_flags (__entry->gfp_flags))Acked-by Ingo Molnar <[email protected]>It would be very nice to make all the other page granular tracepoints output pfn (which is a physical address that can be resolved to 'node' and other properties), not 'struct page *' (which is a kernel resource with little meaning to user-space tooling).
I.e. the following tracepoints:
triton:~/tip> git grep -E '__field.*struct page *'
include/trace/
Quoted 9 lines
include/trace/events/filemap.h: __field(struct page *, page) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/kmem.h: __field( struct page *, page ) include/trace/events/pagemap.h: __field(struct page *, page ) include/trace/events/pagemap.h: __field(struct page *, page ) include/trace/events/vmscan.h: __field(struct page *, page)
Okay, will do.
Quoted 5 lines
there's very little breakage I can imagine: they have traced pointers to 'struct page', which is a pretty opaque page identifier to user-space, and they'll trace pfn's in the future, which still serves as a page identifier.
Agreed.
Quoted 3 lines
One thing would be important: to do all these changes at once, to make sure that the various page identifiers can be compared.
OK
Quoted 3 lines
Also, we might keep the 'page' field name if anything relies on that - but 'pfn' is even better.
Another option is to keep page field and add new pfn field. The events in pagemap.h already do this way. This will minimize the possible breakage but increase the trace size somewhat.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 3/5 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 (KB) | Hits | Order | Migration type | GFP flags | Callsite
---------------------------------------------------------------------------------------------
1,064 | 266 | 0 | UNMOVABLE | 000000d0 | __pollwait
52 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
44 | 11 | 0 | MOVABLE | 000280da | handle_mm_fault
20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
20 | 5 | 0 | MOVABLE | 000200da | do_wp_page
16 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
12 | 3 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
8 | 2 | 0 | UNMOVABLE | 00000010 | bio_copy_user_iov
4 | 1 | 0 | UNMOVABLE | 000200d2 | pipe_write
4 | 1 | 0 | MOVABLE | 000280da | do_wp_page
4 | 1 | 0 | UNMOVABLE | 002084d0 | pgd_alloc---------------------------------------------------------------------------------------------
--- tools/perf/builtin-kmem.c | 298 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 274 insertions(+), 24 deletions(-)
tools/perf/builtin-kmem.c +274 -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"
@@ -21,6 +22,7 @@#include <linux/rbtree.h> #include <linux/string.h> #include <locale.h> +#include <regex.h> static int kmem_slab;static int kmem_page;
@@ -240,6 +242,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
@@ -248,6 +252,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;
@@ -257,12 +262,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 struct page_stat *search_page_stat (unsigned long page,bool create) +static int build_alloc_func_list (void ) { -struct rb_node **node = &page_tree.rb_node; +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); + +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_alloc_tree.rb_node;struct rb_node *parent =NULL ;struct page_stat *data;
@@ -289,7 +424,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;
@@ -304,6 +474,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 ) {
@@ -313,22 +484,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]++;
@@ -347,7 +535,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);
@@ -361,6 +549,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 ; }
@@ -471,35 +665,83 @@ 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%.86s\n" , graph_dotted_line); -printf (" Page | Total alloc (KB) | Hits | Order | Migration type | GFP flags\n" ); -printf ("%.86s\n" , graph_dotted_line); +printf ("\n%.105s\n" , graph_dotted_line); +printf (" Page | Total alloc (KB) | Hits | Order | Migration type | GFP flags | Callsite\n" ); +printf ("%.105s\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 | %'16llu | %'9d | %5d | %14s | %08lx\n" , +printf (" %016llx | %'16llu | %'9d | %5d | %14s | %08lx | %s\n" , (unsigned long long )data->page, (unsigned long long )data->alloc_bytes /1024 , 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 (" ... | ... | ... | ... | ... | ... | ...\n" ); -printf ("%.86s\n" , graph_dotted_line); +printf ("%.105s\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%.105s\n" , graph_dotted_line); +printf (" Total alloc (KB) | Hits | Order | Migration type | GFP flags | Callsite\n" ); +printf ("%.105s\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 (" %'16llu | %'9d | %5d | %14s | %08lx | %s\n" , + (unsigned long long )data->alloc_bytes /1024 , + 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 ("%.105s\n" , graph_dotted_line); }static void print_slab_summary (void )
@@ -571,8 +813,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 (); }
@@ -690,7 +934,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); } }
@@ -720,8 +965,10 @@ static int __cmd_kmem(struct perf_session *session)setup_pager (); err =perf_session__process_events (session); -if (err !=0 ) +if (err !=0 ) { +pr_err ("error during process events: %d\n" , err); goto out; + }sort_result ();print_result (session); out:
@@ -962,7 +1209,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 *));
@@ -977,6 +1224,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]); }
@@ -1035,7 +1284,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 ;
@@ -1048,6 +1297,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.3
Namhyung Kim
maintainerPATCH 4/5 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 (KB) | Hits | Order | Migration type | GFP flags | Callsite
--------------------------------------------------------------------------------------------
64 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab
50,144 | 12,536 | 0 | MOVABLE | 0102005a | __page_cache_alloc
52 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
40 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault
28 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait
20 | 5 | 0 | MOVABLE | 000200da | do_wp_page
20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
16 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc
8 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc
... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
--- tools/perf/Documentation/perf-kmem.txt | 6 +- tools/perf/builtin-kmem.c | 399 +++++++++++++++++++++++++++------ 2 files changed, 336 insertions(+), 69 deletions(-)
tools/perf/Documentation/perf-kmem.txt +5 -1
@@ -37,7 +37,11 @@ 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. This option should be preceded by one of the + mode selection options - i.e. --slab, --page, --alloc and/or --caller. -l <num>:: --line=<num>::
tools/perf/builtin-kmem.c +331 -68
642 lines not on this page · raw message
Signature · 1 line
2.3.3
Re: [PATCH 4/5] perf kmem: Support sort keys on page analysis
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 8 lines
Add 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.
How about storing/printing pid, too?
It would be helpful to distinguish who allocates the page.
Thanks.
Namhyung Kim
maintainerRe: [PATCH 4/5] perf kmem: Support sort keys on page analysis
Quoted 6 lines · Joonsoo Kim
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 5 lines
Add 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.
Quoted 6 lines
Quoted 3 lines
Now it properly groups and sorts allocation stats - so same page/caller with different order/mtype/gfp will be printed on a different line.
How about storing/printing pid, too?
It would be helpful to distinguish who allocates the page.
That's also in my TODO list. :)
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 5/5 perf kmem: Add --live option for current allocation stat
In reply to Namhyung KimCurrently perf kmem 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.
--- tools/perf/Documentation/perf-kmem.txt | 5 ++ tools/perf/builtin-kmem.c | 101 ++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 9 deletions(-)
tools/perf/Documentation/perf-kmem.txt +5 -0
@@ -56,6 +56,11 @@ OPTIONS --page:: 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 -------- linkperf:perf-record[1]
tools/perf/builtin-kmem.c +92 -9
179 lines not on this page · raw message
Signature · 1 line
2.3.3
Hello, Namhyung.
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 63 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.* changes in v3)
- add live page statistics* changes in v2)
- Use thousand grouping for big numbers - i.e. 12345 -> 12,345 (Ingo)
- Improve output stat readability (Ingo)
- Remove alloc size column as it can be calculated from hits and orderPatch 1 is to support thousand grouping on stat output. Patch 2 implements basic support for page allocation analysis, patch 3 deals with the callsite and finally patch 4 implements sorting.
In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_freefor analysis as they can track almost all of 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
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_nid
Quoted 1 line
After skipping those function, it got '
__page_cache_alloc'.
It'd be better to have option for storing more depth of call stack. Just one call path isn't sufficient to distinguish real caller for some functions. For example, new_slab(), one of your callsite example doesn't tell which subsystem try to allocate slab object and fall through the page allocator.
Quoted 30 lines
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 (KB) | Hits | Order | Migration type | GFP flags | Callsite
--------------------------------------------------------------------------------------------64 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab 50,144 | 12,536 | 0 | MOVABLE | 0102005a | __page_cache_alloc 52 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one 40 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault 28 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait 20 | 5 | 0 | MOVABLE | 000200da | do_wp_page 20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault 16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page 16 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc 8 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc ... | ... | ... | ... | ... | ...
Quoted 1 line
--------------------------------------------------------------------------------------------
How about printing GFP flags more intuitively, for example, GFP_NOFS|GFP_ZERO? Tracepoint on mm_page_alloc already print output as this format.
Thanks.
Namhyung Kim
maintainerHi Joonsoo,
Quoted 66 lines · Joonsoo Kim
Hello, Namhyung.
2015-03-23 15:30 GMT+09:00 Namhyung Kim <[email protected]>:
Quoted 63 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.* changes in v3)
- add live page statistics* changes in v2)
- Use thousand grouping for big numbers - i.e. 12345 -> 12,345 (Ingo)
- Improve output stat readability (Ingo)
- Remove alloc size column as it can be calculated from hits and orderPatch 1 is to support thousand grouping on stat output. Patch 2 implements basic support for page allocation analysis, patch 3 deals with the callsite and finally patch 4 implements sorting.
In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_freefor analysis as they can track almost all of 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
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_nid
Quoted 7 lines
Quoted 1 line
After skipping those function, it got '
__page_cache_alloc'.It'd be better to have option for storing more depth of call stack. Just one call path isn't sufficient to distinguish real caller for some functions. For example,
new_slab(), one of your callsite example doesn't tell which subsystem try to allocate slab object and fall through the page allocator.
Agreed. But it'd require more change in the output format. The current table format is fit for single line per data. So I'd like to leave it to further work (please see below).
Quoted 33 lines
Quoted 31 lines
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 (KB) | Hits | Order | Migration type | GFP flags | Callsite
--------------------------------------------------------------------------------------------64 | 4 | 2 | RECLAIMABLE | 00285250 | new_slab 50,144 | 12,536 | 0 | MOVABLE | 0102005a | __page_cache_alloc 52 | 13 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one 40 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault 28 | 7 | 0 | UNMOVABLE | 000000d0 | __pollwait 20 | 5 | 0 | MOVABLE | 000200da | do_wp_page 20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault 16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page 16 | 4 | 0 | UNMOVABLE | 000084d0 | __pmd_alloc 8 | 2 | 0 | UNMOVABLE | 000084d0 | __pud_alloc ... | ... | ... | ... | ... | ...Quoted 1 line
--------------------------------------------------------------------------------------------
Quoted 3 lines
How about printing GFP flags more intuitively, for example, GFP_NOFS|GFP_ZERO? Tracepoint on
mm_page_allocalready print output as this format.
That would be great, but it'd also require more column space. It already uses 105 characters per line and showing textual gfp flags will increase it more.. :-/
Actually I'm thinking about 'perf report' style output - with full callchain, selectable output field and interactive TUI/GUI browser in the end. With this change, I'll be able to feel comfortable to show the textual gfp flags. ;)
Thanks,
Namhyung