xref: /openbmc/linux/tools/perf/builtin-kmem.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ba77c9e1SLi Zefan #include "builtin.h"
3ba77c9e1SLi Zefan 
44a3cec84SArnaldo Carvalho de Melo #include "util/dso.h"
50f7d2f1bSArnaldo Carvalho de Melo #include "util/evlist.h"
6fcf65bf1SArnaldo Carvalho de Melo #include "util/evsel.h"
741840d21STaeung Song #include "util/config.h"
81101f69aSArnaldo Carvalho de Melo #include "util/map.h"
9ba77c9e1SLi Zefan #include "util/symbol.h"
10ba77c9e1SLi Zefan #include "util/thread.h"
11ba77c9e1SLi Zefan #include "util/header.h"
1294c744b6SArnaldo Carvalho de Melo #include "util/session.h"
1345694aa7SArnaldo Carvalho de Melo #include "util/tool.h"
14c9758cc4SNamhyung Kim #include "util/callchain.h"
152a865bd8SDavid Ahern #include "util/time-utils.h"
166ef81c55SMamatha Inamdar #include <linux/err.h>
17ba77c9e1SLi Zefan 
188520a98dSArnaldo Carvalho de Melo #include <subcmd/pager.h>
194b6ab94eSJosh Poimboeuf #include <subcmd/parse-options.h>
20ba77c9e1SLi Zefan #include "util/trace-event.h"
21f5fc1412SJiri Olsa #include "util/data.h"
224b627957SDon Zickus #include "util/cpumap.h"
23ba77c9e1SLi Zefan 
24ba77c9e1SLi Zefan #include "util/debug.h"
256a9fa4e3SArnaldo Carvalho de Melo #include "util/string2.h"
26f12ad272SIan Rogers #include "util/util.h"
27ba77c9e1SLi Zefan 
28877a7a11SArnaldo Carvalho de Melo #include <linux/kernel.h>
29dce088abSLeo Yan #include <linux/numa.h>
30ba77c9e1SLi Zefan #include <linux/rbtree.h>
318d9233f2SArnaldo Carvalho de Melo #include <linux/string.h>
327f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
33a43783aeSArnaldo Carvalho de Melo #include <errno.h>
34fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
3577cfe388SNamhyung Kim #include <locale.h>
36c9758cc4SNamhyung Kim #include <regex.h>
37ba77c9e1SLi Zefan 
383052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
39378ef0f5SIan Rogers #include <traceevent/event-parse.h>
403d689ed6SArnaldo Carvalho de Melo 
410d68bc92SNamhyung Kim static int	kmem_slab;
420d68bc92SNamhyung Kim static int	kmem_page;
430d68bc92SNamhyung Kim 
440d68bc92SNamhyung Kim static long	kmem_page_size;
450c160d49SNamhyung Kim static enum {
460c160d49SNamhyung Kim 	KMEM_SLAB,
470c160d49SNamhyung Kim 	KMEM_PAGE,
480c160d49SNamhyung Kim } kmem_default = KMEM_SLAB;  /* for backward compatibility */
490d68bc92SNamhyung Kim 
50ba77c9e1SLi Zefan struct alloc_stat;
51fb4f313dSNamhyung Kim typedef int (*sort_fn_t)(void *, void *);
52ba77c9e1SLi Zefan 
53ba77c9e1SLi Zefan static int			alloc_flag;
54ba77c9e1SLi Zefan static int			caller_flag;
55ba77c9e1SLi Zefan 
56ba77c9e1SLi Zefan static int			alloc_lines = -1;
57ba77c9e1SLi Zefan static int			caller_lines = -1;
58ba77c9e1SLi Zefan 
597707b6b6SLi Zefan static bool			raw_ip;
607707b6b6SLi Zefan 
61ba77c9e1SLi Zefan struct alloc_stat {
62ba77c9e1SLi Zefan 	u64	call_site;
63ba77c9e1SLi Zefan 	u64	ptr;
64ba77c9e1SLi Zefan 	u64	bytes_req;
65ba77c9e1SLi Zefan 	u64	bytes_alloc;
66aa58e9afSDavid Ahern 	u64	last_alloc;
67ba77c9e1SLi Zefan 	u32	hit;
68079d3f65SLi Zefan 	u32	pingpong;
69079d3f65SLi Zefan 
70079d3f65SLi Zefan 	short	alloc_cpu;
71ba77c9e1SLi Zefan 
72ba77c9e1SLi Zefan 	struct rb_node node;
73ba77c9e1SLi Zefan };
74ba77c9e1SLi Zefan 
75ba77c9e1SLi Zefan static struct rb_root root_alloc_stat;
76ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted;
77ba77c9e1SLi Zefan static struct rb_root root_caller_stat;
78ba77c9e1SLi Zefan static struct rb_root root_caller_sorted;
79ba77c9e1SLi Zefan 
80aa58e9afSDavid Ahern static unsigned long total_requested, total_allocated, total_freed;
817d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs;
82ba77c9e1SLi Zefan 
832a865bd8SDavid Ahern /* filters for controlling start and stop of time of analysis */
842a865bd8SDavid Ahern static struct perf_time_interval ptime;
852a865bd8SDavid Ahern const char *time_str;
862a865bd8SDavid Ahern 
insert_alloc_stat(unsigned long call_site,unsigned long ptr,int bytes_req,int bytes_alloc,int cpu)872814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
88079d3f65SLi Zefan 			     int bytes_req, int bytes_alloc, int cpu)
89ba77c9e1SLi Zefan {
90ba77c9e1SLi Zefan 	struct rb_node **node = &root_alloc_stat.rb_node;
91ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
92ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
93ba77c9e1SLi Zefan 
94ba77c9e1SLi Zefan 	while (*node) {
95ba77c9e1SLi Zefan 		parent = *node;
96ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
97ba77c9e1SLi Zefan 
98ba77c9e1SLi Zefan 		if (ptr > data->ptr)
99ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
100ba77c9e1SLi Zefan 		else if (ptr < data->ptr)
101ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
102ba77c9e1SLi Zefan 		else
103ba77c9e1SLi Zefan 			break;
104ba77c9e1SLi Zefan 	}
105ba77c9e1SLi Zefan 
106ba77c9e1SLi Zefan 	if (data && data->ptr == ptr) {
107ba77c9e1SLi Zefan 		data->hit++;
108ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
1094efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
110ba77c9e1SLi Zefan 	} else {
111ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
1122814eb05SArnaldo Carvalho de Melo 		if (!data) {
1132814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
1142814eb05SArnaldo Carvalho de Melo 			return -1;
1152814eb05SArnaldo Carvalho de Melo 		}
116ba77c9e1SLi Zefan 		data->ptr = ptr;
117079d3f65SLi Zefan 		data->pingpong = 0;
118ba77c9e1SLi Zefan 		data->hit = 1;
119ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
120ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
121ba77c9e1SLi Zefan 
122ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
123ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_alloc_stat);
124ba77c9e1SLi Zefan 	}
125079d3f65SLi Zefan 	data->call_site = call_site;
126079d3f65SLi Zefan 	data->alloc_cpu = cpu;
127aa58e9afSDavid Ahern 	data->last_alloc = bytes_alloc;
128aa58e9afSDavid Ahern 
1292814eb05SArnaldo Carvalho de Melo 	return 0;
130ba77c9e1SLi Zefan }
131ba77c9e1SLi Zefan 
insert_caller_stat(unsigned long call_site,int bytes_req,int bytes_alloc)1322814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site,
133ba77c9e1SLi Zefan 			      int bytes_req, int bytes_alloc)
134ba77c9e1SLi Zefan {
135ba77c9e1SLi Zefan 	struct rb_node **node = &root_caller_stat.rb_node;
136ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
137ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
138ba77c9e1SLi Zefan 
139ba77c9e1SLi Zefan 	while (*node) {
140ba77c9e1SLi Zefan 		parent = *node;
141ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
142ba77c9e1SLi Zefan 
143ba77c9e1SLi Zefan 		if (call_site > data->call_site)
144ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
145ba77c9e1SLi Zefan 		else if (call_site < data->call_site)
146ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
147ba77c9e1SLi Zefan 		else
148ba77c9e1SLi Zefan 			break;
149ba77c9e1SLi Zefan 	}
150ba77c9e1SLi Zefan 
151ba77c9e1SLi Zefan 	if (data && data->call_site == call_site) {
152ba77c9e1SLi Zefan 		data->hit++;
153ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
1544efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
155ba77c9e1SLi Zefan 	} else {
156ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
1572814eb05SArnaldo Carvalho de Melo 		if (!data) {
1582814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
1592814eb05SArnaldo Carvalho de Melo 			return -1;
1602814eb05SArnaldo Carvalho de Melo 		}
161ba77c9e1SLi Zefan 		data->call_site = call_site;
162079d3f65SLi Zefan 		data->pingpong = 0;
163ba77c9e1SLi Zefan 		data->hit = 1;
164ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
165ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
166ba77c9e1SLi Zefan 
167ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
168ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_caller_stat);
169ba77c9e1SLi Zefan 	}
1702814eb05SArnaldo Carvalho de Melo 
1712814eb05SArnaldo Carvalho de Melo 	return 0;
172ba77c9e1SLi Zefan }
173ba77c9e1SLi Zefan 
evsel__process_alloc_event(struct evsel * evsel,struct perf_sample * sample)1748cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample)
175ba77c9e1SLi Zefan {
176efc0cdc9SArnaldo Carvalho de Melo 	unsigned long ptr = evsel__intval(evsel, sample, "ptr"),
177efc0cdc9SArnaldo Carvalho de Melo 		      call_site = evsel__intval(evsel, sample, "call_site");
178efc0cdc9SArnaldo Carvalho de Melo 	int bytes_req = evsel__intval(evsel, sample, "bytes_req"),
179efc0cdc9SArnaldo Carvalho de Melo 	    bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc");
180ba77c9e1SLi Zefan 
1810f7d2f1bSArnaldo Carvalho de Melo 	if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
1822814eb05SArnaldo Carvalho de Melo 	    insert_caller_stat(call_site, bytes_req, bytes_alloc))
1832814eb05SArnaldo Carvalho de Melo 		return -1;
184ba77c9e1SLi Zefan 
185ba77c9e1SLi Zefan 	total_requested += bytes_req;
186ba77c9e1SLi Zefan 	total_allocated += bytes_alloc;
1877d0d3945SLi Zefan 
1880f7d2f1bSArnaldo Carvalho de Melo 	nr_allocs++;
1890f7d2f1bSArnaldo Carvalho de Melo 
190dce088abSLeo Yan 	/*
191dce088abSLeo Yan 	 * Commit 11e9734bcb6a ("mm/slab_common: unify NUMA and UMA
192dce088abSLeo Yan 	 * version of tracepoints") adds the field "node" into the
193dce088abSLeo Yan 	 * tracepoints 'kmalloc' and 'kmem_cache_alloc'.
194dce088abSLeo Yan 	 *
195dce088abSLeo Yan 	 * The legacy tracepoints 'kmalloc_node' and 'kmem_cache_alloc_node'
196dce088abSLeo Yan 	 * also contain the field "node".
197dce088abSLeo Yan 	 *
198dce088abSLeo Yan 	 * If the tracepoint contains the field "node" the tool stats the
199dce088abSLeo Yan 	 * cross allocation.
200dce088abSLeo Yan 	 */
201dce088abSLeo Yan 	if (evsel__field(evsel, "node")) {
202dce088abSLeo Yan 		int node1, node2;
2030f7d2f1bSArnaldo Carvalho de Melo 
204dce088abSLeo Yan 		node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu});
205efc0cdc9SArnaldo Carvalho de Melo 		node2 = evsel__intval(evsel, sample, "node");
2060f7d2f1bSArnaldo Carvalho de Melo 
207dce088abSLeo Yan 		/*
208dce088abSLeo Yan 		 * If the field "node" is NUMA_NO_NODE (-1), we don't take it
209dce088abSLeo Yan 		 * as a cross allocation.
210dce088abSLeo Yan 		 */
211dce088abSLeo Yan 		if ((node2 != NUMA_NO_NODE) && (node1 != node2))
2127d0d3945SLi Zefan 			nr_cross_allocs++;
2137d0d3945SLi Zefan 	}
2140f7d2f1bSArnaldo Carvalho de Melo 
215dce088abSLeo Yan 	return 0;
216ba77c9e1SLi Zefan }
217ba77c9e1SLi Zefan 
218fb4f313dSNamhyung Kim static int ptr_cmp(void *, void *);
219fb4f313dSNamhyung Kim static int slab_callsite_cmp(void *, void *);
220079d3f65SLi Zefan 
search_alloc_stat(unsigned long ptr,unsigned long call_site,struct rb_root * root,sort_fn_t sort_fn)221079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr,
222079d3f65SLi Zefan 					    unsigned long call_site,
223079d3f65SLi Zefan 					    struct rb_root *root,
224079d3f65SLi Zefan 					    sort_fn_t sort_fn)
225079d3f65SLi Zefan {
226079d3f65SLi Zefan 	struct rb_node *node = root->rb_node;
227079d3f65SLi Zefan 	struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
228079d3f65SLi Zefan 
229079d3f65SLi Zefan 	while (node) {
230079d3f65SLi Zefan 		struct alloc_stat *data;
231079d3f65SLi Zefan 		int cmp;
232079d3f65SLi Zefan 
233079d3f65SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
234079d3f65SLi Zefan 
235079d3f65SLi Zefan 		cmp = sort_fn(&key, data);
236079d3f65SLi Zefan 		if (cmp < 0)
237079d3f65SLi Zefan 			node = node->rb_left;
238079d3f65SLi Zefan 		else if (cmp > 0)
239079d3f65SLi Zefan 			node = node->rb_right;
240079d3f65SLi Zefan 		else
241079d3f65SLi Zefan 			return data;
242079d3f65SLi Zefan 	}
243079d3f65SLi Zefan 	return NULL;
244079d3f65SLi Zefan }
245079d3f65SLi Zefan 
evsel__process_free_event(struct evsel * evsel,struct perf_sample * sample)2468cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample)
247ba77c9e1SLi Zefan {
248efc0cdc9SArnaldo Carvalho de Melo 	unsigned long ptr = evsel__intval(evsel, sample, "ptr");
249079d3f65SLi Zefan 	struct alloc_stat *s_alloc, *s_caller;
250079d3f65SLi Zefan 
251079d3f65SLi Zefan 	s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
252079d3f65SLi Zefan 	if (!s_alloc)
2532814eb05SArnaldo Carvalho de Melo 		return 0;
254079d3f65SLi Zefan 
255aa58e9afSDavid Ahern 	total_freed += s_alloc->last_alloc;
256aa58e9afSDavid Ahern 
25722ad798cSArnaldo Carvalho de Melo 	if ((short)sample->cpu != s_alloc->alloc_cpu) {
258079d3f65SLi Zefan 		s_alloc->pingpong++;
259079d3f65SLi Zefan 
260079d3f65SLi Zefan 		s_caller = search_alloc_stat(0, s_alloc->call_site,
261fb4f313dSNamhyung Kim 					     &root_caller_stat,
262fb4f313dSNamhyung Kim 					     slab_callsite_cmp);
2632814eb05SArnaldo Carvalho de Melo 		if (!s_caller)
2642814eb05SArnaldo Carvalho de Melo 			return -1;
265079d3f65SLi Zefan 		s_caller->pingpong++;
266079d3f65SLi Zefan 	}
267079d3f65SLi Zefan 	s_alloc->alloc_cpu = -1;
2682814eb05SArnaldo Carvalho de Melo 
2692814eb05SArnaldo Carvalho de Melo 	return 0;
270ba77c9e1SLi Zefan }
271ba77c9e1SLi Zefan 
2720d68bc92SNamhyung Kim static u64 total_page_alloc_bytes;
2730d68bc92SNamhyung Kim static u64 total_page_free_bytes;
2740d68bc92SNamhyung Kim static u64 total_page_nomatch_bytes;
2750d68bc92SNamhyung Kim static u64 total_page_fail_bytes;
2760d68bc92SNamhyung Kim static unsigned long nr_page_allocs;
2770d68bc92SNamhyung Kim static unsigned long nr_page_frees;
2780d68bc92SNamhyung Kim static unsigned long nr_page_fails;
2790d68bc92SNamhyung Kim static unsigned long nr_page_nomatch;
2800d68bc92SNamhyung Kim 
2810d68bc92SNamhyung Kim static bool use_pfn;
2822a7ef02cSNamhyung Kim static bool live_page;
283c9758cc4SNamhyung Kim static struct perf_session *kmem_session;
2840d68bc92SNamhyung Kim 
2850d68bc92SNamhyung Kim #define MAX_MIGRATE_TYPES  6
2860d68bc92SNamhyung Kim #define MAX_PAGE_ORDER     11
2870d68bc92SNamhyung Kim 
2880d68bc92SNamhyung Kim static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
2890d68bc92SNamhyung Kim 
2900d68bc92SNamhyung Kim struct page_stat {
2910d68bc92SNamhyung Kim 	struct rb_node 	node;
2920d68bc92SNamhyung Kim 	u64 		page;
293c9758cc4SNamhyung Kim 	u64 		callsite;
2940d68bc92SNamhyung Kim 	int 		order;
2950d68bc92SNamhyung Kim 	unsigned 	gfp_flags;
2960d68bc92SNamhyung Kim 	unsigned 	migrate_type;
2970d68bc92SNamhyung Kim 	u64		alloc_bytes;
2980d68bc92SNamhyung Kim 	u64 		free_bytes;
2990d68bc92SNamhyung Kim 	int 		nr_alloc;
3000d68bc92SNamhyung Kim 	int 		nr_free;
3010d68bc92SNamhyung Kim };
3020d68bc92SNamhyung Kim 
3032a7ef02cSNamhyung Kim static struct rb_root page_live_tree;
3040d68bc92SNamhyung Kim static struct rb_root page_alloc_tree;
3050d68bc92SNamhyung Kim static struct rb_root page_alloc_sorted;
306c9758cc4SNamhyung Kim static struct rb_root page_caller_tree;
307c9758cc4SNamhyung Kim static struct rb_root page_caller_sorted;
3080d68bc92SNamhyung Kim 
309c9758cc4SNamhyung Kim struct alloc_func {
310c9758cc4SNamhyung Kim 	u64 start;
311c9758cc4SNamhyung Kim 	u64 end;
312c9758cc4SNamhyung Kim 	char *name;
313c9758cc4SNamhyung Kim };
314c9758cc4SNamhyung Kim 
315c9758cc4SNamhyung Kim static int nr_alloc_funcs;
316c9758cc4SNamhyung Kim static struct alloc_func *alloc_func_list;
317c9758cc4SNamhyung Kim 
funcmp(const void * a,const void * b)318c9758cc4SNamhyung Kim static int funcmp(const void *a, const void *b)
319c9758cc4SNamhyung Kim {
320c9758cc4SNamhyung Kim 	const struct alloc_func *fa = a;
321c9758cc4SNamhyung Kim 	const struct alloc_func *fb = b;
322c9758cc4SNamhyung Kim 
323c9758cc4SNamhyung Kim 	if (fa->start > fb->start)
324c9758cc4SNamhyung Kim 		return 1;
325c9758cc4SNamhyung Kim 	else
326c9758cc4SNamhyung Kim 		return -1;
327c9758cc4SNamhyung Kim }
328c9758cc4SNamhyung Kim 
callcmp(const void * a,const void * b)329c9758cc4SNamhyung Kim static int callcmp(const void *a, const void *b)
330c9758cc4SNamhyung Kim {
331c9758cc4SNamhyung Kim 	const struct alloc_func *fa = a;
332c9758cc4SNamhyung Kim 	const struct alloc_func *fb = b;
333c9758cc4SNamhyung Kim 
334c9758cc4SNamhyung Kim 	if (fb->start <= fa->start && fa->end < fb->end)
335c9758cc4SNamhyung Kim 		return 0;
336c9758cc4SNamhyung Kim 
337c9758cc4SNamhyung Kim 	if (fa->start > fb->start)
338c9758cc4SNamhyung Kim 		return 1;
339c9758cc4SNamhyung Kim 	else
340c9758cc4SNamhyung Kim 		return -1;
341c9758cc4SNamhyung Kim }
342c9758cc4SNamhyung Kim 
build_alloc_func_list(void)343c9758cc4SNamhyung Kim static int build_alloc_func_list(void)
344c9758cc4SNamhyung Kim {
345c9758cc4SNamhyung Kim 	int ret;
346c9758cc4SNamhyung Kim 	struct map *kernel_map;
347c9758cc4SNamhyung Kim 	struct symbol *sym;
348c9758cc4SNamhyung Kim 	struct rb_node *node;
349c9758cc4SNamhyung Kim 	struct alloc_func *func;
350c9758cc4SNamhyung Kim 	struct machine *machine = &kmem_session->machines.host;
351c9758cc4SNamhyung Kim 	regex_t alloc_func_regex;
35249b8e2beSRasmus Villemoes 	static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
353c9758cc4SNamhyung Kim 
354c9758cc4SNamhyung Kim 	ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
355c9758cc4SNamhyung Kim 	if (ret) {
356c9758cc4SNamhyung Kim 		char err[BUFSIZ];
357c9758cc4SNamhyung Kim 
358c9758cc4SNamhyung Kim 		regerror(ret, &alloc_func_regex, err, sizeof(err));
359c9758cc4SNamhyung Kim 		pr_err("Invalid regex: %s\n%s", pattern, err);
360c9758cc4SNamhyung Kim 		return -EINVAL;
361c9758cc4SNamhyung Kim 	}
362c9758cc4SNamhyung Kim 
363a5e813c6SArnaldo Carvalho de Melo 	kernel_map = machine__kernel_map(machine);
364be39db9fSArnaldo Carvalho de Melo 	if (map__load(kernel_map) < 0) {
365c9758cc4SNamhyung Kim 		pr_err("cannot load kernel map\n");
366c9758cc4SNamhyung Kim 		return -ENOENT;
367c9758cc4SNamhyung Kim 	}
368c9758cc4SNamhyung Kim 
369c9758cc4SNamhyung Kim 	map__for_each_symbol(kernel_map, sym, node) {
370c9758cc4SNamhyung Kim 		if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
371c9758cc4SNamhyung Kim 			continue;
372c9758cc4SNamhyung Kim 
373c9758cc4SNamhyung Kim 		func = realloc(alloc_func_list,
374c9758cc4SNamhyung Kim 			       (nr_alloc_funcs + 1) * sizeof(*func));
375c9758cc4SNamhyung Kim 		if (func == NULL)
376c9758cc4SNamhyung Kim 			return -ENOMEM;
377c9758cc4SNamhyung Kim 
378c9758cc4SNamhyung Kim 		pr_debug("alloc func: %s\n", sym->name);
379c9758cc4SNamhyung Kim 		func[nr_alloc_funcs].start = sym->start;
380c9758cc4SNamhyung Kim 		func[nr_alloc_funcs].end   = sym->end;
381c9758cc4SNamhyung Kim 		func[nr_alloc_funcs].name  = sym->name;
382c9758cc4SNamhyung Kim 
383c9758cc4SNamhyung Kim 		alloc_func_list = func;
384c9758cc4SNamhyung Kim 		nr_alloc_funcs++;
385c9758cc4SNamhyung Kim 	}
386c9758cc4SNamhyung Kim 
387c9758cc4SNamhyung Kim 	qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
388c9758cc4SNamhyung Kim 
389c9758cc4SNamhyung Kim 	regfree(&alloc_func_regex);
390c9758cc4SNamhyung Kim 	return 0;
391c9758cc4SNamhyung Kim }
392c9758cc4SNamhyung Kim 
393c9758cc4SNamhyung Kim /*
394c9758cc4SNamhyung Kim  * Find first non-memory allocation function from callchain.
395c9758cc4SNamhyung Kim  * The allocation functions are in the 'alloc_func_list'.
396c9758cc4SNamhyung Kim  */
find_callsite(struct evsel * evsel,struct perf_sample * sample)39732dcd021SJiri Olsa static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
398c9758cc4SNamhyung Kim {
399c9758cc4SNamhyung Kim 	struct addr_location al;
400c9758cc4SNamhyung Kim 	struct machine *machine = &kmem_session->machines.host;
401c9758cc4SNamhyung Kim 	struct callchain_cursor_node *node;
4028ab12a20SIan Rogers 	struct callchain_cursor *cursor;
4030dd5041cSIan Rogers 	u64 result = sample->ip;
404c9758cc4SNamhyung Kim 
4050dd5041cSIan Rogers 	addr_location__init(&al);
406c9758cc4SNamhyung Kim 	if (alloc_func_list == NULL) {
407c9758cc4SNamhyung Kim 		if (build_alloc_func_list() < 0)
408c9758cc4SNamhyung Kim 			goto out;
409c9758cc4SNamhyung Kim 	}
410c9758cc4SNamhyung Kim 
411c9758cc4SNamhyung Kim 	al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
412c9758cc4SNamhyung Kim 
4138ab12a20SIan Rogers 	cursor = get_tls_callchain_cursor();
4148ab12a20SIan Rogers 	if (cursor == NULL)
4158ab12a20SIan Rogers 		goto out;
4168ab12a20SIan Rogers 
4178ab12a20SIan Rogers 	sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16);
4188ab12a20SIan Rogers 
4198ab12a20SIan Rogers 	callchain_cursor_commit(cursor);
420c9758cc4SNamhyung Kim 	while (true) {
421c9758cc4SNamhyung Kim 		struct alloc_func key, *caller;
422c9758cc4SNamhyung Kim 		u64 addr;
423c9758cc4SNamhyung Kim 
4248ab12a20SIan Rogers 		node = callchain_cursor_current(cursor);
425c9758cc4SNamhyung Kim 		if (node == NULL)
426c9758cc4SNamhyung Kim 			break;
427c9758cc4SNamhyung Kim 
428c9758cc4SNamhyung Kim 		key.start = key.end = node->ip;
429c9758cc4SNamhyung Kim 		caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
430c9758cc4SNamhyung Kim 				 sizeof(key), callcmp);
431c9758cc4SNamhyung Kim 		if (!caller) {
432c9758cc4SNamhyung Kim 			/* found */
4335f0fef8aSArnaldo Carvalho de Melo 			if (node->ms.map)
4340e6aa013SIan Rogers 				addr = map__dso_unmap_ip(node->ms.map, node->ip);
435c9758cc4SNamhyung Kim 			else
436c9758cc4SNamhyung Kim 				addr = node->ip;
437c9758cc4SNamhyung Kim 
4380dd5041cSIan Rogers 			result = addr;
4390dd5041cSIan Rogers 			goto out;
440c9758cc4SNamhyung Kim 		} else
441c9758cc4SNamhyung Kim 			pr_debug3("skipping alloc function: %s\n", caller->name);
442c9758cc4SNamhyung Kim 
4438ab12a20SIan Rogers 		callchain_cursor_advance(cursor);
444c9758cc4SNamhyung Kim 	}
445c9758cc4SNamhyung Kim 
446c9758cc4SNamhyung Kim 	pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
4470dd5041cSIan Rogers out:
4480dd5041cSIan Rogers 	addr_location__exit(&al);
4490dd5041cSIan Rogers 	return result;
450c9758cc4SNamhyung Kim }
451c9758cc4SNamhyung Kim 
4522a7ef02cSNamhyung Kim struct sort_dimension {
4532a7ef02cSNamhyung Kim 	const char		name[20];
4542a7ef02cSNamhyung Kim 	sort_fn_t		cmp;
4552a7ef02cSNamhyung Kim 	struct list_head	list;
4562a7ef02cSNamhyung Kim };
4572a7ef02cSNamhyung Kim 
4582a7ef02cSNamhyung Kim static LIST_HEAD(page_alloc_sort_input);
4592a7ef02cSNamhyung Kim static LIST_HEAD(page_caller_sort_input);
4602a7ef02cSNamhyung Kim 
461c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_page(struct page_stat * pstat,bool create)4622a7ef02cSNamhyung Kim __page_stat__findnew_page(struct page_stat *pstat, bool create)
4630d68bc92SNamhyung Kim {
4642a7ef02cSNamhyung Kim 	struct rb_node **node = &page_live_tree.rb_node;
4650d68bc92SNamhyung Kim 	struct rb_node *parent = NULL;
4660d68bc92SNamhyung Kim 	struct page_stat *data;
4670d68bc92SNamhyung Kim 
4680d68bc92SNamhyung Kim 	while (*node) {
4690d68bc92SNamhyung Kim 		s64 cmp;
4700d68bc92SNamhyung Kim 
4710d68bc92SNamhyung Kim 		parent = *node;
4720d68bc92SNamhyung Kim 		data = rb_entry(*node, struct page_stat, node);
4730d68bc92SNamhyung Kim 
4742a7ef02cSNamhyung Kim 		cmp = data->page - pstat->page;
4750d68bc92SNamhyung Kim 		if (cmp < 0)
4760d68bc92SNamhyung Kim 			node = &parent->rb_left;
4770d68bc92SNamhyung Kim 		else if (cmp > 0)
4780d68bc92SNamhyung Kim 			node = &parent->rb_right;
4790d68bc92SNamhyung Kim 		else
4800d68bc92SNamhyung Kim 			return data;
4810d68bc92SNamhyung Kim 	}
4820d68bc92SNamhyung Kim 
4830d68bc92SNamhyung Kim 	if (!create)
4840d68bc92SNamhyung Kim 		return NULL;
4850d68bc92SNamhyung Kim 
4860d68bc92SNamhyung Kim 	data = zalloc(sizeof(*data));
4870d68bc92SNamhyung Kim 	if (data != NULL) {
4882a7ef02cSNamhyung Kim 		data->page = pstat->page;
4892a7ef02cSNamhyung Kim 		data->order = pstat->order;
4902a7ef02cSNamhyung Kim 		data->gfp_flags = pstat->gfp_flags;
4912a7ef02cSNamhyung Kim 		data->migrate_type = pstat->migrate_type;
4920d68bc92SNamhyung Kim 
4930d68bc92SNamhyung Kim 		rb_link_node(&data->node, parent, node);
4942a7ef02cSNamhyung Kim 		rb_insert_color(&data->node, &page_live_tree);
4950d68bc92SNamhyung Kim 	}
4960d68bc92SNamhyung Kim 
4970d68bc92SNamhyung Kim 	return data;
4980d68bc92SNamhyung Kim }
4990d68bc92SNamhyung Kim 
page_stat__find_page(struct page_stat * pstat)5002a7ef02cSNamhyung Kim static struct page_stat *page_stat__find_page(struct page_stat *pstat)
501c9758cc4SNamhyung Kim {
5022a7ef02cSNamhyung Kim 	return __page_stat__findnew_page(pstat, false);
503c9758cc4SNamhyung Kim }
504c9758cc4SNamhyung Kim 
page_stat__findnew_page(struct page_stat * pstat)5052a7ef02cSNamhyung Kim static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
506c9758cc4SNamhyung Kim {
5072a7ef02cSNamhyung Kim 	return __page_stat__findnew_page(pstat, true);
508c9758cc4SNamhyung Kim }
509c9758cc4SNamhyung Kim 
510c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_alloc(struct page_stat * pstat,bool create)511c9758cc4SNamhyung Kim __page_stat__findnew_alloc(struct page_stat *pstat, bool create)
5120d68bc92SNamhyung Kim {
5130d68bc92SNamhyung Kim 	struct rb_node **node = &page_alloc_tree.rb_node;
5140d68bc92SNamhyung Kim 	struct rb_node *parent = NULL;
5150d68bc92SNamhyung Kim 	struct page_stat *data;
516fb4f313dSNamhyung Kim 	struct sort_dimension *sort;
5170d68bc92SNamhyung Kim 
5180d68bc92SNamhyung Kim 	while (*node) {
519fb4f313dSNamhyung Kim 		int cmp = 0;
5200d68bc92SNamhyung Kim 
5210d68bc92SNamhyung Kim 		parent = *node;
5220d68bc92SNamhyung Kim 		data = rb_entry(*node, struct page_stat, node);
5230d68bc92SNamhyung Kim 
524fb4f313dSNamhyung Kim 		list_for_each_entry(sort, &page_alloc_sort_input, list) {
525fb4f313dSNamhyung Kim 			cmp = sort->cmp(pstat, data);
526fb4f313dSNamhyung Kim 			if (cmp)
527fb4f313dSNamhyung Kim 				break;
528fb4f313dSNamhyung Kim 		}
529fb4f313dSNamhyung Kim 
5300d68bc92SNamhyung Kim 		if (cmp < 0)
5310d68bc92SNamhyung Kim 			node = &parent->rb_left;
5320d68bc92SNamhyung Kim 		else if (cmp > 0)
5330d68bc92SNamhyung Kim 			node = &parent->rb_right;
5340d68bc92SNamhyung Kim 		else
5350d68bc92SNamhyung Kim 			return data;
5360d68bc92SNamhyung Kim 	}
5370d68bc92SNamhyung Kim 
5380d68bc92SNamhyung Kim 	if (!create)
5390d68bc92SNamhyung Kim 		return NULL;
5400d68bc92SNamhyung Kim 
5410d68bc92SNamhyung Kim 	data = zalloc(sizeof(*data));
5420d68bc92SNamhyung Kim 	if (data != NULL) {
5436b1a2752SDavid Ahern 		data->page = pstat->page;
5446b1a2752SDavid Ahern 		data->order = pstat->order;
5456b1a2752SDavid Ahern 		data->gfp_flags = pstat->gfp_flags;
5466b1a2752SDavid Ahern 		data->migrate_type = pstat->migrate_type;
5470d68bc92SNamhyung Kim 
5480d68bc92SNamhyung Kim 		rb_link_node(&data->node, parent, node);
5490d68bc92SNamhyung Kim 		rb_insert_color(&data->node, &page_alloc_tree);
5500d68bc92SNamhyung Kim 	}
5510d68bc92SNamhyung Kim 
5520d68bc92SNamhyung Kim 	return data;
5530d68bc92SNamhyung Kim }
5540d68bc92SNamhyung Kim 
page_stat__find_alloc(struct page_stat * pstat)555c9758cc4SNamhyung Kim static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
556c9758cc4SNamhyung Kim {
557c9758cc4SNamhyung Kim 	return __page_stat__findnew_alloc(pstat, false);
558c9758cc4SNamhyung Kim }
559c9758cc4SNamhyung Kim 
page_stat__findnew_alloc(struct page_stat * pstat)560c9758cc4SNamhyung Kim static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
561c9758cc4SNamhyung Kim {
562c9758cc4SNamhyung Kim 	return __page_stat__findnew_alloc(pstat, true);
563c9758cc4SNamhyung Kim }
564c9758cc4SNamhyung Kim 
565c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_caller(struct page_stat * pstat,bool create)566fb4f313dSNamhyung Kim __page_stat__findnew_caller(struct page_stat *pstat, bool create)
567c9758cc4SNamhyung Kim {
568c9758cc4SNamhyung Kim 	struct rb_node **node = &page_caller_tree.rb_node;
569c9758cc4SNamhyung Kim 	struct rb_node *parent = NULL;
570c9758cc4SNamhyung Kim 	struct page_stat *data;
571fb4f313dSNamhyung Kim 	struct sort_dimension *sort;
572c9758cc4SNamhyung Kim 
573c9758cc4SNamhyung Kim 	while (*node) {
574fb4f313dSNamhyung Kim 		int cmp = 0;
575c9758cc4SNamhyung Kim 
576c9758cc4SNamhyung Kim 		parent = *node;
577c9758cc4SNamhyung Kim 		data = rb_entry(*node, struct page_stat, node);
578c9758cc4SNamhyung Kim 
579fb4f313dSNamhyung Kim 		list_for_each_entry(sort, &page_caller_sort_input, list) {
580fb4f313dSNamhyung Kim 			cmp = sort->cmp(pstat, data);
581fb4f313dSNamhyung Kim 			if (cmp)
582fb4f313dSNamhyung Kim 				break;
583fb4f313dSNamhyung Kim 		}
584fb4f313dSNamhyung Kim 
585c9758cc4SNamhyung Kim 		if (cmp < 0)
586c9758cc4SNamhyung Kim 			node = &parent->rb_left;
587c9758cc4SNamhyung Kim 		else if (cmp > 0)
588c9758cc4SNamhyung Kim 			node = &parent->rb_right;
589c9758cc4SNamhyung Kim 		else
590c9758cc4SNamhyung Kim 			return data;
591c9758cc4SNamhyung Kim 	}
592c9758cc4SNamhyung Kim 
593c9758cc4SNamhyung Kim 	if (!create)
594c9758cc4SNamhyung Kim 		return NULL;
595c9758cc4SNamhyung Kim 
596c9758cc4SNamhyung Kim 	data = zalloc(sizeof(*data));
597c9758cc4SNamhyung Kim 	if (data != NULL) {
598fb4f313dSNamhyung Kim 		data->callsite = pstat->callsite;
599fb4f313dSNamhyung Kim 		data->order = pstat->order;
600fb4f313dSNamhyung Kim 		data->gfp_flags = pstat->gfp_flags;
601fb4f313dSNamhyung Kim 		data->migrate_type = pstat->migrate_type;
602c9758cc4SNamhyung Kim 
603c9758cc4SNamhyung Kim 		rb_link_node(&data->node, parent, node);
604c9758cc4SNamhyung Kim 		rb_insert_color(&data->node, &page_caller_tree);
605c9758cc4SNamhyung Kim 	}
606c9758cc4SNamhyung Kim 
607c9758cc4SNamhyung Kim 	return data;
608c9758cc4SNamhyung Kim }
609c9758cc4SNamhyung Kim 
page_stat__find_caller(struct page_stat * pstat)610fb4f313dSNamhyung Kim static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
611c9758cc4SNamhyung Kim {
612fb4f313dSNamhyung Kim 	return __page_stat__findnew_caller(pstat, false);
613c9758cc4SNamhyung Kim }
614c9758cc4SNamhyung Kim 
page_stat__findnew_caller(struct page_stat * pstat)615fb4f313dSNamhyung Kim static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
616c9758cc4SNamhyung Kim {
617fb4f313dSNamhyung Kim 	return __page_stat__findnew_caller(pstat, true);
618c9758cc4SNamhyung Kim }
619c9758cc4SNamhyung Kim 
valid_page(u64 pfn_or_page)6200d68bc92SNamhyung Kim static bool valid_page(u64 pfn_or_page)
6210d68bc92SNamhyung Kim {
6220d68bc92SNamhyung Kim 	if (use_pfn && pfn_or_page == -1UL)
6230d68bc92SNamhyung Kim 		return false;
6240d68bc92SNamhyung Kim 	if (!use_pfn && pfn_or_page == 0)
6250d68bc92SNamhyung Kim 		return false;
6260d68bc92SNamhyung Kim 	return true;
6270d68bc92SNamhyung Kim }
6280d68bc92SNamhyung Kim 
6290e111156SNamhyung Kim struct gfp_flag {
6300e111156SNamhyung Kim 	unsigned int flags;
6310e111156SNamhyung Kim 	char *compact_str;
6320e111156SNamhyung Kim 	char *human_readable;
6330e111156SNamhyung Kim };
6340e111156SNamhyung Kim 
6350e111156SNamhyung Kim static struct gfp_flag *gfps;
6360e111156SNamhyung Kim static int nr_gfps;
6370e111156SNamhyung Kim 
gfpcmp(const void * a,const void * b)6380e111156SNamhyung Kim static int gfpcmp(const void *a, const void *b)
6390e111156SNamhyung Kim {
6400e111156SNamhyung Kim 	const struct gfp_flag *fa = a;
6410e111156SNamhyung Kim 	const struct gfp_flag *fb = b;
6420e111156SNamhyung Kim 
6430e111156SNamhyung Kim 	return fa->flags - fb->flags;
6440e111156SNamhyung Kim }
6450e111156SNamhyung Kim 
646420adbe9SVlastimil Babka /* see include/trace/events/mmflags.h */
6470e111156SNamhyung Kim static const struct {
6480e111156SNamhyung Kim 	const char *original;
6490e111156SNamhyung Kim 	const char *compact;
6500e111156SNamhyung Kim } gfp_compact_table[] = {
6510e111156SNamhyung Kim 	{ "GFP_TRANSHUGE",		"THP" },
65225160354SVlastimil Babka 	{ "GFP_TRANSHUGE_LIGHT",	"THL" },
6530e111156SNamhyung Kim 	{ "GFP_HIGHUSER_MOVABLE",	"HUM" },
6540e111156SNamhyung Kim 	{ "GFP_HIGHUSER",		"HU" },
6550e111156SNamhyung Kim 	{ "GFP_USER",			"U" },
65614e0a214SVlastimil Babka 	{ "GFP_KERNEL_ACCOUNT",		"KAC" },
6570e111156SNamhyung Kim 	{ "GFP_KERNEL",			"K" },
6580e111156SNamhyung Kim 	{ "GFP_NOFS",			"NF" },
6590e111156SNamhyung Kim 	{ "GFP_ATOMIC",			"A" },
6600e111156SNamhyung Kim 	{ "GFP_NOIO",			"NI" },
6610e111156SNamhyung Kim 	{ "GFP_NOWAIT",			"NW" },
66214e0a214SVlastimil Babka 	{ "GFP_DMA",			"D" },
66314e0a214SVlastimil Babka 	{ "__GFP_HIGHMEM",		"HM" },
66414e0a214SVlastimil Babka 	{ "GFP_DMA32",			"D32" },
66514e0a214SVlastimil Babka 	{ "__GFP_HIGH",			"H" },
66614e0a214SVlastimil Babka 	{ "__GFP_IO",			"I" },
66714e0a214SVlastimil Babka 	{ "__GFP_FS",			"F" },
66814e0a214SVlastimil Babka 	{ "__GFP_NOWARN",		"NWR" },
669dcda9b04SMichal Hocko 	{ "__GFP_RETRY_MAYFAIL",	"R" },
67014e0a214SVlastimil Babka 	{ "__GFP_NOFAIL",		"NF" },
67114e0a214SVlastimil Babka 	{ "__GFP_NORETRY",		"NR" },
67214e0a214SVlastimil Babka 	{ "__GFP_COMP",			"C" },
67314e0a214SVlastimil Babka 	{ "__GFP_ZERO",			"Z" },
67414e0a214SVlastimil Babka 	{ "__GFP_NOMEMALLOC",		"NMA" },
67514e0a214SVlastimil Babka 	{ "__GFP_MEMALLOC",		"MA" },
67614e0a214SVlastimil Babka 	{ "__GFP_HARDWALL",		"HW" },
67714e0a214SVlastimil Babka 	{ "__GFP_THISNODE",		"TN" },
67814e0a214SVlastimil Babka 	{ "__GFP_RECLAIMABLE",		"RC" },
67914e0a214SVlastimil Babka 	{ "__GFP_MOVABLE",		"M" },
68014e0a214SVlastimil Babka 	{ "__GFP_ACCOUNT",		"AC" },
68114e0a214SVlastimil Babka 	{ "__GFP_WRITE",		"WR" },
68214e0a214SVlastimil Babka 	{ "__GFP_RECLAIM",		"R" },
68314e0a214SVlastimil Babka 	{ "__GFP_DIRECT_RECLAIM",	"DR" },
68414e0a214SVlastimil Babka 	{ "__GFP_KSWAPD_RECLAIM",	"KR" },
6850e111156SNamhyung Kim };
6860e111156SNamhyung Kim 
6870e111156SNamhyung Kim static size_t max_gfp_len;
6880e111156SNamhyung Kim 
compact_gfp_flags(char * gfp_flags)6890e111156SNamhyung Kim static char *compact_gfp_flags(char *gfp_flags)
6900e111156SNamhyung Kim {
6910e111156SNamhyung Kim 	char *orig_flags = strdup(gfp_flags);
6920e111156SNamhyung Kim 	char *new_flags = NULL;
693b2365122SArnaldo Carvalho de Melo 	char *str, *pos = NULL;
6940e111156SNamhyung Kim 	size_t len = 0;
6950e111156SNamhyung Kim 
6960e111156SNamhyung Kim 	if (orig_flags == NULL)
6970e111156SNamhyung Kim 		return NULL;
6980e111156SNamhyung Kim 
6990e111156SNamhyung Kim 	str = strtok_r(orig_flags, "|", &pos);
7000e111156SNamhyung Kim 	while (str) {
7010e111156SNamhyung Kim 		size_t i;
7020e111156SNamhyung Kim 		char *new;
7030e111156SNamhyung Kim 		const char *cpt;
7040e111156SNamhyung Kim 
7050e111156SNamhyung Kim 		for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
7060e111156SNamhyung Kim 			if (strcmp(gfp_compact_table[i].original, str))
7070e111156SNamhyung Kim 				continue;
7080e111156SNamhyung Kim 
7090e111156SNamhyung Kim 			cpt = gfp_compact_table[i].compact;
7100e111156SNamhyung Kim 			new = realloc(new_flags, len + strlen(cpt) + 2);
7110e111156SNamhyung Kim 			if (new == NULL) {
7120e111156SNamhyung Kim 				free(new_flags);
7131abecfcaSYunfeng Ye 				free(orig_flags);
7140e111156SNamhyung Kim 				return NULL;
7150e111156SNamhyung Kim 			}
7160e111156SNamhyung Kim 
7170e111156SNamhyung Kim 			new_flags = new;
7180e111156SNamhyung Kim 
7190e111156SNamhyung Kim 			if (!len) {
7200e111156SNamhyung Kim 				strcpy(new_flags, cpt);
7210e111156SNamhyung Kim 			} else {
7220e111156SNamhyung Kim 				strcat(new_flags, "|");
7230e111156SNamhyung Kim 				strcat(new_flags, cpt);
7240e111156SNamhyung Kim 				len++;
7250e111156SNamhyung Kim 			}
7260e111156SNamhyung Kim 
7270e111156SNamhyung Kim 			len += strlen(cpt);
7280e111156SNamhyung Kim 		}
7290e111156SNamhyung Kim 
7300e111156SNamhyung Kim 		str = strtok_r(NULL, "|", &pos);
7310e111156SNamhyung Kim 	}
7320e111156SNamhyung Kim 
7330e111156SNamhyung Kim 	if (max_gfp_len < len)
7340e111156SNamhyung Kim 		max_gfp_len = len;
7350e111156SNamhyung Kim 
7360e111156SNamhyung Kim 	free(orig_flags);
7370e111156SNamhyung Kim 	return new_flags;
7380e111156SNamhyung Kim }
7390e111156SNamhyung Kim 
compact_gfp_string(unsigned long gfp_flags)7400e111156SNamhyung Kim static char *compact_gfp_string(unsigned long gfp_flags)
7410e111156SNamhyung Kim {
7420e111156SNamhyung Kim 	struct gfp_flag key = {
7430e111156SNamhyung Kim 		.flags = gfp_flags,
7440e111156SNamhyung Kim 	};
7450e111156SNamhyung Kim 	struct gfp_flag *gfp;
7460e111156SNamhyung Kim 
7470e111156SNamhyung Kim 	gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
7480e111156SNamhyung Kim 	if (gfp)
7490e111156SNamhyung Kim 		return gfp->compact_str;
7500e111156SNamhyung Kim 
7510e111156SNamhyung Kim 	return NULL;
7520e111156SNamhyung Kim }
7530e111156SNamhyung Kim 
parse_gfp_flags(struct evsel * evsel,struct perf_sample * sample,unsigned int gfp_flags)75432dcd021SJiri Olsa static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
7550e111156SNamhyung Kim 			   unsigned int gfp_flags)
7560e111156SNamhyung Kim {
757cbc49b25STzvetomir Stoyanov (VMware) 	struct tep_record record = {
7580e111156SNamhyung Kim 		.cpu = sample->cpu,
7590e111156SNamhyung Kim 		.data = sample->raw_data,
7600e111156SNamhyung Kim 		.size = sample->raw_size,
7610e111156SNamhyung Kim 	};
7620e111156SNamhyung Kim 	struct trace_seq seq;
76308a9b985SArnaldo Carvalho de Melo 	char *str, *pos = NULL;
7640e111156SNamhyung Kim 
7650e111156SNamhyung Kim 	if (nr_gfps) {
7660e111156SNamhyung Kim 		struct gfp_flag key = {
7670e111156SNamhyung Kim 			.flags = gfp_flags,
7680e111156SNamhyung Kim 		};
7690e111156SNamhyung Kim 
7700e111156SNamhyung Kim 		if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
7710e111156SNamhyung Kim 			return 0;
7720e111156SNamhyung Kim 	}
7730e111156SNamhyung Kim 
7740e111156SNamhyung Kim 	trace_seq_init(&seq);
77538847db9STzvetomir Stoyanov 	tep_print_event(evsel->tp_format->tep,
77638847db9STzvetomir Stoyanov 			&seq, &record, "%s", TEP_PRINT_INFO);
7770e111156SNamhyung Kim 
7780e111156SNamhyung Kim 	str = strtok_r(seq.buffer, " ", &pos);
7790e111156SNamhyung Kim 	while (str) {
7800e111156SNamhyung Kim 		if (!strncmp(str, "gfp_flags=", 10)) {
7810e111156SNamhyung Kim 			struct gfp_flag *new;
7820e111156SNamhyung Kim 
7830e111156SNamhyung Kim 			new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
7840e111156SNamhyung Kim 			if (new == NULL)
7850e111156SNamhyung Kim 				return -ENOMEM;
7860e111156SNamhyung Kim 
7870e111156SNamhyung Kim 			gfps = new;
7880e111156SNamhyung Kim 			new += nr_gfps++;
7890e111156SNamhyung Kim 
7900e111156SNamhyung Kim 			new->flags = gfp_flags;
7910e111156SNamhyung Kim 			new->human_readable = strdup(str + 10);
7920e111156SNamhyung Kim 			new->compact_str = compact_gfp_flags(str + 10);
7930e111156SNamhyung Kim 			if (!new->human_readable || !new->compact_str)
7940e111156SNamhyung Kim 				return -ENOMEM;
7950e111156SNamhyung Kim 
7960e111156SNamhyung Kim 			qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
7970e111156SNamhyung Kim 		}
7980e111156SNamhyung Kim 
7990e111156SNamhyung Kim 		str = strtok_r(NULL, " ", &pos);
8000e111156SNamhyung Kim 	}
8010e111156SNamhyung Kim 
8020e111156SNamhyung Kim 	trace_seq_destroy(&seq);
8030e111156SNamhyung Kim 	return 0;
8040e111156SNamhyung Kim }
8050e111156SNamhyung Kim 
evsel__process_page_alloc_event(struct evsel * evsel,struct perf_sample * sample)8068cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample)
8070d68bc92SNamhyung Kim {
8080d68bc92SNamhyung Kim 	u64 page;
809efc0cdc9SArnaldo Carvalho de Melo 	unsigned int order = evsel__intval(evsel, sample, "order");
810efc0cdc9SArnaldo Carvalho de Melo 	unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags");
811efc0cdc9SArnaldo Carvalho de Melo 	unsigned int migrate_type = evsel__intval(evsel, sample,
8120d68bc92SNamhyung Kim 						       "migratetype");
8130d68bc92SNamhyung Kim 	u64 bytes = kmem_page_size << order;
814c9758cc4SNamhyung Kim 	u64 callsite;
8156b1a2752SDavid Ahern 	struct page_stat *pstat;
8160d68bc92SNamhyung Kim 	struct page_stat this = {
8170d68bc92SNamhyung Kim 		.order = order,
8180d68bc92SNamhyung Kim 		.gfp_flags = gfp_flags,
8190d68bc92SNamhyung Kim 		.migrate_type = migrate_type,
8200d68bc92SNamhyung Kim 	};
8210d68bc92SNamhyung Kim 
8220d68bc92SNamhyung Kim 	if (use_pfn)
823efc0cdc9SArnaldo Carvalho de Melo 		page = evsel__intval(evsel, sample, "pfn");
8240d68bc92SNamhyung Kim 	else
825efc0cdc9SArnaldo Carvalho de Melo 		page = evsel__intval(evsel, sample, "page");
8260d68bc92SNamhyung Kim 
8270d68bc92SNamhyung Kim 	nr_page_allocs++;
8280d68bc92SNamhyung Kim 	total_page_alloc_bytes += bytes;
8290d68bc92SNamhyung Kim 
8300d68bc92SNamhyung Kim 	if (!valid_page(page)) {
8310d68bc92SNamhyung Kim 		nr_page_fails++;
8320d68bc92SNamhyung Kim 		total_page_fail_bytes += bytes;
8330d68bc92SNamhyung Kim 
8340d68bc92SNamhyung Kim 		return 0;
8350d68bc92SNamhyung Kim 	}
8360d68bc92SNamhyung Kim 
8370e111156SNamhyung Kim 	if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
8380e111156SNamhyung Kim 		return -1;
8390e111156SNamhyung Kim 
840c9758cc4SNamhyung Kim 	callsite = find_callsite(evsel, sample);
841c9758cc4SNamhyung Kim 
8420d68bc92SNamhyung Kim 	/*
8430d68bc92SNamhyung Kim 	 * This is to find the current page (with correct gfp flags and
8440d68bc92SNamhyung Kim 	 * migrate type) at free event.
8450d68bc92SNamhyung Kim 	 */
8462a7ef02cSNamhyung Kim 	this.page = page;
8472a7ef02cSNamhyung Kim 	pstat = page_stat__findnew_page(&this);
8486b1a2752SDavid Ahern 	if (pstat == NULL)
8490d68bc92SNamhyung Kim 		return -ENOMEM;
8500d68bc92SNamhyung Kim 
8512a7ef02cSNamhyung Kim 	pstat->nr_alloc++;
8522a7ef02cSNamhyung Kim 	pstat->alloc_bytes += bytes;
853c9758cc4SNamhyung Kim 	pstat->callsite = callsite;
8540d68bc92SNamhyung Kim 
8552a7ef02cSNamhyung Kim 	if (!live_page) {
856c9758cc4SNamhyung Kim 		pstat = page_stat__findnew_alloc(&this);
8576b1a2752SDavid Ahern 		if (pstat == NULL)
8580d68bc92SNamhyung Kim 			return -ENOMEM;
8590d68bc92SNamhyung Kim 
8606b1a2752SDavid Ahern 		pstat->nr_alloc++;
8616b1a2752SDavid Ahern 		pstat->alloc_bytes += bytes;
862c9758cc4SNamhyung Kim 		pstat->callsite = callsite;
8632a7ef02cSNamhyung Kim 	}
864c9758cc4SNamhyung Kim 
865fb4f313dSNamhyung Kim 	this.callsite = callsite;
866fb4f313dSNamhyung Kim 	pstat = page_stat__findnew_caller(&this);
867c9758cc4SNamhyung Kim 	if (pstat == NULL)
868c9758cc4SNamhyung Kim 		return -ENOMEM;
869c9758cc4SNamhyung Kim 
870c9758cc4SNamhyung Kim 	pstat->nr_alloc++;
871c9758cc4SNamhyung Kim 	pstat->alloc_bytes += bytes;
8720d68bc92SNamhyung Kim 
8730d68bc92SNamhyung Kim 	order_stats[order][migrate_type]++;
8740d68bc92SNamhyung Kim 
8750d68bc92SNamhyung Kim 	return 0;
8760d68bc92SNamhyung Kim }
8770d68bc92SNamhyung Kim 
evsel__process_page_free_event(struct evsel * evsel,struct perf_sample * sample)8788cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample)
8790d68bc92SNamhyung Kim {
8800d68bc92SNamhyung Kim 	u64 page;
881efc0cdc9SArnaldo Carvalho de Melo 	unsigned int order = evsel__intval(evsel, sample, "order");
8820d68bc92SNamhyung Kim 	u64 bytes = kmem_page_size << order;
8836b1a2752SDavid Ahern 	struct page_stat *pstat;
8840d68bc92SNamhyung Kim 	struct page_stat this = {
8850d68bc92SNamhyung Kim 		.order = order,
8860d68bc92SNamhyung Kim 	};
8870d68bc92SNamhyung Kim 
8880d68bc92SNamhyung Kim 	if (use_pfn)
889efc0cdc9SArnaldo Carvalho de Melo 		page = evsel__intval(evsel, sample, "pfn");
8900d68bc92SNamhyung Kim 	else
891efc0cdc9SArnaldo Carvalho de Melo 		page = evsel__intval(evsel, sample, "page");
8920d68bc92SNamhyung Kim 
8930d68bc92SNamhyung Kim 	nr_page_frees++;
8940d68bc92SNamhyung Kim 	total_page_free_bytes += bytes;
8950d68bc92SNamhyung Kim 
8962a7ef02cSNamhyung Kim 	this.page = page;
8972a7ef02cSNamhyung Kim 	pstat = page_stat__find_page(&this);
8986b1a2752SDavid Ahern 	if (pstat == NULL) {
8990d68bc92SNamhyung Kim 		pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
9000d68bc92SNamhyung Kim 			  page, order);
9010d68bc92SNamhyung Kim 
9020d68bc92SNamhyung Kim 		nr_page_nomatch++;
9030d68bc92SNamhyung Kim 		total_page_nomatch_bytes += bytes;
9040d68bc92SNamhyung Kim 
9050d68bc92SNamhyung Kim 		return 0;
9060d68bc92SNamhyung Kim 	}
9070d68bc92SNamhyung Kim 
9086b1a2752SDavid Ahern 	this.gfp_flags = pstat->gfp_flags;
9096b1a2752SDavid Ahern 	this.migrate_type = pstat->migrate_type;
910c9758cc4SNamhyung Kim 	this.callsite = pstat->callsite;
9110d68bc92SNamhyung Kim 
9122a7ef02cSNamhyung Kim 	rb_erase(&pstat->node, &page_live_tree);
9136b1a2752SDavid Ahern 	free(pstat);
9140d68bc92SNamhyung Kim 
9152a7ef02cSNamhyung Kim 	if (live_page) {
9162a7ef02cSNamhyung Kim 		order_stats[this.order][this.migrate_type]--;
9172a7ef02cSNamhyung Kim 	} else {
918c9758cc4SNamhyung Kim 		pstat = page_stat__find_alloc(&this);
919c9758cc4SNamhyung Kim 		if (pstat == NULL)
9202a7ef02cSNamhyung Kim 			return -ENOMEM;
921c9758cc4SNamhyung Kim 
922c9758cc4SNamhyung Kim 		pstat->nr_free++;
923c9758cc4SNamhyung Kim 		pstat->free_bytes += bytes;
9242a7ef02cSNamhyung Kim 	}
925c9758cc4SNamhyung Kim 
926fb4f313dSNamhyung Kim 	pstat = page_stat__find_caller(&this);
9276b1a2752SDavid Ahern 	if (pstat == NULL)
9280d68bc92SNamhyung Kim 		return -ENOENT;
9290d68bc92SNamhyung Kim 
9306b1a2752SDavid Ahern 	pstat->nr_free++;
9316b1a2752SDavid Ahern 	pstat->free_bytes += bytes;
9320d68bc92SNamhyung Kim 
9332a7ef02cSNamhyung Kim 	if (live_page) {
9342a7ef02cSNamhyung Kim 		pstat->nr_alloc--;
9352a7ef02cSNamhyung Kim 		pstat->alloc_bytes -= bytes;
9362a7ef02cSNamhyung Kim 
9372a7ef02cSNamhyung Kim 		if (pstat->nr_alloc == 0) {
9382a7ef02cSNamhyung Kim 			rb_erase(&pstat->node, &page_caller_tree);
9392a7ef02cSNamhyung Kim 			free(pstat);
9402a7ef02cSNamhyung Kim 		}
9412a7ef02cSNamhyung Kim 	}
9422a7ef02cSNamhyung Kim 
9430d68bc92SNamhyung Kim 	return 0;
9440d68bc92SNamhyung Kim }
9450d68bc92SNamhyung Kim 
perf_kmem__skip_sample(struct perf_sample * sample)9462a865bd8SDavid Ahern static bool perf_kmem__skip_sample(struct perf_sample *sample)
9472a865bd8SDavid Ahern {
9482a865bd8SDavid Ahern 	/* skip sample based on time? */
9492a865bd8SDavid Ahern 	if (perf_time__skip_sample(&ptime, sample->time))
9502a865bd8SDavid Ahern 		return true;
9512a865bd8SDavid Ahern 
9522a865bd8SDavid Ahern 	return false;
9532a865bd8SDavid Ahern }
9542a865bd8SDavid Ahern 
95532dcd021SJiri Olsa typedef int (*tracepoint_handler)(struct evsel *evsel,
9560f7d2f1bSArnaldo Carvalho de Melo 				  struct perf_sample *sample);
957ba77c9e1SLi Zefan 
process_sample_event(struct perf_tool * tool __maybe_unused,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine)9581d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused,
959d20deb64SArnaldo Carvalho de Melo 				union perf_event *event,
9608115d60cSArnaldo Carvalho de Melo 				struct perf_sample *sample,
96132dcd021SJiri Olsa 				struct evsel *evsel,
962743eb868SArnaldo Carvalho de Melo 				struct machine *machine)
963ba77c9e1SLi Zefan {
964b91fc39fSArnaldo Carvalho de Melo 	int err = 0;
965ef89325fSAdrian Hunter 	struct thread *thread = machine__findnew_thread(machine, sample->pid,
96613ce34dfSNamhyung Kim 							sample->tid);
967ba77c9e1SLi Zefan 
968ba77c9e1SLi Zefan 	if (thread == NULL) {
969ba77c9e1SLi Zefan 		pr_debug("problem processing %d event, skipping it.\n",
970ba77c9e1SLi Zefan 			 event->header.type);
971ba77c9e1SLi Zefan 		return -1;
972ba77c9e1SLi Zefan 	}
973ba77c9e1SLi Zefan 
9742a865bd8SDavid Ahern 	if (perf_kmem__skip_sample(sample))
9752a865bd8SDavid Ahern 		return 0;
9762a865bd8SDavid Ahern 
977ee84a303SIan Rogers 	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread));
978ba77c9e1SLi Zefan 
979744a9719SArnaldo Carvalho de Melo 	if (evsel->handler != NULL) {
980744a9719SArnaldo Carvalho de Melo 		tracepoint_handler f = evsel->handler;
981b91fc39fSArnaldo Carvalho de Melo 		err = f(evsel, sample);
9820f7d2f1bSArnaldo Carvalho de Melo 	}
9830f7d2f1bSArnaldo Carvalho de Melo 
984b91fc39fSArnaldo Carvalho de Melo 	thread__put(thread);
985b91fc39fSArnaldo Carvalho de Melo 
986b91fc39fSArnaldo Carvalho de Melo 	return err;
987ba77c9e1SLi Zefan }
988ba77c9e1SLi Zefan 
989fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = {
99055aa640fSArnaldo Carvalho de Melo 	.sample		 = process_sample_event,
9918115d60cSArnaldo Carvalho de Melo 	.comm		 = perf_event__process_comm,
99264c40908SNamhyung Kim 	.mmap		 = perf_event__process_mmap,
99364c40908SNamhyung Kim 	.mmap2		 = perf_event__process_mmap2,
994f3b3614aSHari Bathini 	.namespaces	 = perf_event__process_namespaces,
9950a8cb85cSJiri Olsa 	.ordered_events	 = true,
996ba77c9e1SLi Zefan };
997ba77c9e1SLi Zefan 
fragmentation(unsigned long n_req,unsigned long n_alloc)998ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc)
999ba77c9e1SLi Zefan {
1000ba77c9e1SLi Zefan 	if (n_alloc == 0)
1001ba77c9e1SLi Zefan 		return 0.0;
1002ba77c9e1SLi Zefan 	else
1003ba77c9e1SLi Zefan 		return 100.0 - (100.0 * n_req / n_alloc);
1004ba77c9e1SLi Zefan }
1005ba77c9e1SLi Zefan 
__print_slab_result(struct rb_root * root,struct perf_session * session,int n_lines,int is_caller)10060d68bc92SNamhyung Kim static void __print_slab_result(struct rb_root *root,
10070d68bc92SNamhyung Kim 				struct perf_session *session,
10084aa65636SArnaldo Carvalho de Melo 				int n_lines, int is_caller)
1009ba77c9e1SLi Zefan {
1010ba77c9e1SLi Zefan 	struct rb_node *next;
101134ba5122SArnaldo Carvalho de Melo 	struct machine *machine = &session->machines.host;
1012ba77c9e1SLi Zefan 
101365f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
1014079d3f65SLi Zefan 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
1015079d3f65SLi Zefan 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
101665f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
1017ba77c9e1SLi Zefan 
1018ba77c9e1SLi Zefan 	next = rb_first(root);
1019ba77c9e1SLi Zefan 
1020ba77c9e1SLi Zefan 	while (next && n_lines--) {
10211b145ae5SArnaldo Carvalho de Melo 		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
10221b145ae5SArnaldo Carvalho de Melo 						   node);
10231b145ae5SArnaldo Carvalho de Melo 		struct symbol *sym = NULL;
102471cf8b8fSArnaldo Carvalho de Melo 		struct map *map;
1025079d3f65SLi Zefan 		char buf[BUFSIZ];
10261b145ae5SArnaldo Carvalho de Melo 		u64 addr;
1027ba77c9e1SLi Zefan 
10281b145ae5SArnaldo Carvalho de Melo 		if (is_caller) {
10291b145ae5SArnaldo Carvalho de Melo 			addr = data->call_site;
10307707b6b6SLi Zefan 			if (!raw_ip)
1031107cad95SArnaldo Carvalho de Melo 				sym = machine__find_kernel_symbol(machine, addr, &map);
10321b145ae5SArnaldo Carvalho de Melo 		} else
10331b145ae5SArnaldo Carvalho de Melo 			addr = data->ptr;
1034ba77c9e1SLi Zefan 
10351b145ae5SArnaldo Carvalho de Melo 		if (sym != NULL)
10369486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
103778a1f7cdSIan Rogers 				 addr - map__unmap_ip(map, sym->start));
10381b145ae5SArnaldo Carvalho de Melo 		else
10399486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
1040079d3f65SLi Zefan 		printf(" %-34s |", buf);
10411b145ae5SArnaldo Carvalho de Melo 
104265f46e02SNamhyung Kim 		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
1043079d3f65SLi Zefan 		       (unsigned long long)data->bytes_alloc,
1044ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_alloc / data->hit,
1045ba77c9e1SLi Zefan 		       (unsigned long long)data->bytes_req,
1046ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_req / data->hit,
1047ba77c9e1SLi Zefan 		       (unsigned long)data->hit,
1048079d3f65SLi Zefan 		       (unsigned long)data->pingpong,
1049ba77c9e1SLi Zefan 		       fragmentation(data->bytes_req, data->bytes_alloc));
1050ba77c9e1SLi Zefan 
1051ba77c9e1SLi Zefan 		next = rb_next(next);
1052ba77c9e1SLi Zefan 	}
1053ba77c9e1SLi Zefan 
1054ba77c9e1SLi Zefan 	if (n_lines == -1)
1055079d3f65SLi Zefan 		printf(" ...                                | ...             | ...             | ...      | ...       | ...   \n");
1056ba77c9e1SLi Zefan 
105765f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
1058ba77c9e1SLi Zefan }
1059ba77c9e1SLi Zefan 
10600d68bc92SNamhyung Kim static const char * const migrate_type_str[] = {
10610d68bc92SNamhyung Kim 	"UNMOVABL",
10620d68bc92SNamhyung Kim 	"RECLAIM",
10630d68bc92SNamhyung Kim 	"MOVABLE",
10640d68bc92SNamhyung Kim 	"RESERVED",
10650d68bc92SNamhyung Kim 	"CMA/ISLT",
10660d68bc92SNamhyung Kim 	"UNKNOWN",
10670d68bc92SNamhyung Kim };
10680d68bc92SNamhyung Kim 
__print_page_alloc_result(struct perf_session * session,int n_lines)1069c9758cc4SNamhyung Kim static void __print_page_alloc_result(struct perf_session *session, int n_lines)
1070ba77c9e1SLi Zefan {
1071c9758cc4SNamhyung Kim 	struct rb_node *next = rb_first(&page_alloc_sorted);
1072c9758cc4SNamhyung Kim 	struct machine *machine = &session->machines.host;
10730d68bc92SNamhyung Kim 	const char *format;
10740e111156SNamhyung Kim 	int gfp_len = max(strlen("GFP flags"), max_gfp_len);
10750d68bc92SNamhyung Kim 
1076c9758cc4SNamhyung Kim 	printf("\n%.105s\n", graph_dotted_line);
10770e111156SNamhyung Kim 	printf(" %-16s | %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
10780e111156SNamhyung Kim 	       use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
10790e111156SNamhyung Kim 	       gfp_len, "GFP flags");
1080c9758cc4SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
10810d68bc92SNamhyung Kim 
10820d68bc92SNamhyung Kim 	if (use_pfn)
10830e111156SNamhyung Kim 		format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
10840d68bc92SNamhyung Kim 	else
10850e111156SNamhyung Kim 		format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
10860d68bc92SNamhyung Kim 
10870d68bc92SNamhyung Kim 	while (next && n_lines--) {
10880d68bc92SNamhyung Kim 		struct page_stat *data;
1089c9758cc4SNamhyung Kim 		struct symbol *sym;
1090c9758cc4SNamhyung Kim 		struct map *map;
1091c9758cc4SNamhyung Kim 		char buf[32];
1092c9758cc4SNamhyung Kim 		char *caller = buf;
10930d68bc92SNamhyung Kim 
10940d68bc92SNamhyung Kim 		data = rb_entry(next, struct page_stat, node);
1095107cad95SArnaldo Carvalho de Melo 		sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1096a7c3899cSArnaldo Carvalho de Melo 		if (sym)
1097c9758cc4SNamhyung Kim 			caller = sym->name;
1098c9758cc4SNamhyung Kim 		else
1099c9758cc4SNamhyung Kim 			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
11000d68bc92SNamhyung Kim 
11010d68bc92SNamhyung Kim 		printf(format, (unsigned long long)data->page,
11020d68bc92SNamhyung Kim 		       (unsigned long long)data->alloc_bytes / 1024,
11030d68bc92SNamhyung Kim 		       data->nr_alloc, data->order,
11040d68bc92SNamhyung Kim 		       migrate_type_str[data->migrate_type],
11050e111156SNamhyung Kim 		       gfp_len, compact_gfp_string(data->gfp_flags), caller);
1106c9758cc4SNamhyung Kim 
1107c9758cc4SNamhyung Kim 		next = rb_next(next);
1108c9758cc4SNamhyung Kim 	}
1109c9758cc4SNamhyung Kim 
11100e111156SNamhyung Kim 	if (n_lines == -1) {
11110e111156SNamhyung Kim 		printf(" ...              | ...              | ...       | ...   | ...      | %-*s | ...\n",
11120e111156SNamhyung Kim 		       gfp_len, "...");
11130e111156SNamhyung Kim 	}
1114c9758cc4SNamhyung Kim 
1115c9758cc4SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
1116c9758cc4SNamhyung Kim }
1117c9758cc4SNamhyung Kim 
__print_page_caller_result(struct perf_session * session,int n_lines)1118c9758cc4SNamhyung Kim static void __print_page_caller_result(struct perf_session *session, int n_lines)
1119c9758cc4SNamhyung Kim {
1120c9758cc4SNamhyung Kim 	struct rb_node *next = rb_first(&page_caller_sorted);
1121c9758cc4SNamhyung Kim 	struct machine *machine = &session->machines.host;
11220e111156SNamhyung Kim 	int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1123c9758cc4SNamhyung Kim 
1124c9758cc4SNamhyung Kim 	printf("\n%.105s\n", graph_dotted_line);
11250e111156SNamhyung Kim 	printf(" %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
11260e111156SNamhyung Kim 	       live_page ? "Live" : "Total", gfp_len, "GFP flags");
1127c9758cc4SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
1128c9758cc4SNamhyung Kim 
1129c9758cc4SNamhyung Kim 	while (next && n_lines--) {
1130c9758cc4SNamhyung Kim 		struct page_stat *data;
1131c9758cc4SNamhyung Kim 		struct symbol *sym;
1132c9758cc4SNamhyung Kim 		struct map *map;
1133c9758cc4SNamhyung Kim 		char buf[32];
1134c9758cc4SNamhyung Kim 		char *caller = buf;
1135c9758cc4SNamhyung Kim 
1136c9758cc4SNamhyung Kim 		data = rb_entry(next, struct page_stat, node);
1137107cad95SArnaldo Carvalho de Melo 		sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1138a7c3899cSArnaldo Carvalho de Melo 		if (sym)
1139c9758cc4SNamhyung Kim 			caller = sym->name;
1140c9758cc4SNamhyung Kim 		else
1141c9758cc4SNamhyung Kim 			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1142c9758cc4SNamhyung Kim 
11430e111156SNamhyung Kim 		printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
1144c9758cc4SNamhyung Kim 		       (unsigned long long)data->alloc_bytes / 1024,
1145c9758cc4SNamhyung Kim 		       data->nr_alloc, data->order,
1146c9758cc4SNamhyung Kim 		       migrate_type_str[data->migrate_type],
11470e111156SNamhyung Kim 		       gfp_len, compact_gfp_string(data->gfp_flags), caller);
11480d68bc92SNamhyung Kim 
11490d68bc92SNamhyung Kim 		next = rb_next(next);
11500d68bc92SNamhyung Kim 	}
11510d68bc92SNamhyung Kim 
11520e111156SNamhyung Kim 	if (n_lines == -1) {
11530e111156SNamhyung Kim 		printf(" ...              | ...       | ...   | ...      | %-*s | ...\n",
11540e111156SNamhyung Kim 		       gfp_len, "...");
11550e111156SNamhyung Kim 	}
11560d68bc92SNamhyung Kim 
1157c9758cc4SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
11580d68bc92SNamhyung Kim }
11590d68bc92SNamhyung Kim 
print_gfp_flags(void)11600e111156SNamhyung Kim static void print_gfp_flags(void)
11610e111156SNamhyung Kim {
11620e111156SNamhyung Kim 	int i;
11630e111156SNamhyung Kim 
11640e111156SNamhyung Kim 	printf("#\n");
11650e111156SNamhyung Kim 	printf("# GFP flags\n");
11660e111156SNamhyung Kim 	printf("# ---------\n");
11670e111156SNamhyung Kim 	for (i = 0; i < nr_gfps; i++) {
11680e111156SNamhyung Kim 		printf("# %08x: %*s: %s\n", gfps[i].flags,
11690e111156SNamhyung Kim 		       (int) max_gfp_len, gfps[i].compact_str,
11700e111156SNamhyung Kim 		       gfps[i].human_readable);
11710e111156SNamhyung Kim 	}
11720e111156SNamhyung Kim }
11730e111156SNamhyung Kim 
print_slab_summary(void)11740d68bc92SNamhyung Kim static void print_slab_summary(void)
11750d68bc92SNamhyung Kim {
11760d68bc92SNamhyung Kim 	printf("\nSUMMARY (SLAB allocator)");
11770d68bc92SNamhyung Kim 	printf("\n========================\n");
117877cfe388SNamhyung Kim 	printf("Total bytes requested: %'lu\n", total_requested);
117977cfe388SNamhyung Kim 	printf("Total bytes allocated: %'lu\n", total_allocated);
1180aa58e9afSDavid Ahern 	printf("Total bytes freed:     %'lu\n", total_freed);
1181aa58e9afSDavid Ahern 	if (total_allocated > total_freed) {
1182aa58e9afSDavid Ahern 		printf("Net total bytes allocated: %'lu\n",
1183aa58e9afSDavid Ahern 		total_allocated - total_freed);
1184aa58e9afSDavid Ahern 	}
118577cfe388SNamhyung Kim 	printf("Total bytes wasted on internal fragmentation: %'lu\n",
1186ba77c9e1SLi Zefan 	       total_allocated - total_requested);
1187ba77c9e1SLi Zefan 	printf("Internal fragmentation: %f%%\n",
1188ba77c9e1SLi Zefan 	       fragmentation(total_requested, total_allocated));
118977cfe388SNamhyung Kim 	printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
1190ba77c9e1SLi Zefan }
1191ba77c9e1SLi Zefan 
print_page_summary(void)11920d68bc92SNamhyung Kim static void print_page_summary(void)
11930d68bc92SNamhyung Kim {
11940d68bc92SNamhyung Kim 	int o, m;
11950d68bc92SNamhyung Kim 	u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
11960d68bc92SNamhyung Kim 	u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
11970d68bc92SNamhyung Kim 
11980d68bc92SNamhyung Kim 	printf("\nSUMMARY (page allocator)");
11990d68bc92SNamhyung Kim 	printf("\n========================\n");
12000d68bc92SNamhyung Kim 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation requests",
12010d68bc92SNamhyung Kim 	       nr_page_allocs, total_page_alloc_bytes / 1024);
12020d68bc92SNamhyung Kim 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free requests",
12030d68bc92SNamhyung Kim 	       nr_page_frees, total_page_free_bytes / 1024);
12040d68bc92SNamhyung Kim 	printf("\n");
12050d68bc92SNamhyung Kim 
12066145c259SWill Deacon 	printf("%-30s: %'16"PRIu64"   [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
12070d68bc92SNamhyung Kim 	       nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
12086145c259SWill Deacon 	printf("%-30s: %'16"PRIu64"   [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
12090d68bc92SNamhyung Kim 	       nr_page_allocs - nr_alloc_freed,
12100d68bc92SNamhyung Kim 	       (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
12110d68bc92SNamhyung Kim 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free-only requests",
12120d68bc92SNamhyung Kim 	       nr_page_nomatch, total_page_nomatch_bytes / 1024);
12130d68bc92SNamhyung Kim 	printf("\n");
12140d68bc92SNamhyung Kim 
12150d68bc92SNamhyung Kim 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation failures",
12160d68bc92SNamhyung Kim 	       nr_page_fails, total_page_fail_bytes / 1024);
12170d68bc92SNamhyung Kim 	printf("\n");
12180d68bc92SNamhyung Kim 
12190d68bc92SNamhyung Kim 	printf("%5s  %12s  %12s  %12s  %12s  %12s\n", "Order",  "Unmovable",
12200d68bc92SNamhyung Kim 	       "Reclaimable", "Movable", "Reserved", "CMA/Isolated");
12210d68bc92SNamhyung Kim 	printf("%.5s  %.12s  %.12s  %.12s  %.12s  %.12s\n", graph_dotted_line,
12220d68bc92SNamhyung Kim 	       graph_dotted_line, graph_dotted_line, graph_dotted_line,
12230d68bc92SNamhyung Kim 	       graph_dotted_line, graph_dotted_line);
12240d68bc92SNamhyung Kim 
12250d68bc92SNamhyung Kim 	for (o = 0; o < MAX_PAGE_ORDER; o++) {
12260d68bc92SNamhyung Kim 		printf("%5d", o);
12270d68bc92SNamhyung Kim 		for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
12280d68bc92SNamhyung Kim 			if (order_stats[o][m])
12290d68bc92SNamhyung Kim 				printf("  %'12d", order_stats[o][m]);
12300d68bc92SNamhyung Kim 			else
12310d68bc92SNamhyung Kim 				printf("  %12c", '.');
12320d68bc92SNamhyung Kim 		}
12330d68bc92SNamhyung Kim 		printf("\n");
12340d68bc92SNamhyung Kim 	}
12350d68bc92SNamhyung Kim }
12360d68bc92SNamhyung Kim 
print_slab_result(struct perf_session * session)12370d68bc92SNamhyung Kim static void print_slab_result(struct perf_session *session)
1238ba77c9e1SLi Zefan {
1239ba77c9e1SLi Zefan 	if (caller_flag)
12400d68bc92SNamhyung Kim 		__print_slab_result(&root_caller_sorted, session, caller_lines, 1);
1241ba77c9e1SLi Zefan 	if (alloc_flag)
12420d68bc92SNamhyung Kim 		__print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
12430d68bc92SNamhyung Kim 	print_slab_summary();
12440d68bc92SNamhyung Kim }
12450d68bc92SNamhyung Kim 
print_page_result(struct perf_session * session)12460d68bc92SNamhyung Kim static void print_page_result(struct perf_session *session)
12470d68bc92SNamhyung Kim {
12480e111156SNamhyung Kim 	if (caller_flag || alloc_flag)
12490e111156SNamhyung Kim 		print_gfp_flags();
1250c9758cc4SNamhyung Kim 	if (caller_flag)
1251c9758cc4SNamhyung Kim 		__print_page_caller_result(session, caller_lines);
12520d68bc92SNamhyung Kim 	if (alloc_flag)
1253c9758cc4SNamhyung Kim 		__print_page_alloc_result(session, alloc_lines);
12540d68bc92SNamhyung Kim 	print_page_summary();
12550d68bc92SNamhyung Kim }
12560d68bc92SNamhyung Kim 
print_result(struct perf_session * session)12570d68bc92SNamhyung Kim static void print_result(struct perf_session *session)
12580d68bc92SNamhyung Kim {
12590d68bc92SNamhyung Kim 	if (kmem_slab)
12600d68bc92SNamhyung Kim 		print_slab_result(session);
12610d68bc92SNamhyung Kim 	if (kmem_page)
12620d68bc92SNamhyung Kim 		print_page_result(session);
1263ba77c9e1SLi Zefan }
1264ba77c9e1SLi Zefan 
1265fb4f313dSNamhyung Kim static LIST_HEAD(slab_caller_sort);
1266fb4f313dSNamhyung Kim static LIST_HEAD(slab_alloc_sort);
1267fb4f313dSNamhyung Kim static LIST_HEAD(page_caller_sort);
1268fb4f313dSNamhyung Kim static LIST_HEAD(page_alloc_sort);
126929b3e152SLi Zefan 
sort_slab_insert(struct rb_root * root,struct alloc_stat * data,struct list_head * sort_list)12700d68bc92SNamhyung Kim static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
127129b3e152SLi Zefan 			     struct list_head *sort_list)
1272ba77c9e1SLi Zefan {
1273ba77c9e1SLi Zefan 	struct rb_node **new = &(root->rb_node);
1274ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
127529b3e152SLi Zefan 	struct sort_dimension *sort;
1276ba77c9e1SLi Zefan 
1277ba77c9e1SLi Zefan 	while (*new) {
1278ba77c9e1SLi Zefan 		struct alloc_stat *this;
127929b3e152SLi Zefan 		int cmp = 0;
1280ba77c9e1SLi Zefan 
1281ba77c9e1SLi Zefan 		this = rb_entry(*new, struct alloc_stat, node);
1282ba77c9e1SLi Zefan 		parent = *new;
1283ba77c9e1SLi Zefan 
128429b3e152SLi Zefan 		list_for_each_entry(sort, sort_list, list) {
128529b3e152SLi Zefan 			cmp = sort->cmp(data, this);
128629b3e152SLi Zefan 			if (cmp)
128729b3e152SLi Zefan 				break;
128829b3e152SLi Zefan 		}
1289ba77c9e1SLi Zefan 
1290ba77c9e1SLi Zefan 		if (cmp > 0)
1291ba77c9e1SLi Zefan 			new = &((*new)->rb_left);
1292ba77c9e1SLi Zefan 		else
1293ba77c9e1SLi Zefan 			new = &((*new)->rb_right);
1294ba77c9e1SLi Zefan 	}
1295ba77c9e1SLi Zefan 
1296ba77c9e1SLi Zefan 	rb_link_node(&data->node, parent, new);
1297ba77c9e1SLi Zefan 	rb_insert_color(&data->node, root);
1298ba77c9e1SLi Zefan }
1299ba77c9e1SLi Zefan 
__sort_slab_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)13000d68bc92SNamhyung Kim static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
130129b3e152SLi Zefan 			       struct list_head *sort_list)
1302ba77c9e1SLi Zefan {
1303ba77c9e1SLi Zefan 	struct rb_node *node;
1304ba77c9e1SLi Zefan 	struct alloc_stat *data;
1305ba77c9e1SLi Zefan 
1306ba77c9e1SLi Zefan 	for (;;) {
1307ba77c9e1SLi Zefan 		node = rb_first(root);
1308ba77c9e1SLi Zefan 		if (!node)
1309ba77c9e1SLi Zefan 			break;
1310ba77c9e1SLi Zefan 
1311ba77c9e1SLi Zefan 		rb_erase(node, root);
1312ba77c9e1SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
13130d68bc92SNamhyung Kim 		sort_slab_insert(root_sorted, data, sort_list);
13140d68bc92SNamhyung Kim 	}
13150d68bc92SNamhyung Kim }
13160d68bc92SNamhyung Kim 
sort_page_insert(struct rb_root * root,struct page_stat * data,struct list_head * sort_list)1317fb4f313dSNamhyung Kim static void sort_page_insert(struct rb_root *root, struct page_stat *data,
1318fb4f313dSNamhyung Kim 			     struct list_head *sort_list)
13190d68bc92SNamhyung Kim {
13200d68bc92SNamhyung Kim 	struct rb_node **new = &root->rb_node;
13210d68bc92SNamhyung Kim 	struct rb_node *parent = NULL;
1322fb4f313dSNamhyung Kim 	struct sort_dimension *sort;
13230d68bc92SNamhyung Kim 
13240d68bc92SNamhyung Kim 	while (*new) {
13250d68bc92SNamhyung Kim 		struct page_stat *this;
13260d68bc92SNamhyung Kim 		int cmp = 0;
13270d68bc92SNamhyung Kim 
13280d68bc92SNamhyung Kim 		this = rb_entry(*new, struct page_stat, node);
13290d68bc92SNamhyung Kim 		parent = *new;
13300d68bc92SNamhyung Kim 
1331fb4f313dSNamhyung Kim 		list_for_each_entry(sort, sort_list, list) {
1332fb4f313dSNamhyung Kim 			cmp = sort->cmp(data, this);
1333fb4f313dSNamhyung Kim 			if (cmp)
1334fb4f313dSNamhyung Kim 				break;
1335fb4f313dSNamhyung Kim 		}
13360d68bc92SNamhyung Kim 
13370d68bc92SNamhyung Kim 		if (cmp > 0)
13380d68bc92SNamhyung Kim 			new = &parent->rb_left;
13390d68bc92SNamhyung Kim 		else
13400d68bc92SNamhyung Kim 			new = &parent->rb_right;
13410d68bc92SNamhyung Kim 	}
13420d68bc92SNamhyung Kim 
13430d68bc92SNamhyung Kim 	rb_link_node(&data->node, parent, new);
13440d68bc92SNamhyung Kim 	rb_insert_color(&data->node, root);
13450d68bc92SNamhyung Kim }
13460d68bc92SNamhyung Kim 
__sort_page_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)1347fb4f313dSNamhyung Kim static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
1348fb4f313dSNamhyung Kim 			       struct list_head *sort_list)
13490d68bc92SNamhyung Kim {
13500d68bc92SNamhyung Kim 	struct rb_node *node;
13510d68bc92SNamhyung Kim 	struct page_stat *data;
13520d68bc92SNamhyung Kim 
13530d68bc92SNamhyung Kim 	for (;;) {
13540d68bc92SNamhyung Kim 		node = rb_first(root);
13550d68bc92SNamhyung Kim 		if (!node)
13560d68bc92SNamhyung Kim 			break;
13570d68bc92SNamhyung Kim 
13580d68bc92SNamhyung Kim 		rb_erase(node, root);
13590d68bc92SNamhyung Kim 		data = rb_entry(node, struct page_stat, node);
1360fb4f313dSNamhyung Kim 		sort_page_insert(root_sorted, data, sort_list);
1361ba77c9e1SLi Zefan 	}
1362ba77c9e1SLi Zefan }
1363ba77c9e1SLi Zefan 
sort_result(void)1364ba77c9e1SLi Zefan static void sort_result(void)
1365ba77c9e1SLi Zefan {
13660d68bc92SNamhyung Kim 	if (kmem_slab) {
13670d68bc92SNamhyung Kim 		__sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
1368fb4f313dSNamhyung Kim 				   &slab_alloc_sort);
13690d68bc92SNamhyung Kim 		__sort_slab_result(&root_caller_stat, &root_caller_sorted,
1370fb4f313dSNamhyung Kim 				   &slab_caller_sort);
13710d68bc92SNamhyung Kim 	}
13720d68bc92SNamhyung Kim 	if (kmem_page) {
13732a7ef02cSNamhyung Kim 		if (live_page)
13742a7ef02cSNamhyung Kim 			__sort_page_result(&page_live_tree, &page_alloc_sorted,
13752a7ef02cSNamhyung Kim 					   &page_alloc_sort);
13762a7ef02cSNamhyung Kim 		else
1377fb4f313dSNamhyung Kim 			__sort_page_result(&page_alloc_tree, &page_alloc_sorted,
1378fb4f313dSNamhyung Kim 					   &page_alloc_sort);
13792a7ef02cSNamhyung Kim 
1380fb4f313dSNamhyung Kim 		__sort_page_result(&page_caller_tree, &page_caller_sorted,
1381fb4f313dSNamhyung Kim 				   &page_caller_sort);
13820d68bc92SNamhyung Kim 	}
1383ba77c9e1SLi Zefan }
1384ba77c9e1SLi Zefan 
__cmd_kmem(struct perf_session * session)13852b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session)
1386ba77c9e1SLi Zefan {
1387d549c769SArnaldo Carvalho de Melo 	int err = -EINVAL;
138832dcd021SJiri Olsa 	struct evsel *evsel;
138932dcd021SJiri Olsa 	const struct evsel_str_handler kmem_tracepoints[] = {
13900d68bc92SNamhyung Kim 		/* slab allocator */
13918cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:kmalloc",		evsel__process_alloc_event, },
13928cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:kmem_cache_alloc",	evsel__process_alloc_event, },
1393dce088abSLeo Yan 		{ "kmem:kmalloc_node",		evsel__process_alloc_event, },
1394dce088abSLeo Yan 		{ "kmem:kmem_cache_alloc_node", evsel__process_alloc_event, },
13958cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:kfree",			evsel__process_free_event, },
13968cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:kmem_cache_free",	evsel__process_free_event, },
13970d68bc92SNamhyung Kim 		/* page allocator */
13988cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:mm_page_alloc",		evsel__process_page_alloc_event, },
13998cf5d0e0SArnaldo Carvalho de Melo 		{ "kmem:mm_page_free",		evsel__process_page_free_event, },
14000f7d2f1bSArnaldo Carvalho de Melo 	};
1401ba77c9e1SLi Zefan 
1402d549c769SArnaldo Carvalho de Melo 	if (!perf_session__has_traces(session, "kmem record"))
14032b2b2c68SNamhyung Kim 		goto out;
1404d549c769SArnaldo Carvalho de Melo 
14050f7d2f1bSArnaldo Carvalho de Melo 	if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
14060f7d2f1bSArnaldo Carvalho de Melo 		pr_err("Initializing perf session tracepoint handlers failed\n");
14072b2b2c68SNamhyung Kim 		goto out;
14080f7d2f1bSArnaldo Carvalho de Melo 	}
14090f7d2f1bSArnaldo Carvalho de Melo 
1410e5cadb93SArnaldo Carvalho de Melo 	evlist__for_each_entry(session->evlist, evsel) {
14118ab2e96dSArnaldo Carvalho de Melo 		if (!strcmp(evsel__name(evsel), "kmem:mm_page_alloc") &&
1412efc0cdc9SArnaldo Carvalho de Melo 		    evsel__field(evsel, "pfn")) {
14130d68bc92SNamhyung Kim 			use_pfn = true;
14140d68bc92SNamhyung Kim 			break;
14150d68bc92SNamhyung Kim 		}
14160d68bc92SNamhyung Kim 	}
14170d68bc92SNamhyung Kim 
14184aa65636SArnaldo Carvalho de Melo 	setup_pager();
1419b7b61cbeSArnaldo Carvalho de Melo 	err = perf_session__process_events(session);
14200d68bc92SNamhyung Kim 	if (err != 0) {
14210d68bc92SNamhyung Kim 		pr_err("error during process events: %d\n", err);
14222b2b2c68SNamhyung Kim 		goto out;
14230d68bc92SNamhyung Kim 	}
14244aa65636SArnaldo Carvalho de Melo 	sort_result();
14254aa65636SArnaldo Carvalho de Melo 	print_result(session);
14262b2b2c68SNamhyung Kim out:
14274aa65636SArnaldo Carvalho de Melo 	return err;
1428ba77c9e1SLi Zefan }
1429ba77c9e1SLi Zefan 
1430fb4f313dSNamhyung Kim /* slab sort keys */
ptr_cmp(void * a,void * b)1431fb4f313dSNamhyung Kim static int ptr_cmp(void *a, void *b)
1432ba77c9e1SLi Zefan {
1433fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1434fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1435fb4f313dSNamhyung Kim 
1436ba77c9e1SLi Zefan 	if (l->ptr < r->ptr)
1437ba77c9e1SLi Zefan 		return -1;
1438ba77c9e1SLi Zefan 	else if (l->ptr > r->ptr)
1439ba77c9e1SLi Zefan 		return 1;
1440ba77c9e1SLi Zefan 	return 0;
1441ba77c9e1SLi Zefan }
1442ba77c9e1SLi Zefan 
144329b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = {
144429b3e152SLi Zefan 	.name	= "ptr",
144529b3e152SLi Zefan 	.cmp	= ptr_cmp,
144629b3e152SLi Zefan };
144729b3e152SLi Zefan 
slab_callsite_cmp(void * a,void * b)1448fb4f313dSNamhyung Kim static int slab_callsite_cmp(void *a, void *b)
1449ba77c9e1SLi Zefan {
1450fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1451fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1452fb4f313dSNamhyung Kim 
1453ba77c9e1SLi Zefan 	if (l->call_site < r->call_site)
1454ba77c9e1SLi Zefan 		return -1;
1455ba77c9e1SLi Zefan 	else if (l->call_site > r->call_site)
1456ba77c9e1SLi Zefan 		return 1;
1457ba77c9e1SLi Zefan 	return 0;
1458ba77c9e1SLi Zefan }
1459ba77c9e1SLi Zefan 
146029b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = {
146129b3e152SLi Zefan 	.name	= "callsite",
1462fb4f313dSNamhyung Kim 	.cmp	= slab_callsite_cmp,
146329b3e152SLi Zefan };
146429b3e152SLi Zefan 
hit_cmp(void * a,void * b)1465fb4f313dSNamhyung Kim static int hit_cmp(void *a, void *b)
1466f3ced7cdSPekka Enberg {
1467fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1468fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1469fb4f313dSNamhyung Kim 
1470f3ced7cdSPekka Enberg 	if (l->hit < r->hit)
1471f3ced7cdSPekka Enberg 		return -1;
1472f3ced7cdSPekka Enberg 	else if (l->hit > r->hit)
1473f3ced7cdSPekka Enberg 		return 1;
1474f3ced7cdSPekka Enberg 	return 0;
1475f3ced7cdSPekka Enberg }
1476f3ced7cdSPekka Enberg 
147729b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = {
147829b3e152SLi Zefan 	.name	= "hit",
147929b3e152SLi Zefan 	.cmp	= hit_cmp,
148029b3e152SLi Zefan };
148129b3e152SLi Zefan 
bytes_cmp(void * a,void * b)1482fb4f313dSNamhyung Kim static int bytes_cmp(void *a, void *b)
1483ba77c9e1SLi Zefan {
1484fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1485fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1486fb4f313dSNamhyung Kim 
1487ba77c9e1SLi Zefan 	if (l->bytes_alloc < r->bytes_alloc)
1488ba77c9e1SLi Zefan 		return -1;
1489ba77c9e1SLi Zefan 	else if (l->bytes_alloc > r->bytes_alloc)
1490ba77c9e1SLi Zefan 		return 1;
1491ba77c9e1SLi Zefan 	return 0;
1492ba77c9e1SLi Zefan }
1493ba77c9e1SLi Zefan 
149429b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = {
149529b3e152SLi Zefan 	.name	= "bytes",
149629b3e152SLi Zefan 	.cmp	= bytes_cmp,
149729b3e152SLi Zefan };
149829b3e152SLi Zefan 
frag_cmp(void * a,void * b)1499fb4f313dSNamhyung Kim static int frag_cmp(void *a, void *b)
1500f3ced7cdSPekka Enberg {
1501f3ced7cdSPekka Enberg 	double x, y;
1502fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1503fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1504f3ced7cdSPekka Enberg 
1505f3ced7cdSPekka Enberg 	x = fragmentation(l->bytes_req, l->bytes_alloc);
1506f3ced7cdSPekka Enberg 	y = fragmentation(r->bytes_req, r->bytes_alloc);
1507f3ced7cdSPekka Enberg 
1508f3ced7cdSPekka Enberg 	if (x < y)
1509f3ced7cdSPekka Enberg 		return -1;
1510f3ced7cdSPekka Enberg 	else if (x > y)
1511f3ced7cdSPekka Enberg 		return 1;
1512f3ced7cdSPekka Enberg 	return 0;
1513f3ced7cdSPekka Enberg }
1514f3ced7cdSPekka Enberg 
151529b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = {
151629b3e152SLi Zefan 	.name	= "frag",
151729b3e152SLi Zefan 	.cmp	= frag_cmp,
151829b3e152SLi Zefan };
151929b3e152SLi Zefan 
pingpong_cmp(void * a,void * b)1520fb4f313dSNamhyung Kim static int pingpong_cmp(void *a, void *b)
1521079d3f65SLi Zefan {
1522fb4f313dSNamhyung Kim 	struct alloc_stat *l = a;
1523fb4f313dSNamhyung Kim 	struct alloc_stat *r = b;
1524fb4f313dSNamhyung Kim 
1525079d3f65SLi Zefan 	if (l->pingpong < r->pingpong)
1526079d3f65SLi Zefan 		return -1;
1527079d3f65SLi Zefan 	else if (l->pingpong > r->pingpong)
1528079d3f65SLi Zefan 		return 1;
1529079d3f65SLi Zefan 	return 0;
1530079d3f65SLi Zefan }
1531079d3f65SLi Zefan 
1532079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = {
1533079d3f65SLi Zefan 	.name	= "pingpong",
1534079d3f65SLi Zefan 	.cmp	= pingpong_cmp,
1535079d3f65SLi Zefan };
1536079d3f65SLi Zefan 
1537fb4f313dSNamhyung Kim /* page sort keys */
page_cmp(void * a,void * b)1538fb4f313dSNamhyung Kim static int page_cmp(void *a, void *b)
1539fb4f313dSNamhyung Kim {
1540fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1541fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1542fb4f313dSNamhyung Kim 
1543fb4f313dSNamhyung Kim 	if (l->page < r->page)
1544fb4f313dSNamhyung Kim 		return -1;
1545fb4f313dSNamhyung Kim 	else if (l->page > r->page)
1546fb4f313dSNamhyung Kim 		return 1;
1547fb4f313dSNamhyung Kim 	return 0;
1548fb4f313dSNamhyung Kim }
1549fb4f313dSNamhyung Kim 
1550fb4f313dSNamhyung Kim static struct sort_dimension page_sort_dimension = {
1551fb4f313dSNamhyung Kim 	.name	= "page",
1552fb4f313dSNamhyung Kim 	.cmp	= page_cmp,
1553fb4f313dSNamhyung Kim };
1554fb4f313dSNamhyung Kim 
page_callsite_cmp(void * a,void * b)1555fb4f313dSNamhyung Kim static int page_callsite_cmp(void *a, void *b)
1556fb4f313dSNamhyung Kim {
1557fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1558fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1559fb4f313dSNamhyung Kim 
1560fb4f313dSNamhyung Kim 	if (l->callsite < r->callsite)
1561fb4f313dSNamhyung Kim 		return -1;
1562fb4f313dSNamhyung Kim 	else if (l->callsite > r->callsite)
1563fb4f313dSNamhyung Kim 		return 1;
1564fb4f313dSNamhyung Kim 	return 0;
1565fb4f313dSNamhyung Kim }
1566fb4f313dSNamhyung Kim 
1567fb4f313dSNamhyung Kim static struct sort_dimension page_callsite_sort_dimension = {
1568fb4f313dSNamhyung Kim 	.name	= "callsite",
1569fb4f313dSNamhyung Kim 	.cmp	= page_callsite_cmp,
1570fb4f313dSNamhyung Kim };
1571fb4f313dSNamhyung Kim 
page_hit_cmp(void * a,void * b)1572fb4f313dSNamhyung Kim static int page_hit_cmp(void *a, void *b)
1573fb4f313dSNamhyung Kim {
1574fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1575fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1576fb4f313dSNamhyung Kim 
1577fb4f313dSNamhyung Kim 	if (l->nr_alloc < r->nr_alloc)
1578fb4f313dSNamhyung Kim 		return -1;
1579fb4f313dSNamhyung Kim 	else if (l->nr_alloc > r->nr_alloc)
1580fb4f313dSNamhyung Kim 		return 1;
1581fb4f313dSNamhyung Kim 	return 0;
1582fb4f313dSNamhyung Kim }
1583fb4f313dSNamhyung Kim 
1584fb4f313dSNamhyung Kim static struct sort_dimension page_hit_sort_dimension = {
1585fb4f313dSNamhyung Kim 	.name	= "hit",
1586fb4f313dSNamhyung Kim 	.cmp	= page_hit_cmp,
1587fb4f313dSNamhyung Kim };
1588fb4f313dSNamhyung Kim 
page_bytes_cmp(void * a,void * b)1589fb4f313dSNamhyung Kim static int page_bytes_cmp(void *a, void *b)
1590fb4f313dSNamhyung Kim {
1591fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1592fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1593fb4f313dSNamhyung Kim 
1594fb4f313dSNamhyung Kim 	if (l->alloc_bytes < r->alloc_bytes)
1595fb4f313dSNamhyung Kim 		return -1;
1596fb4f313dSNamhyung Kim 	else if (l->alloc_bytes > r->alloc_bytes)
1597fb4f313dSNamhyung Kim 		return 1;
1598fb4f313dSNamhyung Kim 	return 0;
1599fb4f313dSNamhyung Kim }
1600fb4f313dSNamhyung Kim 
1601fb4f313dSNamhyung Kim static struct sort_dimension page_bytes_sort_dimension = {
1602fb4f313dSNamhyung Kim 	.name	= "bytes",
1603fb4f313dSNamhyung Kim 	.cmp	= page_bytes_cmp,
1604fb4f313dSNamhyung Kim };
1605fb4f313dSNamhyung Kim 
page_order_cmp(void * a,void * b)1606fb4f313dSNamhyung Kim static int page_order_cmp(void *a, void *b)
1607fb4f313dSNamhyung Kim {
1608fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1609fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1610fb4f313dSNamhyung Kim 
1611fb4f313dSNamhyung Kim 	if (l->order < r->order)
1612fb4f313dSNamhyung Kim 		return -1;
1613fb4f313dSNamhyung Kim 	else if (l->order > r->order)
1614fb4f313dSNamhyung Kim 		return 1;
1615fb4f313dSNamhyung Kim 	return 0;
1616fb4f313dSNamhyung Kim }
1617fb4f313dSNamhyung Kim 
1618fb4f313dSNamhyung Kim static struct sort_dimension page_order_sort_dimension = {
1619fb4f313dSNamhyung Kim 	.name	= "order",
1620fb4f313dSNamhyung Kim 	.cmp	= page_order_cmp,
1621fb4f313dSNamhyung Kim };
1622fb4f313dSNamhyung Kim 
migrate_type_cmp(void * a,void * b)1623fb4f313dSNamhyung Kim static int migrate_type_cmp(void *a, void *b)
1624fb4f313dSNamhyung Kim {
1625fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1626fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1627fb4f313dSNamhyung Kim 
1628fb4f313dSNamhyung Kim 	/* for internal use to find free'd page */
1629fb4f313dSNamhyung Kim 	if (l->migrate_type == -1U)
1630fb4f313dSNamhyung Kim 		return 0;
1631fb4f313dSNamhyung Kim 
1632fb4f313dSNamhyung Kim 	if (l->migrate_type < r->migrate_type)
1633fb4f313dSNamhyung Kim 		return -1;
1634fb4f313dSNamhyung Kim 	else if (l->migrate_type > r->migrate_type)
1635fb4f313dSNamhyung Kim 		return 1;
1636fb4f313dSNamhyung Kim 	return 0;
1637fb4f313dSNamhyung Kim }
1638fb4f313dSNamhyung Kim 
1639fb4f313dSNamhyung Kim static struct sort_dimension migrate_type_sort_dimension = {
1640fb4f313dSNamhyung Kim 	.name	= "migtype",
1641fb4f313dSNamhyung Kim 	.cmp	= migrate_type_cmp,
1642fb4f313dSNamhyung Kim };
1643fb4f313dSNamhyung Kim 
gfp_flags_cmp(void * a,void * b)1644fb4f313dSNamhyung Kim static int gfp_flags_cmp(void *a, void *b)
1645fb4f313dSNamhyung Kim {
1646fb4f313dSNamhyung Kim 	struct page_stat *l = a;
1647fb4f313dSNamhyung Kim 	struct page_stat *r = b;
1648fb4f313dSNamhyung Kim 
1649fb4f313dSNamhyung Kim 	/* for internal use to find free'd page */
1650fb4f313dSNamhyung Kim 	if (l->gfp_flags == -1U)
1651fb4f313dSNamhyung Kim 		return 0;
1652fb4f313dSNamhyung Kim 
1653fb4f313dSNamhyung Kim 	if (l->gfp_flags < r->gfp_flags)
1654fb4f313dSNamhyung Kim 		return -1;
1655fb4f313dSNamhyung Kim 	else if (l->gfp_flags > r->gfp_flags)
1656fb4f313dSNamhyung Kim 		return 1;
1657fb4f313dSNamhyung Kim 	return 0;
1658fb4f313dSNamhyung Kim }
1659fb4f313dSNamhyung Kim 
1660fb4f313dSNamhyung Kim static struct sort_dimension gfp_flags_sort_dimension = {
1661fb4f313dSNamhyung Kim 	.name	= "gfp",
1662fb4f313dSNamhyung Kim 	.cmp	= gfp_flags_cmp,
1663fb4f313dSNamhyung Kim };
1664fb4f313dSNamhyung Kim 
1665fb4f313dSNamhyung Kim static struct sort_dimension *slab_sorts[] = {
166629b3e152SLi Zefan 	&ptr_sort_dimension,
166729b3e152SLi Zefan 	&callsite_sort_dimension,
166829b3e152SLi Zefan 	&hit_sort_dimension,
166929b3e152SLi Zefan 	&bytes_sort_dimension,
167029b3e152SLi Zefan 	&frag_sort_dimension,
1671079d3f65SLi Zefan 	&pingpong_sort_dimension,
167229b3e152SLi Zefan };
167329b3e152SLi Zefan 
1674fb4f313dSNamhyung Kim static struct sort_dimension *page_sorts[] = {
1675fb4f313dSNamhyung Kim 	&page_sort_dimension,
1676fb4f313dSNamhyung Kim 	&page_callsite_sort_dimension,
1677fb4f313dSNamhyung Kim 	&page_hit_sort_dimension,
1678fb4f313dSNamhyung Kim 	&page_bytes_sort_dimension,
1679fb4f313dSNamhyung Kim 	&page_order_sort_dimension,
1680fb4f313dSNamhyung Kim 	&migrate_type_sort_dimension,
1681fb4f313dSNamhyung Kim 	&gfp_flags_sort_dimension,
1682fb4f313dSNamhyung Kim };
168329b3e152SLi Zefan 
slab_sort_dimension__add(const char * tok,struct list_head * list)1684fb4f313dSNamhyung Kim static int slab_sort_dimension__add(const char *tok, struct list_head *list)
168529b3e152SLi Zefan {
168629b3e152SLi Zefan 	struct sort_dimension *sort;
168729b3e152SLi Zefan 	int i;
168829b3e152SLi Zefan 
1689fb4f313dSNamhyung Kim 	for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
1690fb4f313dSNamhyung Kim 		if (!strcmp(slab_sorts[i]->name, tok)) {
1691fb4f313dSNamhyung Kim 			sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
16922814eb05SArnaldo Carvalho de Melo 			if (!sort) {
16938d9233f2SArnaldo Carvalho de Melo 				pr_err("%s: memdup failed\n", __func__);
16942814eb05SArnaldo Carvalho de Melo 				return -1;
16952814eb05SArnaldo Carvalho de Melo 			}
169629b3e152SLi Zefan 			list_add_tail(&sort->list, list);
169729b3e152SLi Zefan 			return 0;
169829b3e152SLi Zefan 		}
169929b3e152SLi Zefan 	}
170029b3e152SLi Zefan 
170129b3e152SLi Zefan 	return -1;
170229b3e152SLi Zefan }
170329b3e152SLi Zefan 
page_sort_dimension__add(const char * tok,struct list_head * list)1704fb4f313dSNamhyung Kim static int page_sort_dimension__add(const char *tok, struct list_head *list)
1705fb4f313dSNamhyung Kim {
1706fb4f313dSNamhyung Kim 	struct sort_dimension *sort;
1707fb4f313dSNamhyung Kim 	int i;
1708fb4f313dSNamhyung Kim 
1709fb4f313dSNamhyung Kim 	for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
1710fb4f313dSNamhyung Kim 		if (!strcmp(page_sorts[i]->name, tok)) {
1711fb4f313dSNamhyung Kim 			sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
1712fb4f313dSNamhyung Kim 			if (!sort) {
1713fb4f313dSNamhyung Kim 				pr_err("%s: memdup failed\n", __func__);
1714fb4f313dSNamhyung Kim 				return -1;
1715fb4f313dSNamhyung Kim 			}
1716fb4f313dSNamhyung Kim 			list_add_tail(&sort->list, list);
1717fb4f313dSNamhyung Kim 			return 0;
1718fb4f313dSNamhyung Kim 		}
1719fb4f313dSNamhyung Kim 	}
1720fb4f313dSNamhyung Kim 
1721fb4f313dSNamhyung Kim 	return -1;
1722fb4f313dSNamhyung Kim }
1723fb4f313dSNamhyung Kim 
setup_slab_sorting(struct list_head * sort_list,const char * arg)1724fb4f313dSNamhyung Kim static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
172529b3e152SLi Zefan {
172629b3e152SLi Zefan 	char *tok;
172729b3e152SLi Zefan 	char *str = strdup(arg);
1728405f8755SNamhyung Kim 	char *pos = str;
172929b3e152SLi Zefan 
17302814eb05SArnaldo Carvalho de Melo 	if (!str) {
17312814eb05SArnaldo Carvalho de Melo 		pr_err("%s: strdup failed\n", __func__);
17322814eb05SArnaldo Carvalho de Melo 		return -1;
17332814eb05SArnaldo Carvalho de Melo 	}
173429b3e152SLi Zefan 
173529b3e152SLi Zefan 	while (true) {
1736405f8755SNamhyung Kim 		tok = strsep(&pos, ",");
173729b3e152SLi Zefan 		if (!tok)
173829b3e152SLi Zefan 			break;
1739fb4f313dSNamhyung Kim 		if (slab_sort_dimension__add(tok, sort_list) < 0) {
174062d94b00SArnaldo Carvalho de Melo 			pr_err("Unknown slab --sort key: '%s'", tok);
1741fb4f313dSNamhyung Kim 			free(str);
1742fb4f313dSNamhyung Kim 			return -1;
1743fb4f313dSNamhyung Kim 		}
1744fb4f313dSNamhyung Kim 	}
1745fb4f313dSNamhyung Kim 
1746fb4f313dSNamhyung Kim 	free(str);
1747fb4f313dSNamhyung Kim 	return 0;
1748fb4f313dSNamhyung Kim }
1749fb4f313dSNamhyung Kim 
setup_page_sorting(struct list_head * sort_list,const char * arg)1750fb4f313dSNamhyung Kim static int setup_page_sorting(struct list_head *sort_list, const char *arg)
1751fb4f313dSNamhyung Kim {
1752fb4f313dSNamhyung Kim 	char *tok;
1753fb4f313dSNamhyung Kim 	char *str = strdup(arg);
1754fb4f313dSNamhyung Kim 	char *pos = str;
1755fb4f313dSNamhyung Kim 
1756fb4f313dSNamhyung Kim 	if (!str) {
1757fb4f313dSNamhyung Kim 		pr_err("%s: strdup failed\n", __func__);
1758fb4f313dSNamhyung Kim 		return -1;
1759fb4f313dSNamhyung Kim 	}
1760fb4f313dSNamhyung Kim 
1761fb4f313dSNamhyung Kim 	while (true) {
1762fb4f313dSNamhyung Kim 		tok = strsep(&pos, ",");
1763fb4f313dSNamhyung Kim 		if (!tok)
1764fb4f313dSNamhyung Kim 			break;
1765fb4f313dSNamhyung Kim 		if (page_sort_dimension__add(tok, sort_list) < 0) {
176662d94b00SArnaldo Carvalho de Melo 			pr_err("Unknown page --sort key: '%s'", tok);
17671b22859dSNamhyung Kim 			free(str);
176829b3e152SLi Zefan 			return -1;
176929b3e152SLi Zefan 		}
177029b3e152SLi Zefan 	}
177129b3e152SLi Zefan 
177229b3e152SLi Zefan 	free(str);
177329b3e152SLi Zefan 	return 0;
177429b3e152SLi Zefan }
177529b3e152SLi Zefan 
parse_sort_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)17761d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused,
17771d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
1778ba77c9e1SLi Zefan {
1779ba77c9e1SLi Zefan 	if (!arg)
1780ba77c9e1SLi Zefan 		return -1;
1781ba77c9e1SLi Zefan 
17820c160d49SNamhyung Kim 	if (kmem_page > kmem_slab ||
17830c160d49SNamhyung Kim 	    (kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) {
1784ba77c9e1SLi Zefan 		if (caller_flag > alloc_flag)
1785fb4f313dSNamhyung Kim 			return setup_page_sorting(&page_caller_sort, arg);
1786ba77c9e1SLi Zefan 		else
1787fb4f313dSNamhyung Kim 			return setup_page_sorting(&page_alloc_sort, arg);
1788fb4f313dSNamhyung Kim 	} else {
1789fb4f313dSNamhyung Kim 		if (caller_flag > alloc_flag)
1790fb4f313dSNamhyung Kim 			return setup_slab_sorting(&slab_caller_sort, arg);
1791fb4f313dSNamhyung Kim 		else
1792fb4f313dSNamhyung Kim 			return setup_slab_sorting(&slab_alloc_sort, arg);
1793fb4f313dSNamhyung Kim 	}
1794ba77c9e1SLi Zefan 
1795ba77c9e1SLi Zefan 	return 0;
1796ba77c9e1SLi Zefan }
1797ba77c9e1SLi Zefan 
parse_caller_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)17981d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused,
17991d037ca1SIrina Tirdea 			    const char *arg __maybe_unused,
18001d037ca1SIrina Tirdea 			    int unset __maybe_unused)
1801ba77c9e1SLi Zefan {
1802ba77c9e1SLi Zefan 	caller_flag = (alloc_flag + 1);
180390b86a9fSLi Zefan 	return 0;
180490b86a9fSLi Zefan }
180590b86a9fSLi Zefan 
parse_alloc_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18061d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused,
18071d037ca1SIrina Tirdea 			   const char *arg __maybe_unused,
18081d037ca1SIrina Tirdea 			   int unset __maybe_unused)
180990b86a9fSLi Zefan {
181090b86a9fSLi Zefan 	alloc_flag = (caller_flag + 1);
1811ba77c9e1SLi Zefan 	return 0;
1812ba77c9e1SLi Zefan }
1813ba77c9e1SLi Zefan 
parse_slab_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18140d68bc92SNamhyung Kim static int parse_slab_opt(const struct option *opt __maybe_unused,
18150d68bc92SNamhyung Kim 			  const char *arg __maybe_unused,
18160d68bc92SNamhyung Kim 			  int unset __maybe_unused)
18170d68bc92SNamhyung Kim {
18180d68bc92SNamhyung Kim 	kmem_slab = (kmem_page + 1);
18190d68bc92SNamhyung Kim 	return 0;
18200d68bc92SNamhyung Kim }
18210d68bc92SNamhyung Kim 
parse_page_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18220d68bc92SNamhyung Kim static int parse_page_opt(const struct option *opt __maybe_unused,
18230d68bc92SNamhyung Kim 			  const char *arg __maybe_unused,
18240d68bc92SNamhyung Kim 			  int unset __maybe_unused)
18250d68bc92SNamhyung Kim {
18260d68bc92SNamhyung Kim 	kmem_page = (kmem_slab + 1);
18270d68bc92SNamhyung Kim 	return 0;
18280d68bc92SNamhyung Kim }
18290d68bc92SNamhyung Kim 
parse_line_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)18301d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused,
18311d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
1832ba77c9e1SLi Zefan {
1833ba77c9e1SLi Zefan 	int lines;
1834ba77c9e1SLi Zefan 
1835ba77c9e1SLi Zefan 	if (!arg)
1836ba77c9e1SLi Zefan 		return -1;
1837ba77c9e1SLi Zefan 
1838ba77c9e1SLi Zefan 	lines = strtoul(arg, NULL, 10);
1839ba77c9e1SLi Zefan 
1840ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
1841ba77c9e1SLi Zefan 		caller_lines = lines;
1842ba77c9e1SLi Zefan 	else
1843ba77c9e1SLi Zefan 		alloc_lines = lines;
1844ba77c9e1SLi Zefan 
1845ba77c9e1SLi Zefan 	return 0;
1846ba77c9e1SLi Zefan }
1847ba77c9e1SLi Zefan 
slab_legacy_tp_is_exposed(void)1848b3719108SLeo Yan static bool slab_legacy_tp_is_exposed(void)
1849b3719108SLeo Yan {
1850b3719108SLeo Yan 	/*
1851b3719108SLeo Yan 	 * The tracepoints "kmem:kmalloc_node" and
1852b3719108SLeo Yan 	 * "kmem:kmem_cache_alloc_node" have been removed on the latest
1853b3719108SLeo Yan 	 * kernel, if the tracepoint "kmem:kmalloc_node" is existed it
1854b3719108SLeo Yan 	 * means the tool is running on an old kernel, we need to
1855b3719108SLeo Yan 	 * rollback to support these legacy tracepoints.
1856b3719108SLeo Yan 	 */
1857b3719108SLeo Yan 	return IS_ERR(trace_event__tp_format("kmem", "kmalloc_node")) ?
1858b3719108SLeo Yan 		false : true;
1859b3719108SLeo Yan }
1860b3719108SLeo Yan 
__cmd_record(int argc,const char ** argv)18610433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv)
18620433ffbeSArnaldo Carvalho de Melo {
18630433ffbeSArnaldo Carvalho de Melo 	const char * const record_args[] = {
18644a4d371aSJiri Olsa 	"record", "-a", "-R", "-c", "1",
18650d68bc92SNamhyung Kim 	};
18660d68bc92SNamhyung Kim 	const char * const slab_events[] = {
1867ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc",
1868ba77c9e1SLi Zefan 	"-e", "kmem:kfree",
1869ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc",
1870ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_free",
1871ba77c9e1SLi Zefan 	};
1872b3719108SLeo Yan 	const char * const slab_legacy_events[] = {
1873b3719108SLeo Yan 	"-e", "kmem:kmalloc_node",
1874b3719108SLeo Yan 	"-e", "kmem:kmem_cache_alloc_node",
1875b3719108SLeo Yan 	};
18760d68bc92SNamhyung Kim 	const char * const page_events[] = {
18770d68bc92SNamhyung Kim 	"-e", "kmem:mm_page_alloc",
18780d68bc92SNamhyung Kim 	"-e", "kmem:mm_page_free",
18790d68bc92SNamhyung Kim 	};
1880ba77c9e1SLi Zefan 	unsigned int rec_argc, i, j;
1881ba77c9e1SLi Zefan 	const char **rec_argv;
1882b3719108SLeo Yan 	unsigned int slab_legacy_tp_exposed = slab_legacy_tp_is_exposed();
1883ba77c9e1SLi Zefan 
1884ba77c9e1SLi Zefan 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1885b3719108SLeo Yan 	if (kmem_slab) {
18860d68bc92SNamhyung Kim 		rec_argc += ARRAY_SIZE(slab_events);
1887b3719108SLeo Yan 		if (slab_legacy_tp_exposed)
1888b3719108SLeo Yan 			rec_argc += ARRAY_SIZE(slab_legacy_events);
1889b3719108SLeo Yan 	}
18900d68bc92SNamhyung Kim 	if (kmem_page)
1891c9758cc4SNamhyung Kim 		rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
18920d68bc92SNamhyung Kim 
1893ba77c9e1SLi Zefan 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1894ba77c9e1SLi Zefan 
1895ce47dc56SChris Samuel 	if (rec_argv == NULL)
1896ce47dc56SChris Samuel 		return -ENOMEM;
1897ce47dc56SChris Samuel 
1898ba77c9e1SLi Zefan 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
1899ba77c9e1SLi Zefan 		rec_argv[i] = strdup(record_args[i]);
1900ba77c9e1SLi Zefan 
19010d68bc92SNamhyung Kim 	if (kmem_slab) {
19020d68bc92SNamhyung Kim 		for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
19030d68bc92SNamhyung Kim 			rec_argv[i] = strdup(slab_events[j]);
1904b3719108SLeo Yan 		if (slab_legacy_tp_exposed) {
1905b3719108SLeo Yan 			for (j = 0; j < ARRAY_SIZE(slab_legacy_events); j++, i++)
1906b3719108SLeo Yan 				rec_argv[i] = strdup(slab_legacy_events[j]);
1907b3719108SLeo Yan 		}
19080d68bc92SNamhyung Kim 	}
19090d68bc92SNamhyung Kim 	if (kmem_page) {
1910c9758cc4SNamhyung Kim 		rec_argv[i++] = strdup("-g");
1911c9758cc4SNamhyung Kim 
19120d68bc92SNamhyung Kim 		for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
19130d68bc92SNamhyung Kim 			rec_argv[i] = strdup(page_events[j]);
19140d68bc92SNamhyung Kim 	}
19150d68bc92SNamhyung Kim 
1916ba77c9e1SLi Zefan 	for (j = 1; j < (unsigned int)argc; j++, i++)
1917ba77c9e1SLi Zefan 		rec_argv[i] = argv[j];
1918ba77c9e1SLi Zefan 
1919b0ad8ea6SArnaldo Carvalho de Melo 	return cmd_record(i, rec_argv);
1920ba77c9e1SLi Zefan }
1921ba77c9e1SLi Zefan 
kmem_config(const char * var,const char * value,void * cb __maybe_unused)1922b8cbb349SWang Nan static int kmem_config(const char *var, const char *value, void *cb __maybe_unused)
19230c160d49SNamhyung Kim {
19240c160d49SNamhyung Kim 	if (!strcmp(var, "kmem.default")) {
19250c160d49SNamhyung Kim 		if (!strcmp(value, "slab"))
19260c160d49SNamhyung Kim 			kmem_default = KMEM_SLAB;
19270c160d49SNamhyung Kim 		else if (!strcmp(value, "page"))
19280c160d49SNamhyung Kim 			kmem_default = KMEM_PAGE;
19290c160d49SNamhyung Kim 		else
19300c160d49SNamhyung Kim 			pr_err("invalid default value ('slab' or 'page' required): %s\n",
19310c160d49SNamhyung Kim 			       value);
19320c160d49SNamhyung Kim 		return 0;
19330c160d49SNamhyung Kim 	}
19340c160d49SNamhyung Kim 
1935b8cbb349SWang Nan 	return 0;
19360c160d49SNamhyung Kim }
19370c160d49SNamhyung Kim 
cmd_kmem(int argc,const char ** argv)1938b0ad8ea6SArnaldo Carvalho de Melo int cmd_kmem(int argc, const char **argv)
1939ba77c9e1SLi Zefan {
1940fb4f313dSNamhyung Kim 	const char * const default_slab_sort = "frag,hit,bytes";
1941fb4f313dSNamhyung Kim 	const char * const default_page_sort = "bytes,hit";
19428ceb41d7SJiri Olsa 	struct perf_data data = {
1943d1eeb77cSYunlong Song 		.mode = PERF_DATA_MODE_READ,
1944d1eeb77cSYunlong Song 	};
19450433ffbeSArnaldo Carvalho de Melo 	const struct option kmem_options[] = {
19460433ffbeSArnaldo Carvalho de Melo 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
1947bd72a33eSNamhyung Kim 	OPT_INCR('v', "verbose", &verbose,
1948bd72a33eSNamhyung Kim 		    "be more verbose (show symbol address, etc)"),
19490433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
19500433ffbeSArnaldo Carvalho de Melo 			   "show per-callsite statistics", parse_caller_opt),
19510433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
19520433ffbeSArnaldo Carvalho de Melo 			   "show per-allocation statistics", parse_alloc_opt),
19530433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
1954fb4f313dSNamhyung Kim 		     "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
1955fb4f313dSNamhyung Kim 		     "page, order, migtype, gfp", parse_sort_opt),
19560433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
19570433ffbeSArnaldo Carvalho de Melo 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
19588ceb41d7SJiri Olsa 	OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"),
19590d68bc92SNamhyung Kim 	OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
19600d68bc92SNamhyung Kim 			   parse_slab_opt),
19610d68bc92SNamhyung Kim 	OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
19620d68bc92SNamhyung Kim 			   parse_page_opt),
19632a7ef02cSNamhyung Kim 	OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
19642a865bd8SDavid Ahern 	OPT_STRING(0, "time", &time_str, "str",
19652a865bd8SDavid Ahern 		   "Time span of interest (start,stop)"),
19660433ffbeSArnaldo Carvalho de Melo 	OPT_END()
19670433ffbeSArnaldo Carvalho de Melo 	};
19683bca2354SRamkumar Ramachandra 	const char *const kmem_subcommands[] = { "record", "stat", NULL };
19693bca2354SRamkumar Ramachandra 	const char *kmem_usage[] = {
19703bca2354SRamkumar Ramachandra 		NULL,
19710433ffbeSArnaldo Carvalho de Melo 		NULL
19720433ffbeSArnaldo Carvalho de Melo 	};
19732b2b2c68SNamhyung Kim 	struct perf_session *session;
197449b8e2beSRasmus Villemoes 	static const char errmsg[] = "No %s allocation events found.  Have you run 'perf kmem record --%s'?\n";
1975ecc4c561SArnaldo Carvalho de Melo 	int ret = perf_config(kmem_config, NULL);
19762b2b2c68SNamhyung Kim 
1977ecc4c561SArnaldo Carvalho de Melo 	if (ret)
1978ecc4c561SArnaldo Carvalho de Melo 		return ret;
1979ecc4c561SArnaldo Carvalho de Melo 
19803bca2354SRamkumar Ramachandra 	argc = parse_options_subcommand(argc, argv, kmem_options,
1981be8299e4SIan Rogers 					kmem_subcommands, kmem_usage,
1982be8299e4SIan Rogers 					PARSE_OPT_STOP_AT_NON_OPTION);
1983ba77c9e1SLi Zefan 
198490b86a9fSLi Zefan 	if (!argc)
1985ba77c9e1SLi Zefan 		usage_with_options(kmem_usage, kmem_options);
1986ba77c9e1SLi Zefan 
19870c160d49SNamhyung Kim 	if (kmem_slab == 0 && kmem_page == 0) {
19880c160d49SNamhyung Kim 		if (kmem_default == KMEM_SLAB)
19890c160d49SNamhyung Kim 			kmem_slab = 1;
19900c160d49SNamhyung Kim 		else
19910c160d49SNamhyung Kim 			kmem_page = 1;
19920c160d49SNamhyung Kim 	}
19930d68bc92SNamhyung Kim 
1994ae0f4eb3SWei Li 	if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
19950a7e6d1bSNamhyung Kim 		symbol__init(NULL);
19962b2b2c68SNamhyung Kim 		return __cmd_record(argc, argv);
19972b2b2c68SNamhyung Kim 	}
19982b2b2c68SNamhyung Kim 
19992d4f2799SJiri Olsa 	data.path = input_name;
200028939e1aSJiri Olsa 
20012681bd85SNamhyung Kim 	kmem_session = session = perf_session__new(&data, &perf_kmem);
20026ef81c55SMamatha Inamdar 	if (IS_ERR(session))
20036ef81c55SMamatha Inamdar 		return PTR_ERR(session);
20042b2b2c68SNamhyung Kim 
2005ecc4c561SArnaldo Carvalho de Melo 	ret = -1;
2006ecc4c561SArnaldo Carvalho de Melo 
2007a923e2c4SNamhyung Kim 	if (kmem_slab) {
2008b02736f7SArnaldo Carvalho de Melo 		if (!evlist__find_tracepoint_by_name(session->evlist, "kmem:kmalloc")) {
2009a923e2c4SNamhyung Kim 			pr_err(errmsg, "slab", "slab");
2010249ca1a8STaeung Song 			goto out_delete;
2011a923e2c4SNamhyung Kim 		}
2012a923e2c4SNamhyung Kim 	}
20130d68bc92SNamhyung Kim 
2014a923e2c4SNamhyung Kim 	if (kmem_page) {
2015b02736f7SArnaldo Carvalho de Melo 		struct evsel *evsel = evlist__find_tracepoint_by_name(session->evlist, "kmem:mm_page_alloc");
2016a923e2c4SNamhyung Kim 
2017a923e2c4SNamhyung Kim 		if (evsel == NULL) {
2018a923e2c4SNamhyung Kim 			pr_err(errmsg, "page", "page");
2019249ca1a8STaeung Song 			goto out_delete;
20200d68bc92SNamhyung Kim 		}
20210d68bc92SNamhyung Kim 
202269769ce1STzvetomir Stoyanov 		kmem_page_size = tep_get_page_size(evsel->tp_format->tep);
2023c9758cc4SNamhyung Kim 		symbol_conf.use_callchain = true;
20240d68bc92SNamhyung Kim 	}
20250d68bc92SNamhyung Kim 
20260a7e6d1bSNamhyung Kim 	symbol__init(&session->header.env);
2027655000e7SArnaldo Carvalho de Melo 
20282a865bd8SDavid Ahern 	if (perf_time__parse_str(&ptime, time_str) != 0) {
20292a865bd8SDavid Ahern 		pr_err("Invalid time string\n");
203079f56ebeSChristophe JAILLET 		ret = -EINVAL;
203179f56ebeSChristophe JAILLET 		goto out_delete;
20322a865bd8SDavid Ahern 	}
20332a865bd8SDavid Ahern 
20342b2b2c68SNamhyung Kim 	if (!strcmp(argv[0], "stat")) {
203577cfe388SNamhyung Kim 		setlocale(LC_ALL, "");
203677cfe388SNamhyung Kim 
20374b627957SDon Zickus 		if (cpu__setup_cpunode_map())
20382b2b2c68SNamhyung Kim 			goto out_delete;
203990b86a9fSLi Zefan 
2040fb4f313dSNamhyung Kim 		if (list_empty(&slab_caller_sort))
2041fb4f313dSNamhyung Kim 			setup_slab_sorting(&slab_caller_sort, default_slab_sort);
2042fb4f313dSNamhyung Kim 		if (list_empty(&slab_alloc_sort))
2043fb4f313dSNamhyung Kim 			setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
2044fb4f313dSNamhyung Kim 		if (list_empty(&page_caller_sort))
2045fb4f313dSNamhyung Kim 			setup_page_sorting(&page_caller_sort, default_page_sort);
2046fb4f313dSNamhyung Kim 		if (list_empty(&page_alloc_sort))
2047fb4f313dSNamhyung Kim 			setup_page_sorting(&page_alloc_sort, default_page_sort);
2048ba77c9e1SLi Zefan 
2049fb4f313dSNamhyung Kim 		if (kmem_page) {
2050fb4f313dSNamhyung Kim 			setup_page_sorting(&page_alloc_sort_input,
2051fb4f313dSNamhyung Kim 					   "page,order,migtype,gfp");
2052fb4f313dSNamhyung Kim 			setup_page_sorting(&page_caller_sort_input,
2053fb4f313dSNamhyung Kim 					   "callsite,order,migtype,gfp");
2054fb4f313dSNamhyung Kim 		}
20552b2b2c68SNamhyung Kim 		ret = __cmd_kmem(session);
2056b00eca8cSPekka Enberg 	} else
2057b00eca8cSPekka Enberg 		usage_with_options(kmem_usage, kmem_options);
2058ba77c9e1SLi Zefan 
20592b2b2c68SNamhyung Kim out_delete:
20602b2b2c68SNamhyung Kim 	perf_session__delete(session);
2061*ab656dd9SAditya Gupta 	/* free usage string allocated by parse_options_subcommand */
2062*ab656dd9SAditya Gupta 	free((void *)kmem_usage[0]);
20632b2b2c68SNamhyung Kim 
20642b2b2c68SNamhyung Kim 	return ret;
206590b86a9fSLi Zefan }
206690b86a9fSLi Zefan 
2067