linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
@ 2015-03-12  7:32 Namhyung Kim
  2015-03-12  7:32 ` [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given Namhyung Kim
                   ` (6 more replies)
  0 siblings, 7 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

Hello,

Currently perf kmem command only analyzes SLAB memory allocation.  And
I'd like to introduce page allocation analysis also.  Users can use
 --slab and/or --page option to select it.  If none of these options
are used, it does slab allocation analysis for backward compatibility.

The patch 1-3 are bugfix and cleanups.  Patch 4 implements basic
support for page allocation analysis, patch 5 deals with the callsite
and finally patch 6 implements sorting.

In this patchset, I used two kmem events: kmem:mm_page_alloc and
kmem_page_free for analysis as they can track every memory
allocation/free path AFAIK.  However, unlike slab tracepoint events,
those page allocation events don't provide callsite info directly.  So
I recorded callchains and extracted callsites like below:

Normal page allocation callchains look like this:

  360a7e __alloc_pages_nodemask
  3a711c alloc_pages_current
  357bc7 __page_cache_alloc   <-- callsite
  357cf6 pagecache_get_page
   48b0a prepare_pages
   494d3 __btrfs_buffered_write
   49cdf btrfs_file_write_iter
  3ceb6e new_sync_write
  3cf447 vfs_write
  3cff99 sys_write
  7556e9 system_call
    f880 __write_nocancel
   33eb9 cmd_record
   4b38e cmd_kmem
   7aa23 run_builtin
   27a9a main
   20800 __libc_start_main

But first two are internal page allocation functions so it should be
skipped.  To determine such allocation functions, I used following regex:

  ^_?_?(alloc|get_free|get_zeroed)_pages?

This gave me a following list of functions (you can see this with -v):

  alloc func: __get_free_pages
  alloc func: get_zeroed_page
  alloc func: alloc_pages_exact
  alloc func: __alloc_pages_direct_compact
  alloc func: __alloc_pages_nodemask
  alloc func: alloc_page_interleave
  alloc func: alloc_pages_current
  alloc func: alloc_pages_vma
  alloc func: alloc_page_buffers
  alloc func: alloc_pages_exact_nid

After skipping those function, it got '__page_cache_alloc'.

Other information such as allocation order, migration type and gfp
flags are provided by tracepoint events.

Basically the output will be sorted by total allocation bytes, but you
can change it by using -s/--sort option.  The following sort keys are
added to support page analysis: page, order, mtype, gfp.  Existing
'callsite', 'bytes' and 'hit' sort keys also can be used.

An example follows:

  # perf kmem record --slab --page sleep 1
  [ perf record: Woken up 0 times to write data ]
  [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]

  # perf kmem stat --page --caller -l 10 -s order,hit

  --------------------------------------------------------------------------------------------
   Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite
  --------------------------------------------------------------------------------------------
       65536/16384 |        4 |     2 |  RECLAIMABLE | 00285250 | new_slab
    51347456/4096  |    12536 |     0 |      MOVABLE | 0102005a | __page_cache_alloc
       53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
       40960/4096  |       10 |     0 |      MOVABLE | 000280da | handle_mm_fault
       28672/4096  |        7 |     0 |    UNMOVABLE | 000000d0 | __pollwait
       20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
       20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
       16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
       16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
        8192/4096  |        2 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
   ...             | ...      | ...   | ...          | ...      | ...
  --------------------------------------------------------------------------------------------

  SUMMARY (page allocator)
  ========================
  Total alloc requested: 12593
  Total alloc failure  : 0
  Total bytes allocated: 51630080
  Total free  requested: 115
  Total free  unmatched: 67
  Total bytes freed    : 471040
  
  Order     UNMOVABLE   RECLAIMABLE       MOVABLE      RESERVED   CMA/ISOLATE
  -----  ------------  ------------  ------------  ------------  ------------
      0            32             0         12557             0             0
      1             0             0             0             0             0
      2             0             4             0             0             0
      3             0             0             0             0             0
      4             0             0             0             0             0
      5             0             0             0             0             0
      6             0             0             0             0             0
      7             0             0             0             0             0
      8             0             0             0             0             0
      9             0             0             0             0             0
     10             0             0             0             0             0


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-v1 branch on my tree:

  git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git

Thanks,
Namhyung


Namhyung Kim (6):
  perf kmem: Fix segfault when invalid sort key is given
  perf kmem: Allow -v option
  perf kmem: Fix alignment of slab result table
  perf kmem: Analyze page allocator events also
  perf kmem: Implement stat --page --caller
  perf kmem: Support sort keys on page analysis

 tools/perf/Documentation/perf-kmem.txt |  18 +-
 tools/perf/builtin-kmem.c              | 961 ++++++++++++++++++++++++++++++---
 2 files changed, 915 insertions(+), 64 deletions(-)

-- 
2.3.1


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-14  7:06   ` [tip:perf/core] " tip-bot for Namhyung Kim
  2015-03-12  7:32 ` [PATCH 2/6] perf kmem: Allow -v option Namhyung Kim
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

When it tries to free 'str', it was already updated by strsep() - so
it needs to save the original pointer.

  # perf kmem stat -s xxx,hit
    Error: Unknown --sort key: 'xxx'
  *** Error in `perf': free(): invalid pointer: 0x0000000000e9e7b6 ***
  ======= Backtrace: =========
  /usr/lib/libc.so.6(+0x7198e)[0x7fc7e6e0d98e]
  /usr/lib/libc.so.6(+0x76dee)[0x7fc7e6e12dee]
  /usr/lib/libc.so.6(+0x775cb)[0x7fc7e6e135cb]
  ./perf[0x44a1b5]
  ./perf[0x490b20]
  ./perf(parse_options_step+0x173)[0x491773]
  ./perf(parse_options_subcommand+0xa7)[0x491fb7]
  ./perf(cmd_kmem+0x2bc)[0x44ae4c]
  ./perf[0x47aa13]
  ./perf(main+0x60a)[0x427a9a]
  /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fc7e6dbc800]
  ./perf(_start+0x29)[0x427bb9]

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/builtin-kmem.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index f295141025bc..a0da5fb44b9e 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -559,6 +559,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
 {
 	char *tok;
 	char *str = strdup(arg);
+	char *pos = str;
 
 	if (!str) {
 		pr_err("%s: strdup failed\n", __func__);
@@ -566,7 +567,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
 	}
 
 	while (true) {
-		tok = strsep(&str, ",");
+		tok = strsep(&pos, ",");
 		if (!tok)
 			break;
 		if (sort_dimension__add(tok, sort_list) < 0) {
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 2/6] perf kmem: Allow -v option
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
  2015-03-12  7:32 ` [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-14  7:06   ` [tip:perf/core] " tip-bot for Namhyung Kim
  2015-03-12  7:32 ` [PATCH 3/6] perf kmem: Fix alignment of slab result table Namhyung Kim
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

Current perf kmem fails when -v option is used.  As it's very useful
for debugging, let's allow it.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/Documentation/perf-kmem.txt | 4 ++++
 tools/perf/builtin-kmem.c              | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
index 7c8fbbf3f61c..150253cc3c97 100644
--- a/tools/perf/Documentation/perf-kmem.txt
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -25,6 +25,10 @@ OPTIONS
 --input=<file>::
 	Select the input file (default: perf.data unless stdin is a fifo)
 
+-v::
+--verbose::
+        Be more verbose. (show symbol address, etc)
+
 --caller::
 	Show per-callsite statistics
 
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index a0da5fb44b9e..e8226ae66f53 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -663,6 +663,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 	const char * const default_sort_order = "frag,hit,bytes";
 	const struct option kmem_options[] = {
 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
+	OPT_INCR('v', "verbose", &verbose,
+		    "be more verbose (show symbol address, etc)"),
 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
 			   "show per-callsite statistics", parse_caller_opt),
 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 3/6] perf kmem: Fix alignment of slab result table
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
  2015-03-12  7:32 ` [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given Namhyung Kim
  2015-03-12  7:32 ` [PATCH 2/6] perf kmem: Allow -v option Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-14  7:07   ` [tip:perf/core] " tip-bot for Namhyung Kim
  2015-03-12  7:32 ` [PATCH 4/6] perf kmem: Analyze page allocator events also Namhyung Kim
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

Its table was a bit misaligned.  Fix it.

Before:

  # perf kmem stat --caller -l 10
  ------------------------------------------------------------------------------------------------------
   Callsite                           | Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag
  ------------------------------------------------------------------------------------------------------
   radeon_cs_parser_init.part.1+11a   |      2080/260   |      1504/188   |        8 |        0 | 27.692%
   radeon_cs_parser_init.part.1+e1    |       384/96    |       288/72    |        4 |        0 | 25.000%
   radeon_cs_parser_init.part.1+93    |       128/32    |        96/24    |        4 |        0 | 25.000%
   load_elf_binary+a39                |       512/512   |       392/392   |        1 |        0 | 23.438%
   __alloc_skb+89                     |      6144/877   |      4800/685   |        7 |        6 | 21.875%
   radeon_fence_emit+5c               |      1152/192   |       912/152   |        6 |        0 | 20.833%
   radeon_cs_parser_relocs+ad         |      8192/2048  |      6624/1656  |        4 |        0 | 19.141%
   radeon_sa_bo_new+78                |      1280/64    |      1120/56    |       20 |        0 | 12.500%
   load_elf_binary+2c4                |        32/32    |        28/28    |        1 |        0 | 12.500%
   anon_vma_prepare+101               |       576/72    |       512/64    |        8 |        0 | 11.111%
   ...                                | ...             | ...             | ...    | ...      | ...
  ------------------------------------------------------------------------------------------------------

After:

  ---------------------------------------------------------------------------------------------------------
   Callsite                           | Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag
  ---------------------------------------------------------------------------------------------------------
   radeon_cs_parser_init.part.1+11a   |      2080/260   |      1504/188   |        8 |         0 | 27.692%
   radeon_cs_parser_init.part.1+e1    |       384/96    |       288/72    |        4 |         0 | 25.000%
   radeon_cs_parser_init.part.1+93    |       128/32    |        96/24    |        4 |         0 | 25.000%
   load_elf_binary+a39                |       512/512   |       392/392   |        1 |         0 | 23.438%
   __alloc_skb+89                     |      6144/877   |      4800/685   |        7 |         6 | 21.875%
   radeon_fence_emit+5c               |      1152/192   |       912/152   |        6 |         0 | 20.833%
   radeon_cs_parser_relocs+ad         |      8192/2048  |      6624/1656  |        4 |         0 | 19.141%
   radeon_sa_bo_new+78                |      1280/64    |      1120/56    |       20 |         0 | 12.500%
   load_elf_binary+2c4                |        32/32    |        28/28    |        1 |         0 | 12.500%
   anon_vma_prepare+101               |       576/72    |       512/64    |        8 |         0 | 11.111%
   ...                                | ...             | ...             | ...      | ...       | ...
  ---------------------------------------------------------------------------------------------------------

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/builtin-kmem.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index e8226ae66f53..a58b059827b1 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -275,10 +275,10 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 	struct rb_node *next;
 	struct machine *machine = &session->machines.host;
 
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 
 	next = rb_first(root);
 
@@ -304,7 +304,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
 		printf(" %-34s |", buf);
 
-		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
+		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
 		       (unsigned long long)data->bytes_alloc,
 		       (unsigned long)data->bytes_alloc / data->hit,
 		       (unsigned long long)data->bytes_req,
@@ -317,9 +317,9 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 	}
 
 	if (n_lines == -1)
-		printf(" ...                                | ...             | ...             | ...    | ...      | ...   \n");
+		printf(" ...                                | ...             | ...             | ...      | ...       | ...   \n");
 
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 }
 
 static void print_summary(void)
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 4/6] perf kmem: Analyze page allocator events also
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
                   ` (2 preceding siblings ...)
  2015-03-12  7:32 ` [PATCH 3/6] perf kmem: Fix alignment of slab result table Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-12 11:01   ` Jiri Olsa
  2015-03-12  7:32 ` [PATCH 5/6] perf kmem: Implement stat --page --caller Namhyung Kim
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

The perf kmem command records and analyze kernel memory allocation
only for SLAB objects.  This patch implement a simple page allocator
analyzer using kmem:mm_page_alloc and kmem_mm_page_free events.

It adds two new options of --slab and --page.  The --slab option is
for analyzing SLAB allocator and that's what perf kmem currently does.

The new --page option enables page allocator events and analyze kernel
memory usage in page unit.  Currently, 'stat --alloc' subcommand is
implemented only.

