perf kmem: Implement page allocation analysis (v6)
Subsystems (4)
Namhyung Kim
maintainercover perf kmem: Implement page allocation analysis (v6)
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 v6)
- add -i option fix (Jiri)
- libtraceevent operator priority fix
* changes in v5)
- print migration type and gfp flags in more compact form (Arnaldo)
- add kmem.default config option
* changes in v4)
- use pfn instead of struct page * in tracepoints (Joonsoo, Ingo)
- print gfp flags in human readable string (Joonsoo, Minchan)
* 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 convert tracepoint to save pfn instead of struct page *. Patch 2 implements basic support for page allocation analysis, patch 3 deals with the callsite and patch 4 implements sorting. Patch 5 introduces live page analysis which is to focus on currently allocated pages only. Finally patch 6 prints gfp flags in human readable string.
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 --page sleep 5 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 1.065 MB perf.data (2949 samples) ]
# perf kmem stat --page --caller -l 10
# GFP flags
# ---------
# 00000010: GFP_NOIO
# 000000d0: GFP_KERNEL
# 00000200: GFP_NOWARN
# 000052d0: GFP_KERNEL|GFP_NOWARN|GFP_NORETRY|GFP_COMP
# 000084d0: GFP_KERNEL|GFP_REPEAT|GFP_ZERO
# 000200d0: GFP_USER
# 000200d2: GFP_HIGHUSER
# 000200da: GFP_HIGHUSER_MOVABLE
# 000280da: GFP_HIGHUSER_MOVABLE|GFP_ZERO
# 002084d0: GFP_KERNEL|GFP_REPEAT|GFP_ZERO|GFP_NOTRACK
# 0102005a: GFP_NOFS|GFP_HARDWALL|GFP_MOVABLE
---------------------------------------------------------------------------------------------------------
Total alloc (KB) | Hits | Order | Migration type | GFP flags | Callsite
---------------------------------------------------------------------------------------------------------
16 | 1 | 2 | UNMOVABLE | 000052d0 | alloc_skb_with_frags
24 | 3 | 1 | UNMOVABLE | 000052d0 | alloc_skb_with_frags
3,876 | 969 | 0 | MOVABLE | 000200da | shmem_alloc_page
972 | 243 | 0 | UNMOVABLE | 000000d0 | __pollwait
624 | 156 | 0 | MOVABLE | 0102005a | __page_cache_alloc
304 | 76 | 0 | UNMOVABLE | 000200d0 | dma_generic_alloc_coherent
108 | 27 | 0 | MOVABLE | 000280da | handle_mm_fault
56 | 14 | 0 | UNMOVABLE | 002084d0 | pte_alloc_one
24 | 6 | 0 | MOVABLE | 000200da | do_wp_page
16 | 4 | 0 | UNMOVABLE | 00000200 | __tlb_remove_page
... | ... | ... | ... | ... | ...
--------------------------------------------------------------------------------------------------------- SUMMARY (page allocator)
========================
Total allocation requests : 1,518 [ 6,096 KB ] Total free requests : 1,431 [ 5,748 KB ]
Total alloc+freed requests : 1,330 [ 5,344 KB ] Total alloc-only requests : 188 [ 752 KB ] Total free-only requests : 101 [ 404 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 351 . 1,163 . .
1 3 . . . .
2 1 . . . .
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-v6 branch on my tree:
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Thanks,
Namhyung
Jiri Olsa (1):
perf kmem: Respect -i option
Namhyung Kim (8):
tracing, mm: Record pfn instead of pointer to struct page 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 perf kmem: Print gfp flags in human readable string perf kmem: Add kmem.default config option
tools lib traceevent: Honor operator priority
include/trace/events/filemap.h | 8 +- include/trace/events/kmem.h | 42 +- include/trace/events/vmscan.h | 8 +- tools/lib/traceevent/event-parse.c | 17 +- tools/perf/Documentation/perf-kmem.txt | 19 +- tools/perf/builtin-kmem.c | 1302 ++++++++++++++++++++++++++++++-- 6 files changed, 1307 insertions(+), 89 deletions(-)
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 1/9 perf kmem: Respect -i option
From: Jiri Olsa <[email protected]>
Currently the perf kmem does not respect -i option. Initializing the file.path properly after options get parsed.
--- tools/perf/builtin-kmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/builtin-kmem.c +2 -1
@@ -663,7 +663,6 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) {const char *const default_sort_order ="frag,hit,bytes" ;struct perf_data_file file = { - .path = input_name, .mode =PERF_DATA_MODE_READ , };const struct option kmem_options[] = {
@@ -701,6 +700,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)return __cmd_record (argc, argv); } + file.path = input_name; + session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
tip-bot for Jiri Olsa
merge bot[tip:perf/core] perf kmem: Respect -i option
perf kmem: Respect -i option
Commit-ID: 28939e1a1f6d198239d86b1d77fa9fd55773189a Gitweb: http://git.kernel.org/tip/28939e1a1f6d198239d86b1d77fa9fd55773189a Author: Jiri Olsa <[email protected]> AuthorDate: Mon, 6 Apr 2015 14:36:08 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 8 Apr 2015 09:07:14 -0300
perf kmem: Respect -i option
Currently the perf kmem does not respect -i option.
Initializing the file.path properly after options get parsed.
--- tools/perf/builtin-kmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/builtin-kmem.c +2 -1
@@ -663,7 +663,6 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) {const char *const default_sort_order ="frag,hit,bytes" ;struct perf_data_file file = { - .path = input_name, .mode =PERF_DATA_MODE_READ , };const struct option kmem_options[] = {
@@ -701,6 +700,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)return __cmd_record (argc, argv); } + file.path = input_name; + session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;
Namhyung Kim
maintainerPATCH 2/9 tracing, mm: Record pfn instead of pointer to struct page
In reply to Namhyung KimThe struct page is opaque for userspace tools, so it'd be better to save pfn in order to identify page frames.
The textual output of $debugfs/tracing/trace file remains unchanged and only raw (binary) data format is changed - but thanks to libtraceevent, userspace tools which deal with the raw data (like perf and trace-cmd) can parse the format easily. So impact on the userspace will also be minimal.
--- include/trace/events/filemap.h | 8 ++++---- include/trace/events/kmem.h | 42 +++++++++++++++++++++--------------------- include/trace/events/vmscan.h | 8 ++++---- 3 files changed, 29 insertions(+), 29 deletions(-)
include/trace/events/filemap.h +4 -4
@@ -18,14 +18,14 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,TP_ARGS (page),TP_STRUCT__entry ( - __field(struct page *, page) + __field(unsigned long, pfn)__field (unsigned long ,i_ino ) __field(unsigned long, index)__field (dev_t, s_dev) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page_to_pfn(page); __entry->i_ino = page->mapping->host->i_ino; __entry->index = page->index;if (page->mapping->host->i_sb)
@@ -37,8 +37,8 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,TP_printk ("dev %d:%d ino %lx page=%p pfn=%lu ofs=%lu" ,MAJOR (__entry->s_dev),MINOR (__entry->s_dev), __entry->i_ino, - __entry->page, -page_to_pfn (__entry->page), +pfn_to_page (__entry->pfn), + __entry->pfn, __entry->index <<PAGE_SHIFT ) );
include/trace/events/kmem.h +21 -21
128 lines not on this page · raw message
include/trace/events/vmscan.h +4 -4
@@ -336,18 +336,18 @@ TRACE_EVENT(mm_vmscan_writepage,TP_ARGS (page, reclaim_flags),TP_STRUCT__entry ( - __field(struct page *, page) + __field(unsigned long, pfn)__field (int, reclaim_flags) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page_to_pfn(page); __entry->reclaim_flags = reclaim_flags; ),TP_printk ("page=%p pfn=%lu flags=%s" , - __entry->page, -page_to_pfn (__entry->page), +pfn_to_page (__entry->pfn), + __entry->pfn,show_reclaim_flags (__entry->reclaim_flags)) );
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
tip-bot for Namhyung Kim
merge bot[tip:perf/urgent] tracing, mm: Record pfn instead of pointer to struct page
tracing, mm: Record pfn instead of pointer to struct page
Commit-ID: 9fdd8a875c6f3b02af48d5fa426206ca009b2b06 Gitweb: http://git.kernel.org/tip/9fdd8a875c6f3b02af48d5fa426206ca009b2b06 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 6 Apr 2015 14:36:09 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 13 Apr 2015 11:44:52 -0300
tracing, mm: Record pfn instead of pointer to struct page
The struct page is opaque for userspace tools, so it'd be better to save pfn in order to identify page frames.
The textual output of $debugfs/tracing/trace file remains unchanged and only raw (binary) data format is changed - but thanks to libtraceevent, userspace tools which deal with the raw data (like perf and trace-cmd) can parse the format easily. So impact on the userspace will also be minimal.
--- include/trace/events/filemap.h | 8 ++++---- include/trace/events/kmem.h | 42 +++++++++++++++++++++--------------------- include/trace/events/vmscan.h | 8 ++++---- 3 files changed, 29 insertions(+), 29 deletions(-)
include/trace/events/filemap.h +4 -4
@@ -18,14 +18,14 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,TP_ARGS (page),TP_STRUCT__entry ( - __field(struct page *, page) + __field(unsigned long, pfn)__field (unsigned long ,i_ino ) __field(unsigned long, index)__field (dev_t, s_dev) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page_to_pfn(page); __entry->i_ino = page->mapping->host->i_ino; __entry->index = page->index;if (page->mapping->host->i_sb)
@@ -37,8 +37,8 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,TP_printk ("dev %d:%d ino %lx page=%p pfn=%lu ofs=%lu" ,MAJOR (__entry->s_dev),MINOR (__entry->s_dev), __entry->i_ino, - __entry->page, -page_to_pfn (__entry->page), +pfn_to_page (__entry->pfn), + __entry->pfn, __entry->index <<PAGE_SHIFT ) );
include/trace/events/kmem.h +21 -21
128 lines not on this page · raw message
include/trace/events/vmscan.h +4 -4
@@ -336,18 +336,18 @@ TRACE_EVENT(mm_vmscan_writepage,TP_ARGS (page, reclaim_flags),TP_STRUCT__entry ( - __field(struct page *, page) + __field(unsigned long, pfn)__field (int, reclaim_flags) ),TP_fast_assign ( - __entry->page = page; + __entry->pfn = page_to_pfn(page); __entry->reclaim_flags = reclaim_flags; ),TP_printk ("page=%p pfn=%lu flags=%s" , - __entry->page, -page_to_pfn (__entry->page), +pfn_to_page (__entry->pfn), + __entry->pfn,show_reclaim_flags (__entry->reclaim_flags)) );
Namhyung Kim
maintainerPATCH 3/9 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
-------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
-------------------------------------------------------------------------------
4045014 | 16 | 1 | 2 | RECLAIM | 00285250
4143980 | 16 | 1 | 2 | RECLAIM | 00285250
3938658 | 16 | 1 | 2 | RECLAIM | 00285250
4045400 | 16 | 1 | 2 | RECLAIM | 00285250
3568708 | 16 | 1 | 2 | RECLAIM | 00285250
3729824 | 16 | 1 | 2 | RECLAIM | 00285250
3657210 | 16 | 1 | 2 | RECLAIM | 00285250
4120750 | 16 | 1 | 2 | RECLAIM | 00285250
3678850 | 16 | 1 | 2 | RECLAIM | 00285250
3693874 | 16 | 1 | 2 | RECLAIM | 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 | 500 +++++++++++++++++++++++++++++++-- 2 files changed, 491 insertions(+), 17 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 +484 -16
624 lines not on this page · raw message
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 3/9] perf kmem: Analyze page allocator events also
Em Mon, Apr 06, 2015 at 02:36:10PM +0900, Namhyung Kim escreveu:
Quoted 7 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.
Quoted 7 lines
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
Hi, applied the first patch, the kernel one, reboot with that kernel:
[root@ssdandy ~]# cat /t/events/kmem/mm_page_free/format
name: mm_page_free
ID: 367
format:
field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:unsigned long pfn; offset:8; size:8; signed:0; field:unsigned int order; offset:16; size:4; signed:0;
print fmt: "page=%p pfn=%lu order=%d", (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)), REC->pfn, REC->order [root@ssdandy ~]#
Then did:
[root@ssdandy ~]# perf kmem record
^C[ perf record: Woken up 0 times to write data ]
per[ perf record: Captured and wrote 624.101 MB perf.data (7022790 samples) ]
[root@ssdandy ~]# perf evlist
kmem:kmalloc
kmem:kmalloc_node
kmem:kfree
kmem:kmem_cache_alloc kmem:kmem_cache_alloc_node
kmem:kmem_cache_free
[root@ssdandy ~]# ls -la perf.data
-rw-------. 1 root root 659632943 Apr 10 18:05 perf.data [root@ssdandy ~]#
But I only get:
[root@ssdandy ~]# perf kmem stat --page --alloc
Warning:
188 out of order events recorded.
--------------------------------------------------------------------------------
Page | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
--------------------------------------------------------------------------------
... | ... | ... | ... | ... | ...
--------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total allocation requests : 0 [ 0 KB ] Total free requests : 0 [ 0 KB ]
Total alloc+freed requests : 0 [ 0 KB ] Total alloc-only requests : 0 [ 0 KB ] Total free-only requests : 0 [ 0 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 . . . . .
1 . . . . .
2 . . . . .
3 . . . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .[root@ssdandy ~]#
What am I missing?
- Arnaldo
Quoted 719 lines
-------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
-------------------------------------------------------------------------------4045014 | 16 | 1 | 2 | RECLAIM | 00285250 4143980 | 16 | 1 | 2 | RECLAIM | 00285250 3938658 | 16 | 1 | 2 | RECLAIM | 00285250 4045400 | 16 | 1 | 2 | RECLAIM | 00285250 3568708 | 16 | 1 | 2 | RECLAIM | 00285250 3729824 | 16 | 1 | 2 | RECLAIM | 00285250 3657210 | 16 | 1 | 2 | RECLAIM | 00285250 4120750 | 16 | 1 | 2 | RECLAIM | 00285250 3678850 | 16 | 1 | 2 | RECLAIM | 00285250 3693874 | 16 | 1 | 2 | RECLAIM | 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 . . . . .Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/Documentation/perf-kmem.txt | 8 +- tools/perf/builtin-kmem.c | 500 +++++++++++++++++++++++++++++++-- 2 files changed, 491 insertions(+), 17 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 +484
-16@@ -22,6 +22,11 @@#include <linux/string.h> #include <locale.h> +static int kmem_slab; +static int kmem_page; + +static long kmem_page_size; +struct alloc_stat ;typedef int (*sort_fn_t )(struct alloc_stat *,struct alloc_stat *);@@ -226,6 +231,244 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel,return 0 ; } +static u64 total_page_alloc_bytes; +static u64 total_page_free_bytes; +static u64 total_page_nomatch_bytes; +static u64 total_page_fail_bytes; +static unsigned long nr_page_allocs; +static unsigned long nr_page_frees; +static unsigned long nr_page_fails; +static unsigned long nr_page_nomatch; + +static bool use_pfn; + +#define MAX_MIGRATE_TYPES 6 +#define MAX_PAGE_ORDER 11 + +static int order_stats[MAX_PAGE_ORDER ][MAX_MIGRATE_TYPES ]; + +struct page_stat { +struct rb_node node; +u64 page; +int order; +unsigned gfp_flags; +unsigned migrate_type; +u64 alloc_bytes; +u64 free_bytes; +int nr_alloc; +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 page_stat *search_page (unsigned long page,bool create) +{ +struct rb_node **node = &page_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->page - page; +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->page = page; + +rb_link_node (&data->node, parent, node); +rb_insert_color (&data->node, &page_tree); + } + +return data; +} + +static int page_stat_cmp (struct page_stat *a,struct page_stat *b) +{ +if (a->page > b->page) +return -1 ; +if (a->page < b->page) +return 1 ; +if (a->order > b->order) +return -1 ; +if (a->order < b->order) +return 1 ; +if (a->migrate_type > b->migrate_type) +return -1 ; +if (a->migrate_type < b->migrate_type) +return 1 ; +if (a->gfp_flags > b->gfp_flags) +return -1 ; +if (a->gfp_flags < b->gfp_flags) +return 1 ; +return 0 ; +} + +static struct page_stat *search_page_alloc_stat (struct page_stat *stat,bool create) +{ +struct rb_node **node = &page_alloc_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 =page_stat_cmp (data, stat); +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->page = stat->page; + data->order = stat->order; + data->gfp_flags = stat->gfp_flags; + data->migrate_type = stat->migrate_type; + +rb_link_node (&data->node, parent, node); +rb_insert_color (&data->node, &page_alloc_tree); + } + +return data; +} + +static bool valid_page (u64 pfn_or_page) +{ +if (use_pfn && pfn_or_page ==-1UL ) +return false; +if (!use_pfn && pfn_or_page ==0 ) +return false; +return true; +} + +static int perf_evsel__process_page_alloc_event (struct perf_evsel *evsel, +struct perf_sample *sample) +{ +u64 page; +unsigned int order =perf_evsel__intval (evsel, sample,"order" ); +unsigned int gfp_flags =perf_evsel__intval (evsel, sample,"gfp_flags" ); +unsigned int migrate_type =perf_evsel__intval (evsel, sample, +"migratetype" ); +u64 bytes = kmem_page_size << order; +struct page_stat *stat; +struct page_stat this = { + .order = order, + .gfp_flags = gfp_flags, + .migrate_type = migrate_type, + }; + +if (use_pfn) + page =perf_evsel__intval (evsel, sample,"pfn" ); +else + page =perf_evsel__intval (evsel, sample,"page" ); + + nr_page_allocs++; + total_page_alloc_bytes += bytes; + +if (!valid_page (page)) { + nr_page_fails++; + total_page_fail_bytes += bytes; + +return 0 ; + } + +/* +* This is to find the current page (with correct gfp flags and +* migrate type) at free event. +*/ + stat =search_page (page, true); +if (stat ==NULL ) +return -ENOMEM ; + + stat->order = order; + stat->gfp_flags = gfp_flags; + stat->migrate_type = migrate_type; + + this.page = page; + stat =search_page_alloc_stat (&this, true); +if (stat ==NULL ) +return -ENOMEM ; + + stat->nr_alloc++; + stat->alloc_bytes += bytes; + + order_stats[order][migrate_type]++; + +return 0 ; +} + +static int perf_evsel__process_page_free_event (struct perf_evsel *evsel, +struct perf_sample *sample) +{ +u64 page; +unsigned int order =perf_evsel__intval (evsel, sample,"order" ); +u64 bytes = kmem_page_size << order; +struct page_stat *stat; +struct page_stat this = { + .order = order, + }; + +if (use_pfn) + page =perf_evsel__intval (evsel, sample,"pfn" ); +else + page =perf_evsel__intval (evsel, sample,"page" ); + + nr_page_frees++; + total_page_free_bytes += bytes; + + stat =search_page (page, false); +if (stat ==NULL ) { +pr_debug2 ("missing free at page %" PRIx64" (order: %d)\n" , + page, order); + + nr_page_nomatch++; + total_page_nomatch_bytes += bytes; + +return 0 ; + } + + this.page = page; + this.gfp_flags = stat->gfp_flags; + this.migrate_type = stat->migrate_type; + +rb_erase (&stat->node, &page_tree); +free (stat); + + stat =search_page_alloc_stat (&this, false); +if (stat ==NULL ) +return -ENOENT ; + + stat->nr_free++; + stat->free_bytes += bytes; + +return 0 ; +} +typedef int (*tracepoint_handler )(struct perf_evsel *evsel,struct perf_sample *sample);@@ -270,8 +513,9 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc)return 100.0 - (100.0 * n_req / n_alloc); } -static void __print_result (struct rb_root *root,struct perf_session *session, -int n_lines,int is_caller) +static void __print_slab_result (struct rb_root *root, +struct perf_session *session, +int n_lines,int is_caller) {struct rb_node *next;struct machine *machine = &session->machines.host;@@ -323,9 +567,56 @@ static void __print_result(struct rb_root *root, struct perf_session *session,printf ("%.105s\n" , graph_dotted_line); } -static void print_summary (void ) +static const char *const migrate_type_str[] = { +"UNMOVABL" , +"RECLAIM" , +"MOVABLE" , +"RESERVED" , +"CMA/ISLT" , +"UNKNOWN" , +}; + +static void __print_page_result (struct rb_root *root, +struct perf_session *session__maybe_unused , +int n_lines) +{ +struct rb_node *next =rb_first (root); +const char *format; + +printf ("\n%.80s\n" , graph_dotted_line); +printf (" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags\n" , + use_pfn ?"PFN" :"Page" ); +printf ("%.80s\n" , graph_dotted_line); + +if (use_pfn) + format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx\n" ; +else + format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx\n" ; + +while (next && n_lines--) { +struct page_stat *data; + + data = rb_entry(next, struct page_stat, node); + +printf (format, (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); + + next =rb_next (next); + } + +if (n_lines ==-1 ) +printf (" ... | ... | ... | ... | ... | ... \n" ); + +printf ("%.80s\n" , graph_dotted_line); +} + +static void print_slab_summary (void ) { -printf ("\nSUMMARY\n=======\n" ); +printf ("\nSUMMARY (SLAB allocator)" ); +printf ("\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",@@ -335,13 +626,73 @@ static void print_summary(void)printf ("Cross CPU allocations: %'lu/%'lu\n" , nr_cross_allocs, nr_allocs); } -static void print_result (struct perf_session *session) +static void print_page_summary (void ) +{ +int o, m; +u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch; +u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes; + +printf ("\nSUMMARY (page allocator)" ); +printf ("\n========================\n" ); +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total allocation requests" , + nr_page_allocs, total_page_alloc_bytes /1024 ); +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total free requests" , + nr_page_frees, total_page_free_bytes /1024 ); +printf ("\n" ); + +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total alloc+freed requests" , + nr_alloc_freed, (total_alloc_freed_bytes) /1024 ); +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total alloc-only requests" , + nr_page_allocs - nr_alloc_freed, + (total_page_alloc_bytes - total_alloc_freed_bytes) /1024 ); +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total free-only requests" , + nr_page_nomatch, total_page_nomatch_bytes /1024 ); +printf ("\n" ); + +printf ("%-30s: %'16lu [ %'16" PRIu64" KB ]\n" ,"Total allocation failures" , + nr_page_fails, total_page_fail_bytes /1024 ); +printf ("\n" ); + +printf ("%5s %12s %12s %12s %12s %12s\n" ,"Order" ,"Unmovable" , +"Reclaimable" ,"Movable" ,"Reserved" ,"CMA/Isolated" ); +printf ("%.5s %.12s %.12s %.12s %.12s %.12s\n" , graph_dotted_line, + graph_dotted_line, graph_dotted_line, graph_dotted_line, + graph_dotted_line, graph_dotted_line); + +for (o =0 ; o <MAX_PAGE_ORDER ; o++) { +printf ("%5d" , o); +for (m =0 ; m <MAX_MIGRATE_TYPES -1 ; m++) { +if (order_stats[o][m]) +printf (" %'12d" , order_stats[o][m]); +else +printf (" %12c" ,'.' ); + } +printf ("\n" ); + } +} + +static void print_slab_result (struct perf_session *session) {if (caller_flag) -__print_result (&root_caller_sorted, session, caller_lines,1 ); +__print_slab_result (&root_caller_sorted, session, caller_lines,1 ); +if (alloc_flag) +__print_slab_result (&root_alloc_sorted, session, alloc_lines,0 ); +print_slab_summary (); +} + +static void print_page_result (struct perf_session *session) +{if (alloc_flag) -__print_result (&root_alloc_sorted, session, alloc_lines,0 ); -print_summary (); +__print_page_result (&page_alloc_sorted, session, alloc_lines); +print_page_summary (); +} + +static void print_result (struct perf_session *session) +{ +if (kmem_slab) +print_slab_result (session); +if (kmem_page) +print_page_result (session); }struct sort_dimension {@@ -353,8 +704,8 @@ struct sort_dimension {static LIST_HEAD (caller_sort);static LIST_HEAD (alloc_sort); -static void sort_insert (struct rb_root *root,struct alloc_stat *data, -struct list_head *sort_list) +static void sort_slab_insert (struct rb_root *root,struct alloc_stat *data, +struct list_head *sort_list) {struct rb_node **new = &(root->rb_node);struct rb_node *parent =NULL ;@@ -383,8 +734,8 @@ static void sort_insert(struct rb_root *root, struct alloc_stat *data,rb_insert_color (&data->node, root); } -static void __sort_result (struct rb_root *root,struct rb_root *root_sorted, -struct list_head *sort_list) +static void __sort_slab_result (struct rb_root *root,struct rb_root *root_sorted, +struct list_head *sort_list) {struct rb_node *node;struct alloc_stat *data;@@ -396,26 +747,79 @@ static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,rb_erase (node, root); data = rb_entry(node, struct alloc_stat, node); -sort_insert (root_sorted, data, sort_list); +sort_slab_insert (root_sorted, data, sort_list); + } +} + +static void sort_page_insert (struct rb_root *root,struct page_stat *data) +{ +struct rb_node **new = &root->rb_node; +struct rb_node *parent =NULL ; + +while (*new) { +struct page_stat *this; +int cmp =0 ; + + this = rb_entry(*new, struct page_stat, node); + parent = *new; + +/* TODO: support more sort key */ + cmp = data->alloc_bytes - this->alloc_bytes; + +if (cmp >0 ) + new = &parent->rb_left; +else + new = &parent->rb_right; + } + +rb_link_node (&data->node, parent, new); +rb_insert_color (&data->node, root); +} + +static void __sort_page_result (struct rb_root *root,struct rb_root *root_sorted) +{ +struct rb_node *node; +struct page_stat *data; + +for (;;) { + node =rb_first (root); +if (!node) +break ; + +rb_erase (node, root); + data = rb_entry(node, struct page_stat, node); +sort_page_insert (root_sorted, data); } }static void sort_result (void ) { -__sort_result (&root_alloc_stat, &root_alloc_sorted, &alloc_sort); -__sort_result (&root_caller_stat, &root_caller_sorted, &caller_sort); +if (kmem_slab) { +__sort_slab_result (&root_alloc_stat, &root_alloc_sorted, + &alloc_sort); +__sort_slab_result (&root_caller_stat, &root_caller_sorted, + &caller_sort); + } +if (kmem_page) { +__sort_page_result (&page_alloc_tree, &page_alloc_sorted); + } }static int __cmd_kmem (struct perf_session *session) {int err = -EINVAL ; +struct perf_evsel *evsel;const struct perf_evsel_str_handler kmem_tracepoints[] = { +/* slab allocator */ {"kmem:kmalloc" , perf_evsel__process_alloc_event, }, {"kmem:kmem_cache_alloc" , perf_evsel__process_alloc_event, }, {"kmem:kmalloc_node" , perf_evsel__process_alloc_node_event, }, {"kmem:kmem_cache_alloc_node" , perf_evsel__process_alloc_node_event, }, {"kmem:kfree" , perf_evsel__process_free_event, }, {"kmem:kmem_cache_free" , perf_evsel__process_free_event, }, +/* page allocator */ + {"kmem:mm_page_alloc" , perf_evsel__process_page_alloc_event, }, + {"kmem:mm_page_free" , perf_evsel__process_page_free_event, }, };if (!perf_session__has_traces (session,"kmem record" ))@@ -426,10 +830,20 @@ static int __cmd_kmem(struct perf_session *session) goto out; } +evlist__for_each (session->evlist, evsel) { +if (!strcmp (perf_evsel__name (evsel),"kmem:mm_page_alloc" ) && +perf_evsel__field (evsel,"pfn" )) { + use_pfn = true; +break ; + } + } +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:@@ -612,6 +1026,22 @@ static int parse_alloc_opt(const struct option *opt __maybe_unused,return 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 , +const char *arg__maybe_unused , +int unset__maybe_unused ) +{ + kmem_page = (kmem_slab +1 ); +return 0 ; +} +static int parse_line_opt (const struct option *opt__maybe_unused ,const char *arg,int unset__maybe_unused ) {@@ -634,6 +1064,8 @@ static int __cmd_record(int argc, const char **argv) {const char *const record_args[] = {"record" ,"-a" ,"-R" ,"-c" ,"1" , + }; +const char *const slab_events[] = {"-e" ,"kmem:kmalloc" ,"-e" ,"kmem:kmalloc_node" ,"-e" ,"kmem:kfree" ,@@ -641,10 +1073,19 @@ static int __cmd_record(int argc, const char **argv)"-e" ,"kmem:kmem_cache_alloc_node" , "-e", "kmem:kmem_cache_free", }; +const char *const page_events[] = { +"-e" ,"kmem:mm_page_alloc" , +"-e" ,"kmem:mm_page_free" , + };unsigned int rec_argc, i, j;const char **rec_argv; rec_argc =ARRAY_SIZE (record_args) + argc -1 ; +if (kmem_slab) + rec_argc +=ARRAY_SIZE (slab_events); +if (kmem_page) + rec_argc +=ARRAY_SIZE (page_events); + rec_argv =calloc (rec_argc +1 ,sizeof (char *));if (rec_argv ==NULL )@@ -653,6 +1094,15 @@ static int __cmd_record(int argc, const char **argv)for (i =0 ; i <ARRAY_SIZE (record_args); i++) rec_argv[i] =strdup (record_args[i]); +if (kmem_slab) { +for (j =0 ; j <ARRAY_SIZE (slab_events); j++, i++) + rec_argv[i] =strdup (slab_events[j]); + } +if (kmem_page) { +for (j =0 ; j <ARRAY_SIZE (page_events); j++, i++) + rec_argv[i] =strdup (page_events[j]); + } +for (j =1 ; j < (unsigned int )argc; j++, i++) rec_argv[i] = argv[j];@@ -679,6 +1129,10 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)OPT_CALLBACK ('l' ,"line" ,NULL ,"num" ,"show n lines" , parse_line_opt),OPT_BOOLEAN (0 ,"raw-ip" , &raw_ip,"show raw ip instead of symbol" ),OPT_BOOLEAN ('f' ,"force" , &file.force,"don't complain, do it" ), +OPT_CALLBACK_NOOPT (0 ,"slab" ,NULL ,NULL ,"Analyze slab allocator" , + parse_slab_opt), +OPT_CALLBACK_NOOPT (0 ,"page" ,NULL ,NULL ,"Analyze page allocator" , + parse_page_opt), OPT_END() };const char *const kmem_subcommands[] = {"record" ,"stat" ,NULL };@@ -695,6 +1149,9 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)if (!argc)usage_with_options (kmem_usage, kmem_options); +if (kmem_slab ==0 && kmem_page ==0 ) + kmem_slab =1 ;/* for backward compatibility */ +if (!strncmp (argv[0 ],"rec" ,3 )) {symbol__init (NULL );return __cmd_record (argc, argv);@@ -706,6 +1163,17 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)if (session ==NULL )return -1 ; +if (kmem_page) { +struct perf_evsel *evsel =perf_evlist__first (session->evlist); + +if (evsel ==NULL || evsel->tp_format ==NULL ) { +pr_err ("invalid event found.. aborting\n" ); +return -1 ; + } + + kmem_page_size =pevent_get_page_size (evsel->tp_format->pevent); + } +symbol__init (&session->header.env);if (!strcmp (argv[0 ],"stat" )) {Signature · 1 line
2.3.2
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 3/9] perf kmem: Analyze page allocator events also
Em Fri, Apr 10, 2015 at 06:06:29PM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 17 lines
Em Mon, Apr 06, 2015 at 02:36:10PM +0900, Namhyung Kim escreveu:
Quoted 7 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.
Quoted 7 lines
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 10Hi, applied the first patch, the kernel one, reboot with that kernel:
<SNIP>
Quoted 3 lines
[root@ssdandy ~]#
What am I missing?
Argh, I was expecting to read just what is in that cset and be able to reproduce the results, had to go back to the [PATCH 0/0] cover letter to figure out that I need to run:
perf kmem record --page sleep 5
With that I get:
[root@ssdandy ~]# perf kmem stat --page --alloc --line 20
--------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
--------------------------------------------------------------------------------
3487838 | 12 | 3 | 0 | UNMOVABL | 00020010
3493414 | 8 | 2 | 0 | UNMOVABL | 000284d0
3487761 | 4 | 1 | 0 | UNMOVABL | 000202d0
3487764 | 4 | 1 | 0 | UNMOVABL | 000202d0
3487982 | 4 | 1 | 0 | UNMOVABL | 000202d0
3487991 | 4 | 1 | 0 | UNMOVABL | 000284d0
3488046 | 4 | 1 | 0 | UNMOVABL | 002284d0
3488057 | 4 | 1 | 0 | UNMOVABL | 000200d0
3488191 | 4 | 1 | 0 | UNMOVABL | 002284d0
3488203 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488206 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488210 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488211 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488213 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488215 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488298 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488325 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488326 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488327 | 4 | 1 | 0 | UNMOVABL | 000202d0
3488329 | 4 | 1 | 0 | UNMOVABL | 000202d0
... | ... | ... | ... | ... | ... --------------------------------------------------------------------------------
SUMMARY (page allocator)
========================
Total allocation requests : 166 [ 664 KB ] Total free requests : 239 [ 956 KB ]
Total alloc+freed requests : 49 [ 196 KB ] Total alloc-only requests : 117 [ 468 KB ] Total free-only requests : 190 [ 760 KB ]
Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 143 . 23 . .
1 . . . . .
2 . . . . .
3 . . . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .[root@ssdandy ~]#
- Arnaldo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerRe: [PATCH 3/9] perf kmem: Analyze page allocator events also
Hi Arnaldo,
Quoted 23 lines · Arnaldo Carvalho de Melo
Em Fri, Apr 10, 2015 at 06:06:29PM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 17 lines
Em Mon, Apr 06, 2015 at 02:36:10PM +0900, Namhyung Kim escreveu:
Quoted 7 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.
Quoted 7 lines
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 10Hi, applied the first patch, the kernel one, reboot with that kernel:
<SNIP>
Quoted 2 lines
[root@ssdandy ~]#
Quoted 7 lines
Quoted 1 line
What am I missing?
Argh, I was expecting to read just what is in that cset and be able to reproduce the results, had to go back to the [PATCH 0/0] cover letter to figure out that I need to run:
perf kmem record --page sleep 5
Right. Maybe I need to change to print warning if no events found with option.
Quoted 29 lines
With that I get:
[root@ssdandy ~]# perf kmem stat --page --alloc --line 20
--------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
--------------------------------------------------------------------------------3487838 | 12 | 3 | 0 | UNMOVABL | 00020010 3493414 | 8 | 2 | 0 | UNMOVABL | 000284d0 3487761 | 4 | 1 | 0 | UNMOVABL | 000202d0 3487764 | 4 | 1 | 0 | UNMOVABL | 000202d0 3487982 | 4 | 1 | 0 | UNMOVABL | 000202d0 3487991 | 4 | 1 | 0 | UNMOVABL | 000284d0 3488046 | 4 | 1 | 0 | UNMOVABL | 002284d0 3488057 | 4 | 1 | 0 | UNMOVABL | 000200d0 3488191 | 4 | 1 | 0 | UNMOVABL | 002284d0 3488203 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488206 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488210 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488211 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488213 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488215 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488298 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488325 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488326 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488327 | 4 | 1 | 0 | UNMOVABL | 000202d0 3488329 | 4 | 1 | 0 | UNMOVABL | 000202d0 ... | ... | ... | ... | ... | ...
Quoted 1 line
--------------------------------------------------------------------------------
Hmm.. looks like you ran some old version. Please check v6! :)
Thanks,
Namhyung
Quoted 25 lines
SUMMARY (page allocator)
========================Total allocation requests : 166 [ 664 KB ] Total free requests : 239 [ 956 KB ]Total alloc+freed requests : 49 [ 196 KB ] Total alloc-only requests : 117 [ 468 KB ] Total free-only requests : 190 [ 760 KB ]Total allocation failures : 0 [ 0 KB ]
Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------0 143 . 23 . . 1 . . . . . 2 . . . . . 3 . . . . . 4 . . . . . 5 . . . . . 6 . . . . . 7 . . . . . 8 . . . . . 9 . . . . . 10 . . . . .
Quoted 8 lines
[root@ssdandy ~]#
- Arnaldo
--
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/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 3/9] perf kmem: Analyze page allocator events also
Em Mon, Apr 13, 2015 at 03:59:24PM +0900, Namhyung Kim escreveu:
Quoted 15 lines · Arnaldo Carvalho de Melo
Quoted 13 lines
Em Fri, Apr 10, 2015 at 06:06:29PM -0300, Arnaldo Carvalho de Melo escreveu:
Quoted 6 lines
Em Mon, Apr 06, 2015 at 02:36:10PM +0900, Namhyung Kim escreveu:
Quoted 3 lines
If none of these --slab nor --page is specified, --slab is implied.
# perf kmem stat --page --alloc --line 10Hi, applied the first patch, the kernel one, reboot with that kernel:
<SNIP>
Quoted 3 lines
[root@ssdandy ~]#
What am I missing?
Quoted 8 lines
Quoted 5 lines
Argh, I was expecting to read just what is in that cset and be able to reproduce the results, had to go back to the [PATCH 0/0] cover letter to figure out that I need to run:
perf kmem record --page sleep 5
Right. Maybe I need to change to print warning if no events found with option.
Ok!
Quoted 1 line
Hmm.. looks like you ran some old version. Please check v6! :)
Thanks, will do,
- Arnaldo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 3/9] perf kmem: Analyze page allocator events also
In reply to Namhyung KimEm Mon, Apr 06, 2015 at 02:36:10PM +0900, Namhyung Kim escreveu:
Quoted 7 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.
Quoted 7 lines
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
Applied this and the kernel part, tested, thanks,
- Arnaldo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
tip-bot for Namhyung Kim
merge bot[tip:perf/urgent] perf kmem: Analyze page allocator events also
In reply to Namhyung Kimperf kmem: Analyze page allocator events also
Commit-ID: 0d68bc92c48167130b61b449f08be27dc862dba2 Gitweb: http://git.kernel.org/tip/0d68bc92c48167130b61b449f08be27dc862dba2 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 6 Apr 2015 14:36:10 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 13 Apr 2015 11:44:52 -0300
perf kmem: Analyze page allocator events also
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.
First run 'perf kmem record' to generate a suitable perf.data file:
# perf kmem record --page sleep 5
Then run 'perf kmem stat' to postprocess the perf.data file:
# perf kmem stat --page --alloc --line 10
-------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
-------------------------------------------------------------------------------
4045014 | 16 | 1 | 2 | RECLAIM | 00285250
4143980 | 16 | 1 | 2 | RECLAIM | 00285250
3938658 | 16 | 1 | 2 | RECLAIM | 00285250
4045400 | 16 | 1 | 2 | RECLAIM | 00285250
3568708 | 16 | 1 | 2 | RECLAIM | 00285250
3729824 | 16 | 1 | 2 | RECLAIM | 00285250
3657210 | 16 | 1 | 2 | RECLAIM | 00285250
4120750 | 16 | 1 | 2 | RECLAIM | 00285250
3678850 | 16 | 1 | 2 | RECLAIM | 00285250
3693874 | 16 | 1 | 2 | RECLAIM | 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 | 500 +++++++++++++++++++++++++++++++-- 2 files changed, 491 insertions(+), 17 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 +484 -16
624 lines not on this page · raw message
Namhyung Kim
maintainerPATCH 4/9 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 | Mig.type | GFP flags | Callsite
---------------------------------------------------------------------------------------------
1,064 | 266 | 0 | UNMOVABL | 000000d0 | __pollwait
52 | 13 | 0 | UNMOVABL | 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 | UNMOVABL | 000084d0 | __pmd_alloc
16 | 4 | 0 | UNMOVABL | 00000200 | __tlb_remove_page
12 | 3 | 0 | UNMOVABL | 000084d0 | __pud_alloc
8 | 2 | 0 | UNMOVABL | 00000010 | bio_copy_user_iov
4 | 1 | 0 | UNMOVABL | 000200d2 | pipe_write
4 | 1 | 0 | MOVABLE | 000280da | do_wp_page
4 | 1 | 0 | UNMOVABL | 002084d0 | pgd_alloc---------------------------------------------------------------------------------------------
--- tools/perf/builtin-kmem.c | 279 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 263 insertions(+), 16 deletions(-)
tools/perf/builtin-kmem.c +263 -16
@@ -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;
@@ -241,6 +243,7 @@ static unsigned long nr_page_fails;static unsigned long nr_page_nomatch;static bool use_pfn; +static struct perf_session *kmem_session;#define MAX_MIGRATE_TYPES 6#define MAX_PAGE_ORDER 11
@@ -250,6 +253,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;
@@ -262,8 +266,138 @@ struct page_stat {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; -static struct page_stat *search_page (unsigned long page,bool create) +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); + +callchain_cursor_advance (&callchain_cursor); + } + +pr_debug2 ("unknown callsite: %" PRIx64"\n" , sample->ip); +return sample->ip; +} + +static struct page_stat *search_page (u64 page,bool create) {struct rb_node **node = &page_tree.rb_node;struct rb_node *parent =NULL ;
@@ -357,6 +491,41 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool crereturn 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; +} +static bool valid_page (u64 pfn_or_page) {if (use_pfn && pfn_or_page ==-1UL )
@@ -375,6 +544,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;struct page_stat this = { .order = order,
@@ -397,6 +567,8 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,return 0 ; } + callsite =find_callsite (evsel, sample); +/* * This is to find the current page (with correct gfp flags and * migrate type) at free event.
@@ -408,6 +580,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, stat->order = order; stat->gfp_flags = gfp_flags; stat->migrate_type = migrate_type; + stat->callsite = callsite; this.page = page; stat =search_page_alloc_stat (&this, true);
@@ -416,6 +589,18 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, stat->nr_alloc++; stat->alloc_bytes += bytes; + stat->callsite = callsite; + + stat =search_page_caller_stat (callsite, true); +if (stat ==NULL ) +return -ENOMEM ; + + stat->order = order; + stat->gfp_flags = gfp_flags; + stat->migrate_type = migrate_type; + + stat->nr_alloc++; + stat->alloc_bytes += bytes; order_stats[order][migrate_type]++;
@@ -455,6 +640,7 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, this.page = page; this.gfp_flags = stat->gfp_flags; this.migrate_type = stat->migrate_type; + this.callsite = stat->callsite;rb_erase (&stat->node, &page_tree);free (stat);
@@ -466,6 +652,13 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, stat->nr_free++; stat->free_bytes += bytes; + stat =search_page_caller_stat (this.callsite, false); +if (stat ==NULL ) +return -ENOENT ; + + stat->nr_free++; + stat->free_bytes += bytes; +return 0 ; }
@@ -576,41 +769,89 @@ 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;const char *format; -printf ("\n%.80s\n" , graph_dotted_line); -printf (" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags\n" , +printf ("\n%.105s\n" , graph_dotted_line); +printf (" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n" , use_pfn ?"PFN" :"Page" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line);if (use_pfn) - format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx\n" ; + format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ;else - format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx\n" ; + format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ;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 (format, (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 ("%.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 | Mig.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 | %8s | %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 (" ... | ... | ... | ... | ... | ...\n" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); }static void print_slab_summary (void )
@@ -682,8 +923,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 (); }
@@ -802,6 +1045,7 @@ static void sort_result(void) }if (kmem_page) {__sort_page_result (&page_alloc_tree, &page_alloc_sorted); +__sort_page_result (&page_caller_tree, &page_caller_sorted); } }
@@ -1084,7 +1328,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 *));
@@ -1099,6 +1343,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]); }
@@ -1159,7 +1405,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) file.path = input_name; - session =perf_session__new (&file, false, &perf_kmem); + kmem_session = session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;
@@ -1172,6 +1418,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 · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 4/9] perf kmem: Implement stat --page --caller
Em Mon, Apr 06, 2015 at 02:36:11PM +0900, Namhyung Kim escreveu:
Quoted 132 lines
It 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 | Mig.type | GFP flags | Callsite
---------------------------------------------------------------------------------------------1,064 | 266 | 0 | UNMOVABL | 000000d0 | __pollwait 52 | 13 | 0 | UNMOVABL | 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 | UNMOVABL | 000084d0 | __pmd_alloc 16 | 4 | 0 | UNMOVABL | 00000200 | __tlb_remove_page 12 | 3 | 0 | UNMOVABL | 000084d0 | __pud_alloc 8 | 2 | 0 | UNMOVABL | 00000010 | bio_copy_user_iov 4 | 1 | 0 | UNMOVABL | 000200d2 | pipe_write 4 | 1 | 0 | MOVABLE | 000280da | do_wp_page 4 | 1 | 0 | UNMOVABL | 002084d0 | pgd_alloc---------------------------------------------------------------------------------------------
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-kmem.c | 279 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 263 insertions(+), 16 deletions(-)tools/perf/builtin-kmem.c +39
-1@@ -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;@@ -241,6 +243,7 @@ static unsigned long nr_page_fails;static unsigned long nr_page_nomatch;static bool use_pfn; +static struct perf_session *kmem_session;#define MAX_MIGRATE_TYPES 6#define MAX_PAGE_ORDER 11@@ -250,6 +253,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;@@ -262,8 +266,138 @@ struct page_stat {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; -static struct page_stat *search_page (unsigned long page,bool create) +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 ; +}
tools/perf/builtin-kmem.c struct page_stat { · line 314
Quoted 10 lines
tools/perf/builtin-kmem.c +10
-0+ +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; +
Why having a blank like here?
Quoted 4 lines
tools/perf/builtin-kmem.c +4
-0+regex_t alloc_func_regex; +const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ; + + ret =regcomp (&alloc_func_regex, pattern,REG_EXTENDED );
tools/perf/builtin-kmem.c struct page_stat { · line 328
Quoted 10 lines
tools/perf/builtin-kmem.c +10
-0+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 );
What if the map can't be loaded?
Quoted 76 lines
tools/perf/builtin-kmem.c +72
-0+ +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"\n" , sample->ip); +return sample->ip; +} + +static struct page_stat *search_page (u64 page,bool create) {struct rb_node **node = &page_tree.rb_node;struct rb_node *parent =NULL ;
tools/perf/builtin-kmem.c static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool cre · line 498
Quoted 8 lines
tools/perf/builtin-kmem.c +5
-0@@ -357,6 +491,41 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool crereturn 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;
Please use the "findnew" idiom to name this function, looking at only its name one things it searches a tree, a read only operation, but it may insert elements too, a modify operation.
Since we use the findnew idiom elsewhere for operations that do that, i.e. optimize the "new" part of "findnew" by using the "find" part, please use it here as well.
Quoted 257 lines
tools/perf/builtin-kmem.c +122
-15+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; +} +static bool valid_page (u64 pfn_or_page) {if (use_pfn && pfn_or_page ==-1UL )@@ -375,6 +544,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;struct page_stat this = { .order = order,@@ -397,6 +567,8 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,return 0 ; } + callsite =find_callsite (evsel, sample); +/* * This is to find the current page (with correct gfp flags and * migrate type) at free event. @@ -408,6 +580,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, stat->order = order; stat->gfp_flags = gfp_flags; stat->migrate_type = migrate_type; + stat->callsite = callsite; this.page = page; stat =search_page_alloc_stat (&this, true);@@ -416,6 +589,18 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, stat->nr_alloc++; stat->alloc_bytes += bytes; + stat->callsite = callsite; + + stat =search_page_caller_stat (callsite, true); +if (stat ==NULL ) +return -ENOMEM ; + + stat->order = order; + stat->gfp_flags = gfp_flags; + stat->migrate_type = migrate_type; + + stat->nr_alloc++; + stat->alloc_bytes += bytes; order_stats[order][migrate_type]++;@@ -455,6 +640,7 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, this.page = page; this.gfp_flags = stat->gfp_flags; this.migrate_type = stat->migrate_type; + this.callsite = stat->callsite;rb_erase (&stat->node, &page_tree);free (stat);@@ -466,6 +652,13 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, stat->nr_free++; stat->free_bytes += bytes; + stat =search_page_caller_stat (this.callsite, false); +if (stat ==NULL ) +return -ENOENT ; + + stat->nr_free++; + stat->free_bytes += bytes; +return 0 ; }@@ -576,41 +769,89 @@ 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;const char *format; -printf ("\n%.80s\n" , graph_dotted_line); -printf (" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags\n" , +printf ("\n%.105s\n" , graph_dotted_line); +printf (" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n" , use_pfn ?"PFN" :"Page" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line);if (use_pfn) - format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx\n" ; + format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ;else - format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx\n" ; + format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ;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 (format, (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 ("%.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 | Mig.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 | %8s | %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 (" ... | ... | ... | ... | ... | ...\n" ); -printf ("%.80s\n" , graph_dotted_line); +printf ("%.105s\n" , graph_dotted_line); }static void print_slab_summary (void )@@ -682,8 +923,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 (); }@@ -802,6 +1045,7 @@ static void sort_result(void) }if (kmem_page) {__sort_page_result (&page_alloc_tree, &page_alloc_sorted); +__sort_page_result (&page_caller_tree, &page_caller_sorted); } }@@ -1084,7 +1328,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 *));@@ -1099,6 +1343,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]); }@@ -1159,7 +1405,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) file.path = input_name; - session =perf_session__new (&file, false, &perf_kmem); + kmem_session = session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;@@ -1172,6 +1418,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.2
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerRe: [PATCH 4/9] perf kmem: Implement stat --page --caller
Hi Arnaldo,
Quoted 12 lines · Arnaldo Carvalho de Melo
Em Mon, Apr 06, 2015 at 02:36:11PM +0900, Namhyung Kim escreveu:
tools/perf/builtin-kmem.c struct page_stat { · line 314
Quoted 9 lines
tools/perf/builtin-kmem.c +9
-0+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; +Why having a blank like here?
Will remove.
Quoted 17 lines
Quoted 5 lines
tools/perf/builtin-kmem.c +4
-0+regex_t alloc_func_regex; +const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ; + + ret =regcomp (&alloc_func_regex, pattern,REG_EXTENDED );tools/perf/builtin-kmem.c struct page_stat { · line 328
Quoted 10 lines
tools/perf/builtin-kmem.c +10
-0+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 );What if the map can't be loaded?
Hmm.. yes, I need to check the return code.
Quoted 30 lines
Quoted 30 lines
tools/perf/builtin-kmem.c +29
-0+ +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'. +*/
Quoted 8 lines
tools/perf/builtin-kmem.c struct page_stat { · line 365
Quoted 8 lines
tools/perf/builtin-kmem.c +8
-0+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 ();
And here too..
Quoted 52 lines
Quoted 39 lines
tools/perf/builtin-kmem.c +35
-0+ + 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"\n" , sample->ip); +return sample->ip; +} + +static struct page_stat *search_page (u64 page,bool create) {struct rb_node **node = &page_tree.rb_node;struct rb_node *parent =NULL ;tools/perf/builtin-kmem.c static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool cre · line 498
Quoted 8 lines
tools/perf/builtin-kmem.c +5
-0@@ -357,6 +491,41 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool crereturn 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;Please use the "findnew" idiom to name this function, looking at only its name one things it searches a tree, a read only operation, but it may insert elements too, a modify operation.
Quoted 3 lines
Since we use the findnew idiom elsewhere for operations that do that, i.e. optimize the "new" part of "findnew" by using the "find" part, please use it here as well.
OK. Will change and resend v7 soon.
Thanks for your review!
Namhyung
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 5/9 perf kmem: Support sort keys on page analysis
In reply to Namhyung KimAdd new sort keys for page: page, order, migtype, 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/migtype/gfp will be printed on a different line.
# perf kmem stat --page --caller -l 10 -s order,hit
--------------------------------------------------------------------------------------------
Total alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite
--------------------------------------------------------------------------------------------
64 | 4 | 2 | RECLAIM | 00285250 | new_slab
50,144 | 12,536 | 0 | MOVABLE | 0102005a | __page_cache_alloc
52 | 13 | 0 | UNMOVABL | 002084d0 | pte_alloc_one
40 | 10 | 0 | MOVABLE | 000280da | handle_mm_fault
28 | 7 | 0 | UNMOVABL | 000000d0 | __pollwait
20 | 5 | 0 | MOVABLE | 000200da | do_wp_page
20 | 5 | 0 | MOVABLE | 000200da | do_cow_fault
16 | 4 | 0 | UNMOVABL | 00000200 | __tlb_remove_page
16 | 4 | 0 | UNMOVABL | 000084d0 | __pmd_alloc
8 | 2 | 0 | UNMOVABL | 000084d0 | __pud_alloc
... | ... | ... | ... | ... | ...--------------------------------------------------------------------------------------------
--- tools/perf/Documentation/perf-kmem.txt | 6 +- tools/perf/builtin-kmem.c | 398 ++++++++++++++++++++++++++------- 2 files changed, 317 insertions(+), 87 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, + migtype, 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 +312 -86
625 lines not on this page · raw message
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 6/9 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 | 103 ++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 39 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 +64 -39
203 lines not on this page · raw message
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 7/9 perf kmem: Print gfp flags in human readable string
In reply to Namhyung KimSave libtraceevent output and print it in the header.
# perf kmem stat --page --caller # GFP flags # --------- # 00000010: NI: GFP_NOIO # 000000d0: K: GFP_KERNEL # 00000200: NWR: GFP_NOWARN # 000084d0: K|R|Z: GFP_KERNEL|GFP_REPEAT|GFP_ZERO # 000200d2: HU: GFP_HIGHUSER # 000200da: HUM: GFP_HIGHUSER_MOVABLE # 000280da: HUM|Z: GFP_HIGHUSER_MOVABLE|GFP_ZERO # 002084d0: K|R|Z|NT: GFP_KERNEL|GFP_REPEAT|GFP_ZERO|GFP_NOTRACK # 0102005a: NF|HW|M: GFP_NOFS|GFP_HARDWALL|GFP_MOVABLE
---------------------------------------------------------------------------------------------------------
Total alloc (KB) | Hits | Order | Migration type | GFP flags | Callsite
---------------------------------------------------------------------------------------------------------
60 | 15 | 0 | UNMOVABLE | K|R|Z|NT | pte_alloc_one
40 | 10 | 0 | MOVABLE | HUM|Z | handle_mm_fault
24 | 6 | 0 | MOVABLE | HUM | do_wp_page
24 | 6 | 0 | UNMOVABLE | K | __pollwait...
--- tools/perf/builtin-kmem.c | 221 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 208 insertions(+), 13 deletions(-)
tools/perf/builtin-kmem.c +208 -13
@@ -545,6 +545,176 @@ static bool valid_page(u64 pfn_or_page)return true; } +struct gfp_flag { +unsigned int flags; +char *compact_str; +char *human_readable; +}; + +static struct gfp_flag *gfps; +static int nr_gfps; + +static int gfpcmp (const void *a,const void *b) +{ +const struct gfp_flag *fa = a; +const struct gfp_flag *fb = b; + +return fa->flags - fb->flags; +} + +/* see include/trace/events/gfpflags.h */ +static const struct { +const char *original; +const char *compact; +} gfp_compact_table[] = { + {"GFP_TRANSHUGE" ,"THP" }, + {"GFP_HIGHUSER_MOVABLE" ,"HUM" }, + {"GFP_HIGHUSER" ,"HU" }, + {"GFP_USER" ,"U" }, + {"GFP_TEMPORARY" ,"TMP" }, + {"GFP_KERNEL" ,"K" }, + {"GFP_NOFS" ,"NF" }, + {"GFP_ATOMIC" ,"A" }, + {"GFP_NOIO" ,"NI" }, + {"GFP_HIGH" ,"H" }, + {"GFP_WAIT" ,"W" }, + {"GFP_IO" ,"I" }, + {"GFP_COLD" ,"CO" }, + {"GFP_NOWARN" ,"NWR" }, + {"GFP_REPEAT" ,"R" }, + {"GFP_NOFAIL" ,"NF" }, + {"GFP_NORETRY" ,"NR" }, + {"GFP_COMP" ,"C" }, + {"GFP_ZERO" ,"Z" }, + {"GFP_NOMEMALLOC" ,"NMA" }, + {"GFP_MEMALLOC" ,"MA" }, + {"GFP_HARDWALL" ,"HW" }, + {"GFP_THISNODE" ,"TN" }, + {"GFP_RECLAIMABLE" ,"RC" }, + {"GFP_MOVABLE" ,"M" }, + {"GFP_NOTRACK" ,"NT" }, + {"GFP_NO_KSWAPD" ,"NK" }, + {"GFP_OTHER_NODE" ,"ON" }, + {"GFP_NOWAIT" ,"NW" }, +}; + +static size_t max_gfp_len; + +static char *compact_gfp_flags (char *gfp_flags) +{ +char *orig_flags =strdup (gfp_flags); +char *new_flags =NULL ; +char *str, *pos; +size_t len =0 ; + +if (orig_flags ==NULL ) +return NULL ; + + str =strtok_r (orig_flags,"|" , &pos); +while (str) { +size_t i; +char *new; +const char *cpt; + +for (i =0 ; i <ARRAY_SIZE (gfp_compact_table); i++) { +if (strcmp (gfp_compact_table[i].original, str)) +continue ; + + cpt = gfp_compact_table[i].compact; + new =realloc (new_flags, len +strlen (cpt) +2 ); +if (new ==NULL ) { +free (new_flags); +return NULL ; + } + + new_flags = new; + +if (!len) { +strcpy (new_flags, cpt); + }else { +strcat (new_flags,"|" ); +strcat (new_flags, cpt); + len++; + } + + len +=strlen (cpt); + } + + str =strtok_r (NULL ,"|" , &pos); + } + +if (max_gfp_len < len) + max_gfp_len = len; + +free (orig_flags); +return new_flags; +} + +static char *compact_gfp_string (unsigned long gfp_flags) +{ +struct gfp_flag key = { + .flags = gfp_flags, + }; +struct gfp_flag *gfp; + + gfp =bsearch (&key, gfps, nr_gfps,sizeof (*gfps), gfpcmp); +if (gfp) +return gfp->compact_str; + +return NULL ; +} + +static int parse_gfp_flags (struct perf_evsel *evsel,struct perf_sample *sample, +unsigned int gfp_flags) +{ +struct pevent_record record = { + .cpu = sample->cpu, + .data = sample->raw_data, + .size = sample->raw_size, + }; +struct trace_seq seq; +char *str, *pos; + +if (nr_gfps) { +struct gfp_flag key = { + .flags = gfp_flags, + }; + +if (bsearch (&key, gfps, nr_gfps,sizeof (*gfps), gfpcmp)) +return 0 ; + } + +trace_seq_init (&seq); +pevent_event_info (&seq, evsel->tp_format, &record); + + str =strtok_r (seq.buffer," " , &pos); +while (str) { +if (!strncmp (str,"gfp_flags=" ,10 )) { +struct gfp_flag *new; + + new =realloc (gfps, (nr_gfps +1 ) *sizeof (*gfps)); +if (new ==NULL ) +return -ENOMEM ; + + gfps = new; + new += nr_gfps++; + + new->flags = gfp_flags; + new->human_readable =strdup (str +10 ); + new->compact_str =compact_gfp_flags (str +10 ); +if (!new->human_readable || !new->compact_str) +return -ENOMEM ; + +qsort (gfps, nr_gfps,sizeof (*gfps), gfpcmp); + } + + str =strtok_r (NULL ," " , &pos); + } + +trace_seq_destroy (&seq); +return 0 ; +} +static int perf_evsel__process_page_alloc_event (struct perf_evsel *evsel,struct perf_sample *sample) {
@@ -577,6 +747,9 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,return 0 ; } +if (parse_gfp_flags (evsel, sample, gfp_flags) <0 ) +return -1 ; + callsite =find_callsite (evsel, sample);/*
@@ -796,16 +969,18 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)struct rb_node *next =rb_first (&page_alloc_sorted);struct machine *machine = &session->machines.host;const char *format; +int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line); -printf (" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n" , - use_pfn ?"PFN" :"Page" , live_page ?"Live" :"Total" ); +printf (" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n" , + use_pfn ?"PFN" :"Page" , live_page ?"Live" :"Total" , + gfp_len,"GFP flags" );printf ("%.105s\n" , graph_dotted_line);if (use_pfn) - format =" %16llu | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ; + format =" %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n" ;else - format =" %016llx | %'16llu | %'9d | %5d | %8s | %08lx | %s\n" ; + format =" %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n" ;while (next && n_lines--) {struct page_stat *data;
@@ -826,13 +1001,15 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines) (unsigned long long )data->alloc_bytes /1024 , data->nr_alloc, data->order, migrate_type_str[data->migrate_type], - (unsigned long)data->gfp_flags, caller); + gfp_len, compact_gfp_string(data->gfp_flags), caller); next =rb_next (next); } -if (n_lines ==-1 ) -printf (" ... | ... | ... | ... | ... | ... | ...\n" ); +if (n_lines ==-1 ) { +printf (" ... | ... | ... | ... | ... | %-*s | ...\n" , + gfp_len,"..." ); + }printf ("%.105s\n" , graph_dotted_line); }
@@ -841,10 +1018,11 @@ 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; +int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line); -printf (" %5s alloc (KB) | Hits | Order | Mig.type | GFP flags | Callsite\n" , - live_page ?"Live" :"Total" ); +printf (" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n" , + live_page ?"Live" :"Total" , gfp_len,"GFP flags" );printf ("%.105s\n" , graph_dotted_line);while (next && n_lines--) {
@@ -862,21 +1040,36 @@ static void __print_page_caller_result(struct perf_session *session, int n_lineselse scnprintf (buf,sizeof (buf),"%" PRIx64, data->callsite); -printf (" %'16llu | %'9d | %5d | %8s | %08lx | %s\n" , +printf (" %'16llu | %'9d | %5d | %8s | %-*s | %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); + gfp_len,compact_gfp_string (data->gfp_flags), caller); next =rb_next (next); } -if (n_lines ==-1 ) -printf (" ... | ... | ... | ... | ... | ...\n" ); +if (n_lines ==-1 ) { +printf (" ... | ... | ... | ... | %-*s | ...\n" , + gfp_len,"..." ); + }printf ("%.105s\n" , graph_dotted_line); } +static void print_gfp_flags (void ) +{ +int i; + +printf ("# GFP flags\n" ); +printf ("# ---------\n" ); +for (i =0 ; i < nr_gfps; i++) { +printf ("# %08x: %*s: %s\n" , gfps[i].flags, + (int ) max_gfp_len, gfps[i].compact_str, + gfps[i].human_readable); + } +} +static void print_slab_summary (void ) {printf ("\nSUMMARY (SLAB allocator)" );
@@ -946,6 +1139,8 @@ static void print_slab_result(struct perf_session *session)static void print_page_result (struct perf_session *session) { +if (caller_flag || alloc_flag) +print_gfp_flags ();if (caller_flag)__print_page_caller_result (session, caller_lines);if (alloc_flag)
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 8/9 perf kmem: Add kmem.default config option
In reply to Namhyung KimCurrently perf kmem command will select --slab if neither --slab nor --page is given for backward compatibility. Add kmem.default config option to select the default value ('page' or 'slab').
# cat ~/.perfconfig [kmem] default = page
# perf kmem stat
SUMMARY (page allocator)
========================
Total allocation requests : 1,518 [ 6,096 KB ] Total free requests : 1,431 [ 5,748 KB ]
Total alloc+freed requests : 1,330 [ 5,344 KB ] Total alloc-only requests : 188 [ 752 KB ] Total free-only requests : 101 [ 404 KB ]
Total allocation failures : 0 [ 0 KB ]
...
--- tools/perf/builtin-kmem.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-)
tools/perf/builtin-kmem.c +29 -3
@@ -28,6 +28,10 @@ static int kmem_slab;static int kmem_page;static long kmem_page_size; +static enum { +KMEM_SLAB , +KMEM_PAGE , +} kmem_default =KMEM_SLAB ;/* for backward compatibility */ struct alloc_stat ;typedef int (*sort_fn_t )(void *,void *);
@@ -1673,7 +1677,8 @@ static int parse_sort_opt(const struct option *opt __maybe_unused,if (!arg)return -1 ; -if (kmem_page > kmem_slab) { +if (kmem_page > kmem_slab || + (kmem_page ==0 && kmem_slab ==0 && kmem_default ==KMEM_PAGE )) {if (caller_flag > alloc_flag)return setup_page_sorting (&page_caller_sort, arg);else
@@ -1789,6 +1794,22 @@ static int __cmd_record(int argc, const char **argv)return cmd_record (i, rec_argv,NULL ); } +static int kmem_config (const char *var,const char *value,void *cb) +{ +if (!strcmp (var,"kmem.default" )) { +if (!strcmp (value,"slab" )) + kmem_default =KMEM_SLAB ; +else if (!strcmp (value,"page" )) + kmem_default =KMEM_PAGE ; +else +pr_err ("invalid default value ('slab' or 'page' required): %s\n" , + value); +return 0 ; + } + +return perf_default_config (var, value, cb); +} +int cmd_kmem (int argc,const char **argv,const char *prefix__maybe_unused ) {struct perf_data_file file = {
@@ -1825,14 +1846,19 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)struct perf_session *session;int ret =-1 ; +perf_config (kmem_config,NULL ); argc =parse_options_subcommand (argc, argv, kmem_options, kmem_subcommands, kmem_usage,0 );if (!argc)usage_with_options (kmem_usage, kmem_options); -if (kmem_slab ==0 && kmem_page ==0 ) - kmem_slab =1 ;/* for backward compatibility */ +if (kmem_slab ==0 && kmem_page ==0 ) { +if (kmem_default ==KMEM_SLAB ) + kmem_slab =1 ; +else + kmem_page =1 ; + }if (!strncmp (argv[0 ],"rec" ,3 )) {symbol__init (NULL );
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerPATCH 9/9 tools lib traceevent: Honor operator priority
In reply to Namhyung KimCurrently it ignores operator priority and just sets processed args as a right operand. But it could result in priority inversion in case that the right operand is also a operator arg and its priority is lower.
For example, following print format is from new kmem events.
"page=%p", REC->pfn != -1UL ? (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)) : ((void *)0)
But this was treated as below:
REC->pfn != ((null - 1UL) ? ((struct page *)0xffffea0000000000UL + REC->pfn) : (void *) 0)
In this case, the right arg was '?' operator which has lower priority. But it just sets the whole arg so making the output confusing - page was always 0 or 1 since that's the result of logical operation.
With this patch, it can handle it properly like following:
((REC->pfn != (null - 1UL)) ? ((struct page *)0xffffea0000000000UL + REC->pfn) : (void *) 0)
--- tools/lib/traceevent/event-parse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
tools/lib/traceevent/event-parse.c +16 -1
@@ -1939,7 +1939,22 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) goto out_warn_free; type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* swap ops according to the priority */ + arg->op.right = right->op.left; + + tmp = *arg; + *arg = *right; + *right = tmp; + + arg->op.left = right; + }else { + arg->op.right = right; + } }else if (strcmp (token,"[" ) ==0 ) {
Signature · 7 lines
2.3.2 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 9/9] tools lib traceevent: Honor operator priority
Quoted 12 lines · On Mon, 6 Apr 2015 14:36:16 +0900 Namhyung Kim <[email protected]> wrote:
Currently it ignores operator priority and just sets processed args as a right operand. But it could result in priority inversion in case that the right operand is also a operator arg and its priority is lower.
For example, following print format is from new kmem events.
"page=%p",
REC->pfn!= -1UL ? (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)) : ((void *)0)But this was treated as below:
REC->pfn!= ((null - 1UL) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)
Quoted 7 lines
In this case, the right arg was '?' operator which has lower priority. But it just sets the whole arg so making the output confusing - page was always 0 or 1 since that's the result of logical operation.
With this patch, it can handle it properly like following:
((
REC->pfn!= (null - 1UL)) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)
Nice catch. One nit.
Quoted 12 lines
Cc Steven Rostedt <[email protected]>Signed-off-by Namhyung Kim <[email protected]>--- tools/lib/traceevent/event-parse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)tools/lib/traceevent/event-parse.c +0
-0
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1947
Quoted 10 lines
tools/lib/traceevent/event-parse.c +6
-1@@ -1939,7 +1939,22 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) goto out_warn_free; type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* swap ops according to the priority */
This isn't really a swap. Better term to use is "rotate".
But other than that,
-- Steve
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1960
Quoted 13 lines
tools/lib/traceevent/event-parse.c +10
-0+ arg->op.right = right->op.left; + + tmp = *arg; + *arg = *right; + *right = tmp; + + arg->op.left = right; + }else { + arg->op.right = right; + } }else if (strcmp (token,"[" ) ==0 ) {
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerRe: [PATCH 9/9] tools lib traceevent: Honor operator priority
Hi Steve,
Quoted 39 lines · Steven Rostedt
Quoted 12 lines · On Mon, 6 Apr 2015 14:36:16 +0900 Namhyung Kim <[email protected]> wrote:
Currently it ignores operator priority and just sets processed args as a right operand. But it could result in priority inversion in case that the right operand is also a operator arg and its priority is lower.
For example, following print format is from new kmem events.
"page=%p",
REC->pfn!= -1UL ? (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)) : ((void *)0)But this was treated as below:
REC->pfn!= ((null - 1UL) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)Quoted 7 lines
In this case, the right arg was '?' operator which has lower priority. But it just sets the whole arg so making the output confusing - page was always 0 or 1 since that's the result of logical operation.
With this patch, it can handle it properly like following:
((
REC->pfn!= (null - 1UL)) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)Nice catch. One nit.
Quoted 14 lines
Cc Steven Rostedt <[email protected]>Signed-off-by Namhyung Kim <[email protected]>--- tools/lib/traceevent/event-parse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)tools/lib/traceevent/event-parse.c +0
-0@@ -1939,7 +1939,22 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) goto out_warn_free;
Quoted 10 lines
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1947
Quoted 8 lines
tools/lib/traceevent/event-parse.c +6
-1type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* swap ops according to the priority */ This isn't really a swap. Better term to use is "rotate".
You're right!
Quoted 4 lines
But other than that,
Acked-by Steven Rostedt <[email protected]>
Thanks for the review
Namhyung
Quoted 12 lines
Quoted 12 lines
tools/lib/traceevent/event-parse.c +10
-0+ arg->op.right = right->op.left; + + tmp = *arg; + *arg = *right; + *right = tmp; + + arg->op.left = right; + }else { + arg->op.right = right; + }
Quoted 8 lines
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1960
Quoted 2 lines
tools/lib/traceevent/event-parse.c +0
-0}else if (strcmp (token,"[" ) ==0 ) {--
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/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 9/9] tools lib traceevent: Honor operator priority
Em Tue, Apr 07, 2015 at 04:52:26PM +0900, Namhyung Kim escreveu:
Quoted 12 lines · Steven Rostedt
Quoted 10 lines
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1947
Quoted 8 lines
tools/lib/traceevent/event-parse.c +6
-1type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* swap ops according to the priority */ This isn't really a swap. Better term to use is "rotate".
Quoted 7 lines
You're right!
Quoted 3 lines
But other than that,
Acked-by Steven Rostedt <[email protected]>Thanks for the review
Ok, so just doing that s/swap/rotate/g, sticking Rostedt's ack and applying, ok?
- Arnaldo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Steven Rostedt
maintainerRe: [PATCH 9/9] tools lib traceevent: Honor operator priority
Quoted 2 lines · On Tue, 7 Apr 2015 10:02:08 -0300 Arnaldo Carvalho de Melo <[email protected]>…
Ok, so just doing that s/swap/rotate/g, sticking Rostedt's ack and applying, ok?
I'm fine with that.
-- Steve
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Namhyung Kim
maintainerRe: [PATCH 9/9] tools lib traceevent: Honor operator priority
In reply to Arnaldo Carvalho de MeloQuoted 15 lines · Arnaldo Carvalho de Melo
Em Tue, Apr 07, 2015 at 04:52:26PM +0900, Namhyung Kim escreveu:
Quoted 13 lines · Steven Rostedt
Quoted 10 lines
tools/lib/traceevent/event-parse.c process_op(struct event_format *event, struct print_arg *arg, char **tok) · line 1947
Quoted 8 lines
tools/lib/traceevent/event-parse.c +6
-1type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* swap ops according to the priority */ This isn't really a swap. Better term to use is "rotate".
You're right!
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
But other than that,
Acked-by Steven Rostedt <[email protected]>Thanks for the review
Ok, so just doing that s/swap/rotate/g, sticking Rostedt's ack and applying, ok?
Sure thing!
Thanks for your work,
Namhyung
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
tip-bot for Namhyung Kim
merge bot[tip:perf/core] tools lib traceevent: Honor operator priority
In reply to Namhyung Kimtools lib traceevent: Honor operator priority
Commit-ID: 3201f0dc42f7fad9387afc4692cea3d0c730cba2 Gitweb: http://git.kernel.org/tip/3201f0dc42f7fad9387afc4692cea3d0c730cba2 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 6 Apr 2015 14:36:16 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 8 Apr 2015 09:07:09 -0300
tools lib traceevent: Honor operator priority
Currently it ignores operator priority and just sets processed args as a right operand. But it could result in priority inversion in case that the right operand is also a operator arg and its priority is lower.
For example, following print format is from new kmem events.
"page=%p", REC->pfn != -1UL ? (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)) : ((void *)0)
But this was treated as below:
REC->pfn != ((null - 1UL) ? ((struct page *)0xffffea0000000000UL + REC->pfn) : (void *) 0)
In this case, the right arg was '?' operator which has lower priority. But it just sets the whole arg so making the output confusing - page was always 0 or 1 since that's the result of logical operation.
With this patch, it can handle it properly like following:
((REC->pfn != (null - 1UL)) ? ((struct page *)0xffffea0000000000UL + REC->pfn) : (void *) 0)
--- tools/lib/traceevent/event-parse.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
tools/lib/traceevent/event-parse.c +16 -1
@@ -1939,7 +1939,22 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) goto out_warn_free; type =process_arg_token (event, right, tok, type); - arg->op.right = right; + +if (right->type ==PRINT_OP && +get_op_prio (arg->op.op) <get_op_prio (right->op.op)) { +struct print_arg tmp; + +/* rotate ops according to the priority */ + arg->op.right = right->op.left; + + tmp = *arg; + *arg = *right; + *right = tmp; + + arg->op.left = right; + }else { + arg->op.right = right; + } }else if (strcmp (token,"[" ) ==0 ) {
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 9/9] tools lib traceevent: Honor operator priority
In reply to Namhyung KimEm Mon, Apr 06, 2015 at 02:36:16PM +0900, Namhyung Kim escreveu:
Quoted 12 lines
Currently it ignores operator priority and just sets processed args as a right operand. But it could result in priority inversion in case that the right operand is also a operator arg and its priority is lower.
For example, following print format is from new kmem events.
"page=%p",
REC->pfn!= -1UL ? (((struct page *)(0xffffea0000000000UL)) + (REC->pfn)) : ((void *)0)But this was treated as below:
REC->pfn!= ((null - 1UL) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)
Quoted 7 lines
In this case, the right arg was '?' operator which has lower priority. But it just sets the whole arg so making the output confusing - page was always 0 or 1 since that's the result of logical operation.
With this patch, it can handle it properly like following:
((
REC->pfn!= (null - 1UL)) ? ((struct page *)0xffffea0000000000UL +REC->pfn) : (void *) 0)
And this one already went upstream.
- Arnaldo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>