xref: /OK3568_Linux_fs/kernel/tools/perf/builtin-kmem.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include "builtin.h"
3*4882a593Smuzhiyun #include "perf.h"
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun #include "util/dso.h"
6*4882a593Smuzhiyun #include "util/evlist.h"
7*4882a593Smuzhiyun #include "util/evsel.h"
8*4882a593Smuzhiyun #include "util/config.h"
9*4882a593Smuzhiyun #include "util/map.h"
10*4882a593Smuzhiyun #include "util/symbol.h"
11*4882a593Smuzhiyun #include "util/thread.h"
12*4882a593Smuzhiyun #include "util/header.h"
13*4882a593Smuzhiyun #include "util/session.h"
14*4882a593Smuzhiyun #include "util/tool.h"
15*4882a593Smuzhiyun #include "util/callchain.h"
16*4882a593Smuzhiyun #include "util/time-utils.h"
17*4882a593Smuzhiyun #include <linux/err.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <subcmd/pager.h>
20*4882a593Smuzhiyun #include <subcmd/parse-options.h>
21*4882a593Smuzhiyun #include "util/trace-event.h"
22*4882a593Smuzhiyun #include "util/data.h"
23*4882a593Smuzhiyun #include "util/cpumap.h"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include "util/debug.h"
26*4882a593Smuzhiyun #include "util/string2.h"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #include <linux/kernel.h>
29*4882a593Smuzhiyun #include <linux/rbtree.h>
30*4882a593Smuzhiyun #include <linux/string.h>
31*4882a593Smuzhiyun #include <linux/zalloc.h>
32*4882a593Smuzhiyun #include <errno.h>
33*4882a593Smuzhiyun #include <inttypes.h>
34*4882a593Smuzhiyun #include <locale.h>
35*4882a593Smuzhiyun #include <regex.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #include <linux/ctype.h>
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun static int	kmem_slab;
40*4882a593Smuzhiyun static int	kmem_page;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun static long	kmem_page_size;
43*4882a593Smuzhiyun static enum {
44*4882a593Smuzhiyun 	KMEM_SLAB,
45*4882a593Smuzhiyun 	KMEM_PAGE,
46*4882a593Smuzhiyun } kmem_default = KMEM_SLAB;  /* for backward compatibility */
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun struct alloc_stat;
49*4882a593Smuzhiyun typedef int (*sort_fn_t)(void *, void *);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static int			alloc_flag;
52*4882a593Smuzhiyun static int			caller_flag;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun static int			alloc_lines = -1;
55*4882a593Smuzhiyun static int			caller_lines = -1;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun static bool			raw_ip;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun struct alloc_stat {
60*4882a593Smuzhiyun 	u64	call_site;
61*4882a593Smuzhiyun 	u64	ptr;
62*4882a593Smuzhiyun 	u64	bytes_req;
63*4882a593Smuzhiyun 	u64	bytes_alloc;
64*4882a593Smuzhiyun 	u64	last_alloc;
65*4882a593Smuzhiyun 	u32	hit;
66*4882a593Smuzhiyun 	u32	pingpong;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	short	alloc_cpu;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	struct rb_node node;
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static struct rb_root root_alloc_stat;
74*4882a593Smuzhiyun static struct rb_root root_alloc_sorted;
75*4882a593Smuzhiyun static struct rb_root root_caller_stat;
76*4882a593Smuzhiyun static struct rb_root root_caller_sorted;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun static unsigned long total_requested, total_allocated, total_freed;
79*4882a593Smuzhiyun static unsigned long nr_allocs, nr_cross_allocs;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /* filters for controlling start and stop of time of analysis */
82*4882a593Smuzhiyun static struct perf_time_interval ptime;
83*4882a593Smuzhiyun const char *time_str;
84*4882a593Smuzhiyun 
insert_alloc_stat(unsigned long call_site,unsigned long ptr,int bytes_req,int bytes_alloc,int cpu)85*4882a593Smuzhiyun static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
86*4882a593Smuzhiyun 			     int bytes_req, int bytes_alloc, int cpu)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	struct rb_node **node = &root_alloc_stat.rb_node;
89*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
90*4882a593Smuzhiyun 	struct alloc_stat *data = NULL;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	while (*node) {
93*4882a593Smuzhiyun 		parent = *node;
94*4882a593Smuzhiyun 		data = rb_entry(*node, struct alloc_stat, node);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 		if (ptr > data->ptr)
97*4882a593Smuzhiyun 			node = &(*node)->rb_right;
98*4882a593Smuzhiyun 		else if (ptr < data->ptr)
99*4882a593Smuzhiyun 			node = &(*node)->rb_left;
100*4882a593Smuzhiyun 		else
101*4882a593Smuzhiyun 			break;
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (data && data->ptr == ptr) {
105*4882a593Smuzhiyun 		data->hit++;
106*4882a593Smuzhiyun 		data->bytes_req += bytes_req;
107*4882a593Smuzhiyun 		data->bytes_alloc += bytes_alloc;
108*4882a593Smuzhiyun 	} else {
109*4882a593Smuzhiyun 		data = malloc(sizeof(*data));
110*4882a593Smuzhiyun 		if (!data) {
111*4882a593Smuzhiyun 			pr_err("%s: malloc failed\n", __func__);
112*4882a593Smuzhiyun 			return -1;
113*4882a593Smuzhiyun 		}
114*4882a593Smuzhiyun 		data->ptr = ptr;
115*4882a593Smuzhiyun 		data->pingpong = 0;
116*4882a593Smuzhiyun 		data->hit = 1;
117*4882a593Smuzhiyun 		data->bytes_req = bytes_req;
118*4882a593Smuzhiyun 		data->bytes_alloc = bytes_alloc;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 		rb_link_node(&data->node, parent, node);
121*4882a593Smuzhiyun 		rb_insert_color(&data->node, &root_alloc_stat);
122*4882a593Smuzhiyun 	}
123*4882a593Smuzhiyun 	data->call_site = call_site;
124*4882a593Smuzhiyun 	data->alloc_cpu = cpu;
125*4882a593Smuzhiyun 	data->last_alloc = bytes_alloc;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
insert_caller_stat(unsigned long call_site,int bytes_req,int bytes_alloc)130*4882a593Smuzhiyun static int insert_caller_stat(unsigned long call_site,
131*4882a593Smuzhiyun 			      int bytes_req, int bytes_alloc)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct rb_node **node = &root_caller_stat.rb_node;
134*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
135*4882a593Smuzhiyun 	struct alloc_stat *data = NULL;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	while (*node) {
138*4882a593Smuzhiyun 		parent = *node;
139*4882a593Smuzhiyun 		data = rb_entry(*node, struct alloc_stat, node);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 		if (call_site > data->call_site)
142*4882a593Smuzhiyun 			node = &(*node)->rb_right;
143*4882a593Smuzhiyun 		else if (call_site < data->call_site)
144*4882a593Smuzhiyun 			node = &(*node)->rb_left;
145*4882a593Smuzhiyun 		else
146*4882a593Smuzhiyun 			break;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	if (data && data->call_site == call_site) {
150*4882a593Smuzhiyun 		data->hit++;
151*4882a593Smuzhiyun 		data->bytes_req += bytes_req;
152*4882a593Smuzhiyun 		data->bytes_alloc += bytes_alloc;
153*4882a593Smuzhiyun 	} else {
154*4882a593Smuzhiyun 		data = malloc(sizeof(*data));
155*4882a593Smuzhiyun 		if (!data) {
156*4882a593Smuzhiyun 			pr_err("%s: malloc failed\n", __func__);
157*4882a593Smuzhiyun 			return -1;
158*4882a593Smuzhiyun 		}
159*4882a593Smuzhiyun 		data->call_site = call_site;
160*4882a593Smuzhiyun 		data->pingpong = 0;
161*4882a593Smuzhiyun 		data->hit = 1;
162*4882a593Smuzhiyun 		data->bytes_req = bytes_req;
163*4882a593Smuzhiyun 		data->bytes_alloc = bytes_alloc;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 		rb_link_node(&data->node, parent, node);
166*4882a593Smuzhiyun 		rb_insert_color(&data->node, &root_caller_stat);
167*4882a593Smuzhiyun 	}
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
evsel__process_alloc_event(struct evsel * evsel,struct perf_sample * sample)172*4882a593Smuzhiyun static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	unsigned long ptr = evsel__intval(evsel, sample, "ptr"),
175*4882a593Smuzhiyun 		      call_site = evsel__intval(evsel, sample, "call_site");
176*4882a593Smuzhiyun 	int bytes_req = evsel__intval(evsel, sample, "bytes_req"),
177*4882a593Smuzhiyun 	    bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc");
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
180*4882a593Smuzhiyun 	    insert_caller_stat(call_site, bytes_req, bytes_alloc))
181*4882a593Smuzhiyun 		return -1;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	total_requested += bytes_req;
184*4882a593Smuzhiyun 	total_allocated += bytes_alloc;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	nr_allocs++;
187*4882a593Smuzhiyun 	return 0;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
evsel__process_alloc_node_event(struct evsel * evsel,struct perf_sample * sample)190*4882a593Smuzhiyun static int evsel__process_alloc_node_event(struct evsel *evsel, struct perf_sample *sample)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun 	int ret = evsel__process_alloc_event(evsel, sample);
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	if (!ret) {
195*4882a593Smuzhiyun 		int node1 = cpu__get_node(sample->cpu),
196*4882a593Smuzhiyun 		    node2 = evsel__intval(evsel, sample, "node");
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 		if (node1 != node2)
199*4882a593Smuzhiyun 			nr_cross_allocs++;
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	return ret;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun static int ptr_cmp(void *, void *);
206*4882a593Smuzhiyun static int slab_callsite_cmp(void *, void *);
207*4882a593Smuzhiyun 
search_alloc_stat(unsigned long ptr,unsigned long call_site,struct rb_root * root,sort_fn_t sort_fn)208*4882a593Smuzhiyun static struct alloc_stat *search_alloc_stat(unsigned long ptr,
209*4882a593Smuzhiyun 					    unsigned long call_site,
210*4882a593Smuzhiyun 					    struct rb_root *root,
211*4882a593Smuzhiyun 					    sort_fn_t sort_fn)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun 	struct rb_node *node = root->rb_node;
214*4882a593Smuzhiyun 	struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	while (node) {
217*4882a593Smuzhiyun 		struct alloc_stat *data;
218*4882a593Smuzhiyun 		int cmp;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 		data = rb_entry(node, struct alloc_stat, node);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 		cmp = sort_fn(&key, data);
223*4882a593Smuzhiyun 		if (cmp < 0)
224*4882a593Smuzhiyun 			node = node->rb_left;
225*4882a593Smuzhiyun 		else if (cmp > 0)
226*4882a593Smuzhiyun 			node = node->rb_right;
227*4882a593Smuzhiyun 		else
228*4882a593Smuzhiyun 			return data;
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 	return NULL;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
evsel__process_free_event(struct evsel * evsel,struct perf_sample * sample)233*4882a593Smuzhiyun static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	unsigned long ptr = evsel__intval(evsel, sample, "ptr");
236*4882a593Smuzhiyun 	struct alloc_stat *s_alloc, *s_caller;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
239*4882a593Smuzhiyun 	if (!s_alloc)
240*4882a593Smuzhiyun 		return 0;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	total_freed += s_alloc->last_alloc;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if ((short)sample->cpu != s_alloc->alloc_cpu) {
245*4882a593Smuzhiyun 		s_alloc->pingpong++;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 		s_caller = search_alloc_stat(0, s_alloc->call_site,
248*4882a593Smuzhiyun 					     &root_caller_stat,
249*4882a593Smuzhiyun 					     slab_callsite_cmp);
250*4882a593Smuzhiyun 		if (!s_caller)
251*4882a593Smuzhiyun 			return -1;
252*4882a593Smuzhiyun 		s_caller->pingpong++;
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 	s_alloc->alloc_cpu = -1;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	return 0;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun static u64 total_page_alloc_bytes;
260*4882a593Smuzhiyun static u64 total_page_free_bytes;
261*4882a593Smuzhiyun static u64 total_page_nomatch_bytes;
262*4882a593Smuzhiyun static u64 total_page_fail_bytes;
263*4882a593Smuzhiyun static unsigned long nr_page_allocs;
264*4882a593Smuzhiyun static unsigned long nr_page_frees;
265*4882a593Smuzhiyun static unsigned long nr_page_fails;
266*4882a593Smuzhiyun static unsigned long nr_page_nomatch;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun static bool use_pfn;
269*4882a593Smuzhiyun static bool live_page;
270*4882a593Smuzhiyun static struct perf_session *kmem_session;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun #define MAX_MIGRATE_TYPES  6
273*4882a593Smuzhiyun #define MAX_PAGE_ORDER     11
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun struct page_stat {
278*4882a593Smuzhiyun 	struct rb_node 	node;
279*4882a593Smuzhiyun 	u64 		page;
280*4882a593Smuzhiyun 	u64 		callsite;
281*4882a593Smuzhiyun 	int 		order;
282*4882a593Smuzhiyun 	unsigned 	gfp_flags;
283*4882a593Smuzhiyun 	unsigned 	migrate_type;
284*4882a593Smuzhiyun 	u64		alloc_bytes;
285*4882a593Smuzhiyun 	u64 		free_bytes;
286*4882a593Smuzhiyun 	int 		nr_alloc;
287*4882a593Smuzhiyun 	int 		nr_free;
288*4882a593Smuzhiyun };
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun static struct rb_root page_live_tree;
291*4882a593Smuzhiyun static struct rb_root page_alloc_tree;
292*4882a593Smuzhiyun static struct rb_root page_alloc_sorted;
293*4882a593Smuzhiyun static struct rb_root page_caller_tree;
294*4882a593Smuzhiyun static struct rb_root page_caller_sorted;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun struct alloc_func {
297*4882a593Smuzhiyun 	u64 start;
298*4882a593Smuzhiyun 	u64 end;
299*4882a593Smuzhiyun 	char *name;
300*4882a593Smuzhiyun };
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun static int nr_alloc_funcs;
303*4882a593Smuzhiyun static struct alloc_func *alloc_func_list;
304*4882a593Smuzhiyun 
funcmp(const void * a,const void * b)305*4882a593Smuzhiyun static int funcmp(const void *a, const void *b)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun 	const struct alloc_func *fa = a;
308*4882a593Smuzhiyun 	const struct alloc_func *fb = b;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (fa->start > fb->start)
311*4882a593Smuzhiyun 		return 1;
312*4882a593Smuzhiyun 	else
313*4882a593Smuzhiyun 		return -1;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun 
callcmp(const void * a,const void * b)316*4882a593Smuzhiyun static int callcmp(const void *a, const void *b)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	const struct alloc_func *fa = a;
319*4882a593Smuzhiyun 	const struct alloc_func *fb = b;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	if (fb->start <= fa->start && fa->end < fb->end)
322*4882a593Smuzhiyun 		return 0;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	if (fa->start > fb->start)
325*4882a593Smuzhiyun 		return 1;
326*4882a593Smuzhiyun 	else
327*4882a593Smuzhiyun 		return -1;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun 
build_alloc_func_list(void)330*4882a593Smuzhiyun static int build_alloc_func_list(void)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun 	int ret;
333*4882a593Smuzhiyun 	struct map *kernel_map;
334*4882a593Smuzhiyun 	struct symbol *sym;
335*4882a593Smuzhiyun 	struct rb_node *node;
336*4882a593Smuzhiyun 	struct alloc_func *func;
337*4882a593Smuzhiyun 	struct machine *machine = &kmem_session->machines.host;
338*4882a593Smuzhiyun 	regex_t alloc_func_regex;
339*4882a593Smuzhiyun 	static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
342*4882a593Smuzhiyun 	if (ret) {
343*4882a593Smuzhiyun 		char err[BUFSIZ];
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 		regerror(ret, &alloc_func_regex, err, sizeof(err));
346*4882a593Smuzhiyun 		pr_err("Invalid regex: %s\n%s", pattern, err);
347*4882a593Smuzhiyun 		return -EINVAL;
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	kernel_map = machine__kernel_map(machine);
351*4882a593Smuzhiyun 	if (map__load(kernel_map) < 0) {
352*4882a593Smuzhiyun 		pr_err("cannot load kernel map\n");
353*4882a593Smuzhiyun 		return -ENOENT;
354*4882a593Smuzhiyun 	}
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	map__for_each_symbol(kernel_map, sym, node) {
357*4882a593Smuzhiyun 		if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
358*4882a593Smuzhiyun 			continue;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 		func = realloc(alloc_func_list,
361*4882a593Smuzhiyun 			       (nr_alloc_funcs + 1) * sizeof(*func));
362*4882a593Smuzhiyun 		if (func == NULL)
363*4882a593Smuzhiyun 			return -ENOMEM;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 		pr_debug("alloc func: %s\n", sym->name);
366*4882a593Smuzhiyun 		func[nr_alloc_funcs].start = sym->start;
367*4882a593Smuzhiyun 		func[nr_alloc_funcs].end   = sym->end;
368*4882a593Smuzhiyun 		func[nr_alloc_funcs].name  = sym->name;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 		alloc_func_list = func;
371*4882a593Smuzhiyun 		nr_alloc_funcs++;
372*4882a593Smuzhiyun 	}
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	regfree(&alloc_func_regex);
377*4882a593Smuzhiyun 	return 0;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun /*
381*4882a593Smuzhiyun  * Find first non-memory allocation function from callchain.
382*4882a593Smuzhiyun  * The allocation functions are in the 'alloc_func_list'.
383*4882a593Smuzhiyun  */
find_callsite(struct evsel * evsel,struct perf_sample * sample)384*4882a593Smuzhiyun static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun 	struct addr_location al;
387*4882a593Smuzhiyun 	struct machine *machine = &kmem_session->machines.host;
388*4882a593Smuzhiyun 	struct callchain_cursor_node *node;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	if (alloc_func_list == NULL) {
391*4882a593Smuzhiyun 		if (build_alloc_func_list() < 0)
392*4882a593Smuzhiyun 			goto out;
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
396*4882a593Smuzhiyun 	sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16);
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	callchain_cursor_commit(&callchain_cursor);
399*4882a593Smuzhiyun 	while (true) {
400*4882a593Smuzhiyun 		struct alloc_func key, *caller;
401*4882a593Smuzhiyun 		u64 addr;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 		node = callchain_cursor_current(&callchain_cursor);
404*4882a593Smuzhiyun 		if (node == NULL)
405*4882a593Smuzhiyun 			break;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 		key.start = key.end = node->ip;
408*4882a593Smuzhiyun 		caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
409*4882a593Smuzhiyun 				 sizeof(key), callcmp);
410*4882a593Smuzhiyun 		if (!caller) {
411*4882a593Smuzhiyun 			/* found */
412*4882a593Smuzhiyun 			if (node->ms.map)
413*4882a593Smuzhiyun 				addr = map__unmap_ip(node->ms.map, node->ip);
414*4882a593Smuzhiyun 			else
415*4882a593Smuzhiyun 				addr = node->ip;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 			return addr;
418*4882a593Smuzhiyun 		} else
419*4882a593Smuzhiyun 			pr_debug3("skipping alloc function: %s\n", caller->name);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 		callchain_cursor_advance(&callchain_cursor);
422*4882a593Smuzhiyun 	}
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun out:
425*4882a593Smuzhiyun 	pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
426*4882a593Smuzhiyun 	return sample->ip;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun struct sort_dimension {
430*4882a593Smuzhiyun 	const char		name[20];
431*4882a593Smuzhiyun 	sort_fn_t		cmp;
432*4882a593Smuzhiyun 	struct list_head	list;
433*4882a593Smuzhiyun };
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun static LIST_HEAD(page_alloc_sort_input);
436*4882a593Smuzhiyun static LIST_HEAD(page_caller_sort_input);
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun static struct page_stat *
__page_stat__findnew_page(struct page_stat * pstat,bool create)439*4882a593Smuzhiyun __page_stat__findnew_page(struct page_stat *pstat, bool create)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun 	struct rb_node **node = &page_live_tree.rb_node;
442*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
443*4882a593Smuzhiyun 	struct page_stat *data;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	while (*node) {
446*4882a593Smuzhiyun 		s64 cmp;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 		parent = *node;
449*4882a593Smuzhiyun 		data = rb_entry(*node, struct page_stat, node);
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 		cmp = data->page - pstat->page;
452*4882a593Smuzhiyun 		if (cmp < 0)
453*4882a593Smuzhiyun 			node = &parent->rb_left;
454*4882a593Smuzhiyun 		else if (cmp > 0)
455*4882a593Smuzhiyun 			node = &parent->rb_right;
456*4882a593Smuzhiyun 		else
457*4882a593Smuzhiyun 			return data;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (!create)
461*4882a593Smuzhiyun 		return NULL;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	data = zalloc(sizeof(*data));
464*4882a593Smuzhiyun 	if (data != NULL) {
465*4882a593Smuzhiyun 		data->page = pstat->page;
466*4882a593Smuzhiyun 		data->order = pstat->order;
467*4882a593Smuzhiyun 		data->gfp_flags = pstat->gfp_flags;
468*4882a593Smuzhiyun 		data->migrate_type = pstat->migrate_type;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 		rb_link_node(&data->node, parent, node);
471*4882a593Smuzhiyun 		rb_insert_color(&data->node, &page_live_tree);
472*4882a593Smuzhiyun 	}
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	return data;
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun 
page_stat__find_page(struct page_stat * pstat)477*4882a593Smuzhiyun static struct page_stat *page_stat__find_page(struct page_stat *pstat)
478*4882a593Smuzhiyun {
479*4882a593Smuzhiyun 	return __page_stat__findnew_page(pstat, false);
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun 
page_stat__findnew_page(struct page_stat * pstat)482*4882a593Smuzhiyun static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun 	return __page_stat__findnew_page(pstat, true);
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun static struct page_stat *
__page_stat__findnew_alloc(struct page_stat * pstat,bool create)488*4882a593Smuzhiyun __page_stat__findnew_alloc(struct page_stat *pstat, bool create)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun 	struct rb_node **node = &page_alloc_tree.rb_node;
491*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
492*4882a593Smuzhiyun 	struct page_stat *data;
493*4882a593Smuzhiyun 	struct sort_dimension *sort;
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	while (*node) {
496*4882a593Smuzhiyun 		int cmp = 0;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 		parent = *node;
499*4882a593Smuzhiyun 		data = rb_entry(*node, struct page_stat, node);
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 		list_for_each_entry(sort, &page_alloc_sort_input, list) {
502*4882a593Smuzhiyun 			cmp = sort->cmp(pstat, data);
503*4882a593Smuzhiyun 			if (cmp)
504*4882a593Smuzhiyun 				break;
505*4882a593Smuzhiyun 		}
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 		if (cmp < 0)
508*4882a593Smuzhiyun 			node = &parent->rb_left;
509*4882a593Smuzhiyun 		else if (cmp > 0)
510*4882a593Smuzhiyun 			node = &parent->rb_right;
511*4882a593Smuzhiyun 		else
512*4882a593Smuzhiyun 			return data;
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	if (!create)
516*4882a593Smuzhiyun 		return NULL;
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	data = zalloc(sizeof(*data));
519*4882a593Smuzhiyun 	if (data != NULL) {
520*4882a593Smuzhiyun 		data->page = pstat->page;
521*4882a593Smuzhiyun 		data->order = pstat->order;
522*4882a593Smuzhiyun 		data->gfp_flags = pstat->gfp_flags;
523*4882a593Smuzhiyun 		data->migrate_type = pstat->migrate_type;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 		rb_link_node(&data->node, parent, node);
526*4882a593Smuzhiyun 		rb_insert_color(&data->node, &page_alloc_tree);
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	return data;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
page_stat__find_alloc(struct page_stat * pstat)532*4882a593Smuzhiyun static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	return __page_stat__findnew_alloc(pstat, false);
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun 
page_stat__findnew_alloc(struct page_stat * pstat)537*4882a593Smuzhiyun static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun 	return __page_stat__findnew_alloc(pstat, true);
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun static struct page_stat *
__page_stat__findnew_caller(struct page_stat * pstat,bool create)543*4882a593Smuzhiyun __page_stat__findnew_caller(struct page_stat *pstat, bool create)
544*4882a593Smuzhiyun {
545*4882a593Smuzhiyun 	struct rb_node **node = &page_caller_tree.rb_node;
546*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
547*4882a593Smuzhiyun 	struct page_stat *data;
548*4882a593Smuzhiyun 	struct sort_dimension *sort;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	while (*node) {
551*4882a593Smuzhiyun 		int cmp = 0;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 		parent = *node;
554*4882a593Smuzhiyun 		data = rb_entry(*node, struct page_stat, node);
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 		list_for_each_entry(sort, &page_caller_sort_input, list) {
557*4882a593Smuzhiyun 			cmp = sort->cmp(pstat, data);
558*4882a593Smuzhiyun 			if (cmp)
559*4882a593Smuzhiyun 				break;
560*4882a593Smuzhiyun 		}
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 		if (cmp < 0)
563*4882a593Smuzhiyun 			node = &parent->rb_left;
564*4882a593Smuzhiyun 		else if (cmp > 0)
565*4882a593Smuzhiyun 			node = &parent->rb_right;
566*4882a593Smuzhiyun 		else
567*4882a593Smuzhiyun 			return data;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	if (!create)
571*4882a593Smuzhiyun 		return NULL;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	data = zalloc(sizeof(*data));
574*4882a593Smuzhiyun 	if (data != NULL) {
575*4882a593Smuzhiyun 		data->callsite = pstat->callsite;
576*4882a593Smuzhiyun 		data->order = pstat->order;
577*4882a593Smuzhiyun 		data->gfp_flags = pstat->gfp_flags;
578*4882a593Smuzhiyun 		data->migrate_type = pstat->migrate_type;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 		rb_link_node(&data->node, parent, node);
581*4882a593Smuzhiyun 		rb_insert_color(&data->node, &page_caller_tree);
582*4882a593Smuzhiyun 	}
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	return data;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun 
page_stat__find_caller(struct page_stat * pstat)587*4882a593Smuzhiyun static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	return __page_stat__findnew_caller(pstat, false);
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun 
page_stat__findnew_caller(struct page_stat * pstat)592*4882a593Smuzhiyun static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun 	return __page_stat__findnew_caller(pstat, true);
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
valid_page(u64 pfn_or_page)597*4882a593Smuzhiyun static bool valid_page(u64 pfn_or_page)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	if (use_pfn && pfn_or_page == -1UL)
600*4882a593Smuzhiyun 		return false;
601*4882a593Smuzhiyun 	if (!use_pfn && pfn_or_page == 0)
602*4882a593Smuzhiyun 		return false;
603*4882a593Smuzhiyun 	return true;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun struct gfp_flag {
607*4882a593Smuzhiyun 	unsigned int flags;
608*4882a593Smuzhiyun 	char *compact_str;
609*4882a593Smuzhiyun 	char *human_readable;
610*4882a593Smuzhiyun };
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun static struct gfp_flag *gfps;
613*4882a593Smuzhiyun static int nr_gfps;
614*4882a593Smuzhiyun 
gfpcmp(const void * a,const void * b)615*4882a593Smuzhiyun static int gfpcmp(const void *a, const void *b)
616*4882a593Smuzhiyun {
617*4882a593Smuzhiyun 	const struct gfp_flag *fa = a;
618*4882a593Smuzhiyun 	const struct gfp_flag *fb = b;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	return fa->flags - fb->flags;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun /* see include/trace/events/mmflags.h */
624*4882a593Smuzhiyun static const struct {
625*4882a593Smuzhiyun 	const char *original;
626*4882a593Smuzhiyun 	const char *compact;
627*4882a593Smuzhiyun } gfp_compact_table[] = {
628*4882a593Smuzhiyun 	{ "GFP_TRANSHUGE",		"THP" },
629*4882a593Smuzhiyun 	{ "GFP_TRANSHUGE_LIGHT",	"THL" },
630*4882a593Smuzhiyun 	{ "GFP_HIGHUSER_MOVABLE",	"HUM" },
631*4882a593Smuzhiyun 	{ "GFP_HIGHUSER",		"HU" },
632*4882a593Smuzhiyun 	{ "GFP_USER",			"U" },
633*4882a593Smuzhiyun 	{ "GFP_KERNEL_ACCOUNT",		"KAC" },
634*4882a593Smuzhiyun 	{ "GFP_KERNEL",			"K" },
635*4882a593Smuzhiyun 	{ "GFP_NOFS",			"NF" },
636*4882a593Smuzhiyun 	{ "GFP_ATOMIC",			"A" },
637*4882a593Smuzhiyun 	{ "GFP_NOIO",			"NI" },
638*4882a593Smuzhiyun 	{ "GFP_NOWAIT",			"NW" },
639*4882a593Smuzhiyun 	{ "GFP_DMA",			"D" },
640*4882a593Smuzhiyun 	{ "__GFP_HIGHMEM",		"HM" },
641*4882a593Smuzhiyun 	{ "GFP_DMA32",			"D32" },
642*4882a593Smuzhiyun 	{ "__GFP_HIGH",			"H" },
643*4882a593Smuzhiyun 	{ "__GFP_ATOMIC",		"_A" },
644*4882a593Smuzhiyun 	{ "__GFP_IO",			"I" },
645*4882a593Smuzhiyun 	{ "__GFP_FS",			"F" },
646*4882a593Smuzhiyun 	{ "__GFP_NOWARN",		"NWR" },
647*4882a593Smuzhiyun 	{ "__GFP_RETRY_MAYFAIL",	"R" },
648*4882a593Smuzhiyun 	{ "__GFP_NOFAIL",		"NF" },
649*4882a593Smuzhiyun 	{ "__GFP_NORETRY",		"NR" },
650*4882a593Smuzhiyun 	{ "__GFP_COMP",			"C" },
651*4882a593Smuzhiyun 	{ "__GFP_ZERO",			"Z" },
652*4882a593Smuzhiyun 	{ "__GFP_NOMEMALLOC",		"NMA" },
653*4882a593Smuzhiyun 	{ "__GFP_MEMALLOC",		"MA" },
654*4882a593Smuzhiyun 	{ "__GFP_HARDWALL",		"HW" },
655*4882a593Smuzhiyun 	{ "__GFP_THISNODE",		"TN" },
656*4882a593Smuzhiyun 	{ "__GFP_RECLAIMABLE",		"RC" },
657*4882a593Smuzhiyun 	{ "__GFP_MOVABLE",		"M" },
658*4882a593Smuzhiyun 	{ "__GFP_ACCOUNT",		"AC" },
659*4882a593Smuzhiyun 	{ "__GFP_WRITE",		"WR" },
660*4882a593Smuzhiyun 	{ "__GFP_RECLAIM",		"R" },
661*4882a593Smuzhiyun 	{ "__GFP_DIRECT_RECLAIM",	"DR" },
662*4882a593Smuzhiyun 	{ "__GFP_KSWAPD_RECLAIM",	"KR" },
663*4882a593Smuzhiyun };
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun static size_t max_gfp_len;
666*4882a593Smuzhiyun 
compact_gfp_flags(char * gfp_flags)667*4882a593Smuzhiyun static char *compact_gfp_flags(char *gfp_flags)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	char *orig_flags = strdup(gfp_flags);
670*4882a593Smuzhiyun 	char *new_flags = NULL;
671*4882a593Smuzhiyun 	char *str, *pos = NULL;
672*4882a593Smuzhiyun 	size_t len = 0;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	if (orig_flags == NULL)
675*4882a593Smuzhiyun 		return NULL;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	str = strtok_r(orig_flags, "|", &pos);
678*4882a593Smuzhiyun 	while (str) {
679*4882a593Smuzhiyun 		size_t i;
680*4882a593Smuzhiyun 		char *new;
681*4882a593Smuzhiyun 		const char *cpt;
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
684*4882a593Smuzhiyun 			if (strcmp(gfp_compact_table[i].original, str))
685*4882a593Smuzhiyun 				continue;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 			cpt = gfp_compact_table[i].compact;
688*4882a593Smuzhiyun 			new = realloc(new_flags, len + strlen(cpt) + 2);
689*4882a593Smuzhiyun 			if (new == NULL) {
690*4882a593Smuzhiyun 				free(new_flags);
691*4882a593Smuzhiyun 				free(orig_flags);
692*4882a593Smuzhiyun 				return NULL;
693*4882a593Smuzhiyun 			}
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 			new_flags = new;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 			if (!len) {
698*4882a593Smuzhiyun 				strcpy(new_flags, cpt);
699*4882a593Smuzhiyun 			} else {
700*4882a593Smuzhiyun 				strcat(new_flags, "|");
701*4882a593Smuzhiyun 				strcat(new_flags, cpt);
702*4882a593Smuzhiyun 				len++;
703*4882a593Smuzhiyun 			}
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 			len += strlen(cpt);
706*4882a593Smuzhiyun 		}
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 		str = strtok_r(NULL, "|", &pos);
709*4882a593Smuzhiyun 	}
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	if (max_gfp_len < len)
712*4882a593Smuzhiyun 		max_gfp_len = len;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	free(orig_flags);
715*4882a593Smuzhiyun 	return new_flags;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun 
compact_gfp_string(unsigned long gfp_flags)718*4882a593Smuzhiyun static char *compact_gfp_string(unsigned long gfp_flags)
719*4882a593Smuzhiyun {
720*4882a593Smuzhiyun 	struct gfp_flag key = {
721*4882a593Smuzhiyun 		.flags = gfp_flags,
722*4882a593Smuzhiyun 	};
723*4882a593Smuzhiyun 	struct gfp_flag *gfp;
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 	gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
726*4882a593Smuzhiyun 	if (gfp)
727*4882a593Smuzhiyun 		return gfp->compact_str;
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	return NULL;
730*4882a593Smuzhiyun }
731*4882a593Smuzhiyun 
parse_gfp_flags(struct evsel * evsel,struct perf_sample * sample,unsigned int gfp_flags)732*4882a593Smuzhiyun static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
733*4882a593Smuzhiyun 			   unsigned int gfp_flags)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun 	struct tep_record record = {
736*4882a593Smuzhiyun 		.cpu = sample->cpu,
737*4882a593Smuzhiyun 		.data = sample->raw_data,
738*4882a593Smuzhiyun 		.size = sample->raw_size,
739*4882a593Smuzhiyun 	};
740*4882a593Smuzhiyun 	struct trace_seq seq;
741*4882a593Smuzhiyun 	char *str, *pos = NULL;
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 	if (nr_gfps) {
744*4882a593Smuzhiyun 		struct gfp_flag key = {
745*4882a593Smuzhiyun 			.flags = gfp_flags,
746*4882a593Smuzhiyun 		};
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 		if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
749*4882a593Smuzhiyun 			return 0;
750*4882a593Smuzhiyun 	}
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	trace_seq_init(&seq);
753*4882a593Smuzhiyun 	tep_print_event(evsel->tp_format->tep,
754*4882a593Smuzhiyun 			&seq, &record, "%s", TEP_PRINT_INFO);
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	str = strtok_r(seq.buffer, " ", &pos);
757*4882a593Smuzhiyun 	while (str) {
758*4882a593Smuzhiyun 		if (!strncmp(str, "gfp_flags=", 10)) {
759*4882a593Smuzhiyun 			struct gfp_flag *new;
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 			new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
762*4882a593Smuzhiyun 			if (new == NULL)
763*4882a593Smuzhiyun 				return -ENOMEM;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 			gfps = new;
766*4882a593Smuzhiyun 			new += nr_gfps++;
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 			new->flags = gfp_flags;
769*4882a593Smuzhiyun 			new->human_readable = strdup(str + 10);
770*4882a593Smuzhiyun 			new->compact_str = compact_gfp_flags(str + 10);
771*4882a593Smuzhiyun 			if (!new->human_readable || !new->compact_str)
772*4882a593Smuzhiyun 				return -ENOMEM;
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 			qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
775*4882a593Smuzhiyun 		}
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 		str = strtok_r(NULL, " ", &pos);
778*4882a593Smuzhiyun 	}
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	trace_seq_destroy(&seq);
781*4882a593Smuzhiyun 	return 0;
782*4882a593Smuzhiyun }
783*4882a593Smuzhiyun 
evsel__process_page_alloc_event(struct evsel * evsel,struct perf_sample * sample)784*4882a593Smuzhiyun static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample)
785*4882a593Smuzhiyun {
786*4882a593Smuzhiyun 	u64 page;
787*4882a593Smuzhiyun 	unsigned int order = evsel__intval(evsel, sample, "order");
788*4882a593Smuzhiyun 	unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags");
789*4882a593Smuzhiyun 	unsigned int migrate_type = evsel__intval(evsel, sample,
790*4882a593Smuzhiyun 						       "migratetype");
791*4882a593Smuzhiyun 	u64 bytes = kmem_page_size << order;
792*4882a593Smuzhiyun 	u64 callsite;
793*4882a593Smuzhiyun 	struct page_stat *pstat;
794*4882a593Smuzhiyun 	struct page_stat this = {
795*4882a593Smuzhiyun 		.order = order,
796*4882a593Smuzhiyun 		.gfp_flags = gfp_flags,
797*4882a593Smuzhiyun 		.migrate_type = migrate_type,
798*4882a593Smuzhiyun 	};
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 	if (use_pfn)
801*4882a593Smuzhiyun 		page = evsel__intval(evsel, sample, "pfn");
802*4882a593Smuzhiyun 	else
803*4882a593Smuzhiyun 		page = evsel__intval(evsel, sample, "page");
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	nr_page_allocs++;
806*4882a593Smuzhiyun 	total_page_alloc_bytes += bytes;
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 	if (!valid_page(page)) {
809*4882a593Smuzhiyun 		nr_page_fails++;
810*4882a593Smuzhiyun 		total_page_fail_bytes += bytes;
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 		return 0;
813*4882a593Smuzhiyun 	}
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
816*4882a593Smuzhiyun 		return -1;
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	callsite = find_callsite(evsel, sample);
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun 	/*
821*4882a593Smuzhiyun 	 * This is to find the current page (with correct gfp flags and
822*4882a593Smuzhiyun 	 * migrate type) at free event.
823*4882a593Smuzhiyun 	 */
824*4882a593Smuzhiyun 	this.page = page;
825*4882a593Smuzhiyun 	pstat = page_stat__findnew_page(&this);
826*4882a593Smuzhiyun 	if (pstat == NULL)
827*4882a593Smuzhiyun 		return -ENOMEM;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	pstat->nr_alloc++;
830*4882a593Smuzhiyun 	pstat->alloc_bytes += bytes;
831*4882a593Smuzhiyun 	pstat->callsite = callsite;
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	if (!live_page) {
834*4882a593Smuzhiyun 		pstat = page_stat__findnew_alloc(&this);
835*4882a593Smuzhiyun 		if (pstat == NULL)
836*4882a593Smuzhiyun 			return -ENOMEM;
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 		pstat->nr_alloc++;
839*4882a593Smuzhiyun 		pstat->alloc_bytes += bytes;
840*4882a593Smuzhiyun 		pstat->callsite = callsite;
841*4882a593Smuzhiyun 	}
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	this.callsite = callsite;
844*4882a593Smuzhiyun 	pstat = page_stat__findnew_caller(&this);
845*4882a593Smuzhiyun 	if (pstat == NULL)
846*4882a593Smuzhiyun 		return -ENOMEM;
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	pstat->nr_alloc++;
849*4882a593Smuzhiyun 	pstat->alloc_bytes += bytes;
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	order_stats[order][migrate_type]++;
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	return 0;
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun 
evsel__process_page_free_event(struct evsel * evsel,struct perf_sample * sample)856*4882a593Smuzhiyun static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample)
857*4882a593Smuzhiyun {
858*4882a593Smuzhiyun 	u64 page;
859*4882a593Smuzhiyun 	unsigned int order = evsel__intval(evsel, sample, "order");
860*4882a593Smuzhiyun 	u64 bytes = kmem_page_size << order;
861*4882a593Smuzhiyun 	struct page_stat *pstat;
862*4882a593Smuzhiyun 	struct page_stat this = {
863*4882a593Smuzhiyun 		.order = order,
864*4882a593Smuzhiyun 	};
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	if (use_pfn)
867*4882a593Smuzhiyun 		page = evsel__intval(evsel, sample, "pfn");
868*4882a593Smuzhiyun 	else
869*4882a593Smuzhiyun 		page = evsel__intval(evsel, sample, "page");
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun 	nr_page_frees++;
872*4882a593Smuzhiyun 	total_page_free_bytes += bytes;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	this.page = page;
875*4882a593Smuzhiyun 	pstat = page_stat__find_page(&this);
876*4882a593Smuzhiyun 	if (pstat == NULL) {
877*4882a593Smuzhiyun 		pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
878*4882a593Smuzhiyun 			  page, order);
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 		nr_page_nomatch++;
881*4882a593Smuzhiyun 		total_page_nomatch_bytes += bytes;
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun 		return 0;
884*4882a593Smuzhiyun 	}
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	this.gfp_flags = pstat->gfp_flags;
887*4882a593Smuzhiyun 	this.migrate_type = pstat->migrate_type;
888*4882a593Smuzhiyun 	this.callsite = pstat->callsite;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	rb_erase(&pstat->node, &page_live_tree);
891*4882a593Smuzhiyun 	free(pstat);
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	if (live_page) {
894*4882a593Smuzhiyun 		order_stats[this.order][this.migrate_type]--;
895*4882a593Smuzhiyun 	} else {
896*4882a593Smuzhiyun 		pstat = page_stat__find_alloc(&this);
897*4882a593Smuzhiyun 		if (pstat == NULL)
898*4882a593Smuzhiyun 			return -ENOMEM;
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 		pstat->nr_free++;
901*4882a593Smuzhiyun 		pstat->free_bytes += bytes;
902*4882a593Smuzhiyun 	}
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun 	pstat = page_stat__find_caller(&this);
905*4882a593Smuzhiyun 	if (pstat == NULL)
906*4882a593Smuzhiyun 		return -ENOENT;
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 	pstat->nr_free++;
909*4882a593Smuzhiyun 	pstat->free_bytes += bytes;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	if (live_page) {
912*4882a593Smuzhiyun 		pstat->nr_alloc--;
913*4882a593Smuzhiyun 		pstat->alloc_bytes -= bytes;
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 		if (pstat->nr_alloc == 0) {
916*4882a593Smuzhiyun 			rb_erase(&pstat->node, &page_caller_tree);
917*4882a593Smuzhiyun 			free(pstat);
918*4882a593Smuzhiyun 		}
919*4882a593Smuzhiyun 	}
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	return 0;
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun 
perf_kmem__skip_sample(struct perf_sample * sample)924*4882a593Smuzhiyun static bool perf_kmem__skip_sample(struct perf_sample *sample)
925*4882a593Smuzhiyun {
926*4882a593Smuzhiyun 	/* skip sample based on time? */
927*4882a593Smuzhiyun 	if (perf_time__skip_sample(&ptime, sample->time))
928*4882a593Smuzhiyun 		return true;
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	return false;
931*4882a593Smuzhiyun }
932*4882a593Smuzhiyun 
933*4882a593Smuzhiyun typedef int (*tracepoint_handler)(struct evsel *evsel,
934*4882a593Smuzhiyun 				  struct perf_sample *sample);
935*4882a593Smuzhiyun 
process_sample_event(struct perf_tool * tool __maybe_unused,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine)936*4882a593Smuzhiyun static int process_sample_event(struct perf_tool *tool __maybe_unused,
937*4882a593Smuzhiyun 				union perf_event *event,
938*4882a593Smuzhiyun 				struct perf_sample *sample,
939*4882a593Smuzhiyun 				struct evsel *evsel,
940*4882a593Smuzhiyun 				struct machine *machine)
941*4882a593Smuzhiyun {
942*4882a593Smuzhiyun 	int err = 0;
943*4882a593Smuzhiyun 	struct thread *thread = machine__findnew_thread(machine, sample->pid,
944*4882a593Smuzhiyun 							sample->tid);
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	if (thread == NULL) {
947*4882a593Smuzhiyun 		pr_debug("problem processing %d event, skipping it.\n",
948*4882a593Smuzhiyun 			 event->header.type);
949*4882a593Smuzhiyun 		return -1;
950*4882a593Smuzhiyun 	}
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	if (perf_kmem__skip_sample(sample))
953*4882a593Smuzhiyun 		return 0;
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun 	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	if (evsel->handler != NULL) {
958*4882a593Smuzhiyun 		tracepoint_handler f = evsel->handler;
959*4882a593Smuzhiyun 		err = f(evsel, sample);
960*4882a593Smuzhiyun 	}
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun 	thread__put(thread);
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 	return err;
965*4882a593Smuzhiyun }
966*4882a593Smuzhiyun 
967*4882a593Smuzhiyun static struct perf_tool perf_kmem = {
968*4882a593Smuzhiyun 	.sample		 = process_sample_event,
969*4882a593Smuzhiyun 	.comm		 = perf_event__process_comm,
970*4882a593Smuzhiyun 	.mmap		 = perf_event__process_mmap,
971*4882a593Smuzhiyun 	.mmap2		 = perf_event__process_mmap2,
972*4882a593Smuzhiyun 	.namespaces	 = perf_event__process_namespaces,
973*4882a593Smuzhiyun 	.ordered_events	 = true,
974*4882a593Smuzhiyun };
975*4882a593Smuzhiyun 
fragmentation(unsigned long n_req,unsigned long n_alloc)976*4882a593Smuzhiyun static double fragmentation(unsigned long n_req, unsigned long n_alloc)
977*4882a593Smuzhiyun {
978*4882a593Smuzhiyun 	if (n_alloc == 0)
979*4882a593Smuzhiyun 		return 0.0;
980*4882a593Smuzhiyun 	else
981*4882a593Smuzhiyun 		return 100.0 - (100.0 * n_req / n_alloc);
982*4882a593Smuzhiyun }
983*4882a593Smuzhiyun 
__print_slab_result(struct rb_root * root,struct perf_session * session,int n_lines,int is_caller)984*4882a593Smuzhiyun static void __print_slab_result(struct rb_root *root,
985*4882a593Smuzhiyun 				struct perf_session *session,
986*4882a593Smuzhiyun 				int n_lines, int is_caller)
987*4882a593Smuzhiyun {
988*4882a593Smuzhiyun 	struct rb_node *next;
989*4882a593Smuzhiyun 	struct machine *machine = &session->machines.host;
990*4882a593Smuzhiyun 
991*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
992*4882a593Smuzhiyun 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
993*4882a593Smuzhiyun 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
994*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun 	next = rb_first(root);
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun 	while (next && n_lines--) {
999*4882a593Smuzhiyun 		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
1000*4882a593Smuzhiyun 						   node);
1001*4882a593Smuzhiyun 		struct symbol *sym = NULL;
1002*4882a593Smuzhiyun 		struct map *map;
1003*4882a593Smuzhiyun 		char buf[BUFSIZ];
1004*4882a593Smuzhiyun 		u64 addr;
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun 		if (is_caller) {
1007*4882a593Smuzhiyun 			addr = data->call_site;
1008*4882a593Smuzhiyun 			if (!raw_ip)
1009*4882a593Smuzhiyun 				sym = machine__find_kernel_symbol(machine, addr, &map);
1010*4882a593Smuzhiyun 		} else
1011*4882a593Smuzhiyun 			addr = data->ptr;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 		if (sym != NULL)
1014*4882a593Smuzhiyun 			snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
1015*4882a593Smuzhiyun 				 addr - map->unmap_ip(map, sym->start));
1016*4882a593Smuzhiyun 		else
1017*4882a593Smuzhiyun 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
1018*4882a593Smuzhiyun 		printf(" %-34s |", buf);
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun 		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
1021*4882a593Smuzhiyun 		       (unsigned long long)data->bytes_alloc,
1022*4882a593Smuzhiyun 		       (unsigned long)data->bytes_alloc / data->hit,
1023*4882a593Smuzhiyun 		       (unsigned long long)data->bytes_req,
1024*4882a593Smuzhiyun 		       (unsigned long)data->bytes_req / data->hit,
1025*4882a593Smuzhiyun 		       (unsigned long)data->hit,
1026*4882a593Smuzhiyun 		       (unsigned long)data->pingpong,
1027*4882a593Smuzhiyun 		       fragmentation(data->bytes_req, data->bytes_alloc));
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 		next = rb_next(next);
1030*4882a593Smuzhiyun 	}
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun 	if (n_lines == -1)
1033*4882a593Smuzhiyun 		printf(" ...                                | ...             | ...             | ...      | ...       | ...   \n");
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
1036*4882a593Smuzhiyun }
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun static const char * const migrate_type_str[] = {
1039*4882a593Smuzhiyun 	"UNMOVABL",
1040*4882a593Smuzhiyun 	"RECLAIM",
1041*4882a593Smuzhiyun 	"MOVABLE",
1042*4882a593Smuzhiyun 	"RESERVED",
1043*4882a593Smuzhiyun 	"CMA/ISLT",
1044*4882a593Smuzhiyun 	"UNKNOWN",
1045*4882a593Smuzhiyun };
1046*4882a593Smuzhiyun 
__print_page_alloc_result(struct perf_session * session,int n_lines)1047*4882a593Smuzhiyun static void __print_page_alloc_result(struct perf_session *session, int n_lines)
1048*4882a593Smuzhiyun {
1049*4882a593Smuzhiyun 	struct rb_node *next = rb_first(&page_alloc_sorted);
1050*4882a593Smuzhiyun 	struct machine *machine = &session->machines.host;
1051*4882a593Smuzhiyun 	const char *format;
1052*4882a593Smuzhiyun 	int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1053*4882a593Smuzhiyun 
1054*4882a593Smuzhiyun 	printf("\n%.105s\n", graph_dotted_line);
1055*4882a593Smuzhiyun 	printf(" %-16s | %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
1056*4882a593Smuzhiyun 	       use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
1057*4882a593Smuzhiyun 	       gfp_len, "GFP flags");
1058*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	if (use_pfn)
1061*4882a593Smuzhiyun 		format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
1062*4882a593Smuzhiyun 	else
1063*4882a593Smuzhiyun 		format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
1064*4882a593Smuzhiyun 
1065*4882a593Smuzhiyun 	while (next && n_lines--) {
1066*4882a593Smuzhiyun 		struct page_stat *data;
1067*4882a593Smuzhiyun 		struct symbol *sym;
1068*4882a593Smuzhiyun 		struct map *map;
1069*4882a593Smuzhiyun 		char buf[32];
1070*4882a593Smuzhiyun 		char *caller = buf;
1071*4882a593Smuzhiyun 
1072*4882a593Smuzhiyun 		data = rb_entry(next, struct page_stat, node);
1073*4882a593Smuzhiyun 		sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1074*4882a593Smuzhiyun 		if (sym)
1075*4882a593Smuzhiyun 			caller = sym->name;
1076*4882a593Smuzhiyun 		else
1077*4882a593Smuzhiyun 			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun 		printf(format, (unsigned long long)data->page,
1080*4882a593Smuzhiyun 		       (unsigned long long)data->alloc_bytes / 1024,
1081*4882a593Smuzhiyun 		       data->nr_alloc, data->order,
1082*4882a593Smuzhiyun 		       migrate_type_str[data->migrate_type],
1083*4882a593Smuzhiyun 		       gfp_len, compact_gfp_string(data->gfp_flags), caller);
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 		next = rb_next(next);
1086*4882a593Smuzhiyun 	}
1087*4882a593Smuzhiyun 
1088*4882a593Smuzhiyun 	if (n_lines == -1) {
1089*4882a593Smuzhiyun 		printf(" ...              | ...              | ...       | ...   | ...      | %-*s | ...\n",
1090*4882a593Smuzhiyun 		       gfp_len, "...");
1091*4882a593Smuzhiyun 	}
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
1094*4882a593Smuzhiyun }
1095*4882a593Smuzhiyun 
__print_page_caller_result(struct perf_session * session,int n_lines)1096*4882a593Smuzhiyun static void __print_page_caller_result(struct perf_session *session, int n_lines)
1097*4882a593Smuzhiyun {
1098*4882a593Smuzhiyun 	struct rb_node *next = rb_first(&page_caller_sorted);
1099*4882a593Smuzhiyun 	struct machine *machine = &session->machines.host;
1100*4882a593Smuzhiyun 	int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun 	printf("\n%.105s\n", graph_dotted_line);
1103*4882a593Smuzhiyun 	printf(" %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
1104*4882a593Smuzhiyun 	       live_page ? "Live" : "Total", gfp_len, "GFP flags");
1105*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	while (next && n_lines--) {
1108*4882a593Smuzhiyun 		struct page_stat *data;
1109*4882a593Smuzhiyun 		struct symbol *sym;
1110*4882a593Smuzhiyun 		struct map *map;
1111*4882a593Smuzhiyun 		char buf[32];
1112*4882a593Smuzhiyun 		char *caller = buf;
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun 		data = rb_entry(next, struct page_stat, node);
1115*4882a593Smuzhiyun 		sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1116*4882a593Smuzhiyun 		if (sym)
1117*4882a593Smuzhiyun 			caller = sym->name;
1118*4882a593Smuzhiyun 		else
1119*4882a593Smuzhiyun 			scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun 		printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
1122*4882a593Smuzhiyun 		       (unsigned long long)data->alloc_bytes / 1024,
1123*4882a593Smuzhiyun 		       data->nr_alloc, data->order,
1124*4882a593Smuzhiyun 		       migrate_type_str[data->migrate_type],
1125*4882a593Smuzhiyun 		       gfp_len, compact_gfp_string(data->gfp_flags), caller);
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 		next = rb_next(next);
1128*4882a593Smuzhiyun 	}
1129*4882a593Smuzhiyun 
1130*4882a593Smuzhiyun 	if (n_lines == -1) {
1131*4882a593Smuzhiyun 		printf(" ...              | ...       | ...   | ...      | %-*s | ...\n",
1132*4882a593Smuzhiyun 		       gfp_len, "...");
1133*4882a593Smuzhiyun 	}
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun 	printf("%.105s\n", graph_dotted_line);
1136*4882a593Smuzhiyun }
1137*4882a593Smuzhiyun 
print_gfp_flags(void)1138*4882a593Smuzhiyun static void print_gfp_flags(void)
1139*4882a593Smuzhiyun {
1140*4882a593Smuzhiyun 	int i;
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun 	printf("#\n");
1143*4882a593Smuzhiyun 	printf("# GFP flags\n");
1144*4882a593Smuzhiyun 	printf("# ---------\n");
1145*4882a593Smuzhiyun 	for (i = 0; i < nr_gfps; i++) {
1146*4882a593Smuzhiyun 		printf("# %08x: %*s: %s\n", gfps[i].flags,
1147*4882a593Smuzhiyun 		       (int) max_gfp_len, gfps[i].compact_str,
1148*4882a593Smuzhiyun 		       gfps[i].human_readable);
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun }
1151*4882a593Smuzhiyun 
print_slab_summary(void)1152*4882a593Smuzhiyun static void print_slab_summary(void)
1153*4882a593Smuzhiyun {
1154*4882a593Smuzhiyun 	printf("\nSUMMARY (SLAB allocator)");
1155*4882a593Smuzhiyun 	printf("\n========================\n");
1156*4882a593Smuzhiyun 	printf("Total bytes requested: %'lu\n", total_requested);
1157*4882a593Smuzhiyun 	printf("Total bytes allocated: %'lu\n", total_allocated);
1158*4882a593Smuzhiyun 	printf("Total bytes freed:     %'lu\n", total_freed);
1159*4882a593Smuzhiyun 	if (total_allocated > total_freed) {
1160*4882a593Smuzhiyun 		printf("Net total bytes allocated: %'lu\n",
1161*4882a593Smuzhiyun 		total_allocated - total_freed);
1162*4882a593Smuzhiyun 	}
1163*4882a593Smuzhiyun 	printf("Total bytes wasted on internal fragmentation: %'lu\n",
1164*4882a593Smuzhiyun 	       total_allocated - total_requested);
1165*4882a593Smuzhiyun 	printf("Internal fragmentation: %f%%\n",
1166*4882a593Smuzhiyun 	       fragmentation(total_requested, total_allocated));
1167*4882a593Smuzhiyun 	printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
1168*4882a593Smuzhiyun }
1169*4882a593Smuzhiyun 
print_page_summary(void)1170*4882a593Smuzhiyun static void print_page_summary(void)
1171*4882a593Smuzhiyun {
1172*4882a593Smuzhiyun 	int o, m;
1173*4882a593Smuzhiyun 	u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
1174*4882a593Smuzhiyun 	u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 	printf("\nSUMMARY (page allocator)");
1177*4882a593Smuzhiyun 	printf("\n========================\n");
1178*4882a593Smuzhiyun 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation requests",
1179*4882a593Smuzhiyun 	       nr_page_allocs, total_page_alloc_bytes / 1024);
1180*4882a593Smuzhiyun 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free requests",
1181*4882a593Smuzhiyun 	       nr_page_frees, total_page_free_bytes / 1024);
1182*4882a593Smuzhiyun 	printf("\n");
1183*4882a593Smuzhiyun 
1184*4882a593Smuzhiyun 	printf("%-30s: %'16"PRIu64"   [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
1185*4882a593Smuzhiyun 	       nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
1186*4882a593Smuzhiyun 	printf("%-30s: %'16"PRIu64"   [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
1187*4882a593Smuzhiyun 	       nr_page_allocs - nr_alloc_freed,
1188*4882a593Smuzhiyun 	       (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
1189*4882a593Smuzhiyun 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free-only requests",
1190*4882a593Smuzhiyun 	       nr_page_nomatch, total_page_nomatch_bytes / 1024);
1191*4882a593Smuzhiyun 	printf("\n");
1192*4882a593Smuzhiyun 
1193*4882a593Smuzhiyun 	printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation failures",
1194*4882a593Smuzhiyun 	       nr_page_fails, total_page_fail_bytes / 1024);
1195*4882a593Smuzhiyun 	printf("\n");
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun 	printf("%5s  %12s  %12s  %12s  %12s  %12s\n", "Order",  "Unmovable",
1198*4882a593Smuzhiyun 	       "Reclaimable", "Movable", "Reserved", "CMA/Isolated");
1199*4882a593Smuzhiyun 	printf("%.5s  %.12s  %.12s  %.12s  %.12s  %.12s\n", graph_dotted_line,
1200*4882a593Smuzhiyun 	       graph_dotted_line, graph_dotted_line, graph_dotted_line,
1201*4882a593Smuzhiyun 	       graph_dotted_line, graph_dotted_line);
1202*4882a593Smuzhiyun 
1203*4882a593Smuzhiyun 	for (o = 0; o < MAX_PAGE_ORDER; o++) {
1204*4882a593Smuzhiyun 		printf("%5d", o);
1205*4882a593Smuzhiyun 		for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
1206*4882a593Smuzhiyun 			if (order_stats[o][m])
1207*4882a593Smuzhiyun 				printf("  %'12d", order_stats[o][m]);
1208*4882a593Smuzhiyun 			else
1209*4882a593Smuzhiyun 				printf("  %12c", '.');
1210*4882a593Smuzhiyun 		}
1211*4882a593Smuzhiyun 		printf("\n");
1212*4882a593Smuzhiyun 	}
1213*4882a593Smuzhiyun }
1214*4882a593Smuzhiyun 
print_slab_result(struct perf_session * session)1215*4882a593Smuzhiyun static void print_slab_result(struct perf_session *session)
1216*4882a593Smuzhiyun {
1217*4882a593Smuzhiyun 	if (caller_flag)
1218*4882a593Smuzhiyun 		__print_slab_result(&root_caller_sorted, session, caller_lines, 1);
1219*4882a593Smuzhiyun 	if (alloc_flag)
1220*4882a593Smuzhiyun 		__print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
1221*4882a593Smuzhiyun 	print_slab_summary();
1222*4882a593Smuzhiyun }
1223*4882a593Smuzhiyun 
print_page_result(struct perf_session * session)1224*4882a593Smuzhiyun static void print_page_result(struct perf_session *session)
1225*4882a593Smuzhiyun {
1226*4882a593Smuzhiyun 	if (caller_flag || alloc_flag)
1227*4882a593Smuzhiyun 		print_gfp_flags();
1228*4882a593Smuzhiyun 	if (caller_flag)
1229*4882a593Smuzhiyun 		__print_page_caller_result(session, caller_lines);
1230*4882a593Smuzhiyun 	if (alloc_flag)
1231*4882a593Smuzhiyun 		__print_page_alloc_result(session, alloc_lines);
1232*4882a593Smuzhiyun 	print_page_summary();
1233*4882a593Smuzhiyun }
1234*4882a593Smuzhiyun 
print_result(struct perf_session * session)1235*4882a593Smuzhiyun static void print_result(struct perf_session *session)
1236*4882a593Smuzhiyun {
1237*4882a593Smuzhiyun 	if (kmem_slab)
1238*4882a593Smuzhiyun 		print_slab_result(session);
1239*4882a593Smuzhiyun 	if (kmem_page)
1240*4882a593Smuzhiyun 		print_page_result(session);
1241*4882a593Smuzhiyun }
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun static LIST_HEAD(slab_caller_sort);
1244*4882a593Smuzhiyun static LIST_HEAD(slab_alloc_sort);
1245*4882a593Smuzhiyun static LIST_HEAD(page_caller_sort);
1246*4882a593Smuzhiyun static LIST_HEAD(page_alloc_sort);
1247*4882a593Smuzhiyun 
sort_slab_insert(struct rb_root * root,struct alloc_stat * data,struct list_head * sort_list)1248*4882a593Smuzhiyun static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
1249*4882a593Smuzhiyun 			     struct list_head *sort_list)
1250*4882a593Smuzhiyun {
1251*4882a593Smuzhiyun 	struct rb_node **new = &(root->rb_node);
1252*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
1253*4882a593Smuzhiyun 	struct sort_dimension *sort;
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 	while (*new) {
1256*4882a593Smuzhiyun 		struct alloc_stat *this;
1257*4882a593Smuzhiyun 		int cmp = 0;
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun 		this = rb_entry(*new, struct alloc_stat, node);
1260*4882a593Smuzhiyun 		parent = *new;
1261*4882a593Smuzhiyun 
1262*4882a593Smuzhiyun 		list_for_each_entry(sort, sort_list, list) {
1263*4882a593Smuzhiyun 			cmp = sort->cmp(data, this);
1264*4882a593Smuzhiyun 			if (cmp)
1265*4882a593Smuzhiyun 				break;
1266*4882a593Smuzhiyun 		}
1267*4882a593Smuzhiyun 
1268*4882a593Smuzhiyun 		if (cmp > 0)
1269*4882a593Smuzhiyun 			new = &((*new)->rb_left);
1270*4882a593Smuzhiyun 		else
1271*4882a593Smuzhiyun 			new = &((*new)->rb_right);
1272*4882a593Smuzhiyun 	}
1273*4882a593Smuzhiyun 
1274*4882a593Smuzhiyun 	rb_link_node(&data->node, parent, new);
1275*4882a593Smuzhiyun 	rb_insert_color(&data->node, root);
1276*4882a593Smuzhiyun }
1277*4882a593Smuzhiyun 
__sort_slab_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)1278*4882a593Smuzhiyun static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
1279*4882a593Smuzhiyun 			       struct list_head *sort_list)
1280*4882a593Smuzhiyun {
1281*4882a593Smuzhiyun 	struct rb_node *node;
1282*4882a593Smuzhiyun 	struct alloc_stat *data;
1283*4882a593Smuzhiyun 
1284*4882a593Smuzhiyun 	for (;;) {
1285*4882a593Smuzhiyun 		node = rb_first(root);
1286*4882a593Smuzhiyun 		if (!node)
1287*4882a593Smuzhiyun 			break;
1288*4882a593Smuzhiyun 
1289*4882a593Smuzhiyun 		rb_erase(node, root);
1290*4882a593Smuzhiyun 		data = rb_entry(node, struct alloc_stat, node);
1291*4882a593Smuzhiyun 		sort_slab_insert(root_sorted, data, sort_list);
1292*4882a593Smuzhiyun 	}
1293*4882a593Smuzhiyun }
1294*4882a593Smuzhiyun 
sort_page_insert(struct rb_root * root,struct page_stat * data,struct list_head * sort_list)1295*4882a593Smuzhiyun static void sort_page_insert(struct rb_root *root, struct page_stat *data,
1296*4882a593Smuzhiyun 			     struct list_head *sort_list)
1297*4882a593Smuzhiyun {
1298*4882a593Smuzhiyun 	struct rb_node **new = &root->rb_node;
1299*4882a593Smuzhiyun 	struct rb_node *parent = NULL;
1300*4882a593Smuzhiyun 	struct sort_dimension *sort;
1301*4882a593Smuzhiyun 
1302*4882a593Smuzhiyun 	while (*new) {
1303*4882a593Smuzhiyun 		struct page_stat *this;
1304*4882a593Smuzhiyun 		int cmp = 0;
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 		this = rb_entry(*new, struct page_stat, node);
1307*4882a593Smuzhiyun 		parent = *new;
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun 		list_for_each_entry(sort, sort_list, list) {
1310*4882a593Smuzhiyun 			cmp = sort->cmp(data, this);
1311*4882a593Smuzhiyun 			if (cmp)
1312*4882a593Smuzhiyun 				break;
1313*4882a593Smuzhiyun 		}
1314*4882a593Smuzhiyun 
1315*4882a593Smuzhiyun 		if (cmp > 0)
1316*4882a593Smuzhiyun 			new = &parent->rb_left;
1317*4882a593Smuzhiyun 		else
1318*4882a593Smuzhiyun 			new = &parent->rb_right;
1319*4882a593Smuzhiyun 	}
1320*4882a593Smuzhiyun 
1321*4882a593Smuzhiyun 	rb_link_node(&data->node, parent, new);
1322*4882a593Smuzhiyun 	rb_insert_color(&data->node, root);
1323*4882a593Smuzhiyun }
1324*4882a593Smuzhiyun 
__sort_page_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)1325*4882a593Smuzhiyun static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
1326*4882a593Smuzhiyun 			       struct list_head *sort_list)
1327*4882a593Smuzhiyun {
1328*4882a593Smuzhiyun 	struct rb_node *node;
1329*4882a593Smuzhiyun 	struct page_stat *data;
1330*4882a593Smuzhiyun 
1331*4882a593Smuzhiyun 	for (;;) {
1332*4882a593Smuzhiyun 		node = rb_first(root);
1333*4882a593Smuzhiyun 		if (!node)
1334*4882a593Smuzhiyun 			break;
1335*4882a593Smuzhiyun 
1336*4882a593Smuzhiyun 		rb_erase(node, root);
1337*4882a593Smuzhiyun 		data = rb_entry(node, struct page_stat, node);
1338*4882a593Smuzhiyun 		sort_page_insert(root_sorted, data, sort_list);
1339*4882a593Smuzhiyun 	}
1340*4882a593Smuzhiyun }
1341*4882a593Smuzhiyun 
sort_result(void)1342*4882a593Smuzhiyun static void sort_result(void)
1343*4882a593Smuzhiyun {
1344*4882a593Smuzhiyun 	if (kmem_slab) {
1345*4882a593Smuzhiyun 		__sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
1346*4882a593Smuzhiyun 				   &slab_alloc_sort);
1347*4882a593Smuzhiyun 		__sort_slab_result(&root_caller_stat, &root_caller_sorted,
1348*4882a593Smuzhiyun 				   &slab_caller_sort);
1349*4882a593Smuzhiyun 	}
1350*4882a593Smuzhiyun 	if (kmem_page) {
1351*4882a593Smuzhiyun 		if (live_page)
1352*4882a593Smuzhiyun 			__sort_page_result(&page_live_tree, &page_alloc_sorted,
1353*4882a593Smuzhiyun 					   &page_alloc_sort);
1354*4882a593Smuzhiyun 		else
1355*4882a593Smuzhiyun 			__sort_page_result(&page_alloc_tree, &page_alloc_sorted,
1356*4882a593Smuzhiyun 					   &page_alloc_sort);
1357*4882a593Smuzhiyun 
1358*4882a593Smuzhiyun 		__sort_page_result(&page_caller_tree, &page_caller_sorted,
1359*4882a593Smuzhiyun 				   &page_caller_sort);
1360*4882a593Smuzhiyun 	}
1361*4882a593Smuzhiyun }
1362*4882a593Smuzhiyun 
__cmd_kmem(struct perf_session * session)1363*4882a593Smuzhiyun static int __cmd_kmem(struct perf_session *session)
1364*4882a593Smuzhiyun {
1365*4882a593Smuzhiyun 	int err = -EINVAL;
1366*4882a593Smuzhiyun 	struct evsel *evsel;
1367*4882a593Smuzhiyun 	const struct evsel_str_handler kmem_tracepoints[] = {
1368*4882a593Smuzhiyun 		/* slab allocator */
1369*4882a593Smuzhiyun 		{ "kmem:kmalloc",		evsel__process_alloc_event, },
1370*4882a593Smuzhiyun 		{ "kmem:kmem_cache_alloc",	evsel__process_alloc_event, },
1371*4882a593Smuzhiyun 		{ "kmem:kmalloc_node",		evsel__process_alloc_node_event, },
1372*4882a593Smuzhiyun 		{ "kmem:kmem_cache_alloc_node", evsel__process_alloc_node_event, },
1373*4882a593Smuzhiyun 		{ "kmem:kfree",			evsel__process_free_event, },
1374*4882a593Smuzhiyun 		{ "kmem:kmem_cache_free",	evsel__process_free_event, },
1375*4882a593Smuzhiyun 		/* page allocator */
1376*4882a593Smuzhiyun 		{ "kmem:mm_page_alloc",		evsel__process_page_alloc_event, },
1377*4882a593Smuzhiyun 		{ "kmem:mm_page_free",		evsel__process_page_free_event, },
1378*4882a593Smuzhiyun 	};
1379*4882a593Smuzhiyun 
1380*4882a593Smuzhiyun 	if (!perf_session__has_traces(session, "kmem record"))
1381*4882a593Smuzhiyun 		goto out;
1382*4882a593Smuzhiyun 
1383*4882a593Smuzhiyun 	if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
1384*4882a593Smuzhiyun 		pr_err("Initializing perf session tracepoint handlers failed\n");
1385*4882a593Smuzhiyun 		goto out;
1386*4882a593Smuzhiyun 	}
1387*4882a593Smuzhiyun 
1388*4882a593Smuzhiyun 	evlist__for_each_entry(session->evlist, evsel) {
1389*4882a593Smuzhiyun 		if (!strcmp(evsel__name(evsel), "kmem:mm_page_alloc") &&
1390*4882a593Smuzhiyun 		    evsel__field(evsel, "pfn")) {
1391*4882a593Smuzhiyun 			use_pfn = true;
1392*4882a593Smuzhiyun 			break;
1393*4882a593Smuzhiyun 		}
1394*4882a593Smuzhiyun 	}
1395*4882a593Smuzhiyun 
1396*4882a593Smuzhiyun 	setup_pager();
1397*4882a593Smuzhiyun 	err = perf_session__process_events(session);
1398*4882a593Smuzhiyun 	if (err != 0) {
1399*4882a593Smuzhiyun 		pr_err("error during process events: %d\n", err);
1400*4882a593Smuzhiyun 		goto out;
1401*4882a593Smuzhiyun 	}
1402*4882a593Smuzhiyun 	sort_result();
1403*4882a593Smuzhiyun 	print_result(session);
1404*4882a593Smuzhiyun out:
1405*4882a593Smuzhiyun 	return err;
1406*4882a593Smuzhiyun }
1407*4882a593Smuzhiyun 
1408*4882a593Smuzhiyun /* slab sort keys */
ptr_cmp(void * a,void * b)1409*4882a593Smuzhiyun static int ptr_cmp(void *a, void *b)
1410*4882a593Smuzhiyun {
1411*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1412*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1413*4882a593Smuzhiyun 
1414*4882a593Smuzhiyun 	if (l->ptr < r->ptr)
1415*4882a593Smuzhiyun 		return -1;
1416*4882a593Smuzhiyun 	else if (l->ptr > r->ptr)
1417*4882a593Smuzhiyun 		return 1;
1418*4882a593Smuzhiyun 	return 0;
1419*4882a593Smuzhiyun }
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun static struct sort_dimension ptr_sort_dimension = {
1422*4882a593Smuzhiyun 	.name	= "ptr",
1423*4882a593Smuzhiyun 	.cmp	= ptr_cmp,
1424*4882a593Smuzhiyun };
1425*4882a593Smuzhiyun 
slab_callsite_cmp(void * a,void * b)1426*4882a593Smuzhiyun static int slab_callsite_cmp(void *a, void *b)
1427*4882a593Smuzhiyun {
1428*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1429*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1430*4882a593Smuzhiyun 
1431*4882a593Smuzhiyun 	if (l->call_site < r->call_site)
1432*4882a593Smuzhiyun 		return -1;
1433*4882a593Smuzhiyun 	else if (l->call_site > r->call_site)
1434*4882a593Smuzhiyun 		return 1;
1435*4882a593Smuzhiyun 	return 0;
1436*4882a593Smuzhiyun }
1437*4882a593Smuzhiyun 
1438*4882a593Smuzhiyun static struct sort_dimension callsite_sort_dimension = {
1439*4882a593Smuzhiyun 	.name	= "callsite",
1440*4882a593Smuzhiyun 	.cmp	= slab_callsite_cmp,
1441*4882a593Smuzhiyun };
1442*4882a593Smuzhiyun 
hit_cmp(void * a,void * b)1443*4882a593Smuzhiyun static int hit_cmp(void *a, void *b)
1444*4882a593Smuzhiyun {
1445*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1446*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun 	if (l->hit < r->hit)
1449*4882a593Smuzhiyun 		return -1;
1450*4882a593Smuzhiyun 	else if (l->hit > r->hit)
1451*4882a593Smuzhiyun 		return 1;
1452*4882a593Smuzhiyun 	return 0;
1453*4882a593Smuzhiyun }
1454*4882a593Smuzhiyun 
1455*4882a593Smuzhiyun static struct sort_dimension hit_sort_dimension = {
1456*4882a593Smuzhiyun 	.name	= "hit",
1457*4882a593Smuzhiyun 	.cmp	= hit_cmp,
1458*4882a593Smuzhiyun };
1459*4882a593Smuzhiyun 
bytes_cmp(void * a,void * b)1460*4882a593Smuzhiyun static int bytes_cmp(void *a, void *b)
1461*4882a593Smuzhiyun {
1462*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1463*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1464*4882a593Smuzhiyun 
1465*4882a593Smuzhiyun 	if (l->bytes_alloc < r->bytes_alloc)
1466*4882a593Smuzhiyun 		return -1;
1467*4882a593Smuzhiyun 	else if (l->bytes_alloc > r->bytes_alloc)
1468*4882a593Smuzhiyun 		return 1;
1469*4882a593Smuzhiyun 	return 0;
1470*4882a593Smuzhiyun }
1471*4882a593Smuzhiyun 
1472*4882a593Smuzhiyun static struct sort_dimension bytes_sort_dimension = {
1473*4882a593Smuzhiyun 	.name	= "bytes",
1474*4882a593Smuzhiyun 	.cmp	= bytes_cmp,
1475*4882a593Smuzhiyun };
1476*4882a593Smuzhiyun 
frag_cmp(void * a,void * b)1477*4882a593Smuzhiyun static int frag_cmp(void *a, void *b)
1478*4882a593Smuzhiyun {
1479*4882a593Smuzhiyun 	double x, y;
1480*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1481*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1482*4882a593Smuzhiyun 
1483*4882a593Smuzhiyun 	x = fragmentation(l->bytes_req, l->bytes_alloc);
1484*4882a593Smuzhiyun 	y = fragmentation(r->bytes_req, r->bytes_alloc);
1485*4882a593Smuzhiyun 
1486*4882a593Smuzhiyun 	if (x < y)
1487*4882a593Smuzhiyun 		return -1;
1488*4882a593Smuzhiyun 	else if (x > y)
1489*4882a593Smuzhiyun 		return 1;
1490*4882a593Smuzhiyun 	return 0;
1491*4882a593Smuzhiyun }
1492*4882a593Smuzhiyun 
1493*4882a593Smuzhiyun static struct sort_dimension frag_sort_dimension = {
1494*4882a593Smuzhiyun 	.name	= "frag",
1495*4882a593Smuzhiyun 	.cmp	= frag_cmp,
1496*4882a593Smuzhiyun };
1497*4882a593Smuzhiyun 
pingpong_cmp(void * a,void * b)1498*4882a593Smuzhiyun static int pingpong_cmp(void *a, void *b)
1499*4882a593Smuzhiyun {
1500*4882a593Smuzhiyun 	struct alloc_stat *l = a;
1501*4882a593Smuzhiyun 	struct alloc_stat *r = b;
1502*4882a593Smuzhiyun 
1503*4882a593Smuzhiyun 	if (l->pingpong < r->pingpong)
1504*4882a593Smuzhiyun 		return -1;
1505*4882a593Smuzhiyun 	else if (l->pingpong > r->pingpong)
1506*4882a593Smuzhiyun 		return 1;
1507*4882a593Smuzhiyun 	return 0;
1508*4882a593Smuzhiyun }
1509*4882a593Smuzhiyun 
1510*4882a593Smuzhiyun static struct sort_dimension pingpong_sort_dimension = {
1511*4882a593Smuzhiyun 	.name	= "pingpong",
1512*4882a593Smuzhiyun 	.cmp	= pingpong_cmp,
1513*4882a593Smuzhiyun };
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun /* page sort keys */
page_cmp(void * a,void * b)1516*4882a593Smuzhiyun static int page_cmp(void *a, void *b)
1517*4882a593Smuzhiyun {
1518*4882a593Smuzhiyun 	struct page_stat *l = a;
1519*4882a593Smuzhiyun 	struct page_stat *r = b;
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 	if (l->page < r->page)
1522*4882a593Smuzhiyun 		return -1;
1523*4882a593Smuzhiyun 	else if (l->page > r->page)
1524*4882a593Smuzhiyun 		return 1;
1525*4882a593Smuzhiyun 	return 0;
1526*4882a593Smuzhiyun }
1527*4882a593Smuzhiyun 
1528*4882a593Smuzhiyun static struct sort_dimension page_sort_dimension = {
1529*4882a593Smuzhiyun 	.name	= "page",
1530*4882a593Smuzhiyun 	.cmp	= page_cmp,
1531*4882a593Smuzhiyun };
1532*4882a593Smuzhiyun 
page_callsite_cmp(void * a,void * b)1533*4882a593Smuzhiyun static int page_callsite_cmp(void *a, void *b)
1534*4882a593Smuzhiyun {
1535*4882a593Smuzhiyun 	struct page_stat *l = a;
1536*4882a593Smuzhiyun 	struct page_stat *r = b;
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 	if (l->callsite < r->callsite)
1539*4882a593Smuzhiyun 		return -1;
1540*4882a593Smuzhiyun 	else if (l->callsite > r->callsite)
1541*4882a593Smuzhiyun 		return 1;
1542*4882a593Smuzhiyun 	return 0;
1543*4882a593Smuzhiyun }
1544*4882a593Smuzhiyun 
1545*4882a593Smuzhiyun static struct sort_dimension page_callsite_sort_dimension = {
1546*4882a593Smuzhiyun 	.name	= "callsite",
1547*4882a593Smuzhiyun 	.cmp	= page_callsite_cmp,
1548*4882a593Smuzhiyun };
1549*4882a593Smuzhiyun 
page_hit_cmp(void * a,void * b)1550*4882a593Smuzhiyun static int page_hit_cmp(void *a, void *b)
1551*4882a593Smuzhiyun {
1552*4882a593Smuzhiyun 	struct page_stat *l = a;
1553*4882a593Smuzhiyun 	struct page_stat *r = b;
1554*4882a593Smuzhiyun 
1555*4882a593Smuzhiyun 	if (l->nr_alloc < r->nr_alloc)
1556*4882a593Smuzhiyun 		return -1;
1557*4882a593Smuzhiyun 	else if (l->nr_alloc > r->nr_alloc)
1558*4882a593Smuzhiyun 		return 1;
1559*4882a593Smuzhiyun 	return 0;
1560*4882a593Smuzhiyun }
1561*4882a593Smuzhiyun 
1562*4882a593Smuzhiyun static struct sort_dimension page_hit_sort_dimension = {
1563*4882a593Smuzhiyun 	.name	= "hit",
1564*4882a593Smuzhiyun 	.cmp	= page_hit_cmp,
1565*4882a593Smuzhiyun };
1566*4882a593Smuzhiyun 
page_bytes_cmp(void * a,void * b)1567*4882a593Smuzhiyun static int page_bytes_cmp(void *a, void *b)
1568*4882a593Smuzhiyun {
1569*4882a593Smuzhiyun 	struct page_stat *l = a;
1570*4882a593Smuzhiyun 	struct page_stat *r = b;
1571*4882a593Smuzhiyun 
1572*4882a593Smuzhiyun 	if (l->alloc_bytes < r->alloc_bytes)
1573*4882a593Smuzhiyun 		return -1;
1574*4882a593Smuzhiyun 	else if (l->alloc_bytes > r->alloc_bytes)
1575*4882a593Smuzhiyun 		return 1;
1576*4882a593Smuzhiyun 	return 0;
1577*4882a593Smuzhiyun }
1578*4882a593Smuzhiyun 
1579*4882a593Smuzhiyun static struct sort_dimension page_bytes_sort_dimension = {
1580*4882a593Smuzhiyun 	.name	= "bytes",
1581*4882a593Smuzhiyun 	.cmp	= page_bytes_cmp,
1582*4882a593Smuzhiyun };
1583*4882a593Smuzhiyun 
page_order_cmp(void * a,void * b)1584*4882a593Smuzhiyun static int page_order_cmp(void *a, void *b)
1585*4882a593Smuzhiyun {
1586*4882a593Smuzhiyun 	struct page_stat *l = a;
1587*4882a593Smuzhiyun 	struct page_stat *r = b;
1588*4882a593Smuzhiyun 
1589*4882a593Smuzhiyun 	if (l->order < r->order)
1590*4882a593Smuzhiyun 		return -1;
1591*4882a593Smuzhiyun 	else if (l->order > r->order)
1592*4882a593Smuzhiyun 		return 1;
1593*4882a593Smuzhiyun 	return 0;
1594*4882a593Smuzhiyun }
1595*4882a593Smuzhiyun 
1596*4882a593Smuzhiyun static struct sort_dimension page_order_sort_dimension = {
1597*4882a593Smuzhiyun 	.name	= "order",
1598*4882a593Smuzhiyun 	.cmp	= page_order_cmp,
1599*4882a593Smuzhiyun };
1600*4882a593Smuzhiyun 
migrate_type_cmp(void * a,void * b)1601*4882a593Smuzhiyun static int migrate_type_cmp(void *a, void *b)
1602*4882a593Smuzhiyun {
1603*4882a593Smuzhiyun 	struct page_stat *l = a;
1604*4882a593Smuzhiyun 	struct page_stat *r = b;
1605*4882a593Smuzhiyun 
1606*4882a593Smuzhiyun 	/* for internal use to find free'd page */
1607*4882a593Smuzhiyun 	if (l->migrate_type == -1U)
1608*4882a593Smuzhiyun 		return 0;
1609*4882a593Smuzhiyun 
1610*4882a593Smuzhiyun 	if (l->migrate_type < r->migrate_type)
1611*4882a593Smuzhiyun 		return -1;
1612*4882a593Smuzhiyun 	else if (l->migrate_type > r->migrate_type)
1613*4882a593Smuzhiyun 		return 1;
1614*4882a593Smuzhiyun 	return 0;
1615*4882a593Smuzhiyun }
1616*4882a593Smuzhiyun 
1617*4882a593Smuzhiyun static struct sort_dimension migrate_type_sort_dimension = {
1618*4882a593Smuzhiyun 	.name	= "migtype",
1619*4882a593Smuzhiyun 	.cmp	= migrate_type_cmp,
1620*4882a593Smuzhiyun };
1621*4882a593Smuzhiyun 
gfp_flags_cmp(void * a,void * b)1622*4882a593Smuzhiyun static int gfp_flags_cmp(void *a, void *b)
1623*4882a593Smuzhiyun {
1624*4882a593Smuzhiyun 	struct page_stat *l = a;
1625*4882a593Smuzhiyun 	struct page_stat *r = b;
1626*4882a593Smuzhiyun 
1627*4882a593Smuzhiyun 	/* for internal use to find free'd page */
1628*4882a593Smuzhiyun 	if (l->gfp_flags == -1U)
1629*4882a593Smuzhiyun 		return 0;
1630*4882a593Smuzhiyun 
1631*4882a593Smuzhiyun 	if (l->gfp_flags < r->gfp_flags)
1632*4882a593Smuzhiyun 		return -1;
1633*4882a593Smuzhiyun 	else if (l->gfp_flags > r->gfp_flags)
1634*4882a593Smuzhiyun 		return 1;
1635*4882a593Smuzhiyun 	return 0;
1636*4882a593Smuzhiyun }
1637*4882a593Smuzhiyun 
1638*4882a593Smuzhiyun static struct sort_dimension gfp_flags_sort_dimension = {
1639*4882a593Smuzhiyun 	.name	= "gfp",
1640*4882a593Smuzhiyun 	.cmp	= gfp_flags_cmp,
1641*4882a593Smuzhiyun };
1642*4882a593Smuzhiyun 
1643*4882a593Smuzhiyun static struct sort_dimension *slab_sorts[] = {
1644*4882a593Smuzhiyun 	&ptr_sort_dimension,
1645*4882a593Smuzhiyun 	&callsite_sort_dimension,
1646*4882a593Smuzhiyun 	&hit_sort_dimension,
1647*4882a593Smuzhiyun 	&bytes_sort_dimension,
1648*4882a593Smuzhiyun 	&frag_sort_dimension,
1649*4882a593Smuzhiyun 	&pingpong_sort_dimension,
1650*4882a593Smuzhiyun };
1651*4882a593Smuzhiyun 
1652*4882a593Smuzhiyun static struct sort_dimension *page_sorts[] = {
1653*4882a593Smuzhiyun 	&page_sort_dimension,
1654*4882a593Smuzhiyun 	&page_callsite_sort_dimension,
1655*4882a593Smuzhiyun 	&page_hit_sort_dimension,
1656*4882a593Smuzhiyun 	&page_bytes_sort_dimension,
1657*4882a593Smuzhiyun 	&page_order_sort_dimension,
1658*4882a593Smuzhiyun 	&migrate_type_sort_dimension,
1659*4882a593Smuzhiyun 	&gfp_flags_sort_dimension,
1660*4882a593Smuzhiyun };
1661*4882a593Smuzhiyun 
slab_sort_dimension__add(const char * tok,struct list_head * list)1662*4882a593Smuzhiyun static int slab_sort_dimension__add(const char *tok, struct list_head *list)
1663*4882a593Smuzhiyun {
1664*4882a593Smuzhiyun 	struct sort_dimension *sort;
1665*4882a593Smuzhiyun 	int i;
1666*4882a593Smuzhiyun 
1667*4882a593Smuzhiyun 	for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
1668*4882a593Smuzhiyun 		if (!strcmp(slab_sorts[i]->name, tok)) {
1669*4882a593Smuzhiyun 			sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
1670*4882a593Smuzhiyun 			if (!sort) {
1671*4882a593Smuzhiyun 				pr_err("%s: memdup failed\n", __func__);
1672*4882a593Smuzhiyun 				return -1;
1673*4882a593Smuzhiyun 			}
1674*4882a593Smuzhiyun 			list_add_tail(&sort->list, list);
1675*4882a593Smuzhiyun 			return 0;
1676*4882a593Smuzhiyun 		}
1677*4882a593Smuzhiyun 	}
1678*4882a593Smuzhiyun 
1679*4882a593Smuzhiyun 	return -1;
1680*4882a593Smuzhiyun }
1681*4882a593Smuzhiyun 
page_sort_dimension__add(const char * tok,struct list_head * list)1682*4882a593Smuzhiyun static int page_sort_dimension__add(const char *tok, struct list_head *list)
1683*4882a593Smuzhiyun {
1684*4882a593Smuzhiyun 	struct sort_dimension *sort;
1685*4882a593Smuzhiyun 	int i;
1686*4882a593Smuzhiyun 
1687*4882a593Smuzhiyun 	for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
1688*4882a593Smuzhiyun 		if (!strcmp(page_sorts[i]->name, tok)) {
1689*4882a593Smuzhiyun 			sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
1690*4882a593Smuzhiyun 			if (!sort) {
1691*4882a593Smuzhiyun 				pr_err("%s: memdup failed\n", __func__);
1692*4882a593Smuzhiyun 				return -1;
1693*4882a593Smuzhiyun 			}
1694*4882a593Smuzhiyun 			list_add_tail(&sort->list, list);
1695*4882a593Smuzhiyun 			return 0;
1696*4882a593Smuzhiyun 		}
1697*4882a593Smuzhiyun 	}
1698*4882a593Smuzhiyun 
1699*4882a593Smuzhiyun 	return -1;
1700*4882a593Smuzhiyun }
1701*4882a593Smuzhiyun 
setup_slab_sorting(struct list_head * sort_list,const char * arg)1702*4882a593Smuzhiyun static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
1703*4882a593Smuzhiyun {
1704*4882a593Smuzhiyun 	char *tok;
1705*4882a593Smuzhiyun 	char *str = strdup(arg);
1706*4882a593Smuzhiyun 	char *pos = str;
1707*4882a593Smuzhiyun 
1708*4882a593Smuzhiyun 	if (!str) {
1709*4882a593Smuzhiyun 		pr_err("%s: strdup failed\n", __func__);
1710*4882a593Smuzhiyun 		return -1;
1711*4882a593Smuzhiyun 	}
1712*4882a593Smuzhiyun 
1713*4882a593Smuzhiyun 	while (true) {
1714*4882a593Smuzhiyun 		tok = strsep(&pos, ",");
1715*4882a593Smuzhiyun 		if (!tok)
1716*4882a593Smuzhiyun 			break;
1717*4882a593Smuzhiyun 		if (slab_sort_dimension__add(tok, sort_list) < 0) {
1718*4882a593Smuzhiyun 			pr_err("Unknown slab --sort key: '%s'", tok);
1719*4882a593Smuzhiyun 			free(str);
1720*4882a593Smuzhiyun 			return -1;
1721*4882a593Smuzhiyun 		}
1722*4882a593Smuzhiyun 	}
1723*4882a593Smuzhiyun 
1724*4882a593Smuzhiyun 	free(str);
1725*4882a593Smuzhiyun 	return 0;
1726*4882a593Smuzhiyun }
1727*4882a593Smuzhiyun 
setup_page_sorting(struct list_head * sort_list,const char * arg)1728*4882a593Smuzhiyun static int setup_page_sorting(struct list_head *sort_list, const char *arg)
1729*4882a593Smuzhiyun {
1730*4882a593Smuzhiyun 	char *tok;
1731*4882a593Smuzhiyun 	char *str = strdup(arg);
1732*4882a593Smuzhiyun 	char *pos = str;
1733*4882a593Smuzhiyun 
1734*4882a593Smuzhiyun 	if (!str) {
1735*4882a593Smuzhiyun 		pr_err("%s: strdup failed\n", __func__);
1736*4882a593Smuzhiyun 		return -1;
1737*4882a593Smuzhiyun 	}
1738*4882a593Smuzhiyun 
1739*4882a593Smuzhiyun 	while (true) {
1740*4882a593Smuzhiyun 		tok = strsep(&pos, ",");
1741*4882a593Smuzhiyun 		if (!tok)
1742*4882a593Smuzhiyun 			break;
1743*4882a593Smuzhiyun 		if (page_sort_dimension__add(tok, sort_list) < 0) {
1744*4882a593Smuzhiyun 			pr_err("Unknown page --sort key: '%s'", tok);
1745*4882a593Smuzhiyun 			free(str);
1746*4882a593Smuzhiyun 			return -1;
1747*4882a593Smuzhiyun 		}
1748*4882a593Smuzhiyun 	}
1749*4882a593Smuzhiyun 
1750*4882a593Smuzhiyun 	free(str);
1751*4882a593Smuzhiyun 	return 0;
1752*4882a593Smuzhiyun }
1753*4882a593Smuzhiyun 
parse_sort_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)1754*4882a593Smuzhiyun static int parse_sort_opt(const struct option *opt __maybe_unused,
1755*4882a593Smuzhiyun 			  const char *arg, int unset __maybe_unused)
1756*4882a593Smuzhiyun {
1757*4882a593Smuzhiyun 	if (!arg)
1758*4882a593Smuzhiyun 		return -1;
1759*4882a593Smuzhiyun 
1760*4882a593Smuzhiyun 	if (kmem_page > kmem_slab ||
1761*4882a593Smuzhiyun 	    (kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) {
1762*4882a593Smuzhiyun 		if (caller_flag > alloc_flag)
1763*4882a593Smuzhiyun 			return setup_page_sorting(&page_caller_sort, arg);
1764*4882a593Smuzhiyun 		else
1765*4882a593Smuzhiyun 			return setup_page_sorting(&page_alloc_sort, arg);
1766*4882a593Smuzhiyun 	} else {
1767*4882a593Smuzhiyun 		if (caller_flag > alloc_flag)
1768*4882a593Smuzhiyun 			return setup_slab_sorting(&slab_caller_sort, arg);
1769*4882a593Smuzhiyun 		else
1770*4882a593Smuzhiyun 			return setup_slab_sorting(&slab_alloc_sort, arg);
1771*4882a593Smuzhiyun 	}
1772*4882a593Smuzhiyun 
1773*4882a593Smuzhiyun 	return 0;
1774*4882a593Smuzhiyun }
1775*4882a593Smuzhiyun 
parse_caller_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)1776*4882a593Smuzhiyun static int parse_caller_opt(const struct option *opt __maybe_unused,
1777*4882a593Smuzhiyun 			    const char *arg __maybe_unused,
1778*4882a593Smuzhiyun 			    int unset __maybe_unused)
1779*4882a593Smuzhiyun {
1780*4882a593Smuzhiyun 	caller_flag = (alloc_flag + 1);
1781*4882a593Smuzhiyun 	return 0;
1782*4882a593Smuzhiyun }
1783*4882a593Smuzhiyun 
parse_alloc_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)1784*4882a593Smuzhiyun static int parse_alloc_opt(const struct option *opt __maybe_unused,
1785*4882a593Smuzhiyun 			   const char *arg __maybe_unused,
1786*4882a593Smuzhiyun 			   int unset __maybe_unused)
1787*4882a593Smuzhiyun {
1788*4882a593Smuzhiyun 	alloc_flag = (caller_flag + 1);
1789*4882a593Smuzhiyun 	return 0;
1790*4882a593Smuzhiyun }
1791*4882a593Smuzhiyun 
parse_slab_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)1792*4882a593Smuzhiyun static int parse_slab_opt(const struct option *opt __maybe_unused,
1793*4882a593Smuzhiyun 			  const char *arg __maybe_unused,
1794*4882a593Smuzhiyun 			  int unset __maybe_unused)
1795*4882a593Smuzhiyun {
1796*4882a593Smuzhiyun 	kmem_slab = (kmem_page + 1);
1797*4882a593Smuzhiyun 	return 0;
1798*4882a593Smuzhiyun }
1799*4882a593Smuzhiyun 
parse_page_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)1800*4882a593Smuzhiyun static int parse_page_opt(const struct option *opt __maybe_unused,
1801*4882a593Smuzhiyun 			  const char *arg __maybe_unused,
1802*4882a593Smuzhiyun 			  int unset __maybe_unused)
1803*4882a593Smuzhiyun {
1804*4882a593Smuzhiyun 	kmem_page = (kmem_slab + 1);
1805*4882a593Smuzhiyun 	return 0;
1806*4882a593Smuzhiyun }
1807*4882a593Smuzhiyun 
parse_line_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)1808*4882a593Smuzhiyun static int parse_line_opt(const struct option *opt __maybe_unused,
1809*4882a593Smuzhiyun 			  const char *arg, int unset __maybe_unused)
1810*4882a593Smuzhiyun {
1811*4882a593Smuzhiyun 	int lines;
1812*4882a593Smuzhiyun 
1813*4882a593Smuzhiyun 	if (!arg)
1814*4882a593Smuzhiyun 		return -1;
1815*4882a593Smuzhiyun 
1816*4882a593Smuzhiyun 	lines = strtoul(arg, NULL, 10);
1817*4882a593Smuzhiyun 
1818*4882a593Smuzhiyun 	if (caller_flag > alloc_flag)
1819*4882a593Smuzhiyun 		caller_lines = lines;
1820*4882a593Smuzhiyun 	else
1821*4882a593Smuzhiyun 		alloc_lines = lines;
1822*4882a593Smuzhiyun 
1823*4882a593Smuzhiyun 	return 0;
1824*4882a593Smuzhiyun }
1825*4882a593Smuzhiyun 
__cmd_record(int argc,const char ** argv)1826*4882a593Smuzhiyun static int __cmd_record(int argc, const char **argv)
1827*4882a593Smuzhiyun {
1828*4882a593Smuzhiyun 	const char * const record_args[] = {
1829*4882a593Smuzhiyun 	"record", "-a", "-R", "-c", "1",
1830*4882a593Smuzhiyun 	};
1831*4882a593Smuzhiyun 	const char * const slab_events[] = {
1832*4882a593Smuzhiyun 	"-e", "kmem:kmalloc",
1833*4882a593Smuzhiyun 	"-e", "kmem:kmalloc_node",
1834*4882a593Smuzhiyun 	"-e", "kmem:kfree",
1835*4882a593Smuzhiyun 	"-e", "kmem:kmem_cache_alloc",
1836*4882a593Smuzhiyun 	"-e", "kmem:kmem_cache_alloc_node",
1837*4882a593Smuzhiyun 	"-e", "kmem:kmem_cache_free",
1838*4882a593Smuzhiyun 	};
1839*4882a593Smuzhiyun 	const char * const page_events[] = {
1840*4882a593Smuzhiyun 	"-e", "kmem:mm_page_alloc",
1841*4882a593Smuzhiyun 	"-e", "kmem:mm_page_free",
1842*4882a593Smuzhiyun 	};
1843*4882a593Smuzhiyun 	unsigned int rec_argc, i, j;
1844*4882a593Smuzhiyun 	const char **rec_argv;
1845*4882a593Smuzhiyun 
1846*4882a593Smuzhiyun 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1847*4882a593Smuzhiyun 	if (kmem_slab)
1848*4882a593Smuzhiyun 		rec_argc += ARRAY_SIZE(slab_events);
1849*4882a593Smuzhiyun 	if (kmem_page)
1850*4882a593Smuzhiyun 		rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1853*4882a593Smuzhiyun 
1854*4882a593Smuzhiyun 	if (rec_argv == NULL)
1855*4882a593Smuzhiyun 		return -ENOMEM;
1856*4882a593Smuzhiyun 
1857*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
1858*4882a593Smuzhiyun 		rec_argv[i] = strdup(record_args[i]);
1859*4882a593Smuzhiyun 
1860*4882a593Smuzhiyun 	if (kmem_slab) {
1861*4882a593Smuzhiyun 		for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
1862*4882a593Smuzhiyun 			rec_argv[i] = strdup(slab_events[j]);
1863*4882a593Smuzhiyun 	}
1864*4882a593Smuzhiyun 	if (kmem_page) {
1865*4882a593Smuzhiyun 		rec_argv[i++] = strdup("-g");
1866*4882a593Smuzhiyun 
1867*4882a593Smuzhiyun 		for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
1868*4882a593Smuzhiyun 			rec_argv[i] = strdup(page_events[j]);
1869*4882a593Smuzhiyun 	}
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	for (j = 1; j < (unsigned int)argc; j++, i++)
1872*4882a593Smuzhiyun 		rec_argv[i] = argv[j];
1873*4882a593Smuzhiyun 
1874*4882a593Smuzhiyun 	return cmd_record(i, rec_argv);
1875*4882a593Smuzhiyun }
1876*4882a593Smuzhiyun 
kmem_config(const char * var,const char * value,void * cb __maybe_unused)1877*4882a593Smuzhiyun static int kmem_config(const char *var, const char *value, void *cb __maybe_unused)
1878*4882a593Smuzhiyun {
1879*4882a593Smuzhiyun 	if (!strcmp(var, "kmem.default")) {
1880*4882a593Smuzhiyun 		if (!strcmp(value, "slab"))
1881*4882a593Smuzhiyun 			kmem_default = KMEM_SLAB;
1882*4882a593Smuzhiyun 		else if (!strcmp(value, "page"))
1883*4882a593Smuzhiyun 			kmem_default = KMEM_PAGE;
1884*4882a593Smuzhiyun 		else
1885*4882a593Smuzhiyun 			pr_err("invalid default value ('slab' or 'page' required): %s\n",
1886*4882a593Smuzhiyun 			       value);
1887*4882a593Smuzhiyun 		return 0;
1888*4882a593Smuzhiyun 	}
1889*4882a593Smuzhiyun 
1890*4882a593Smuzhiyun 	return 0;
1891*4882a593Smuzhiyun }
1892*4882a593Smuzhiyun 
cmd_kmem(int argc,const char ** argv)1893*4882a593Smuzhiyun int cmd_kmem(int argc, const char **argv)
1894*4882a593Smuzhiyun {
1895*4882a593Smuzhiyun 	const char * const default_slab_sort = "frag,hit,bytes";
1896*4882a593Smuzhiyun 	const char * const default_page_sort = "bytes,hit";
1897*4882a593Smuzhiyun 	struct perf_data data = {
1898*4882a593Smuzhiyun 		.mode = PERF_DATA_MODE_READ,
1899*4882a593Smuzhiyun 	};
1900*4882a593Smuzhiyun 	const struct option kmem_options[] = {
1901*4882a593Smuzhiyun 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
1902*4882a593Smuzhiyun 	OPT_INCR('v', "verbose", &verbose,
1903*4882a593Smuzhiyun 		    "be more verbose (show symbol address, etc)"),
1904*4882a593Smuzhiyun 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
1905*4882a593Smuzhiyun 			   "show per-callsite statistics", parse_caller_opt),
1906*4882a593Smuzhiyun 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
1907*4882a593Smuzhiyun 			   "show per-allocation statistics", parse_alloc_opt),
1908*4882a593Smuzhiyun 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
1909*4882a593Smuzhiyun 		     "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
1910*4882a593Smuzhiyun 		     "page, order, migtype, gfp", parse_sort_opt),
1911*4882a593Smuzhiyun 	OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
1912*4882a593Smuzhiyun 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
1913*4882a593Smuzhiyun 	OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"),
1914*4882a593Smuzhiyun 	OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
1915*4882a593Smuzhiyun 			   parse_slab_opt),
1916*4882a593Smuzhiyun 	OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
1917*4882a593Smuzhiyun 			   parse_page_opt),
1918*4882a593Smuzhiyun 	OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
1919*4882a593Smuzhiyun 	OPT_STRING(0, "time", &time_str, "str",
1920*4882a593Smuzhiyun 		   "Time span of interest (start,stop)"),
1921*4882a593Smuzhiyun 	OPT_END()
1922*4882a593Smuzhiyun 	};
1923*4882a593Smuzhiyun 	const char *const kmem_subcommands[] = { "record", "stat", NULL };
1924*4882a593Smuzhiyun 	const char *kmem_usage[] = {
1925*4882a593Smuzhiyun 		NULL,
1926*4882a593Smuzhiyun 		NULL
1927*4882a593Smuzhiyun 	};
1928*4882a593Smuzhiyun 	struct perf_session *session;
1929*4882a593Smuzhiyun 	static const char errmsg[] = "No %s allocation events found.  Have you run 'perf kmem record --%s'?\n";
1930*4882a593Smuzhiyun 	int ret = perf_config(kmem_config, NULL);
1931*4882a593Smuzhiyun 
1932*4882a593Smuzhiyun 	if (ret)
1933*4882a593Smuzhiyun 		return ret;
1934*4882a593Smuzhiyun 
1935*4882a593Smuzhiyun 	argc = parse_options_subcommand(argc, argv, kmem_options,
1936*4882a593Smuzhiyun 					kmem_subcommands, kmem_usage,
1937*4882a593Smuzhiyun 					PARSE_OPT_STOP_AT_NON_OPTION);
1938*4882a593Smuzhiyun 
1939*4882a593Smuzhiyun 	if (!argc)
1940*4882a593Smuzhiyun 		usage_with_options(kmem_usage, kmem_options);
1941*4882a593Smuzhiyun 
1942*4882a593Smuzhiyun 	if (kmem_slab == 0 && kmem_page == 0) {
1943*4882a593Smuzhiyun 		if (kmem_default == KMEM_SLAB)
1944*4882a593Smuzhiyun 			kmem_slab = 1;
1945*4882a593Smuzhiyun 		else
1946*4882a593Smuzhiyun 			kmem_page = 1;
1947*4882a593Smuzhiyun 	}
1948*4882a593Smuzhiyun 
1949*4882a593Smuzhiyun 	if (!strncmp(argv[0], "rec", 3)) {
1950*4882a593Smuzhiyun 		symbol__init(NULL);
1951*4882a593Smuzhiyun 		return __cmd_record(argc, argv);
1952*4882a593Smuzhiyun 	}
1953*4882a593Smuzhiyun 
1954*4882a593Smuzhiyun 	data.path = input_name;
1955*4882a593Smuzhiyun 
1956*4882a593Smuzhiyun 	kmem_session = session = perf_session__new(&data, false, &perf_kmem);
1957*4882a593Smuzhiyun 	if (IS_ERR(session))
1958*4882a593Smuzhiyun 		return PTR_ERR(session);
1959*4882a593Smuzhiyun 
1960*4882a593Smuzhiyun 	ret = -1;
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun 	if (kmem_slab) {
1963*4882a593Smuzhiyun 		if (!perf_evlist__find_tracepoint_by_name(session->evlist,
1964*4882a593Smuzhiyun 							  "kmem:kmalloc")) {
1965*4882a593Smuzhiyun 			pr_err(errmsg, "slab", "slab");
1966*4882a593Smuzhiyun 			goto out_delete;
1967*4882a593Smuzhiyun 		}
1968*4882a593Smuzhiyun 	}
1969*4882a593Smuzhiyun 
1970*4882a593Smuzhiyun 	if (kmem_page) {
1971*4882a593Smuzhiyun 		struct evsel *evsel;
1972*4882a593Smuzhiyun 
1973*4882a593Smuzhiyun 		evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
1974*4882a593Smuzhiyun 							     "kmem:mm_page_alloc");
1975*4882a593Smuzhiyun 		if (evsel == NULL) {
1976*4882a593Smuzhiyun 			pr_err(errmsg, "page", "page");
1977*4882a593Smuzhiyun 			goto out_delete;
1978*4882a593Smuzhiyun 		}
1979*4882a593Smuzhiyun 
1980*4882a593Smuzhiyun 		kmem_page_size = tep_get_page_size(evsel->tp_format->tep);
1981*4882a593Smuzhiyun 		symbol_conf.use_callchain = true;
1982*4882a593Smuzhiyun 	}
1983*4882a593Smuzhiyun 
1984*4882a593Smuzhiyun 	symbol__init(&session->header.env);
1985*4882a593Smuzhiyun 
1986*4882a593Smuzhiyun 	if (perf_time__parse_str(&ptime, time_str) != 0) {
1987*4882a593Smuzhiyun 		pr_err("Invalid time string\n");
1988*4882a593Smuzhiyun 		ret = -EINVAL;
1989*4882a593Smuzhiyun 		goto out_delete;
1990*4882a593Smuzhiyun 	}
1991*4882a593Smuzhiyun 
1992*4882a593Smuzhiyun 	if (!strcmp(argv[0], "stat")) {
1993*4882a593Smuzhiyun 		setlocale(LC_ALL, "");
1994*4882a593Smuzhiyun 
1995*4882a593Smuzhiyun 		if (cpu__setup_cpunode_map())
1996*4882a593Smuzhiyun 			goto out_delete;
1997*4882a593Smuzhiyun 
1998*4882a593Smuzhiyun 		if (list_empty(&slab_caller_sort))
1999*4882a593Smuzhiyun 			setup_slab_sorting(&slab_caller_sort, default_slab_sort);
2000*4882a593Smuzhiyun 		if (list_empty(&slab_alloc_sort))
2001*4882a593Smuzhiyun 			setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
2002*4882a593Smuzhiyun 		if (list_empty(&page_caller_sort))
2003*4882a593Smuzhiyun 			setup_page_sorting(&page_caller_sort, default_page_sort);
2004*4882a593Smuzhiyun 		if (list_empty(&page_alloc_sort))
2005*4882a593Smuzhiyun 			setup_page_sorting(&page_alloc_sort, default_page_sort);
2006*4882a593Smuzhiyun 
2007*4882a593Smuzhiyun 		if (kmem_page) {
2008*4882a593Smuzhiyun 			setup_page_sorting(&page_alloc_sort_input,
2009*4882a593Smuzhiyun 					   "page,order,migtype,gfp");
2010*4882a593Smuzhiyun 			setup_page_sorting(&page_caller_sort_input,
2011*4882a593Smuzhiyun 					   "callsite,order,migtype,gfp");
2012*4882a593Smuzhiyun 		}
2013*4882a593Smuzhiyun 		ret = __cmd_kmem(session);
2014*4882a593Smuzhiyun 	} else
2015*4882a593Smuzhiyun 		usage_with_options(kmem_usage, kmem_options);
2016*4882a593Smuzhiyun 
2017*4882a593Smuzhiyun out_delete:
2018*4882a593Smuzhiyun 	perf_session__delete(session);
2019*4882a593Smuzhiyun 
2020*4882a593Smuzhiyun 	return ret;
2021*4882a593Smuzhiyun }
2022*4882a593Smuzhiyun 
2023