If none of these --slab nor --page is specified, --slab is implied.

  # perf kmem stat --page --alloc --line 10

  --------------------------------------------------------------------------------
   Page             | Total_alloc/Per | Hit      | Order | Migrate type | GFP flag
  --------------------------------------------------------------------------------
   ffffea0015e48e00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea0015e47400 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea001440f600 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea001440cc00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c6300 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c5c00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c5000 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c4f00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c4e00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ffffea00140c4d00 |     16384/16384 |        1 |     2 |  RECLAIMABLE | 00285250
   ...              | ...             | ...      | ...   | ...          | ...
  --------------------------------------------------------------------------------

  SUMMARY (page allocator)
  ========================
  Total alloc requested: 44260
  Total alloc failure  : 0
  Total bytes allocated: 181510144
  Total free  requested: 49
  Total free  unmatched: 68
  Total bytes freed    : 200704

  Order     UNMOVABLE   RECLAIMABLE       MOVABLE      RESERVED   CMA/ISOLATE
  -----  ------------  ------------  ------------  ------------  ------------
      0            32             0         44210             0             0
      1             0             0             0             0             0
      2             0            18             0             0             0
      3             0             0             0             0             0
      4             0             0             0             0             0
      5             0             0             0             0             0
      6             0             0             0             0             0
      7             0             0             0             0             0
      8             0             0             0             0             0
      9             0             0             0             0             0
     10             0             0             0             0             0

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/Documentation/perf-kmem.txt |   9 +-
 tools/perf/builtin-kmem.c              | 353 +++++++++++++++++++++++++++++++--
 2 files changed, 346 insertions(+), 16 deletions(-)

diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
index 150253cc3c97..0ccc0a248022 100644
--- a/tools/perf/Documentation/perf-kmem.txt
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -3,7 +3,7 @@ perf-kmem(1)
 
 NAME
 ----
-perf-kmem - Tool to trace/measure kernel memory(slab) properties
+perf-kmem - Tool to trace/measure kernel memory properties
 
 SYNOPSIS
 --------
@@ -46,6 +46,13 @@ OPTIONS
 --raw-ip::
 	Print raw ip instead of symbol
 
+--slab::
+	Analyze SLAB allocator events.  This option is enabled by default.
+	To disable it, use --no-slab.
+
+--page::
+	Analyze page allocator events
+
 SEE ALSO
 --------
 linkperf:perf-record[1]
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index a58b059827b1..0807183e63ae 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -21,6 +21,11 @@
 #include <linux/rbtree.h>
 #include <linux/string.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 *);
 
@@ -225,6 +230,132 @@ 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 unsigned long nr_page_allocs;
+static unsigned long nr_page_frees;
+static unsigned long nr_page_fails;
+static unsigned long nr_page_nomatch;
+
+#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_sorted;
+
+static struct page_stat *search_page_stat(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 perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
+						struct perf_sample *sample)
+{
+	u64 page = perf_evsel__intval(evsel, sample, "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;
+
+	if (page == 0) {
+		nr_page_fails++;
+		return 0;
+	}
+
+	/*
+	 * XXX: We'd better to use PFN instead of page pointer to deal
+	 * with things like partial freeing.  But AFAIK there's no way
+	 * to convert a pointer to struct page into PFN in userspace.
+	 */
+	stat = search_page_stat(page, true);
+	if (stat == NULL)
+		return -1;
+
+	stat->order = order;
+	stat->gfp_flags = gfp_flags;
+	stat->migrate_type = migrate_type;
+
+	stat->nr_alloc++;
+	nr_page_allocs++;
+	stat->alloc_bytes += bytes;
+	total_page_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 = perf_evsel__intval(evsel, sample, "page");
+	unsigned int order = perf_evsel__intval(evsel, sample, "order");
+	u64 bytes = kmem_page_size << order;
+	struct page_stat *stat;
+
+	nr_page_frees++;
+	total_page_free_bytes += bytes;
+
+	stat = search_page_stat(page, false);
+	if (stat == NULL) {
+		pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
+			  page, order);
+		nr_page_nomatch++;
+		return 0;
+	}
+
+	stat->nr_free++;
+	stat->free_bytes += bytes;
+
+	return 0;
+}
+
 typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
 				  struct perf_sample *sample);
 
@@ -269,8 +400,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;
@@ -322,9 +454,51 @@ 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[] = {
+	"UNMOVABLE",
+	"RECLAIMABLE",
+	"MOVABLE",
+	"RESERVED",
+	"CMA/ISOLATE",
+	"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);
+
+	printf("\n%.80s\n", graph_dotted_line);
+	printf(" Page             | Total_alloc/Per | Hit      | Order | Migrate type | GFP flag\n");
+	printf("%.80s\n", graph_dotted_line);
+
+	while (next && n_lines--) {
+		struct page_stat *data;
+
+		data = rb_entry(next, struct page_stat, node);
+
+		printf(" %016llx | %9llu/%-5lu | %8d | %5d | %12s | %08lx\n",
+		       (unsigned long long)data->page,
+		       (unsigned long long)data->alloc_bytes,
+		       (unsigned long)data->alloc_bytes / data->nr_alloc,
+		       data->nr_alloc, data->order,
+		       migrate_type_str[data->migrate_type],
+		       (unsigned long)data->gfp_flags);
+
+		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",
@@ -334,13 +508,56 @@ 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;
+
+	printf("\nSUMMARY (page allocator)");
+	printf("\n========================\n");
+	printf("Total alloc requested: %lu\n", nr_page_allocs);
+	printf("Total alloc failure  : %lu\n", nr_page_fails);
+	printf("Total bytes allocated: %lu\n", total_page_alloc_bytes);
+	printf("Total free  requested: %lu\n", nr_page_frees);
+	printf("Total free  unmatched: %lu\n", nr_page_nomatch);
+	printf("Total bytes freed    : %lu\n", total_page_free_bytes);
+
+	printf("\n");
+	printf("%5s  %12s  %12s  %12s  %12s  %12s\n", "Order",
+	       migrate_type_str[0], migrate_type_str[1], migrate_type_str[2],
+	       migrate_type_str[3], migrate_type_str[4]);
+	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  %12d  %12d  %12d  %12d  %12d\n", o,
+		       order_stats[o][0], order_stats[o][1], order_stats[o][2],
+		       order_stats[o][3], order_stats[o][4]);
+	}
+}
+
+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 {
@@ -352,8 +569,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;
@@ -382,8 +599,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;
@@ -395,26 +612,78 @@ 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_tree, &page_alloc_sorted);
+	}
 }
 
 static int __cmd_kmem(struct perf_session *session)
 {
 	int err = -EINVAL;
 	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"))
@@ -611,6 +880,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)
 {
@@ -633,6 +918,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",
@@ -640,10 +927,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)
@@ -652,6 +948,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];
 
@@ -674,6 +979,10 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 		     parse_sort_opt),
 	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_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 };
@@ -694,6 +1003,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);
@@ -703,6 +1015,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")) {
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 5/6] perf kmem: Implement stat --page --caller
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
                   ` (3 preceding siblings ...)
  2015-03-12  7:32 ` [PATCH 4/6] perf kmem: Analyze page allocator events also Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-12  7:32 ` [PATCH 6/6] perf kmem: Support sort keys on page analysis Namhyung Kim
  2015-03-12 10:41 ` [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Ingo Molnar
  6 siblings, 0 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

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/Per | Hit      | Order | Migrate type | GFP flag | Callsite
  --------------------------------------------------------------------------------
    1089536/4096  |      266 |     0 |    UNMOVABLE | 000000d0 | __pollwait
      53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
      45056/4096  |       11 |     0 |      MOVABLE | 000280da | handle_mm_fault
      20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
      20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
      16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
      16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
      12288/4096  |        3 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
       8192/4096  |        2 |     0 |    UNMOVABLE | 00000010 | bio_copy_user_iov
       4096/4096  |        1 |     0 |    UNMOVABLE | 000200d2 | pipe_write
       4096/4096  |        1 |     0 |      MOVABLE | 000280da | do_wp_page
       4096/4096  |        1 |     0 |    UNMOVABLE | 002084d0 | pgd_alloc
  --------------------------------------------------------------------------------

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/builtin-kmem.c | 299 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 275 insertions(+), 24 deletions(-)

diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 0807183e63ae..59475bd3d6d4 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -10,6 +10,7 @@
 #include "util/header.h"
 #include "util/session.h"
 #include "util/tool.h"
+#include "util/callchain.h"
 
 #include "util/parse-options.h"
 #include "util/trace-event.h"
@@ -20,6 +21,7 @@
 
 #include <linux/rbtree.h>
 #include <linux/string.h>
+#include <regex.h>
 
 static int	kmem_slab;
 static int	kmem_page;
@@ -237,6 +239,8 @@ static unsigned long nr_page_frees;
 static unsigned long nr_page_fails;
 static unsigned long nr_page_nomatch;
 
+static struct perf_session *kmem_session;
+
 #define MAX_MIGRATE_TYPES  6
 #define MAX_PAGE_ORDER     11
 
@@ -245,6 +249,7 @@ static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
 struct page_stat {
 	struct rb_node 	node;
 	u64 		page;
+	u64 		callsite;
 	int 		order;
 	unsigned 	gfp_flags;
 	unsigned 	migrate_type;
@@ -254,12 +259,142 @@ struct page_stat {
 	int 		nr_free;
 };
 
-static struct rb_root page_tree;
+static struct rb_root page_alloc_tree;
 static struct rb_root page_alloc_sorted;
+static struct rb_root page_caller_tree;
+static struct rb_root page_caller_sorted;
+
+struct alloc_func {
+	u64 start;
+	u64 end;
+	char *name;
+};
+
+static int nr_alloc_funcs;
+static struct alloc_func *alloc_func_list;
+
+static int funcmp(const void *a, const void *b)
+{
+	const struct alloc_func *fa = a;
+	const struct alloc_func *fb = b;
+
+	if (fa->start > fb->start)
+		return 1;
+	else
+		return -1;
+}
+
+static int callcmp(const void *a, const void *b)
+{
+	const struct alloc_func *fa = a;
+	const struct alloc_func *fb = b;
+
+	if (fb->start <= fa->start && fa->end < fb->end)
+		return 0;
+
+	if (fa->start > fb->start)
+		return 1;
+	else
+		return -1;
+}
+
+static int build_alloc_func_list(void)
+{
+	int ret;
+	struct map *kernel_map;
+	struct symbol *sym;
+	struct rb_node *node;
+	struct alloc_func *func;
+	struct machine *machine = &kmem_session->machines.host;
+
+	regex_t alloc_func_regex;
+	const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
+
+	ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
+	if (ret) {
+		char err[BUFSIZ];
+
+		regerror(ret, &alloc_func_regex, err, sizeof(err));
+		pr_err("Invalid regex: %s\n%s", pattern, err);
+		return -EINVAL;
+	}
+
+	kernel_map = machine->vmlinux_maps[MAP__FUNCTION];
+	map__load(kernel_map, NULL);
+
+	map__for_each_symbol(kernel_map, sym, node) {
+		if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
+			continue;
+
+		func = realloc(alloc_func_list,
+			       (nr_alloc_funcs + 1) * sizeof(*func));
+		if (func == NULL)
+			return -ENOMEM;
+
+		pr_debug("alloc func: %s\n", sym->name);
+		func[nr_alloc_funcs].start = sym->start;
+		func[nr_alloc_funcs].end   = sym->end;
+		func[nr_alloc_funcs].name  = sym->name;
+
+		alloc_func_list = func;
+		nr_alloc_funcs++;
+	}
+
+	qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
+
+	regfree(&alloc_func_regex);
+	return 0;
+}
+
+/*
+ * Find first non-memory allocation function from callchain.
+ * The allocation functions are in the 'alloc_func_list'.
+ */
+static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
+{
+	struct addr_location al;
+	struct machine *machine = &kmem_session->machines.host;
+	struct callchain_cursor_node *node;
+
+	if (alloc_func_list == NULL)
+		build_alloc_func_list();
+
+	al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
+	sample__resolve_callchain(sample, NULL, evsel, &al, 16);
+
+	callchain_cursor_commit(&callchain_cursor);
+	while (true) {
+		struct alloc_func key, *caller;
+		u64 addr;
+
+		node = callchain_cursor_current(&callchain_cursor);
+		if (node == NULL)
+			break;
+
+		key.start = key.end = node->ip;
+		caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
+				 sizeof(key), callcmp);
+		if (!caller) {
+			/* found */
+			if (node->map)
+				addr = map__unmap_ip(node->map, node->ip);
+			else
+				addr = node->ip;
+
+			return addr;
+		} else
+			pr_debug3("skipping alloc function: %s\n", caller->name);
 
-static struct page_stat *search_page_stat(unsigned long page, bool create)
+		callchain_cursor_advance(&callchain_cursor);
+	}
+
+	pr_debug2("unknown callsite: %"PRIx64, sample->ip);
+	return sample->ip;
+}
+
+static struct page_stat *search_page_alloc_stat(u64 page, bool create)
 {
-	struct rb_node **node = &page_tree.rb_node;
+	struct rb_node **node = &page_alloc_tree.rb_node;
 	struct rb_node *parent = NULL;
 	struct page_stat *data;
 
@@ -286,7 +421,42 @@ static struct page_stat *search_page_stat(unsigned long page, bool create)
 		data->page = page;
 
 		rb_link_node(&data->node, parent, node);
-		rb_insert_color(&data->node, &page_tree);
+		rb_insert_color(&data->node, &page_alloc_tree);
+	}
+
+	return data;
+}
+
+static struct page_stat *search_page_caller_stat(u64 callsite, bool create)
+{
+	struct rb_node **node = &page_caller_tree.rb_node;
+	struct rb_node *parent = NULL;
+	struct page_stat *data;
+
+	while (*node) {
+		s64 cmp;
+
+		parent = *node;
+		data = rb_entry(*node, struct page_stat, node);
+
+		cmp = data->callsite - callsite;
+		if (cmp < 0)
+			node = &parent->rb_left;
+		else if (cmp > 0)
+			node = &parent->rb_right;
+		else
+			return data;
+	}
+
+	if (!create)
+		return NULL;
+
+	data = zalloc(sizeof(*data));
+	if (data != NULL) {
+		data->callsite = callsite;
+
+		rb_link_node(&data->node, parent, node);
+		rb_insert_color(&data->node, &page_caller_tree);
 	}
 
 	return data;
@@ -301,6 +471,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
 	unsigned int migrate_type = perf_evsel__intval(evsel, sample,
 						       "migratetype");
 	u64 bytes = kmem_page_size << order;
+	u64 callsite;
 	struct page_stat *stat;
 
 	if (page == 0) {
@@ -308,22 +479,39 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
 		return 0;
 	}
 
+	callsite = find_callsite(evsel, sample);
+
 	/*
 	 * XXX: We'd better to use PFN instead of page pointer to deal
 	 * with things like partial freeing.  But AFAIK there's no way
 	 * to convert a pointer to struct page into PFN in userspace.
 	 */
-	stat = search_page_stat(page, true);
-	if (stat == NULL)
+	stat = search_page_alloc_stat(page, true);
+	if (stat == NULL) {
+		pr_err("cannot create page alloc stat\n");
 		return -1;
+	}
 
 	stat->order = order;
 	stat->gfp_flags = gfp_flags;
 	stat->migrate_type = migrate_type;
+	stat->callsite = callsite;
+	stat->nr_alloc++;
+	stat->alloc_bytes += bytes;
+
+	stat = search_page_caller_stat(callsite, true);
+	if (stat == NULL) {
+		pr_err("cannot create page caller stat\n");
+		return -1;
+	}
 
+	stat->order = order;
+	stat->gfp_flags = gfp_flags;
+	stat->migrate_type = migrate_type;
 	stat->nr_alloc++;
-	nr_page_allocs++;
 	stat->alloc_bytes += bytes;
+
+	nr_page_allocs++;
 	total_page_alloc_bytes += bytes;
 
 	order_stats[order][migrate_type]++;
@@ -342,7 +530,7 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
 	nr_page_frees++;
 	total_page_free_bytes += bytes;
 
-	stat = search_page_stat(page, false);
+	stat = search_page_alloc_stat(page, false);
 	if (stat == NULL) {
 		pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
 			  page, order);
@@ -353,6 +541,12 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
 	stat->nr_free++;
 	stat->free_bytes += bytes;
 
+	stat = search_page_caller_stat(stat->callsite, false);
+	if (stat != NULL) {
+		stat->nr_free++;
+		stat->free_bytes += bytes;
+	}
+
 	return 0;
 }
 
