xref: /openbmc/linux/tools/perf/builtin-kmem.c (revision 77cfe388767572586b7d4fd533c38f902d020f17)
1ba77c9e1SLi Zefan #include "builtin.h"
2ba77c9e1SLi Zefan #include "perf.h"
3ba77c9e1SLi Zefan 
40f7d2f1bSArnaldo Carvalho de Melo #include "util/evlist.h"
5fcf65bf1SArnaldo Carvalho de Melo #include "util/evsel.h"
6ba77c9e1SLi Zefan #include "util/util.h"
7ba77c9e1SLi Zefan #include "util/cache.h"
8ba77c9e1SLi Zefan #include "util/symbol.h"
9ba77c9e1SLi Zefan #include "util/thread.h"
10ba77c9e1SLi Zefan #include "util/header.h"
1194c744b6SArnaldo Carvalho de Melo #include "util/session.h"
1245694aa7SArnaldo Carvalho de Melo #include "util/tool.h"
13ba77c9e1SLi Zefan 
14ba77c9e1SLi Zefan #include "util/parse-options.h"
15ba77c9e1SLi Zefan #include "util/trace-event.h"
16f5fc1412SJiri Olsa #include "util/data.h"
174b627957SDon Zickus #include "util/cpumap.h"
18ba77c9e1SLi Zefan 
19ba77c9e1SLi Zefan #include "util/debug.h"
20ba77c9e1SLi Zefan 
21ba77c9e1SLi Zefan #include <linux/rbtree.h>
228d9233f2SArnaldo Carvalho de Melo #include <linux/string.h>
23*77cfe388SNamhyung Kim #include <locale.h>
24ba77c9e1SLi Zefan 
25ba77c9e1SLi Zefan struct alloc_stat;
26ba77c9e1SLi Zefan typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
27ba77c9e1SLi Zefan 
28ba77c9e1SLi Zefan static int			alloc_flag;
29ba77c9e1SLi Zefan static int			caller_flag;
30ba77c9e1SLi Zefan 
31ba77c9e1SLi Zefan static int			alloc_lines = -1;
32ba77c9e1SLi Zefan static int			caller_lines = -1;
33ba77c9e1SLi Zefan 
347707b6b6SLi Zefan static bool			raw_ip;
357707b6b6SLi Zefan 
36ba77c9e1SLi Zefan struct alloc_stat {
37ba77c9e1SLi Zefan 	u64	call_site;
38ba77c9e1SLi Zefan 	u64	ptr;
39ba77c9e1SLi Zefan 	u64	bytes_req;
40ba77c9e1SLi Zefan 	u64	bytes_alloc;
41ba77c9e1SLi Zefan 	u32	hit;
42079d3f65SLi Zefan 	u32	pingpong;
43079d3f65SLi Zefan 
44079d3f65SLi Zefan 	short	alloc_cpu;
45ba77c9e1SLi Zefan 
46ba77c9e1SLi Zefan 	struct rb_node node;
47ba77c9e1SLi Zefan };
48ba77c9e1SLi Zefan 
49ba77c9e1SLi Zefan static struct rb_root root_alloc_stat;
50ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted;
51ba77c9e1SLi Zefan static struct rb_root root_caller_stat;
52ba77c9e1SLi Zefan static struct rb_root root_caller_sorted;
53ba77c9e1SLi Zefan 
54ba77c9e1SLi Zefan static unsigned long total_requested, total_allocated;
557d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs;
56ba77c9e1SLi Zefan 
572814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
58079d3f65SLi Zefan 			     int bytes_req, int bytes_alloc, int cpu)
59ba77c9e1SLi Zefan {
60ba77c9e1SLi Zefan 	struct rb_node **node = &root_alloc_stat.rb_node;
61ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
62ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
63ba77c9e1SLi Zefan 
64ba77c9e1SLi Zefan 	while (*node) {
65ba77c9e1SLi Zefan 		parent = *node;
66ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
67ba77c9e1SLi Zefan 
68ba77c9e1SLi Zefan 		if (ptr > data->ptr)
69ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
70ba77c9e1SLi Zefan 		else if (ptr < data->ptr)
71ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
72ba77c9e1SLi Zefan 		else
73ba77c9e1SLi Zefan 			break;
74ba77c9e1SLi Zefan 	}
75ba77c9e1SLi Zefan 
76ba77c9e1SLi Zefan 	if (data && data->ptr == ptr) {
77ba77c9e1SLi Zefan 		data->hit++;
78ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
794efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
80ba77c9e1SLi Zefan 	} else {
81ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
822814eb05SArnaldo Carvalho de Melo 		if (!data) {
832814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
842814eb05SArnaldo Carvalho de Melo 			return -1;
852814eb05SArnaldo Carvalho de Melo 		}
86ba77c9e1SLi Zefan 		data->ptr = ptr;
87079d3f65SLi Zefan 		data->pingpong = 0;
88ba77c9e1SLi Zefan 		data->hit = 1;
89ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
90ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
91ba77c9e1SLi Zefan 
92ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
93ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_alloc_stat);
94ba77c9e1SLi Zefan 	}
95079d3f65SLi Zefan 	data->call_site = call_site;
96079d3f65SLi Zefan 	data->alloc_cpu = cpu;
972814eb05SArnaldo Carvalho de Melo 	return 0;
98ba77c9e1SLi Zefan }
99ba77c9e1SLi Zefan 
1002814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site,
101ba77c9e1SLi Zefan 			      int bytes_req, int bytes_alloc)
102ba77c9e1SLi Zefan {
103ba77c9e1SLi Zefan 	struct rb_node **node = &root_caller_stat.rb_node;
104ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
105ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
106ba77c9e1SLi Zefan 
107ba77c9e1SLi Zefan 	while (*node) {
108ba77c9e1SLi Zefan 		parent = *node;
109ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
110ba77c9e1SLi Zefan 
111ba77c9e1SLi Zefan 		if (call_site > data->call_site)
112ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
113ba77c9e1SLi Zefan 		else if (call_site < data->call_site)
114ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
115ba77c9e1SLi Zefan 		else
116ba77c9e1SLi Zefan 			break;
117ba77c9e1SLi Zefan 	}
118ba77c9e1SLi Zefan 
119ba77c9e1SLi Zefan 	if (data && data->call_site == call_site) {
120ba77c9e1SLi Zefan 		data->hit++;
121ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
1224efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
123ba77c9e1SLi Zefan 	} else {
124ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
1252814eb05SArnaldo Carvalho de Melo 		if (!data) {
1262814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
1272814eb05SArnaldo Carvalho de Melo 			return -1;
1282814eb05SArnaldo Carvalho de Melo 		}
129ba77c9e1SLi Zefan 		data->call_site = call_site;
130079d3f65SLi Zefan 		data->pingpong = 0;
131ba77c9e1SLi Zefan 		data->hit = 1;
132ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
133ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
134ba77c9e1SLi Zefan 
135ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
136ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_caller_stat);
137ba77c9e1SLi Zefan 	}
1382814eb05SArnaldo Carvalho de Melo 
1392814eb05SArnaldo Carvalho de Melo 	return 0;
140ba77c9e1SLi Zefan }
141ba77c9e1SLi Zefan 
1422814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
1430f7d2f1bSArnaldo Carvalho de Melo 					   struct perf_sample *sample)
144ba77c9e1SLi Zefan {
1450f7d2f1bSArnaldo Carvalho de Melo 	unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
1460f7d2f1bSArnaldo Carvalho de Melo 		      call_site = perf_evsel__intval(evsel, sample, "call_site");
1470f7d2f1bSArnaldo Carvalho de Melo 	int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
1480f7d2f1bSArnaldo Carvalho de Melo 	    bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
149ba77c9e1SLi Zefan 
1500f7d2f1bSArnaldo Carvalho de Melo 	if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
1512814eb05SArnaldo Carvalho de Melo 	    insert_caller_stat(call_site, bytes_req, bytes_alloc))
1522814eb05SArnaldo Carvalho de Melo 		return -1;
153ba77c9e1SLi Zefan 
154ba77c9e1SLi Zefan 	total_requested += bytes_req;
155ba77c9e1SLi Zefan 	total_allocated += bytes_alloc;
1567d0d3945SLi Zefan 
1570f7d2f1bSArnaldo Carvalho de Melo 	nr_allocs++;
1580f7d2f1bSArnaldo Carvalho de Melo 	return 0;
1590f7d2f1bSArnaldo Carvalho de Melo }
1600f7d2f1bSArnaldo Carvalho de Melo 
1610f7d2f1bSArnaldo Carvalho de Melo static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
1620f7d2f1bSArnaldo Carvalho de Melo 						struct perf_sample *sample)
1630f7d2f1bSArnaldo Carvalho de Melo {
1640f7d2f1bSArnaldo Carvalho de Melo 	int ret = perf_evsel__process_alloc_event(evsel, sample);
1650f7d2f1bSArnaldo Carvalho de Melo 
1660f7d2f1bSArnaldo Carvalho de Melo 	if (!ret) {
1674b627957SDon Zickus 		int node1 = cpu__get_node(sample->cpu),
1680f7d2f1bSArnaldo Carvalho de Melo 		    node2 = perf_evsel__intval(evsel, sample, "node");
1690f7d2f1bSArnaldo Carvalho de Melo 
1707d0d3945SLi Zefan 		if (node1 != node2)
1717d0d3945SLi Zefan 			nr_cross_allocs++;
1727d0d3945SLi Zefan 	}
1730f7d2f1bSArnaldo Carvalho de Melo 
1740f7d2f1bSArnaldo Carvalho de Melo 	return ret;
175ba77c9e1SLi Zefan }
176ba77c9e1SLi Zefan 
177079d3f65SLi Zefan static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
178079d3f65SLi Zefan static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
179079d3f65SLi Zefan 
180079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr,
181079d3f65SLi Zefan 					    unsigned long call_site,
182079d3f65SLi Zefan 					    struct rb_root *root,
183079d3f65SLi Zefan 					    sort_fn_t sort_fn)
184079d3f65SLi Zefan {
185079d3f65SLi Zefan 	struct rb_node *node = root->rb_node;
186079d3f65SLi Zefan 	struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
187079d3f65SLi Zefan 
188079d3f65SLi Zefan 	while (node) {
189079d3f65SLi Zefan 		struct alloc_stat *data;
190079d3f65SLi Zefan 		int cmp;
191079d3f65SLi Zefan 
192079d3f65SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
193079d3f65SLi Zefan 
194079d3f65SLi Zefan 		cmp = sort_fn(&key, data);
195079d3f65SLi Zefan 		if (cmp < 0)
196079d3f65SLi Zefan 			node = node->rb_left;
197079d3f65SLi Zefan 		else if (cmp > 0)
198079d3f65SLi Zefan 			node = node->rb_right;
199079d3f65SLi Zefan 		else
200079d3f65SLi Zefan 			return data;
201079d3f65SLi Zefan 	}
202079d3f65SLi Zefan 	return NULL;
203079d3f65SLi Zefan }
204079d3f65SLi Zefan 
2052814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_free_event(struct perf_evsel *evsel,
20622ad798cSArnaldo Carvalho de Melo 					  struct perf_sample *sample)
207ba77c9e1SLi Zefan {
2080f7d2f1bSArnaldo Carvalho de Melo 	unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
209079d3f65SLi Zefan 	struct alloc_stat *s_alloc, *s_caller;
210079d3f65SLi Zefan 
211079d3f65SLi Zefan 	s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
212079d3f65SLi Zefan 	if (!s_alloc)
2132814eb05SArnaldo Carvalho de Melo 		return 0;
214079d3f65SLi Zefan 
21522ad798cSArnaldo Carvalho de Melo 	if ((short)sample->cpu != s_alloc->alloc_cpu) {
216079d3f65SLi Zefan 		s_alloc->pingpong++;
217079d3f65SLi Zefan 
218079d3f65SLi Zefan 		s_caller = search_alloc_stat(0, s_alloc->call_site,
219079d3f65SLi Zefan 					     &root_caller_stat, callsite_cmp);
2202814eb05SArnaldo Carvalho de Melo 		if (!s_caller)
2212814eb05SArnaldo Carvalho de Melo 			return -1;
222079d3f65SLi Zefan 		s_caller->pingpong++;
223079d3f65SLi Zefan 	}
224079d3f65SLi Zefan 	s_alloc->alloc_cpu = -1;
2252814eb05SArnaldo Carvalho de Melo 
2262814eb05SArnaldo Carvalho de Melo 	return 0;
227ba77c9e1SLi Zefan }
228ba77c9e1SLi Zefan 
2290f7d2f1bSArnaldo Carvalho de Melo typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
2300f7d2f1bSArnaldo Carvalho de Melo 				  struct perf_sample *sample);
231ba77c9e1SLi Zefan 
2321d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused,
233d20deb64SArnaldo Carvalho de Melo 				union perf_event *event,
2348115d60cSArnaldo Carvalho de Melo 				struct perf_sample *sample,
235fcf65bf1SArnaldo Carvalho de Melo 				struct perf_evsel *evsel,
236743eb868SArnaldo Carvalho de Melo 				struct machine *machine)
237ba77c9e1SLi Zefan {
238ef89325fSAdrian Hunter 	struct thread *thread = machine__findnew_thread(machine, sample->pid,
23913ce34dfSNamhyung Kim 							sample->tid);
240ba77c9e1SLi Zefan 
241ba77c9e1SLi Zefan 	if (thread == NULL) {
242ba77c9e1SLi Zefan 		pr_debug("problem processing %d event, skipping it.\n",
243ba77c9e1SLi Zefan 			 event->header.type);
244ba77c9e1SLi Zefan 		return -1;
245ba77c9e1SLi Zefan 	}
246ba77c9e1SLi Zefan 
247b9c5143aSFrederic Weisbecker 	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
248ba77c9e1SLi Zefan 
249744a9719SArnaldo Carvalho de Melo 	if (evsel->handler != NULL) {
250744a9719SArnaldo Carvalho de Melo 		tracepoint_handler f = evsel->handler;
2510f7d2f1bSArnaldo Carvalho de Melo 		return f(evsel, sample);
2520f7d2f1bSArnaldo Carvalho de Melo 	}
2530f7d2f1bSArnaldo Carvalho de Melo 
2540f7d2f1bSArnaldo Carvalho de Melo 	return 0;
255ba77c9e1SLi Zefan }
256ba77c9e1SLi Zefan 
257fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = {
25855aa640fSArnaldo Carvalho de Melo 	.sample		 = process_sample_event,
2598115d60cSArnaldo Carvalho de Melo 	.comm		 = perf_event__process_comm,
26064c40908SNamhyung Kim 	.mmap		 = perf_event__process_mmap,
26164c40908SNamhyung Kim 	.mmap2		 = perf_event__process_mmap2,
2620a8cb85cSJiri Olsa 	.ordered_events	 = true,
263ba77c9e1SLi Zefan };
264ba77c9e1SLi Zefan 
265ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc)
266ba77c9e1SLi Zefan {
267ba77c9e1SLi Zefan 	if (n_alloc == 0)
268ba77c9e1SLi Zefan 		return 0.0;
269ba77c9e1SLi Zefan 	else
270ba77c9e1SLi Zefan 		return 100.0 - (100.0 * n_req / n_alloc);
271ba77c9e1SLi Zefan }
272ba77c9e1SLi Zefan 
2734aa65636SArnaldo Carvalho de Melo static void __print_result(struct rb_root *root, struct perf_session *session,
2744aa65636SArnaldo Carvalho de Melo 			   int n_lines, int is_caller)
275ba77c9e1SLi Zefan {
276ba77c9e1SLi Zefan 	struct rb_node *next;
27734ba5122SArnaldo Carvalho de Melo 	struct machine *machine = &session->machines.host;
278ba77c9e1SLi Zefan 
27965f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
280079d3f65SLi Zefan 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
281079d3f65SLi Zefan 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
28265f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
283ba77c9e1SLi Zefan 
284ba77c9e1SLi Zefan 	next = rb_first(root);
285ba77c9e1SLi Zefan 
286ba77c9e1SLi Zefan 	while (next && n_lines--) {
2871b145ae5SArnaldo Carvalho de Melo 		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
2881b145ae5SArnaldo Carvalho de Melo 						   node);
2891b145ae5SArnaldo Carvalho de Melo 		struct symbol *sym = NULL;
29071cf8b8fSArnaldo Carvalho de Melo 		struct map *map;
291079d3f65SLi Zefan 		char buf[BUFSIZ];
2921b145ae5SArnaldo Carvalho de Melo 		u64 addr;
293ba77c9e1SLi Zefan 
2941b145ae5SArnaldo Carvalho de Melo 		if (is_caller) {
2951b145ae5SArnaldo Carvalho de Melo 			addr = data->call_site;
2967707b6b6SLi Zefan 			if (!raw_ip)
2975c0541d5SArnaldo Carvalho de Melo 				sym = machine__find_kernel_function(machine, addr, &map, NULL);
2981b145ae5SArnaldo Carvalho de Melo 		} else
2991b145ae5SArnaldo Carvalho de Melo 			addr = data->ptr;
300ba77c9e1SLi Zefan 
3011b145ae5SArnaldo Carvalho de Melo 		if (sym != NULL)
3029486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
30371cf8b8fSArnaldo Carvalho de Melo 				 addr - map->unmap_ip(map, sym->start));
3041b145ae5SArnaldo Carvalho de Melo 		else
3059486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
306079d3f65SLi Zefan 		printf(" %-34s |", buf);
3071b145ae5SArnaldo Carvalho de Melo 
30865f46e02SNamhyung Kim 		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
309079d3f65SLi Zefan 		       (unsigned long long)data->bytes_alloc,
310ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_alloc / data->hit,
311ba77c9e1SLi Zefan 		       (unsigned long long)data->bytes_req,
312ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_req / data->hit,
313ba77c9e1SLi Zefan 		       (unsigned long)data->hit,
314079d3f65SLi Zefan 		       (unsigned long)data->pingpong,
315ba77c9e1SLi Zefan 		       fragmentation(data->bytes_req, data->bytes_alloc));
316ba77c9e1SLi Zefan 
317ba77c9e1SLi Zefan 		next = rb_next(next);
318ba77c9e1SLi Zefan 	}
319ba77c9e1SLi Zefan 
320ba77c9e1SLi Zefan 	if (n_lines == -1)
321079d3f65SLi Zefan 		printf(" ...                                | ...             | ...             | ...      | ...       | ...   \n");
322ba77c9e1SLi Zefan 
32365f46e02SNamhyung Kim 	printf("%.105s\n", graph_dotted_line);
324ba77c9e1SLi Zefan }
325ba77c9e1SLi Zefan 
326ba77c9e1SLi Zefan static void print_summary(void)
327ba77c9e1SLi Zefan {
328ba77c9e1SLi Zefan 	printf("\nSUMMARY\n=======\n");
329*77cfe388SNamhyung Kim 	printf("Total bytes requested: %'lu\n", total_requested);
330*77cfe388SNamhyung Kim 	printf("Total bytes allocated: %'lu\n", total_allocated);
331*77cfe388SNamhyung Kim 	printf("Total bytes wasted on internal fragmentation: %'lu\n",
332ba77c9e1SLi Zefan 	       total_allocated - total_requested);
333ba77c9e1SLi Zefan 	printf("Internal fragmentation: %f%%\n",
334ba77c9e1SLi Zefan 	       fragmentation(total_requested, total_allocated));
335*77cfe388SNamhyung Kim 	printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
336ba77c9e1SLi Zefan }
337ba77c9e1SLi Zefan 
3384aa65636SArnaldo Carvalho de Melo static void print_result(struct perf_session *session)
339ba77c9e1SLi Zefan {
340ba77c9e1SLi Zefan 	if (caller_flag)
3414aa65636SArnaldo Carvalho de Melo 		__print_result(&root_caller_sorted, session, caller_lines, 1);
342ba77c9e1SLi Zefan 	if (alloc_flag)
3434aa65636SArnaldo Carvalho de Melo 		__print_result(&root_alloc_sorted, session, alloc_lines, 0);
344ba77c9e1SLi Zefan 	print_summary();
345ba77c9e1SLi Zefan }
346ba77c9e1SLi Zefan 
34729b3e152SLi Zefan struct sort_dimension {
34829b3e152SLi Zefan 	const char		name[20];
34929b3e152SLi Zefan 	sort_fn_t		cmp;
35029b3e152SLi Zefan 	struct list_head	list;
35129b3e152SLi Zefan };
35229b3e152SLi Zefan 
35329b3e152SLi Zefan static LIST_HEAD(caller_sort);
35429b3e152SLi Zefan static LIST_HEAD(alloc_sort);
35529b3e152SLi Zefan 
356ba77c9e1SLi Zefan static void sort_insert(struct rb_root *root, struct alloc_stat *data,
35729b3e152SLi Zefan 			struct list_head *sort_list)
358ba77c9e1SLi Zefan {
359ba77c9e1SLi Zefan 	struct rb_node **new = &(root->rb_node);
360ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
36129b3e152SLi Zefan 	struct sort_dimension *sort;
362ba77c9e1SLi Zefan 
363ba77c9e1SLi Zefan 	while (*new) {
364ba77c9e1SLi Zefan 		struct alloc_stat *this;
36529b3e152SLi Zefan 		int cmp = 0;
366ba77c9e1SLi Zefan 
367ba77c9e1SLi Zefan 		this = rb_entry(*new, struct alloc_stat, node);
368ba77c9e1SLi Zefan 		parent = *new;
369ba77c9e1SLi Zefan 
37029b3e152SLi Zefan 		list_for_each_entry(sort, sort_list, list) {
37129b3e152SLi Zefan 			cmp = sort->cmp(data, this);
37229b3e152SLi Zefan 			if (cmp)
37329b3e152SLi Zefan 				break;
37429b3e152SLi Zefan 		}
375ba77c9e1SLi Zefan 
376ba77c9e1SLi Zefan 		if (cmp > 0)
377ba77c9e1SLi Zefan 			new = &((*new)->rb_left);
378ba77c9e1SLi Zefan 		else
379ba77c9e1SLi Zefan 			new = &((*new)->rb_right);
380ba77c9e1SLi Zefan 	}
381ba77c9e1SLi Zefan 
382ba77c9e1SLi Zefan 	rb_link_node(&data->node, parent, new);
383ba77c9e1SLi Zefan 	rb_insert_color(&data->node, root);
384ba77c9e1SLi Zefan }
385ba77c9e1SLi Zefan 
386ba77c9e1SLi Zefan static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
38729b3e152SLi Zefan 			  struct list_head *sort_list)
388ba77c9e1SLi Zefan {
389ba77c9e1SLi Zefan 	struct rb_node *node;
390ba77c9e1SLi Zefan 	struct alloc_stat *data;
391ba77c9e1SLi Zefan 
392ba77c9e1SLi Zefan 	for (;;) {
393ba77c9e1SLi Zefan 		node = rb_first(root);
394ba77c9e1SLi Zefan 		if (!node)
395ba77c9e1SLi Zefan 			break;
396ba77c9e1SLi Zefan 
397ba77c9e1SLi Zefan 		rb_erase(node, root);
398ba77c9e1SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
39929b3e152SLi Zefan 		sort_insert(root_sorted, data, sort_list);
400ba77c9e1SLi Zefan 	}
401ba77c9e1SLi Zefan }
402ba77c9e1SLi Zefan 
403ba77c9e1SLi Zefan static void sort_result(void)
404ba77c9e1SLi Zefan {
40529b3e152SLi Zefan 	__sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
40629b3e152SLi Zefan 	__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
407ba77c9e1SLi Zefan }
408ba77c9e1SLi Zefan 
4092b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session)
410ba77c9e1SLi Zefan {
411d549c769SArnaldo Carvalho de Melo 	int err = -EINVAL;
4120f7d2f1bSArnaldo Carvalho de Melo 	const struct perf_evsel_str_handler kmem_tracepoints[] = {
4130f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kmalloc",		perf_evsel__process_alloc_event, },
4140f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_alloc",	perf_evsel__process_alloc_event, },
4150f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kmalloc_node",		perf_evsel__process_alloc_node_event, },
4160f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
4170f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kfree",			perf_evsel__process_free_event, },
4180f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_free",	perf_evsel__process_free_event, },
4190f7d2f1bSArnaldo Carvalho de Melo 	};
420ba77c9e1SLi Zefan 
421d549c769SArnaldo Carvalho de Melo 	if (!perf_session__has_traces(session, "kmem record"))
4222b2b2c68SNamhyung Kim 		goto out;
423d549c769SArnaldo Carvalho de Melo 
4240f7d2f1bSArnaldo Carvalho de Melo 	if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
4250f7d2f1bSArnaldo Carvalho de Melo 		pr_err("Initializing perf session tracepoint handlers failed\n");
4262b2b2c68SNamhyung Kim 		goto out;
4270f7d2f1bSArnaldo Carvalho de Melo 	}
4280f7d2f1bSArnaldo Carvalho de Melo 
4294aa65636SArnaldo Carvalho de Melo 	setup_pager();
430b7b61cbeSArnaldo Carvalho de Melo 	err = perf_session__process_events(session);
4314aa65636SArnaldo Carvalho de Melo 	if (err != 0)
4322b2b2c68SNamhyung Kim 		goto out;
4334aa65636SArnaldo Carvalho de Melo 	sort_result();
4344aa65636SArnaldo Carvalho de Melo 	print_result(session);
4352b2b2c68SNamhyung Kim out:
4364aa65636SArnaldo Carvalho de Melo 	return err;
437ba77c9e1SLi Zefan }
438ba77c9e1SLi Zefan 
439ba77c9e1SLi Zefan static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
440ba77c9e1SLi Zefan {
441ba77c9e1SLi Zefan 	if (l->ptr < r->ptr)
442ba77c9e1SLi Zefan 		return -1;
443ba77c9e1SLi Zefan 	else if (l->ptr > r->ptr)
444ba77c9e1SLi Zefan 		return 1;
445ba77c9e1SLi Zefan 	return 0;
446ba77c9e1SLi Zefan }
447ba77c9e1SLi Zefan 
44829b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = {
44929b3e152SLi Zefan 	.name	= "ptr",
45029b3e152SLi Zefan 	.cmp	= ptr_cmp,
45129b3e152SLi Zefan };
45229b3e152SLi Zefan 
453ba77c9e1SLi Zefan static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
454ba77c9e1SLi Zefan {
455ba77c9e1SLi Zefan 	if (l->call_site < r->call_site)
456ba77c9e1SLi Zefan 		return -1;
457ba77c9e1SLi Zefan 	else if (l->call_site > r->call_site)
458ba77c9e1SLi Zefan 		return 1;
459ba77c9e1SLi Zefan 	return 0;
460ba77c9e1SLi Zefan }
461ba77c9e1SLi Zefan 
46229b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = {
46329b3e152SLi Zefan 	.name	= "callsite",
46429b3e152SLi Zefan 	.cmp	= callsite_cmp,
46529b3e152SLi Zefan };
46629b3e152SLi Zefan 
467f3ced7cdSPekka Enberg static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
468f3ced7cdSPekka Enberg {
469f3ced7cdSPekka Enberg 	if (l->hit < r->hit)
470f3ced7cdSPekka Enberg 		return -1;
471f3ced7cdSPekka Enberg 	else if (l->hit > r->hit)
472f3ced7cdSPekka Enberg 		return 1;
473f3ced7cdSPekka Enberg 	return 0;
474f3ced7cdSPekka Enberg }
475f3ced7cdSPekka Enberg 
47629b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = {
47729b3e152SLi Zefan 	.name	= "hit",
47829b3e152SLi Zefan 	.cmp	= hit_cmp,
47929b3e152SLi Zefan };
48029b3e152SLi Zefan 
481ba77c9e1SLi Zefan static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
482ba77c9e1SLi Zefan {
483ba77c9e1SLi Zefan 	if (l->bytes_alloc < r->bytes_alloc)
484ba77c9e1SLi Zefan 		return -1;
485ba77c9e1SLi Zefan 	else if (l->bytes_alloc > r->bytes_alloc)
486ba77c9e1SLi Zefan 		return 1;
487ba77c9e1SLi Zefan 	return 0;
488ba77c9e1SLi Zefan }
489ba77c9e1SLi Zefan 
49029b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = {
49129b3e152SLi Zefan 	.name	= "bytes",
49229b3e152SLi Zefan 	.cmp	= bytes_cmp,
49329b3e152SLi Zefan };
49429b3e152SLi Zefan 
495f3ced7cdSPekka Enberg static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
496f3ced7cdSPekka Enberg {
497f3ced7cdSPekka Enberg 	double x, y;
498f3ced7cdSPekka Enberg 
499f3ced7cdSPekka Enberg 	x = fragmentation(l->bytes_req, l->bytes_alloc);
500f3ced7cdSPekka Enberg 	y = fragmentation(r->bytes_req, r->bytes_alloc);
501f3ced7cdSPekka Enberg 
502f3ced7cdSPekka Enberg 	if (x < y)
503f3ced7cdSPekka Enberg 		return -1;
504f3ced7cdSPekka Enberg 	else if (x > y)
505f3ced7cdSPekka Enberg 		return 1;
506f3ced7cdSPekka Enberg 	return 0;
507f3ced7cdSPekka Enberg }
508f3ced7cdSPekka Enberg 
50929b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = {
51029b3e152SLi Zefan 	.name	= "frag",
51129b3e152SLi Zefan 	.cmp	= frag_cmp,
51229b3e152SLi Zefan };
51329b3e152SLi Zefan 
514079d3f65SLi Zefan static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
515079d3f65SLi Zefan {
516079d3f65SLi Zefan 	if (l->pingpong < r->pingpong)
517079d3f65SLi Zefan 		return -1;
518079d3f65SLi Zefan 	else if (l->pingpong > r->pingpong)
519079d3f65SLi Zefan 		return 1;
520079d3f65SLi Zefan 	return 0;
521079d3f65SLi Zefan }
522079d3f65SLi Zefan 
523079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = {
524079d3f65SLi Zefan 	.name	= "pingpong",
525079d3f65SLi Zefan 	.cmp	= pingpong_cmp,
526079d3f65SLi Zefan };
527079d3f65SLi Zefan 
52829b3e152SLi Zefan static struct sort_dimension *avail_sorts[] = {
52929b3e152SLi Zefan 	&ptr_sort_dimension,
53029b3e152SLi Zefan 	&callsite_sort_dimension,
53129b3e152SLi Zefan 	&hit_sort_dimension,
53229b3e152SLi Zefan 	&bytes_sort_dimension,
53329b3e152SLi Zefan 	&frag_sort_dimension,
534079d3f65SLi Zefan 	&pingpong_sort_dimension,
53529b3e152SLi Zefan };
53629b3e152SLi Zefan 
53749e4ba54SSasha Levin #define NUM_AVAIL_SORTS	((int)ARRAY_SIZE(avail_sorts))
53829b3e152SLi Zefan 
53929b3e152SLi Zefan static int sort_dimension__add(const char *tok, struct list_head *list)
54029b3e152SLi Zefan {
54129b3e152SLi Zefan 	struct sort_dimension *sort;
54229b3e152SLi Zefan 	int i;
54329b3e152SLi Zefan 
54429b3e152SLi Zefan 	for (i = 0; i < NUM_AVAIL_SORTS; i++) {
54529b3e152SLi Zefan 		if (!strcmp(avail_sorts[i]->name, tok)) {
5468d9233f2SArnaldo Carvalho de Melo 			sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
5472814eb05SArnaldo Carvalho de Melo 			if (!sort) {
5488d9233f2SArnaldo Carvalho de Melo 				pr_err("%s: memdup failed\n", __func__);
5492814eb05SArnaldo Carvalho de Melo 				return -1;
5502814eb05SArnaldo Carvalho de Melo 			}
55129b3e152SLi Zefan 			list_add_tail(&sort->list, list);
55229b3e152SLi Zefan 			return 0;
55329b3e152SLi Zefan 		}
55429b3e152SLi Zefan 	}
55529b3e152SLi Zefan 
55629b3e152SLi Zefan 	return -1;
55729b3e152SLi Zefan }
55829b3e152SLi Zefan 
55929b3e152SLi Zefan static int setup_sorting(struct list_head *sort_list, const char *arg)
56029b3e152SLi Zefan {
56129b3e152SLi Zefan 	char *tok;
56229b3e152SLi Zefan 	char *str = strdup(arg);
563405f8755SNamhyung Kim 	char *pos = str;
56429b3e152SLi Zefan 
5652814eb05SArnaldo Carvalho de Melo 	if (!str) {
5662814eb05SArnaldo Carvalho de Melo 		pr_err("%s: strdup failed\n", __func__);
5672814eb05SArnaldo Carvalho de Melo 		return -1;
5682814eb05SArnaldo Carvalho de Melo 	}
56929b3e152SLi Zefan 
57029b3e152SLi Zefan 	while (true) {
571405f8755SNamhyung Kim 		tok = strsep(&pos, ",");
57229b3e152SLi Zefan 		if (!tok)
57329b3e152SLi Zefan 			break;
57429b3e152SLi Zefan 		if (sort_dimension__add(tok, sort_list) < 0) {
57529b3e152SLi Zefan 			error("Unknown --sort key: '%s'", tok);
5761b22859dSNamhyung Kim 			free(str);
57729b3e152SLi Zefan 			return -1;
57829b3e152SLi Zefan 		}
57929b3e152SLi Zefan 	}
58029b3e152SLi Zefan 
58129b3e152SLi Zefan 	free(str);
58229b3e152SLi Zefan 	return 0;
58329b3e152SLi Zefan }
58429b3e152SLi Zefan 
5851d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused,
5861d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
587ba77c9e1SLi Zefan {
588ba77c9e1SLi Zefan 	if (!arg)
589ba77c9e1SLi Zefan 		return -1;
590ba77c9e1SLi Zefan 
591ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
59229b3e152SLi Zefan 		return setup_sorting(&caller_sort, arg);
593ba77c9e1SLi Zefan 	else
59429b3e152SLi Zefan 		return setup_sorting(&alloc_sort, arg);
595ba77c9e1SLi Zefan 
596ba77c9e1SLi Zefan 	return 0;
597ba77c9e1SLi Zefan }
598ba77c9e1SLi Zefan 
5991d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused,
6001d037ca1SIrina Tirdea 			    const char *arg __maybe_unused,
6011d037ca1SIrina Tirdea 			    int unset __maybe_unused)
602ba77c9e1SLi Zefan {
603ba77c9e1SLi Zefan 	caller_flag = (alloc_flag + 1);
60490b86a9fSLi Zefan 	return 0;
60590b86a9fSLi Zefan }
60690b86a9fSLi Zefan 
6071d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused,
6081d037ca1SIrina Tirdea 			   const char *arg __maybe_unused,
6091d037ca1SIrina Tirdea 			   int unset __maybe_unused)
61090b86a9fSLi Zefan {
61190b86a9fSLi Zefan 	alloc_flag = (caller_flag + 1);
612ba77c9e1SLi Zefan 	return 0;
613ba77c9e1SLi Zefan }
614ba77c9e1SLi Zefan 
6151d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused,
6161d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
617ba77c9e1SLi Zefan {
618ba77c9e1SLi Zefan 	int lines;
619ba77c9e1SLi Zefan 
620ba77c9e1SLi Zefan 	if (!arg)
621ba77c9e1SLi Zefan 		return -1;
622ba77c9e1SLi Zefan 
623ba77c9e1SLi Zefan 	lines = strtoul(arg, NULL, 10);
624ba77c9e1SLi Zefan 
625ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
626ba77c9e1SLi Zefan 		caller_lines = lines;
627ba77c9e1SLi Zefan 	else
628ba77c9e1SLi Zefan 		alloc_lines = lines;
629ba77c9e1SLi Zefan 
630ba77c9e1SLi Zefan 	return 0;
631ba77c9e1SLi Zefan }
632ba77c9e1SLi Zefan 
6330433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv)
6340433ffbeSArnaldo Carvalho de Melo {
6350433ffbeSArnaldo Carvalho de Melo 	const char * const record_args[] = {
6364a4d371aSJiri Olsa 	"record", "-a", "-R", "-c", "1",
637ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc",
638ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc_node",
639ba77c9e1SLi Zefan 	"-e", "kmem:kfree",
640ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc",
641ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc_node",
642ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_free",
643ba77c9e1SLi Zefan 	};
644ba77c9e1SLi Zefan 	unsigned int rec_argc, i, j;
645ba77c9e1SLi Zefan 	const char **rec_argv;
646ba77c9e1SLi Zefan 
647ba77c9e1SLi Zefan 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
648ba77c9e1SLi Zefan 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
649ba77c9e1SLi Zefan 
650ce47dc56SChris Samuel 	if (rec_argv == NULL)
651ce47dc56SChris Samuel 		return -ENOMEM;
652ce47dc56SChris Samuel 
653ba77c9e1SLi Zefan 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
654ba77c9e1SLi Zefan 		rec_argv[i] = strdup(record_args[i]);
655ba77c9e1SLi Zefan 
656ba77c9e1SLi Zefan 	for (j = 1; j < (unsigned int)argc; j++, i++)
657ba77c9e1SLi Zefan 		rec_argv[i] = argv[j];
658ba77c9e1SLi Zefan 
659ba77c9e1SLi Zefan 	return cmd_record(i, rec_argv, NULL);
660ba77c9e1SLi Zefan }
661ba77c9e1SLi Zefan 
6621d037ca1SIrina Tirdea int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
663ba77c9e1SLi Zefan {
6640433ffbeSArnaldo Carvalho de Melo 	const char * const default_sort_order = "frag,hit,bytes";
6650433ffbeSArnaldo Carvalho de Melo 	const struct option kmem_options[] = {
6660433ffbeSArnaldo Carvalho de Melo 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
667bd72a33eSNamhyung Kim 	OPT_INCR('v', "verbose", &verbose,
668bd72a33eSNamhyung Kim 		    "be more verbose (show symbol address, etc)"),
6690433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
6700433ffbeSArnaldo Carvalho de Melo 			   "show per-callsite statistics", parse_caller_opt),
6710433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
6720433ffbeSArnaldo Carvalho de Melo 			   "show per-allocation statistics", parse_alloc_opt),
6730433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
6740433ffbeSArnaldo Carvalho de Melo 		     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
6750433ffbeSArnaldo Carvalho de Melo 		     parse_sort_opt),
6760433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
6770433ffbeSArnaldo Carvalho de Melo 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
6780433ffbeSArnaldo Carvalho de Melo 	OPT_END()
6790433ffbeSArnaldo Carvalho de Melo 	};
6803bca2354SRamkumar Ramachandra 	const char *const kmem_subcommands[] = { "record", "stat", NULL };
6813bca2354SRamkumar Ramachandra 	const char *kmem_usage[] = {
6823bca2354SRamkumar Ramachandra 		NULL,
6830433ffbeSArnaldo Carvalho de Melo 		NULL
6840433ffbeSArnaldo Carvalho de Melo 	};
6852b2b2c68SNamhyung Kim 	struct perf_session *session;
6862b2b2c68SNamhyung Kim 	struct perf_data_file file = {
6872b2b2c68SNamhyung Kim 		.path = input_name,
6882b2b2c68SNamhyung Kim 		.mode = PERF_DATA_MODE_READ,
6892b2b2c68SNamhyung Kim 	};
6902b2b2c68SNamhyung Kim 	int ret = -1;
6912b2b2c68SNamhyung Kim 
6923bca2354SRamkumar Ramachandra 	argc = parse_options_subcommand(argc, argv, kmem_options,
6933bca2354SRamkumar Ramachandra 					kmem_subcommands, kmem_usage, 0);
694ba77c9e1SLi Zefan 
69590b86a9fSLi Zefan 	if (!argc)
696ba77c9e1SLi Zefan 		usage_with_options(kmem_usage, kmem_options);
697ba77c9e1SLi Zefan 
6982b2b2c68SNamhyung Kim 	if (!strncmp(argv[0], "rec", 3)) {
6990a7e6d1bSNamhyung Kim 		symbol__init(NULL);
7002b2b2c68SNamhyung Kim 		return __cmd_record(argc, argv);
7012b2b2c68SNamhyung Kim 	}
7022b2b2c68SNamhyung Kim 
7032b2b2c68SNamhyung Kim 	session = perf_session__new(&file, false, &perf_kmem);
7042b2b2c68SNamhyung Kim 	if (session == NULL)
70552e02834STaeung Song 		return -1;
7062b2b2c68SNamhyung Kim 
7070a7e6d1bSNamhyung Kim 	symbol__init(&session->header.env);
708655000e7SArnaldo Carvalho de Melo 
7092b2b2c68SNamhyung Kim 	if (!strcmp(argv[0], "stat")) {
710*77cfe388SNamhyung Kim 		setlocale(LC_ALL, "");
711*77cfe388SNamhyung Kim 
7124b627957SDon Zickus 		if (cpu__setup_cpunode_map())
7132b2b2c68SNamhyung Kim 			goto out_delete;
71490b86a9fSLi Zefan 
71529b3e152SLi Zefan 		if (list_empty(&caller_sort))
71629b3e152SLi Zefan 			setup_sorting(&caller_sort, default_sort_order);
71729b3e152SLi Zefan 		if (list_empty(&alloc_sort))
71829b3e152SLi Zefan 			setup_sorting(&alloc_sort, default_sort_order);
719ba77c9e1SLi Zefan 
7202b2b2c68SNamhyung Kim 		ret = __cmd_kmem(session);
721b00eca8cSPekka Enberg 	} else
722b00eca8cSPekka Enberg 		usage_with_options(kmem_usage, kmem_options);
723ba77c9e1SLi Zefan 
7242b2b2c68SNamhyung Kim out_delete:
7252b2b2c68SNamhyung Kim 	perf_session__delete(session);
7262b2b2c68SNamhyung Kim 
7272b2b2c68SNamhyung Kim 	return ret;
72890b86a9fSLi Zefan }
72990b86a9fSLi Zefan 
730