@@ -463,36 +657,85 @@ static const char * const migrate_type_str[] = {
 	"UNKNOWN",
 };
 
-static void __print_page_result(struct rb_root *root,
-				struct perf_session *session __maybe_unused,
-				int n_lines)
+static void __print_page_alloc_result(struct perf_session *session, int n_lines)
 {
-	struct rb_node *next = rb_first(root);
+	struct rb_node *next = rb_first(&page_alloc_sorted);
+	struct machine *machine = &session->machines.host;
 
-	printf("\n%.80s\n", graph_dotted_line);
-	printf(" Page             | Total_alloc/Per | Hit      | Order | Migrate type | GFP flag\n");
-	printf("%.80s\n", graph_dotted_line);
+	printf("\n%.92s\n", graph_dotted_line);
+	printf(" Page             | Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite\n");
+	printf("%.92s\n", graph_dotted_line);
 
 	while (next && n_lines--) {
 		struct page_stat *data;
+		struct symbol *sym;
+		struct map *map;
+		char buf[32];
+		char *caller = buf;
 
 		data = rb_entry(next, struct page_stat, node);
+		sym = machine__find_kernel_function(machine, data->callsite,
+						    &map, NULL);
+		if (sym && sym->name)
+			caller = sym->name;
+		else
+			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
 
-		printf(" %016llx | %9llu/%-5lu | %8d | %5d | %12s | %08lx\n",
+		printf(" %016llx | %9llu/%-5lu | %8d | %5d | %12s | %08lx | %s\n",
 		       (unsigned long long)data->page,
 		       (unsigned long long)data->alloc_bytes,
 		       (unsigned long)data->alloc_bytes / data->nr_alloc,
 		       data->nr_alloc, data->order,
 		       migrate_type_str[data->migrate_type],
-		       (unsigned long)data->gfp_flags);
+		       (unsigned long)data->gfp_flags, caller);
+
+		next = rb_next(next);
+	}
+
+	if (n_lines == -1)
+		printf(" ...              | ...             | ...      | ...   | ...          | ...      | ...\n");
+
+	printf("%.92s\n", graph_dotted_line);
+}
+
+static void __print_page_caller_result(struct perf_session *session, int n_lines)
+{
+	struct rb_node *next = rb_first(&page_caller_sorted);
+	struct machine *machine = &session->machines.host;
+
+	printf("\n%.92s\n", graph_dotted_line);
+	printf(" Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite\n");
+	printf("%.92s\n", graph_dotted_line);
+
+	while (next && n_lines--) {
+		struct page_stat *data;
+		struct symbol *sym;
+		struct map *map;
+		char buf[32];
+		char *caller = buf;
+
+		data = rb_entry(next, struct page_stat, node);
+		sym = machine__find_kernel_function(machine, data->callsite,
+						    &map, NULL);
+		if (sym && sym->name)
+			caller = sym->name;
+		else
+			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
+
+		printf(" %9llu/%-5lu | %8d | %5d | %12s | %08lx | %s\n",
+		       (unsigned long long)data->alloc_bytes,
+		       (unsigned long)data->alloc_bytes / data->nr_alloc,
+		       data->nr_alloc, data->order,
+		       migrate_type_str[data->migrate_type],
+		       (unsigned long)data->gfp_flags, caller);
 
 		next = rb_next(next);
 	}
 
 	if (n_lines == -1)
-		printf(" ...              | ...             | ...      | ...   | ...          | ...     \n");
+		printf(" ...             | ...      | ...   | ...          | ...      | ...\n");
 
-	printf("%.80s\n", graph_dotted_line);
+	printf("%.92s\n", graph_dotted_line);
 }
 
 static void print_slab_summary(void)
@@ -547,8 +790,10 @@ static void print_slab_result(struct perf_session *session)
 
 static void print_page_result(struct perf_session *session)
 {
+	if (caller_flag)
+		__print_page_caller_result(session, caller_lines);
 	if (alloc_flag)
-		__print_page_result(&page_alloc_sorted, session, alloc_lines);
+		__print_page_alloc_result(session, alloc_lines);
 	print_page_summary();
 }
 
@@ -666,7 +911,8 @@ static void sort_result(void)
 				   &caller_sort);
 	}
 	if (kmem_page) {
-		__sort_page_result(&page_tree, &page_alloc_sorted);
+		__sort_page_result(&page_alloc_tree, &page_alloc_sorted);
+		__sort_page_result(&page_caller_tree, &page_caller_sorted);
 	}
 }
 
@@ -696,8 +942,10 @@ static int __cmd_kmem(struct perf_session *session)
 
 	setup_pager();
 	err = perf_session__process_events(session, &perf_kmem);
-	if (err != 0)
+	if (err != 0) {
+		pr_err("error during process events: %d\n", err);
 		goto out;
+	}
 	sort_result();
 	print_result(session);
 out:
@@ -938,7 +1186,7 @@ static int __cmd_record(int argc, const char **argv)
 	if (kmem_slab)
 		rec_argc += ARRAY_SIZE(slab_events);
 	if (kmem_page)
-		rec_argc += ARRAY_SIZE(page_events);
+		rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
 
 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
@@ -953,6 +1201,8 @@ static int __cmd_record(int argc, const char **argv)
 			rec_argv[i] = strdup(slab_events[j]);
 	}
 	if (kmem_page) {
+		rec_argv[i++] = strdup("-g");
+
 		for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
 			rec_argv[i] = strdup(page_events[j]);
 	}
@@ -1011,7 +1261,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 		return __cmd_record(argc, argv);
 	}
 
-	session = perf_session__new(&file, false, &perf_kmem);
+	kmem_session = session = perf_session__new(&file, false, &perf_kmem);
 	if (session == NULL)
 		return -1;
 
@@ -1024,6 +1274,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 		}
 
 		kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
+		symbol_conf.use_callchain = true;
 	}
 
 	symbol__init(&session->header.env);
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 6/6] perf kmem: Support sort keys on page analysis
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
                   ` (4 preceding siblings ...)
  2015-03-12  7:32 ` [PATCH 5/6] perf kmem: Implement stat --page --caller Namhyung Kim
@ 2015-03-12  7:32 ` Namhyung Kim
  2015-03-12 10:41 ` [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Ingo Molnar
  6 siblings, 0 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12  7:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Ingo Molnar, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

Add new sort keys for page: page, order, mtype, gfp - existing
'bytes', 'hit' and 'callsite' sort keys also work for page.  Note that
-s/--sort option should be preceded by either of --slab or --page
option to determine where the sort keys applies.

Now it properly groups and sorts allocation stats - so same
page/caller with different order/mtype/gfp will be printed on a
different line.

  # perf kmem stat --page --caller -l 10 -s order,hit

  --------------------------------------------------------------------------------------------
   Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite
  --------------------------------------------------------------------------------------------
       65536/16384 |        4 |     2 |  RECLAIMABLE | 00285250 | new_slab
    51347456/4096  |    12536 |     0 |      MOVABLE | 0102005a | __page_cache_alloc
       53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
       40960/4096  |       10 |     0 |      MOVABLE | 000280da | handle_mm_fault
       28672/4096  |        7 |     0 |    UNMOVABLE | 000000d0 | __pollwait
       20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
       20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
       16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
       16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
        8192/4096  |        2 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
   ...             | ...      | ...   | ...          | ...      | ...
  --------------------------------------------------------------------------------------------

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/Documentation/perf-kmem.txt |   5 +-
 tools/perf/builtin-kmem.c              | 396 +++++++++++++++++++++++++++------
 2 files changed, 332 insertions(+), 69 deletions(-)

diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
index 0ccc0a248022..71f3737cf26c 100644
--- a/tools/perf/Documentation/perf-kmem.txt
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -37,7 +37,10 @@ OPTIONS
 
 -s <key[,key2...]>::
 --sort=<key[,key2...]>::
-	Sort the output (default: frag,hit,bytes)
+	Sort the output (default: 'frag,hit,bytes' for slab and 'bytes,hit'
+	for page).  Available sort keys are 'ptr, callsite, bytes, hit,
+	pingpong, frag' for slab and 'page, callsite, bytes, hit, order,
+	mtype, gfp' for page.
 
 -l <num>::
 --line=<num>::
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 59475bd3d6d4..7fe16bcb9846 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -29,7 +29,7 @@ static int	kmem_page;
 static long	kmem_page_size;
 
 struct alloc_stat;
-typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
+typedef int (*sort_fn_t)(void *, void *);
 
 static int			alloc_flag;
 static int			caller_flag;
@@ -180,8 +180,8 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
 	return ret;
 }
 
-static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
-static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
+static int ptr_cmp(void *, void *);
+static int slab_callsite_cmp(void *, void *);
 
 static struct alloc_stat *search_alloc_stat(unsigned long ptr,
 					    unsigned long call_site,
@@ -222,7 +222,8 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel,
 		s_alloc->pingpong++;
 
 		s_caller = search_alloc_stat(0, s_alloc->call_site,
-					     &root_caller_stat, callsite_cmp);
+					     &root_caller_stat,
+					     slab_callsite_cmp);
 		if (!s_caller)
 			return -1;
 		s_caller->pingpong++;
@@ -392,19 +393,35 @@ static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
 	return sample->ip;
 }
 
-static struct page_stat *search_page_alloc_stat(u64 page, bool create)
+struct sort_dimension {
+	const char		name[20];
+	sort_fn_t		cmp;
+	struct list_head	list;
+};
+
+static LIST_HEAD(page_alloc_sort_input);
+static LIST_HEAD(page_caller_sort_input);
+
+static struct page_stat *search_page_alloc_stat(struct page_stat *this,
+						bool create)
 {
 	struct rb_node **node = &page_alloc_tree.rb_node;
 	struct rb_node *parent = NULL;
 	struct page_stat *data;
+	struct sort_dimension *sort;
 
 	while (*node) {
-		s64 cmp;
+		int cmp = 0;
 
 		parent = *node;
 		data = rb_entry(*node, struct page_stat, node);
 
-		cmp = data->page - page;
+		list_for_each_entry(sort, &page_alloc_sort_input, list) {
+			cmp = sort->cmp(this, data);
+			if (cmp)
+				break;
+		}
+
 		if (cmp < 0)
 			node = &parent->rb_left;
 		else if (cmp > 0)
@@ -418,7 +435,10 @@ static struct page_stat *search_page_alloc_stat(u64 page, bool create)
 
 	data = zalloc(sizeof(*data));
 	if (data != NULL) {
-		data->page = page;
+		data->page = this->page;
+		data->order = this->order;
+		data->migrate_type = this->migrate_type;
+		data->gfp_flags = this->gfp_flags;
 
 		rb_link_node(&data->node, parent, node);
 		rb_insert_color(&data->node, &page_alloc_tree);
@@ -427,19 +447,26 @@ static struct page_stat *search_page_alloc_stat(u64 page, bool create)
 	return data;
 }
 
-static struct page_stat *search_page_caller_stat(u64 callsite, bool create)
+static struct page_stat *search_page_caller_stat(struct page_stat *this,
+						 bool create)
 {
 	struct rb_node **node = &page_caller_tree.rb_node;
 	struct rb_node *parent = NULL;
 	struct page_stat *data;
+	struct sort_dimension *sort;
 
 	while (*node) {
-		s64 cmp;
+		int cmp = 0;
 
 		parent = *node;
 		data = rb_entry(*node, struct page_stat, node);
 
-		cmp = data->callsite - callsite;
+		list_for_each_entry(sort, &page_caller_sort_input, list) {
+			cmp = sort->cmp(this, data);
+			if (cmp)
+				break;
+		}
+
 		if (cmp < 0)
 			node = &parent->rb_left;
 		else if (cmp > 0)
@@ -453,7 +480,10 @@ static struct page_stat *search_page_caller_stat(u64 callsite, bool create)
 
 	data = zalloc(sizeof(*data));
 	if (data != NULL) {
-		data->callsite = callsite;
+		data->callsite = this->callsite;
+		data->order = this->order;
+		data->migrate_type = this->migrate_type;
+		data->gfp_flags = this->gfp_flags;
 
 		rb_link_node(&data->node, parent, node);
 		rb_insert_color(&data->node, &page_caller_tree);
@@ -473,6 +503,12 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
 	u64 bytes = kmem_page_size << order;
 	u64 callsite;
 	struct page_stat *stat;
+	struct page_stat this = {
+		.page = page,
+		.order = order,
+		.gfp_flags = gfp_flags,
+		.migrate_type = migrate_type,
+	};
 
 	if (page == 0) {
 		nr_page_fails++;
@@ -486,28 +522,23 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
 	 * with things like partial freeing.  But AFAIK there's no way
 	 * to convert a pointer to struct page into PFN in userspace.
 	 */
-	stat = search_page_alloc_stat(page, true);
+	stat = search_page_alloc_stat(&this, true);
 	if (stat == NULL) {
 		pr_err("cannot create page alloc stat\n");
 		return -1;
 	}
 
-	stat->order = order;
-	stat->gfp_flags = gfp_flags;
-	stat->migrate_type = migrate_type;
-	stat->callsite = callsite;
 	stat->nr_alloc++;
 	stat->alloc_bytes += bytes;
+	stat->callsite = callsite;
 
-	stat = search_page_caller_stat(callsite, true);
+	this.callsite = callsite;
+	stat = search_page_caller_stat(&this, true);
 	if (stat == NULL) {
 		pr_err("cannot create page caller stat\n");
 		return -1;
 	}
 
-	stat->order = order;
-	stat->gfp_flags = gfp_flags;
-	stat->migrate_type = migrate_type;
 	stat->nr_alloc++;
 	stat->alloc_bytes += bytes;
 
@@ -526,11 +557,17 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
 	unsigned int order = perf_evsel__intval(evsel, sample, "order");
 	u64 bytes = kmem_page_size << order;
 	struct page_stat *stat;
+	struct page_stat this = {
+		.page = page,
+		.order = order,
+		.gfp_flags = -1U,
+		.migrate_type = -1U,
+	};
 
 	nr_page_frees++;
 	total_page_free_bytes += bytes;
 
-	stat = search_page_alloc_stat(page, false);
+	stat = search_page_alloc_stat(&this, false);
 	if (stat == NULL) {
 		pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
 			  page, order);
@@ -541,7 +578,8 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
 	stat->nr_free++;
 	stat->free_bytes += bytes;
 
-	stat = search_page_caller_stat(stat->callsite, false);
+	this.callsite = stat->callsite;
+	stat = search_page_caller_stat(&this, false);
 	if (stat != NULL) {
 		stat->nr_free++;
 		stat->free_bytes += bytes;
@@ -805,14 +843,10 @@ static void print_result(struct perf_session *session)
 		print_page_result(session);
 }
 
-struct sort_dimension {
-	const char		name[20];
-	sort_fn_t		cmp;
-	struct list_head	list;
-};
-
-static LIST_HEAD(caller_sort);
-static LIST_HEAD(alloc_sort);
+static LIST_HEAD(slab_caller_sort);
+static LIST_HEAD(slab_alloc_sort);
+static LIST_HEAD(page_caller_sort);
+static LIST_HEAD(page_alloc_sort);
 
 static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
 			     struct list_head *sort_list)
@@ -861,10 +895,12 @@ static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted
 	}
 }
 
-static void sort_page_insert(struct rb_root *root, struct page_stat *data)
+static void sort_page_insert(struct rb_root *root, struct page_stat *data,
+			     struct list_head *sort_list)
 {
 	struct rb_node **new = &root->rb_node;
 	struct rb_node *parent = NULL;
+	struct sort_dimension *sort;
 
 	while (*new) {
 		struct page_stat *this;
@@ -873,8 +909,11 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data)
 		this = rb_entry(*new, struct page_stat, node);
 		parent = *new;
 
-		/* TODO: support more sort key */
-		cmp = data->alloc_bytes - this->alloc_bytes;
+		list_for_each_entry(sort, sort_list, list) {
+			cmp = sort->cmp(data, this);
+			if (cmp)
+				break;
+		}
 
 		if (cmp > 0)
 			new = &parent->rb_left;
@@ -886,7 +925,8 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data)
 	rb_insert_color(&data->node, root);
 }
 
-static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted)
+static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
+			       struct list_head *sort_list)
 {
 	struct rb_node *node;
 	struct page_stat *data;
@@ -898,7 +938,7 @@ static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted
 
 		rb_erase(node, root);
 		data = rb_entry(node, struct page_stat, node);
-		sort_page_insert(root_sorted, data);
+		sort_page_insert(root_sorted, data, sort_list);
 	}
 }
 
@@ -906,13 +946,15 @@ static void sort_result(void)
 {
 	if (kmem_slab) {
 		__sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
-				   &alloc_sort);
+				   &slab_alloc_sort);
 		__sort_slab_result(&root_caller_stat, &root_caller_sorted,
-				   &caller_sort);
+				   &slab_caller_sort);
 	}
 	if (kmem_page) {
-		__sort_page_result(&page_alloc_tree, &page_alloc_sorted);
-		__sort_page_result(&page_caller_tree, &page_caller_sorted);
+		__sort_page_result(&page_alloc_tree, &page_alloc_sorted,
+				   &page_alloc_sort);
+		__sort_page_result(&page_caller_tree, &page_caller_sorted,
+				   &page_caller_sort);
 	}
 }
 
@@ -952,8 +994,12 @@ static int __cmd_kmem(struct perf_session *session)
 	return err;
 }
 
-static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
+/* slab sort keys */
+static int ptr_cmp(void *a, void *b)
 {
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
+
 	if (l->ptr < r->ptr)
 		return -1;
 	else if (l->ptr > r->ptr)
@@ -966,8 +1012,11 @@ static struct sort_dimension ptr_sort_dimension = {
 	.cmp	= ptr_cmp,
 };
 
-static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int slab_callsite_cmp(void *a, void *b)
 {
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
+
 	if (l->call_site < r->call_site)
 		return -1;
 	else if (l->call_site > r->call_site)
@@ -977,11 +1026,14 @@ static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
 
 static struct sort_dimension callsite_sort_dimension = {
 	.name	= "callsite",
-	.cmp	= callsite_cmp,
+	.cmp	= slab_callsite_cmp,
 };
 
-static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int hit_cmp(void *a, void *b)
 {
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
+
 	if (l->hit < r->hit)
 		return -1;
 	else if (l->hit > r->hit)
@@ -994,8 +1046,11 @@ static struct sort_dimension hit_sort_dimension = {
 	.cmp	= hit_cmp,
 };
 
-static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int bytes_cmp(void *a, void *b)
 {
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
+
 	if (l->bytes_alloc < r->bytes_alloc)
 		return -1;
 	else if (l->bytes_alloc > r->bytes_alloc)
@@ -1008,9 +1063,11 @@ static struct sort_dimension bytes_sort_dimension = {
 	.cmp	= bytes_cmp,
 };
 
-static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int frag_cmp(void *a, void *b)
 {
 	double x, y;
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
 
 	x = fragmentation(l->bytes_req, l->bytes_alloc);
 	y = fragmentation(r->bytes_req, r->bytes_alloc);
@@ -1027,8 +1084,11 @@ static struct sort_dimension frag_sort_dimension = {
 	.cmp	= frag_cmp,
 };
 
-static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int pingpong_cmp(void *a, void *b)
 {
+	struct alloc_stat *l = a;
+	struct alloc_stat *r = b;
+
 	if (l->pingpong < r->pingpong)
 		return -1;
 	else if (l->pingpong > r->pingpong)
@@ -1041,7 +1101,135 @@ static struct sort_dimension pingpong_sort_dimension = {
 	.cmp	= pingpong_cmp,
 };
 
-static struct sort_dimension *avail_sorts[] = {
+/* page sort keys */
+static int page_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	if (l->page < r->page)
+		return -1;
+	else if (l->page > r->page)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension page_sort_dimension = {
+	.name	= "page",
+	.cmp	= page_cmp,
+};
+
+static int page_callsite_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	if (l->callsite < r->callsite)
+		return -1;
+	else if (l->callsite > r->callsite)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension page_callsite_sort_dimension = {
+	.name	= "callsite",
+	.cmp	= page_callsite_cmp,
+};
+
+static int page_hit_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	if (l->nr_alloc < r->nr_alloc)
+		return -1;
+	else if (l->nr_alloc > r->nr_alloc)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension page_hit_sort_dimension = {
+	.name	= "hit",
+	.cmp	= page_hit_cmp,
+};
+
+static int page_bytes_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	if (l->alloc_bytes < r->alloc_bytes)
+		return -1;
+	else if (l->alloc_bytes > r->alloc_bytes)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension page_bytes_sort_dimension = {
+	.name	= "bytes",
+	.cmp	= page_bytes_cmp,
+};
+
+static int page_order_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	if (l->order < r->order)
+		return -1;
+	else if (l->order > r->order)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension page_order_sort_dimension = {
+	.name	= "order",
+	.cmp	= page_order_cmp,
+};
+
+static int migrate_type_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	/* for internal use to find free'd page */
+	if (l->migrate_type == -1U)
+		return 0;
+
+	if (l->migrate_type < r->migrate_type)
+		return -1;
+	else if (l->migrate_type > r->migrate_type)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension migrate_type_sort_dimension = {
+	.name	= "mtype",
+	.cmp	= migrate_type_cmp,
+};
+
+static int gfp_flags_cmp(void *a, void *b)
+{
+	struct page_stat *l = a;
+	struct page_stat *r = b;
+
+	/* for internal use to find free'd page */
+	if (l->gfp_flags == -1U)
+		return 0;
+
+	if (l->gfp_flags < r->gfp_flags)
+		return -1;
+	else if (l->gfp_flags > r->gfp_flags)
+		return 1;
+	return 0;
+}
+
+static struct sort_dimension gfp_flags_sort_dimension = {
+	.name	= "gfp",
+	.cmp	= gfp_flags_cmp,
+};
+
+static struct sort_dimension *slab_sorts[] = {
 	&ptr_sort_dimension,
 	&callsite_sort_dimension,
 	&hit_sort_dimension,
@@ -1050,16 +1238,44 @@ static struct sort_dimension *avail_sorts[] = {
 	&pingpong_sort_dimension,
 };
 
-#define NUM_AVAIL_SORTS	((int)ARRAY_SIZE(avail_sorts))
+static struct sort_dimension *page_sorts[] = {
+	&page_sort_dimension,
+	&page_callsite_sort_dimension,
+	&page_hit_sort_dimension,
+	&page_bytes_sort_dimension,
+	&page_order_sort_dimension,
+	&migrate_type_sort_dimension,
+	&gfp_flags_sort_dimension,
+};
+
+static int slab_sort_dimension__add(const char *tok, struct list_head *list)
+{
+	struct sort_dimension *sort;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
+		if (!strcmp(slab_sorts[i]->name, tok)) {
+			sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
+			if (!sort) {
+				pr_err("%s: memdup failed\n", __func__);
+				return -1;
+			}
+			list_add_tail(&sort->list, list);
+			return 0;
+		}
+	}
+
+	return -1;
+}
 
-static int sort_dimension__add(const char *tok, struct list_head *list)
+static int page_sort_dimension__add(const char *tok, struct list_head *list)
 {
 	struct sort_dimension *sort;
 	int i;
 
-	for (i = 0; i < NUM_AVAIL_SORTS; i++) {
-		if (!strcmp(avail_sorts[i]->name, tok)) {
-			sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
+	for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
+		if (!strcmp(page_sorts[i]->name, tok)) {
+			sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
 			if (!sort) {
 				pr_err("%s: memdup failed\n", __func__);
 				return -1;
@@ -1072,7 +1288,33 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
 	return -1;
 }
 
-static int setup_sorting(struct list_head *sort_list, const char *arg)
+static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
+{
+	char *tok;
+	char *str = strdup(arg);
+	char *pos = str;
+
+	if (!str) {
+		pr_err("%s: strdup failed\n", __func__);
+		return -1;
+	}
+
+	while (true) {
+		tok = strsep(&pos, ",");
+		if (!tok)
+			break;
+		if (slab_sort_dimension__add(tok, sort_list) < 0) {
+			error("Unknown slab --sort key: '%s'", tok);
+			free(str);
+			return -1;
+		}
+	}
+
+	free(str);
+	return 0;
+}
+
+static int setup_page_sorting(struct list_head *sort_list, const char *arg)
 {
 	char *tok;
 	char *str = strdup(arg);
@@ -1087,8 +1329,8 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
 		tok = strsep(&pos, ",");
 		if (!tok)
 			break;
-		if (sort_dimension__add(tok, sort_list) < 0) {
-			error("Unknown --sort key: '%s'", tok);
+		if (page_sort_dimension__add(tok, sort_list) < 0) {
+			error("Unknown page --sort key: '%s'", tok);
 			free(str);
 			return -1;
 		}
@@ -1104,10 +1346,17 @@ static int parse_sort_opt(const struct option *opt __maybe_unused,
 	if (!arg)
 		return -1;
 
-	if (caller_flag > alloc_flag)
-		return setup_sorting(&caller_sort, arg);
-	else
-		return setup_sorting(&alloc_sort, arg);
+	if (kmem_page > kmem_slab) {
+		if (caller_flag > alloc_flag)
+			return setup_page_sorting(&page_caller_sort, arg);
+		else
+			return setup_page_sorting(&page_alloc_sort, arg);
+	} else {
+		if (caller_flag > alloc_flag)
+			return setup_slab_sorting(&slab_caller_sort, arg);
+		else
+			return setup_slab_sorting(&slab_alloc_sort, arg);
+	}
 
 	return 0;
 }
@@ -1215,7 +1464,8 @@ static int __cmd_record(int argc, const char **argv)
 
 int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 {
-	const char * const default_sort_order = "frag,hit,bytes";
+	const char * const default_slab_sort = "frag,hit,bytes";
+	const char * const default_page_sort = "bytes,hit";
 	const struct option kmem_options[] = {
 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
 	OPT_INCR('v', "verbose", &verbose,
@@ -1225,8 +1475,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
 			   "show per-allocation statistics", parse_alloc_opt),
 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
-		     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
-		     parse_sort_opt),
+		     "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
+		     "page, order, mtype, gfp", parse_sort_opt),
 	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_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
@@ -1283,11 +1533,21 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 		if (cpu__setup_cpunode_map())
 			goto out_delete;
 
-		if (list_empty(&caller_sort))
-			setup_sorting(&caller_sort, default_sort_order);
-		if (list_empty(&alloc_sort))
-			setup_sorting(&alloc_sort, default_sort_order);
-
+		if (list_empty(&slab_caller_sort))
+			setup_slab_sorting(&slab_caller_sort, default_slab_sort);
+		if (list_empty(&slab_alloc_sort))
+			setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
+		if (list_empty(&page_caller_sort))
+			setup_page_sorting(&page_caller_sort, default_page_sort);
+		if (list_empty(&page_alloc_sort))
+			setup_page_sorting(&page_alloc_sort, default_page_sort);
+
+		if (kmem_page) {
+			setup_page_sorting(&page_alloc_sort_input,
+					   "page,order,mtype,gfp");
+			setup_page_sorting(&page_caller_sort_input,
+					   "callsite,order,mtype,gfp");
+		}
 		ret = __cmd_kmem(session);
 	} else
 		usage_with_options(kmem_usage, kmem_options);
-- 
2.3.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
                   ` (5 preceding siblings ...)
  2015-03-12  7:32 ` [PATCH 6/6] perf kmem: Support sort keys on page analysis Namhyung Kim
@ 2015-03-12 10:41 ` Ingo Molnar
  2015-03-12 14:58   ` Namhyung Kim
  2015-03-12 19:07   ` Arnaldo Carvalho de Melo
  6 siblings, 2 replies; 23+ messages in thread
From: Ingo Molnar @ 2015-03-12 10:41 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

* Namhyung Kim <namhyung@kernel.org> wrote:

> Hello,
> 
> Currently perf kmem command only analyzes SLAB memory allocation.  And
> I'd like to introduce page allocation analysis also.  Users can use
>  --slab and/or --page option to select it.  If none of these options
> are used, it does slab allocation analysis for backward compatibility.
> 
> The patch 1-3 are bugfix and cleanups.  Patch 4 implements basic
> support for page allocation analysis, patch 5 deals with the callsite
> and finally patch 6 implements sorting.
> 
> In this patchset, I used two kmem events: kmem:mm_page_alloc and
> kmem_page_free for analysis as they can track every memory
> allocation/free path AFAIK.  However, unlike slab tracepoint events,
> those page allocation events don't provide callsite info directly.  So
> I recorded callchains and extracted callsites like below:

Really cool features!

I have a couple of output typography observations:

> Normal page allocation callchains look like this:
> 
>   360a7e __alloc_pages_nodemask
>   3a711c alloc_pages_current
>   357bc7 __page_cache_alloc   <-- callsite
>   357cf6 pagecache_get_page
>    48b0a prepare_pages
>    494d3 __btrfs_buffered_write
>    49cdf btrfs_file_write_iter
>   3ceb6e new_sync_write
>   3cf447 vfs_write
>   3cff99 sys_write
>   7556e9 system_call
>     f880 __write_nocancel
>    33eb9 cmd_record
>    4b38e cmd_kmem
>    7aa23 run_builtin
>    27a9a main
>    20800 __libc_start_main
> 
> But first two are internal page allocation functions so it should be
> skipped.  To determine such allocation functions, I used following regex:
> 
>   ^_?_?(alloc|get_free|get_zeroed)_pages?
> 
> This gave me a following list of functions (you can see this with -v):
> 
>   alloc func: __get_free_pages
>   alloc func: get_zeroed_page
>   alloc func: alloc_pages_exact
>   alloc func: __alloc_pages_direct_compact
>   alloc func: __alloc_pages_nodemask
>   alloc func: alloc_page_interleave
>   alloc func: alloc_pages_current
>   alloc func: alloc_pages_vma
>   alloc func: alloc_page_buffers
>   alloc func: alloc_pages_exact_nid
> 
> After skipping those function, it got '__page_cache_alloc'.
> 
> Other information such as allocation order, migration type and gfp
> flags are provided by tracepoint events.
> 
> Basically the output will be sorted by total allocation bytes, but you
> can change it by using -s/--sort option.  The following sort keys are
> added to support page analysis: page, order, mtype, gfp.  Existing
> 'callsite', 'bytes' and 'hit' sort keys also can be used.
> 
> An example follows:
> 
>   # perf kmem record --slab --page sleep 1
>   [ perf record: Woken up 0 times to write data ]
>   [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]
> 
>   # perf kmem stat --page --caller -l 10 -s order,hit
> 
>   --------------------------------------------------------------------------------------------
>    Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite

s/Per/Size
s/Hit/Hits
s/Migrate type/Migration type
s/GFP flag/GFP flags

?

>   --------------------------------------------------------------------------------------------
>        65536/16384 |        4 |     2 |  RECLAIMABLE | 00285250 | new_slab
>     51347456/4096  |    12536 |     0 |      MOVABLE | 0102005a | __page_cache_alloc
>        53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
>        40960/4096  |       10 |     0 |      MOVABLE | 000280da | handle_mm_fault
>        28672/4096  |        7 |     0 |    UNMOVABLE | 000000d0 | __pollwait
>        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
>        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
>        16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
>        16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
>         8192/4096  |        2 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
>    ...             | ...      | ...   | ...          | ...      | ...
>   --------------------------------------------------------------------------------------------
> 
>   SUMMARY (page allocator)
>   ========================
>   Total alloc requested: 12593
>   Total alloc failure  : 0
>   Total bytes allocated: 51630080
>   Total free  requested: 115
>   Total free  unmatched: 67
>   Total bytes freed    : 471040

I'd suggest the following changes to the format:

  - Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 
    'freed only', depending on how much of their lifetime we've 
    managed to trace. These groups are really distinct and it makes 
    little sense to mix up their stats.

  - Add commas to the numbers, to make it easier to read and compare 
    larger numbers.

  - Right-align the numbers, to make them easy to compare when they
    are placed under each other.

  - Merge the 'count' and 'bytes' stats into a single line, so that 
    it's more compact, easier to navigate, but also only comparable 
    type numbers are placed under each other.

I.e. something like this (mockup) output:

   SUMMARY (page allocator)
   ========================

   Pages allocated+freed:       12,593   [     51,630,080 bytes ]

   Pages allocated-only:         2,342   [      1,235,010 bytes ]
   Pages freed-only:                67   [        135,311 bytes ]

   Page allocation failures :        0


>   Order     UNMOVABLE   RECLAIMABLE       MOVABLE      RESERVED   CMA/ISOLATE
>   -----  ------------  ------------  ------------  ------------  ------------
>       0            32             0         12557             0             0
>       1             0             0             0             0             0
>       2             0             4             0             0             0
>       3             0             0             0             0             0
>       4             0             0             0             0             0
>       5             0             0             0             0             0
>       6             0             0             0             0             0
>       7             0             0             0             0             0
>       8             0             0             0             0             0
>       9             0             0             0             0             0
>      10             0             0             0             0             0

Here I'd suggest the following refinements:

 - Use '.' instead of '0', to make actual nonzero values stand out 
   visually, while still keeping a tabular format

 - Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special' 
   colum: this will be zero in 99.9% of the cases, as those pages 
   mostly deal with driver interfaces, mostly used during init/deinit.

 - Capitalize less.

 - Use comma-separated numbers for better readability.

So something like this:


   Order     Unmovable   Reclaimable       Movable       Special
   -----  ------------  ------------  ------------  ------------
       0            32             .        12,557             .
       1             .             .             .             .
       2             .             4             .             .
       3             .             .             .             .
       4             .             .             .             .
       5             .             .             .             .
       6             .             .             .             .
       7             .             .             .             .
       8             .             .             .             .
       9             .             .             .             .
      10             .             .             .             .


Look for example how easily noticeable the '4' value is now, while it 
was pretty easy to miss in the original table.

> I have some idea how to improve it.  But I'd also like to hear other 
> idea, suggestion, feedback and so on.

So there's one thing that would be useful: to track pages allocated on 
one node, but freed on another. Those kinds of allocation/free 
patterns are especially expensive and might make sense to visualize.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/6] perf kmem: Analyze page allocator events also
  2015-03-12  7:32 ` [PATCH 4/6] perf kmem: Analyze page allocator events also Namhyung Kim
@ 2015-03-12 11:01   ` Jiri Olsa
  2015-03-12 15:11     ` Namhyung Kim
  0 siblings, 1 reply; 23+ messages in thread
From: Jiri Olsa @ 2015-03-12 11:01 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Ingo Molnar, Peter Zijlstra, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

On Thu, Mar 12, 2015 at 04:32:49PM +0900, Namhyung Kim wrote:

SNIP

> +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;
> +}

hum, just curious.. why not just assign 1, I'm missing the magic ;-)

thanks,
jirk

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12 10:41 ` [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Ingo Molnar
@ 2015-03-12 14:58   ` Namhyung Kim
  2015-03-12 15:54     ` Ingo Molnar
  2015-03-16  2:10     ` Namhyung Kim
  2015-03-12 19:07   ` Arnaldo Carvalho de Melo
  1 sibling, 2 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12 14:58 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

Hi Ingo,

On Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar wrote:
> * Namhyung Kim <namhyung@kernel.org> wrote:
> 
> > Hello,
> > 
> > Currently perf kmem command only analyzes SLAB memory allocation.  And
> > I'd like to introduce page allocation analysis also.  Users can use
> >  --slab and/or --page option to select it.  If none of these options
> > are used, it does slab allocation analysis for backward compatibility.
> > 
> > The patch 1-3 are bugfix and cleanups.  Patch 4 implements basic
> > support for page allocation analysis, patch 5 deals with the callsite
> > and finally patch 6 implements sorting.
> > 
> > In this patchset, I used two kmem events: kmem:mm_page_alloc and
> > kmem_page_free for analysis as they can track every memory
> > allocation/free path AFAIK.  However, unlike slab tracepoint events,
> > those page allocation events don't provide callsite info directly.  So
> > I recorded callchains and extracted callsites like below:
> 
> Really cool features!

Thanks!


> 
> I have a couple of output typography observations:
> 
> > Normal page allocation callchains look like this:
> > 
> >   360a7e __alloc_pages_nodemask
> >   3a711c alloc_pages_current
> >   357bc7 __page_cache_alloc   <-- callsite
> >   357cf6 pagecache_get_page
> >    48b0a prepare_pages
> >    494d3 __btrfs_buffered_write
> >    49cdf btrfs_file_write_iter
> >   3ceb6e new_sync_write
> >   3cf447 vfs_write
> >   3cff99 sys_write
> >   7556e9 system_call
> >     f880 __write_nocancel
> >    33eb9 cmd_record
> >    4b38e cmd_kmem
> >    7aa23 run_builtin
> >    27a9a main
> >    20800 __libc_start_main
> > 
> > But first two are internal page allocation functions so it should be
> > skipped.  To determine such allocation functions, I used following regex:
> > 
> >   ^_?_?(alloc|get_free|get_zeroed)_pages?
> > 
> > This gave me a following list of functions (you can see this with -v):
> > 
> >   alloc func: __get_free_pages
> >   alloc func: get_zeroed_page
> >   alloc func: alloc_pages_exact
> >   alloc func: __alloc_pages_direct_compact
> >   alloc func: __alloc_pages_nodemask
> >   alloc func: alloc_page_interleave
> >   alloc func: alloc_pages_current
> >   alloc func: alloc_pages_vma
> >   alloc func: alloc_page_buffers
> >   alloc func: alloc_pages_exact_nid
> > 
> > After skipping those function, it got '__page_cache_alloc'.
> > 
> > Other information such as allocation order, migration type and gfp
> > flags are provided by tracepoint events.
> > 
> > Basically the output will be sorted by total allocation bytes, but you
> > can change it by using -s/--sort option.  The following sort keys are
> > added to support page analysis: page, order, mtype, gfp.  Existing
> > 'callsite', 'bytes' and 'hit' sort keys also can be used.
> > 
> > An example follows:
> > 
> >   # perf kmem record --slab --page sleep 1
> >   [ perf record: Woken up 0 times to write data ]
> >   [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]
> > 
> >   # perf kmem stat --page --caller -l 10 -s order,hit
> > 
> >   --------------------------------------------------------------------------------------------
> >    Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite
> 
> s/Per/Size
> s/Hit/Hits
> s/Migrate type/Migration type
> s/GFP flag/GFP flags
> 
> ?

OK, will change.  (They'll spend a bit more column spaces though.)


> 
> >   --------------------------------------------------------------------------------------------
> >        65536/16384 |        4 |     2 |  RECLAIMABLE | 00285250 | new_slab
> >     51347456/4096  |    12536 |     0 |      MOVABLE | 0102005a | __page_cache_alloc
> >        53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
> >        40960/4096  |       10 |     0 |      MOVABLE | 000280da | handle_mm_fault
> >        28672/4096  |        7 |     0 |    UNMOVABLE | 000000d0 | __pollwait
> >        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
> >        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
> >        16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
> >        16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
> >         8192/4096  |        2 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
> >    ...             | ...      | ...   | ...          | ...      | ...
> >   --------------------------------------------------------------------------------------------
> > 
> >   SUMMARY (page allocator)
> >   ========================
> >   Total alloc requested: 12593
> >   Total alloc failure  : 0
> >   Total bytes allocated: 51630080
> >   Total free  requested: 115
> >   Total free  unmatched: 67
> >   Total bytes freed    : 471040
> 
> I'd suggest the following changes to the format:
> 
>   - Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 
>     'freed only', depending on how much of their lifetime we've 
>     managed to trace. These groups are really distinct and it makes 
>     little sense to mix up their stats.

Good idea.  Actually I'm thinking about a new option that shows only
lively allocated memory (excluding freed page) in the table.  FYI
current number is total allocated memory (including freed page).


> 
>   - Add commas to the numbers, to make it easier to read and compare 
>     larger numbers.

OK

> 
>   - Right-align the numbers, to make them easy to compare when they
>     are placed under each other.

OK

> 
>   - Merge the 'count' and 'bytes' stats into a single line, so that 
>     it's more compact, easier to navigate, but also only comparable 
>     type numbers are placed under each other.

OK

> 
> I.e. something like this (mockup) output:
> 
>    SUMMARY (page allocator)
>    ========================
> 
>    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> 
>    Pages allocated-only:         2,342   [      1,235,010 bytes ]
>    Pages freed-only:                67   [        135,311 bytes ]
> 
>    Page allocation failures :        0

Looks a lot better!

One thing I need to tell you is that the numbers are not pages but
requests.


> 
> 
> >   Order     UNMOVABLE   RECLAIMABLE       MOVABLE      RESERVED   CMA/ISOLATE
> >   -----  ------------  ------------  ------------  ------------  ------------
> >       0            32             0         12557             0             0
> >       1             0             0             0             0             0
> >       2             0             4             0             0             0
> >       3             0             0             0             0             0
> >       4             0             0             0             0             0
> >       5             0             0             0             0             0
> >       6             0             0             0             0             0
> >       7             0             0             0             0             0
> >       8             0             0             0             0             0
> >       9             0             0             0             0             0
> >      10             0             0             0             0             0
> 
> Here I'd suggest the following refinements:
> 
>  - Use '.' instead of '0', to make actual nonzero values stand out 
>    visually, while still keeping a tabular format

OK

> 
>  - Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special' 
>    colum: this will be zero in 99.9% of the cases, as those pages 
>    mostly deal with driver interfaces, mostly used during init/deinit.

I'm not sure about the CMA pages..

> 
>  - Capitalize less.

OK

> 
>  - Use comma-separated numbers for better readability.

OK

> 
> So something like this:
> 
> 
>    Order     Unmovable   Reclaimable       Movable       Special
>    -----  ------------  ------------  ------------  ------------
>        0            32             .        12,557             .
>        1             .             .             .             .
>        2             .             4             .             .
>        3             .             .             .             .
>        4             .             .             .             .
>        5             .             .             .             .
>        6             .             .             .             .
>        7             .             .             .             .
>        8             .             .             .             .
>        9             .             .             .             .
>       10             .             .             .             .
> 
> 
> Look for example how easily noticeable the '4' value is now, while it 
> was pretty easy to miss in the original table.

Indeed!

> 
> > I have some idea how to improve it.  But I'd also like to hear other 
> > idea, suggestion, feedback and so on.
> 
> So there's one thing that would be useful: to track pages allocated on 
> one node, but freed on another. Those kinds of allocation/free 
> patterns are especially expensive and might make sense to visualize.

I think it can be done easily as slab analysis already contains the info.

Thanks for your useful feedbacks!
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/6] perf kmem: Analyze page allocator events also
  2015-03-12 11:01   ` Jiri Olsa
@ 2015-03-12 15:11     ` Namhyung Kim
  0 siblings, 0 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-12 15:11 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Arnaldo Carvalho de Melo, Ingo Molnar, Peter Zijlstra, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

Hi Jiri,

On Thu, Mar 12, 2015 at 12:01:27PM +0100, Jiri Olsa wrote:
> On Thu, Mar 12, 2015 at 04:32:49PM +0900, Namhyung Kim wrote:
> 
> SNIP
> 
> > +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;
> > +}
> 
> hum, just curious.. why not just assign 1, I'm missing the magic ;-)

Well, basically I just followed the existing convention. :)

Anyway it controls the effect of -s/--sort option.  The single -s
option will be used to set sort keys for both of --alloc and --caller
stat.  So it needs to known currently selected mode at option parsing
time.  I extended it to page/slab selection also.

For example,

  # perf kmem --page --alloc -s bytes --slab --caller -s hit

The first -s option will set sort keys for page/alloc stat and the
second -s option will set sort keys for slab/caller stat.  So the -s
option should be preceded by at least one of the mode selection
options (i.e. --page/slab/alloc/caller).  I'll add it to the man page
also.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12 14:58   ` Namhyung Kim
@ 2015-03-12 15:54     ` Ingo Molnar
  2015-03-13  8:19       ` Namhyung Kim
  2015-03-16  2:10     ` Namhyung Kim
  1 sibling, 1 reply; 23+ messages in thread
From: Ingo Molnar @ 2015-03-12 15:54 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim


* Namhyung Kim <namhyung@kernel.org> wrote:

> > I.e. something like this (mockup) output:
> > 
> >    SUMMARY (page allocator)
> >    ========================
> > 
> >    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> > 
> >    Pages allocated-only:         2,342   [      1,235,010 bytes ]
> >    Pages freed-only:                67   [        135,311 bytes ]
> > 
> >    Page allocation failures :        0
> 
> Looks a lot better!
> 
> One thing I need to tell you is that the numbers are not pages but 
> requests.

Yes, but in the MM code we tend to call larger order allocations 
'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K 
order-1 page. So at least in MM-speak it should be OK to call them 
'pages'.

But your call!

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12 10:41 ` [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Ingo Molnar
  2015-03-12 14:58   ` Namhyung Kim
@ 2015-03-12 19:07   ` Arnaldo Carvalho de Melo
  1 sibling, 0 replies; 23+ messages in thread
From: Arnaldo Carvalho de Melo @ 2015-03-12 19:07 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Namhyung Kim, Peter Zijlstra, Jiri Olsa, LKML, David Ahern,
	Minchan Kim, Joonsoo Kim

Em Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar escreveu:
> * Namhyung Kim <namhyung@kernel.org> wrote:
> 
> > Hello,
> > 
> > Currently perf kmem command only analyzes SLAB memory allocation.  And
> > I'd like to introduce page allocation analysis also.  Users can use
> >  --slab and/or --page option to select it.  If none of these options
> > are used, it does slab allocation analysis for backward compatibility.
> > 
> > The patch 1-3 are bugfix and cleanups.  Patch 4 implements basic
> > support for page allocation analysis, patch 5 deals with the callsite
> > and finally patch 6 implements sorting.
> > 
> > In this patchset, I used two kmem events: kmem:mm_page_alloc and
> > kmem_page_free for analysis as they can track every memory
> > allocation/free path AFAIK.  However, unlike slab tracepoint events,
> > those page allocation events don't provide callsite info directly.  So
> > I recorded callchains and extracted callsites like below:
> 
> Really cool features!

Yeah, indeed.

I cherry picked the first three, will push to Ingo soon.

- Arnaldo
 
> I have a couple of output typography observations:
> 
> > Normal page allocation callchains look like this:
> > 
> >   360a7e __alloc_pages_nodemask
> >   3a711c alloc_pages_current
> >   357bc7 __page_cache_alloc   <-- callsite
> >   357cf6 pagecache_get_page
> >    48b0a prepare_pages
> >    494d3 __btrfs_buffered_write
> >    49cdf btrfs_file_write_iter
> >   3ceb6e new_sync_write
> >   3cf447 vfs_write
> >   3cff99 sys_write
> >   7556e9 system_call
> >     f880 __write_nocancel
> >    33eb9 cmd_record
> >    4b38e cmd_kmem
> >    7aa23 run_builtin
> >    27a9a main
> >    20800 __libc_start_main
> > 
> > But first two are internal page allocation functions so it should be
> > skipped.  To determine such allocation functions, I used following regex:
> > 
> >   ^_?_?(alloc|get_free|get_zeroed)_pages?
> > 
> > This gave me a following list of functions (you can see this with -v):
> > 
> >   alloc func: __get_free_pages
> >   alloc func: get_zeroed_page
> >   alloc func: alloc_pages_exact
> >   alloc func: __alloc_pages_direct_compact
> >   alloc func: __alloc_pages_nodemask
> >   alloc func: alloc_page_interleave
> >   alloc func: alloc_pages_current
> >   alloc func: alloc_pages_vma
> >   alloc func: alloc_page_buffers
> >   alloc func: alloc_pages_exact_nid
> > 
> > After skipping those function, it got '__page_cache_alloc'.
> > 
> > Other information such as allocation order, migration type and gfp
> > flags are provided by tracepoint events.
> > 
> > Basically the output will be sorted by total allocation bytes, but you
> > can change it by using -s/--sort option.  The following sort keys are
> > added to support page analysis: page, order, mtype, gfp.  Existing
> > 'callsite', 'bytes' and 'hit' sort keys also can be used.
> > 
> > An example follows:
> > 
> >   # perf kmem record --slab --page sleep 1
> >   [ perf record: Woken up 0 times to write data ]
> >   [ perf record: Captured and wrote 49.277 MB perf.data (191027 samples) ]
> > 
> >   # perf kmem stat --page --caller -l 10 -s order,hit
> > 
> >   --------------------------------------------------------------------------------------------
> >    Total_alloc/Per | Hit      | Order | Migrate type | GFP flag | Callsite
> 
> s/Per/Size
> s/Hit/Hits
> s/Migrate type/Migration type
> s/GFP flag/GFP flags
> 
> ?
> 
> >   --------------------------------------------------------------------------------------------
> >        65536/16384 |        4 |     2 |  RECLAIMABLE | 00285250 | new_slab
> >     51347456/4096  |    12536 |     0 |      MOVABLE | 0102005a | __page_cache_alloc
> >        53248/4096  |       13 |     0 |    UNMOVABLE | 002084d0 | pte_alloc_one
> >        40960/4096  |       10 |     0 |      MOVABLE | 000280da | handle_mm_fault
> >        28672/4096  |        7 |     0 |    UNMOVABLE | 000000d0 | __pollwait
> >        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_wp_page
> >        20480/4096  |        5 |     0 |      MOVABLE | 000200da | do_cow_fault
> >        16384/4096  |        4 |     0 |    UNMOVABLE | 00000200 | __tlb_remove_page
> >        16384/4096  |        4 |     0 |    UNMOVABLE | 000084d0 | __pmd_alloc
> >         8192/4096  |        2 |     0 |    UNMOVABLE | 000084d0 | __pud_alloc
> >    ...             | ...      | ...   | ...          | ...      | ...
> >   --------------------------------------------------------------------------------------------
> > 
> >   SUMMARY (page allocator)
> >   ========================
> >   Total alloc requested: 12593
> >   Total alloc failure  : 0
> >   Total bytes allocated: 51630080
> >   Total free  requested: 115
> >   Total free  unmatched: 67
> >   Total bytes freed    : 471040
> 
> I'd suggest the following changes to the format:
> 
>   - Collapse stats into 3 groups: 'allocated+freed', 'allocated only', 
>     'freed only', depending on how much of their lifetime we've 
>     managed to trace. These groups are really distinct and it makes 
>     little sense to mix up their stats.
> 
>   - Add commas to the numbers, to make it easier to read and compare 
>     larger numbers.
> 
>   - Right-align the numbers, to make them easy to compare when they
>     are placed under each other.
> 
>   - Merge the 'count' and 'bytes' stats into a single line, so that 
>     it's more compact, easier to navigate, but also only comparable 
>     type numbers are placed under each other.
> 
> I.e. something like this (mockup) output:
> 
>    SUMMARY (page allocator)
>    ========================
> 
>    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> 
>    Pages allocated-only:         2,342   [      1,235,010 bytes ]
>    Pages freed-only:                67   [        135,311 bytes ]
> 
>    Page allocation failures :        0
> 
> 
> >   Order     UNMOVABLE   RECLAIMABLE       MOVABLE      RESERVED   CMA/ISOLATE
> >   -----  ------------  ------------  ------------  ------------  ------------
> >       0            32             0         12557             0             0
> >       1             0             0             0             0             0
> >       2             0             4             0             0             0
> >       3             0             0             0             0             0
> >       4             0             0             0             0             0
> >       5             0             0             0             0             0
> >       6             0             0             0             0             0
> >       7             0             0             0             0             0
> >       8             0             0             0             0             0
> >       9             0             0             0             0             0
> >      10             0             0             0             0             0
> 
> Here I'd suggest the following refinements:
> 
>  - Use '.' instead of '0', to make actual nonzero values stand out 
>    visually, while still keeping a tabular format
> 
>  - Merge the 'Reserved', 'CMA/Isolate' columns into a single 'Special' 
>    colum: this will be zero in 99.9% of the cases, as those pages 
>    mostly deal with driver interfaces, mostly used during init/deinit.
> 
>  - Capitalize less.
> 
>  - Use comma-separated numbers for better readability.
> 
> So something like this:
> 
> 
>    Order     Unmovable   Reclaimable       Movable       Special
>    -----  ------------  ------------  ------------  ------------
>        0            32             .        12,557             .
>        1             .             .             .             .
>        2             .             4             .             .
>        3             .             .             .             .
>        4             .             .             .             .
>        5             .             .             .             .
>        6             .             .             .             .
>        7             .             .             .             .
>        8             .             .             .             .
>        9             .             .             .             .
>       10             .             .             .             .
> 
> 
> Look for example how easily noticeable the '4' value is now, while it 
> was pretty easy to miss in the original table.
> 
> > I have some idea how to improve it.  But I'd also like to hear other 
> > idea, suggestion, feedback and so on.
> 
> So there's one thing that would be useful: to track pages allocated on 
> one node, but freed on another. Those kinds of allocation/free 
> patterns are especially expensive and might make sense to visualize.
> 
> Thanks,
> 
> 	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12 15:54     ` Ingo Molnar
@ 2015-03-13  8:19       ` Namhyung Kim
  2015-03-13 12:44         ` Ingo Molnar
  0 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-13  8:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

Hi Ingo,

On Thu, Mar 12, 2015 at 04:54:22PM +0100, Ingo Molnar wrote:
> 
> * Namhyung Kim <namhyung@kernel.org> wrote:
> 
> > > I.e. something like this (mockup) output:
> > > 
> > >    SUMMARY (page allocator)
> > >    ========================
> > > 
> > >    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> > > 
> > >    Pages allocated-only:         2,342   [      1,235,010 bytes ]
> > >    Pages freed-only:                67   [        135,311 bytes ]
> > > 
> > >    Page allocation failures :        0
> > 
> > Looks a lot better!
> > 
> > One thing I need to tell you is that the numbers are not pages but 
> > requests.
> 
> Yes, but in the MM code we tend to call larger order allocations 
> 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K 
> order-1 page. So at least in MM-speak it should be OK to call them 
> 'pages'.
> 
> But your call!

How about this?

  SUMMARY (page allocator)
  ========================
  Total allocation requests   :        9,015  [     37,200 Kbytes ]  (A)
  Total free requests         :        8,093  [     33,176 Kbytes ]  (B)

  Total alloc+freed requests  :        7,985  [     32,732 Kbytes ]  (C)
  Total alloc-only requests   :        1,030  [      4,468 Kbytes ]  (D)
  Total free-only requests    :          108  [        444 Kbytes ]  (E)

  Total allocation failure    :            0  [          0 Kbytes ]



(A) = (C) + (D)
(B) = (C) + (E)

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-13  8:19       ` Namhyung Kim
@ 2015-03-13 12:44         ` Ingo Molnar
  2015-03-16  2:06           ` Namhyung Kim
  0 siblings, 1 reply; 23+ messages in thread
From: Ingo Molnar @ 2015-03-13 12:44 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim


* Namhyung Kim <namhyung@kernel.org> wrote:

> Hi Ingo,
> 
> On Thu, Mar 12, 2015 at 04:54:22PM +0100, Ingo Molnar wrote:
> > 
> > * Namhyung Kim <namhyung@kernel.org> wrote:
> > 
> > > > I.e. something like this (mockup) output:
> > > > 
> > > >    SUMMARY (page allocator)
> > > >    ========================
> > > > 
> > > >    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> > > > 
> > > >    Pages allocated-only:         2,342   [      1,235,010 bytes ]
> > > >    Pages freed-only:                67   [        135,311 bytes ]
> > > > 
> > > >    Page allocation failures :        0
> > > 
> > > Looks a lot better!
> > > 
> > > One thing I need to tell you is that the numbers are not pages but 
> > > requests.
> > 
> > Yes, but in the MM code we tend to call larger order allocations 
> > 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K 
> > order-1 page. So at least in MM-speak it should be OK to call them 
> > 'pages'.
> > 
> > But your call!
> 
> How about this?
> 
>   SUMMARY (page allocator)
>   ========================
>   Total allocation requests   :        9,015  [     37,200 Kbytes ]  (A)
>   Total free requests         :        8,093  [     33,176 Kbytes ]  (B)
> 
>   Total alloc+freed requests  :        7,985  [     32,732 Kbytes ]  (C)
>   Total alloc-only requests   :        1,030  [      4,468 Kbytes ]  (D)
>   Total free-only requests    :          108  [        444 Kbytes ]  (E)
> 
>   Total allocation failure    :            0  [          0 Kbytes ]

s/failure/failures
s/Kbytes/KB

I'd leave a bit more space for the numbers, for up into billions of 
requests and terabytes of data. Other than that, sounds good to me!

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [tip:perf/core] perf kmem: Fix segfault when invalid sort key is given
  2015-03-12  7:32 ` [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given Namhyung Kim
@ 2015-03-14  7:06   ` tip-bot for Namhyung Kim
  0 siblings, 0 replies; 23+ messages in thread
From: tip-bot for Namhyung Kim @ 2015-03-14  7:06 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: dsahern, jolsa, hpa, mingo, namhyung, acme, tglx, js1304,
	a.p.zijlstra, linux-kernel, minchan

Commit-ID:  405f87557da35a03ba4663eca971ffac58b0a818
Gitweb:     http://git.kernel.org/tip/405f87557da35a03ba4663eca971ffac58b0a818
Author:     Namhyung Kim <namhyung@kernel.org>
AuthorDate: Thu, 12 Mar 2015 16:32:46 +0900
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 13 Mar 2015 07:47:47 -0300

perf kmem: Fix segfault when invalid sort key is given

When it tries to free 'str', it was already updated by strsep() - so it
needs to save the original pointer.

  # perf kmem stat -s xxx,hit
    Error: Unknown --sort key: 'xxx'
  *** Error in `perf': free(): invalid pointer: 0x0000000000e9e7b6 ***
  ======= Backtrace: =========
  /usr/lib/libc.so.6(+0x7198e)[0x7fc7e6e0d98e]
  /usr/lib/libc.so.6(+0x76dee)[0x7fc7e6e12dee]
  /usr/lib/libc.so.6(+0x775cb)[0x7fc7e6e135cb]
  ./perf[0x44a1b5]
  ./perf[0x490b20]
  ./perf(parse_options_step+0x173)[0x491773]
  ./perf(parse_options_subcommand+0xa7)[0x491fb7]
  ./perf(cmd_kmem+0x2bc)[0x44ae4c]
  ./perf[0x47aa13]
  ./perf(main+0x60a)[0x427a9a]
  /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fc7e6dbc800]
  ./perf(_start+0x29)[0x427bb9]

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joonsoo Kim <js1304@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1426145571-3065-2-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/builtin-kmem.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 62f165a..1e69ea5 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -559,6 +559,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
 {
 	char *tok;
 	char *str = strdup(arg);
+	char *pos = str;
 
 	if (!str) {
 		pr_err("%s: strdup failed\n", __func__);
@@ -566,7 +567,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
 	}
 
 	while (true) {
-		tok = strsep(&str, ",");
+		tok = strsep(&pos, ",");
 		if (!tok)
 			break;
 		if (sort_dimension__add(tok, sort_list) < 0) {

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [tip:perf/core] perf kmem: Allow -v option
  2015-03-12  7:32 ` [PATCH 2/6] perf kmem: Allow -v option Namhyung Kim
@ 2015-03-14  7:06   ` tip-bot for Namhyung Kim
  0 siblings, 0 replies; 23+ messages in thread
From: tip-bot for Namhyung Kim @ 2015-03-14  7:06 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: namhyung, dsahern, mingo, a.p.zijlstra, linux-kernel, js1304,
	hpa, minchan, acme, tglx, jolsa

Commit-ID:  bd72a33ebae8b4d37e3d2a3f0f3f3333ac9654dd
Gitweb:     http://git.kernel.org/tip/bd72a33ebae8b4d37e3d2a3f0f3f3333ac9654dd
Author:     Namhyung Kim <namhyung@kernel.org>
AuthorDate: Thu, 12 Mar 2015 16:32:47 +0900
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 13 Mar 2015 07:47:48 -0300

perf kmem: Allow -v option

Current perf kmem fails when -v option is used.  As it's very useful for
debugging, let's allow it.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joonsoo Kim <js1304@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1426145571-3065-3-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/Documentation/perf-kmem.txt | 4 ++++
 tools/perf/builtin-kmem.c              | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
index 7c8fbbf..150253c 100644
--- a/tools/perf/Documentation/perf-kmem.txt
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -25,6 +25,10 @@ OPTIONS
 --input=<file>::
 	Select the input file (default: perf.data unless stdin is a fifo)
 
+-v::
+--verbose::
+        Be more verbose. (show symbol address, etc)
+
 --caller::
 	Show per-callsite statistics
 
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 1e69ea5..02b7697 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -663,6 +663,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 	const char * const default_sort_order = "frag,hit,bytes";
 	const struct option kmem_options[] = {
 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
+	OPT_INCR('v', "verbose", &verbose,
+		    "be more verbose (show symbol address, etc)"),
 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
 			   "show per-callsite statistics", parse_caller_opt),
 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [tip:perf/core] perf kmem: Fix alignment of slab result table
  2015-03-12  7:32 ` [PATCH 3/6] perf kmem: Fix alignment of slab result table Namhyung Kim
@ 2015-03-14  7:07   ` tip-bot for Namhyung Kim
  0 siblings, 0 replies; 23+ messages in thread
From: tip-bot for Namhyung Kim @ 2015-03-14  7:07 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: dsahern, minchan, js1304, mingo, acme, hpa, linux-kernel, tglx,
	namhyung, a.p.zijlstra

Commit-ID:  65f46e0214c64198a0266c37181a7776e16b7e53
Gitweb:     http://git.kernel.org/tip/65f46e0214c64198a0266c37181a7776e16b7e53
Author:     Namhyung Kim <namhyung@kernel.org>
AuthorDate: Thu, 12 Mar 2015 16:32:48 +0900
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 13 Mar 2015 07:47:48 -0300

perf kmem: Fix alignment of slab result table

Its table was a bit misaligned.  Fix it.

Before:

  # perf kmem stat --caller -l 10
  ------------------------------------------------------------------------------------------------------
   Callsite                           | Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag
  ------------------------------------------------------------------------------------------------------
   radeon_cs_parser_init.part.1+11a   |      2080/260   |      1504/188   |        8 |        0 | 27.692%
   radeon_cs_parser_init.part.1+e1    |       384/96    |       288/72    |        4 |        0 | 25.000%
   radeon_cs_parser_init.part.1+93    |       128/32    |        96/24    |        4 |        0 | 25.000%
   load_elf_binary+a39                |       512/512   |       392/392   |        1 |        0 | 23.438%
   __alloc_skb+89                     |      6144/877   |      4800/685   |        7 |        6 | 21.875%
   radeon_fence_emit+5c               |      1152/192   |       912/152   |        6 |        0 | 20.833%
   radeon_cs_parser_relocs+ad         |      8192/2048  |      6624/1656  |        4 |        0 | 19.141%
   radeon_sa_bo_new+78                |      1280/64    |      1120/56    |       20 |        0 | 12.500%
   load_elf_binary+2c4                |        32/32    |        28/28    |        1 |        0 | 12.500%
   anon_vma_prepare+101               |       576/72    |       512/64    |        8 |        0 | 11.111%
   ...                                | ...             | ...             | ...    | ...      | ...
  ------------------------------------------------------------------------------------------------------

After:

  ---------------------------------------------------------------------------------------------------------
   Callsite                           | Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag
  ---------------------------------------------------------------------------------------------------------
   radeon_cs_parser_init.part.1+11a   |      2080/260   |      1504/188   |        8 |         0 | 27.692%
   radeon_cs_parser_init.part.1+e1    |       384/96    |       288/72    |        4 |         0 | 25.000%
   radeon_cs_parser_init.part.1+93    |       128/32    |        96/24    |        4 |         0 | 25.000%
   load_elf_binary+a39                |       512/512   |       392/392   |        1 |         0 | 23.438%
   __alloc_skb+89                     |      6144/877   |      4800/685   |        7 |         6 | 21.875%
   radeon_fence_emit+5c               |      1152/192   |       912/152   |        6 |         0 | 20.833%
   radeon_cs_parser_relocs+ad         |      8192/2048  |      6624/1656  |        4 |         0 | 19.141%
   radeon_sa_bo_new+78                |      1280/64    |      1120/56    |       20 |         0 | 12.500%
   load_elf_binary+2c4                |        32/32    |        28/28    |        1 |         0 | 12.500%
   anon_vma_prepare+101               |       576/72    |       512/64    |        8 |         0 | 11.111%
   ...                                | ...             | ...             | ...      | ...       | ...
  ---------------------------------------------------------------------------------------------------------

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Joonsoo Kim <js1304@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1426145571-3065-4-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/builtin-kmem.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 02b7697..8c85aeb 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -275,10 +275,10 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 	struct rb_node *next;
 	struct machine *machine = &session->machines.host;
 
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 
 	next = rb_first(root);
 
@@ -304,7 +304,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
 		printf(" %-34s |", buf);
 
-		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
+		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
 		       (unsigned long long)data->bytes_alloc,
 		       (unsigned long)data->bytes_alloc / data->hit,
 		       (unsigned long long)data->bytes_req,
@@ -317,9 +317,9 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
 	}
 
 	if (n_lines == -1)
-		printf(" ...                                | ...             | ...             | ...    | ...      | ...   \n");
+		printf(" ...                                | ...             | ...             | ...      | ...       | ...   \n");
 
-	printf("%.102s\n", graph_dotted_line);
+	printf("%.105s\n", graph_dotted_line);
 }
 
 static void print_summary(void)

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-13 12:44         ` Ingo Molnar
@ 2015-03-16  2:06           ` Namhyung Kim
  0 siblings, 0 replies; 23+ messages in thread
From: Namhyung Kim @ 2015-03-16  2:06 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

Hi Ingo,

On Fri, Mar 13, 2015 at 01:44:20PM +0100, Ingo Molnar wrote:
> 
> * Namhyung Kim <namhyung@kernel.org> wrote:
> 
> > Hi Ingo,
> > 
> > On Thu, Mar 12, 2015 at 04:54:22PM +0100, Ingo Molnar wrote:
> > > 
> > > * Namhyung Kim <namhyung@kernel.org> wrote:
> > > 
> > > > > I.e. something like this (mockup) output:
> > > > > 
> > > > >    SUMMARY (page allocator)
> > > > >    ========================
> > > > > 
> > > > >    Pages allocated+freed:       12,593   [     51,630,080 bytes ]
> > > > > 
> > > > >    Pages allocated-only:         2,342   [      1,235,010 bytes ]
> > > > >    Pages freed-only:                67   [        135,311 bytes ]
> > > > > 
> > > > >    Page allocation failures :        0
> > > > 
> > > > Looks a lot better!
> > > > 
> > > > One thing I need to tell you is that the numbers are not pages but 
> > > > requests.
> > > 
> > > Yes, but in the MM code we tend to call larger order allocations 
> > > 'pages' as well: higher order pages, such as a 2MB hugepage, or a 8K 
> > > order-1 page. So at least in MM-speak it should be OK to call them 
> > > 'pages'.
> > > 
> > > But your call!
> > 
> > How about this?
> > 
> >   SUMMARY (page allocator)
> >   ========================
> >   Total allocation requests   :        9,015  [     37,200 Kbytes ]  (A)
> >   Total free requests         :        8,093  [     33,176 Kbytes ]  (B)
> > 
> >   Total alloc+freed requests  :        7,985  [     32,732 Kbytes ]  (C)
> >   Total alloc-only requests   :        1,030  [      4,468 Kbytes ]  (D)
> >   Total free-only requests    :          108  [        444 Kbytes ]  (E)
> > 
> >   Total allocation failure    :            0  [          0 Kbytes ]
> 
> s/failure/failures
> s/Kbytes/KB

OK

> 
> I'd leave a bit more space for the numbers, for up into billions of 
> requests and terabytes of data. Other than that, sounds good to me!

I reserved 16 character space for each!

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-12 14:58   ` Namhyung Kim
  2015-03-12 15:54     ` Ingo Molnar
@ 2015-03-16  2:10     ` Namhyung Kim
  2015-03-16  8:26       ` Ingo Molnar
  1 sibling, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-16  2:10 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

On Thu, Mar 12, 2015 at 11:58:37PM +0900, Namhyung Kim wrote:
> On Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar wrote:
> > So there's one thing that would be useful: to track pages allocated on 
> > one node, but freed on another. Those kinds of allocation/free 
> > patterns are especially expensive and might make sense to visualize.
> 
> I think it can be done easily as slab analysis already contains the info.

Hmm.. it seems slab events provide the node info but page events
don't.  Without it, I don't know which node a page is in so cannot
determine such cross-node alloc+free patterns.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-16  2:10     ` Namhyung Kim
@ 2015-03-16  8:26       ` Ingo Molnar
  2015-03-16  8:35         ` Namhyung Kim
  0 siblings, 1 reply; 23+ messages in thread
From: Ingo Molnar @ 2015-03-16  8:26 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim


* Namhyung Kim <namhyung@kernel.org> wrote:

> On Thu, Mar 12, 2015 at 11:58:37PM +0900, Namhyung Kim wrote:
> > On Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar wrote:
> > > So there's one thing that would be useful: to track pages allocated on 
> > > one node, but freed on another. Those kinds of allocation/free 
> > > patterns are especially expensive and might make sense to visualize.
> > 
> > I think it can be done easily as slab analysis already contains the info.
> 
> Hmm.. it seems slab events provide the node info but page events
> don't.  Without it, I don't know which node a page is in so cannot
> determine such cross-node alloc+free patterns.

Well, but we know the CPU that the allocation occured on, and can map 
the CPU to the node.

libnuma can do the mappings, 'man numa' will show you the interfaces, 
I think you'll need numa_node_of_cpu().

numalib needs no initialization, and 'perf bench numa' already links 
to it - so I think you should be fine just doing:

	#include <numa.h>
	#include <numaif.h>

and using the API straight away.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-16  8:26       ` Ingo Molnar
@ 2015-03-16  8:35         ` Namhyung Kim
  2015-03-16  8:43           ` Ingo Molnar
  0 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2015-03-16  8:35 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim

Hi Ingo,

On Mon, Mar 16, 2015 at 5:26 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> * Namhyung Kim <namhyung@kernel.org> wrote:
>
>> On Thu, Mar 12, 2015 at 11:58:37PM +0900, Namhyung Kim wrote:
>> > On Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar wrote:
>> > > So there's one thing that would be useful: to track pages allocated on
>> > > one node, but freed on another. Those kinds of allocation/free
>> > > patterns are especially expensive and might make sense to visualize.
>> >
>> > I think it can be done easily as slab analysis already contains the info.
>>
>> Hmm.. it seems slab events provide the node info but page events
>> don't.  Without it, I don't know which node a page is in so cannot
>> determine such cross-node alloc+free patterns.
>
> Well, but we know the CPU that the allocation occured on, and can map
> the CPU to the node.

Does it gaurantee that the page always belongs to a same node
allocating cpu is in?  I can simply map the page with an node using
cpu info but was not sure whether that's always correct.


Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1)
  2015-03-16  8:35         ` Namhyung Kim
@ 2015-03-16  8:43           ` Ingo Molnar
  0 siblings, 0 replies; 23+ messages in thread
From: Ingo Molnar @ 2015-03-16  8:43 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Jiri Olsa, LKML,
	David Ahern, Minchan Kim, Joonsoo Kim


* Namhyung Kim <namhyung@kernel.org> wrote:

> Hi Ingo,
> 
> On Mon, Mar 16, 2015 at 5:26 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > * Namhyung Kim <namhyung@kernel.org> wrote:
> >
> >> On Thu, Mar 12, 2015 at 11:58:37PM +0900, Namhyung Kim wrote:
> >> > On Thu, Mar 12, 2015 at 11:41:19AM +0100, Ingo Molnar wrote:
> >> > > So there's one thing that would be useful: to track pages allocated on
> >> > > one node, but freed on another. Those kinds of allocation/free
> >> > > patterns are especially expensive and might make sense to visualize.
> >> >
> >> > I think it can be done easily as slab analysis already contains the info.
> >>
> >> Hmm.. it seems slab events provide the node info but page events 
> >> don't.  Without it, I don't know which node a page is in so 
> >> cannot determine such cross-node alloc+free patterns.
> >
> > Well, but we know the CPU that the allocation occured on, and can 
> > map the CPU to the node.
> 
> Does it gaurantee that the page always belongs to a same node 
> allocating cpu is in?  I can simply map the page with an node using 
> cpu info but was not sure whether that's always correct.

There's no such guarantee :-(

We try to (and generally we succeed in) allocating on the same node, 
but tooling really needs to know about the memory pressure and other 
corner cases as well to be dependable.

I suspect we need a new trace_mm_page_alloc tracepoint, and a 
CONFIG_DEBUG_MM_TRACE_PAGE_COMPAT setting to phase out the old 
tracepoint on new distros?

Then we'd also have to track page migration to be able to track the 
full lifetime of a node in tooling: I think most of the tracepoints 
are in place, but it has to be checked whether coverage is 
comprehensive.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2015-03-16  8:43 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-12  7:32 [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Namhyung Kim
2015-03-12  7:32 ` [PATCH 1/6] perf kmem: Fix segfault when invalid sort key is given Namhyung Kim
2015-03-14  7:06   ` [tip:perf/core] " tip-bot for Namhyung Kim
2015-03-12  7:32 ` [PATCH 2/6] perf kmem: Allow -v option Namhyung Kim
2015-03-14  7:06   ` [tip:perf/core] " tip-bot for Namhyung Kim
2015-03-12  7:32 ` [PATCH 3/6] perf kmem: Fix alignment of slab result table Namhyung Kim
2015-03-14  7:07   ` [tip:perf/core] " tip-bot for Namhyung Kim
2015-03-12  7:32 ` [PATCH 4/6] perf kmem: Analyze page allocator events also Namhyung Kim
2015-03-12 11:01   ` Jiri Olsa
2015-03-12 15:11     ` Namhyung Kim
2015-03-12  7:32 ` [PATCH 5/6] perf kmem: Implement stat --page --caller Namhyung Kim
2015-03-12  7:32 ` [PATCH 6/6] perf kmem: Support sort keys on page analysis Namhyung Kim
2015-03-12 10:41 ` [RFC/PATCHSET 0/6] perf kmem: Implement page allocation analysis (v1) Ingo Molnar
2015-03-12 14:58   ` Namhyung Kim
2015-03-12 15:54     ` Ingo Molnar
2015-03-13  8:19       ` Namhyung Kim
2015-03-13 12:44         ` Ingo Molnar
2015-03-16  2:06           ` Namhyung Kim
2015-03-16  2:10     ` Namhyung Kim
2015-03-16  8:26       ` Ingo Molnar
2015-03-16  8:35         ` Namhyung Kim
2015-03-16  8:43           ` Ingo Molnar
2015-03-12 19:07   ` Arnaldo Carvalho de Melo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).