xref: /openbmc/linux/tools/perf/builtin-kmem.c (revision b7b61cbebd789a3dbca522e3fdb727fe5c95593f)
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>
23ba77c9e1SLi Zefan 
24ba77c9e1SLi Zefan struct alloc_stat;
25ba77c9e1SLi Zefan typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
26ba77c9e1SLi Zefan 
27ba77c9e1SLi Zefan static int			alloc_flag;
28ba77c9e1SLi Zefan static int			caller_flag;
29ba77c9e1SLi Zefan 
30ba77c9e1SLi Zefan static int			alloc_lines = -1;
31ba77c9e1SLi Zefan static int			caller_lines = -1;
32ba77c9e1SLi Zefan 
337707b6b6SLi Zefan static bool			raw_ip;
347707b6b6SLi Zefan 
35ba77c9e1SLi Zefan struct alloc_stat {
36ba77c9e1SLi Zefan 	u64	call_site;
37ba77c9e1SLi Zefan 	u64	ptr;
38ba77c9e1SLi Zefan 	u64	bytes_req;
39ba77c9e1SLi Zefan 	u64	bytes_alloc;
40ba77c9e1SLi Zefan 	u32	hit;
41079d3f65SLi Zefan 	u32	pingpong;
42079d3f65SLi Zefan 
43079d3f65SLi Zefan 	short	alloc_cpu;
44ba77c9e1SLi Zefan 
45ba77c9e1SLi Zefan 	struct rb_node node;
46ba77c9e1SLi Zefan };
47ba77c9e1SLi Zefan 
48ba77c9e1SLi Zefan static struct rb_root root_alloc_stat;
49ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted;
50ba77c9e1SLi Zefan static struct rb_root root_caller_stat;
51ba77c9e1SLi Zefan static struct rb_root root_caller_sorted;
52ba77c9e1SLi Zefan 
53ba77c9e1SLi Zefan static unsigned long total_requested, total_allocated;
547d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs;
55ba77c9e1SLi Zefan 
562814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
57079d3f65SLi Zefan 			     int bytes_req, int bytes_alloc, int cpu)
58ba77c9e1SLi Zefan {
59ba77c9e1SLi Zefan 	struct rb_node **node = &root_alloc_stat.rb_node;
60ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
61ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
62ba77c9e1SLi Zefan 
63ba77c9e1SLi Zefan 	while (*node) {
64ba77c9e1SLi Zefan 		parent = *node;
65ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
66ba77c9e1SLi Zefan 
67ba77c9e1SLi Zefan 		if (ptr > data->ptr)
68ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
69ba77c9e1SLi Zefan 		else if (ptr < data->ptr)
70ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
71ba77c9e1SLi Zefan 		else
72ba77c9e1SLi Zefan 			break;
73ba77c9e1SLi Zefan 	}
74ba77c9e1SLi Zefan 
75ba77c9e1SLi Zefan 	if (data && data->ptr == ptr) {
76ba77c9e1SLi Zefan 		data->hit++;
77ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
784efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
79ba77c9e1SLi Zefan 	} else {
80ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
812814eb05SArnaldo Carvalho de Melo 		if (!data) {
822814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
832814eb05SArnaldo Carvalho de Melo 			return -1;
842814eb05SArnaldo Carvalho de Melo 		}
85ba77c9e1SLi Zefan 		data->ptr = ptr;
86079d3f65SLi Zefan 		data->pingpong = 0;
87ba77c9e1SLi Zefan 		data->hit = 1;
88ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
89ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
90ba77c9e1SLi Zefan 
91ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
92ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_alloc_stat);
93ba77c9e1SLi Zefan 	}
94079d3f65SLi Zefan 	data->call_site = call_site;
95079d3f65SLi Zefan 	data->alloc_cpu = cpu;
962814eb05SArnaldo Carvalho de Melo 	return 0;
97ba77c9e1SLi Zefan }
98ba77c9e1SLi Zefan 
992814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site,
100ba77c9e1SLi Zefan 			      int bytes_req, int bytes_alloc)
101ba77c9e1SLi Zefan {
102ba77c9e1SLi Zefan 	struct rb_node **node = &root_caller_stat.rb_node;
103ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
104ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
105ba77c9e1SLi Zefan 
106ba77c9e1SLi Zefan 	while (*node) {
107ba77c9e1SLi Zefan 		parent = *node;
108ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
109ba77c9e1SLi Zefan 
110ba77c9e1SLi Zefan 		if (call_site > data->call_site)
111ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
112ba77c9e1SLi Zefan 		else if (call_site < data->call_site)
113ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
114ba77c9e1SLi Zefan 		else
115ba77c9e1SLi Zefan 			break;
116ba77c9e1SLi Zefan 	}
117ba77c9e1SLi Zefan 
118ba77c9e1SLi Zefan 	if (data && data->call_site == call_site) {
119ba77c9e1SLi Zefan 		data->hit++;
120ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
1214efb5290SWenji Huang 		data->bytes_alloc += bytes_alloc;
122ba77c9e1SLi Zefan 	} else {
123ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
1242814eb05SArnaldo Carvalho de Melo 		if (!data) {
1252814eb05SArnaldo Carvalho de Melo 			pr_err("%s: malloc failed\n", __func__);
1262814eb05SArnaldo Carvalho de Melo 			return -1;
1272814eb05SArnaldo Carvalho de Melo 		}
128ba77c9e1SLi Zefan 		data->call_site = call_site;
129079d3f65SLi Zefan 		data->pingpong = 0;
130ba77c9e1SLi Zefan 		data->hit = 1;
131ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
132ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
133ba77c9e1SLi Zefan 
134ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
135ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_caller_stat);
136ba77c9e1SLi Zefan 	}
1372814eb05SArnaldo Carvalho de Melo 
1382814eb05SArnaldo Carvalho de Melo 	return 0;
139ba77c9e1SLi Zefan }
140ba77c9e1SLi Zefan 
1412814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
1420f7d2f1bSArnaldo Carvalho de Melo 					   struct perf_sample *sample)
143ba77c9e1SLi Zefan {
1440f7d2f1bSArnaldo Carvalho de Melo 	unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
1450f7d2f1bSArnaldo Carvalho de Melo 		      call_site = perf_evsel__intval(evsel, sample, "call_site");
1460f7d2f1bSArnaldo Carvalho de Melo 	int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
1470f7d2f1bSArnaldo Carvalho de Melo 	    bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
148ba77c9e1SLi Zefan 
1490f7d2f1bSArnaldo Carvalho de Melo 	if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
1502814eb05SArnaldo Carvalho de Melo 	    insert_caller_stat(call_site, bytes_req, bytes_alloc))
1512814eb05SArnaldo Carvalho de Melo 		return -1;
152ba77c9e1SLi Zefan 
153ba77c9e1SLi Zefan 	total_requested += bytes_req;
154ba77c9e1SLi Zefan 	total_allocated += bytes_alloc;
1557d0d3945SLi Zefan 
1560f7d2f1bSArnaldo Carvalho de Melo 	nr_allocs++;
1570f7d2f1bSArnaldo Carvalho de Melo 	return 0;
1580f7d2f1bSArnaldo Carvalho de Melo }
1590f7d2f1bSArnaldo Carvalho de Melo 
1600f7d2f1bSArnaldo Carvalho de Melo static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
1610f7d2f1bSArnaldo Carvalho de Melo 						struct perf_sample *sample)
1620f7d2f1bSArnaldo Carvalho de Melo {
1630f7d2f1bSArnaldo Carvalho de Melo 	int ret = perf_evsel__process_alloc_event(evsel, sample);
1640f7d2f1bSArnaldo Carvalho de Melo 
1650f7d2f1bSArnaldo Carvalho de Melo 	if (!ret) {
1664b627957SDon Zickus 		int node1 = cpu__get_node(sample->cpu),
1670f7d2f1bSArnaldo Carvalho de Melo 		    node2 = perf_evsel__intval(evsel, sample, "node");
1680f7d2f1bSArnaldo Carvalho de Melo 
1697d0d3945SLi Zefan 		if (node1 != node2)
1707d0d3945SLi Zefan 			nr_cross_allocs++;
1717d0d3945SLi Zefan 	}
1720f7d2f1bSArnaldo Carvalho de Melo 
1730f7d2f1bSArnaldo Carvalho de Melo 	return ret;
174ba77c9e1SLi Zefan }
175ba77c9e1SLi Zefan 
176079d3f65SLi Zefan static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
177079d3f65SLi Zefan static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
178079d3f65SLi Zefan 
179079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr,
180079d3f65SLi Zefan 					    unsigned long call_site,
181079d3f65SLi Zefan 					    struct rb_root *root,
182079d3f65SLi Zefan 					    sort_fn_t sort_fn)
183079d3f65SLi Zefan {
184079d3f65SLi Zefan 	struct rb_node *node = root->rb_node;
185079d3f65SLi Zefan 	struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
186079d3f65SLi Zefan 
187079d3f65SLi Zefan 	while (node) {
188079d3f65SLi Zefan 		struct alloc_stat *data;
189079d3f65SLi Zefan 		int cmp;
190079d3f65SLi Zefan 
191079d3f65SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
192079d3f65SLi Zefan 
193079d3f65SLi Zefan 		cmp = sort_fn(&key, data);
194079d3f65SLi Zefan 		if (cmp < 0)
195079d3f65SLi Zefan 			node = node->rb_left;
196079d3f65SLi Zefan 		else if (cmp > 0)
197079d3f65SLi Zefan 			node = node->rb_right;
198079d3f65SLi Zefan 		else
199079d3f65SLi Zefan 			return data;
200079d3f65SLi Zefan 	}
201079d3f65SLi Zefan 	return NULL;
202079d3f65SLi Zefan }
203079d3f65SLi Zefan 
2042814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_free_event(struct perf_evsel *evsel,
20522ad798cSArnaldo Carvalho de Melo 					  struct perf_sample *sample)
206ba77c9e1SLi Zefan {
2070f7d2f1bSArnaldo Carvalho de Melo 	unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
208079d3f65SLi Zefan 	struct alloc_stat *s_alloc, *s_caller;
209079d3f65SLi Zefan 
210079d3f65SLi Zefan 	s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
211079d3f65SLi Zefan 	if (!s_alloc)
2122814eb05SArnaldo Carvalho de Melo 		return 0;
213079d3f65SLi Zefan 
21422ad798cSArnaldo Carvalho de Melo 	if ((short)sample->cpu != s_alloc->alloc_cpu) {
215079d3f65SLi Zefan 		s_alloc->pingpong++;
216079d3f65SLi Zefan 
217079d3f65SLi Zefan 		s_caller = search_alloc_stat(0, s_alloc->call_site,
218079d3f65SLi Zefan 					     &root_caller_stat, callsite_cmp);
2192814eb05SArnaldo Carvalho de Melo 		if (!s_caller)
2202814eb05SArnaldo Carvalho de Melo 			return -1;
221079d3f65SLi Zefan 		s_caller->pingpong++;
222079d3f65SLi Zefan 	}
223079d3f65SLi Zefan 	s_alloc->alloc_cpu = -1;
2242814eb05SArnaldo Carvalho de Melo 
2252814eb05SArnaldo Carvalho de Melo 	return 0;
226ba77c9e1SLi Zefan }
227ba77c9e1SLi Zefan 
2280f7d2f1bSArnaldo Carvalho de Melo typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
2290f7d2f1bSArnaldo Carvalho de Melo 				  struct perf_sample *sample);
230ba77c9e1SLi Zefan 
2311d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused,
232d20deb64SArnaldo Carvalho de Melo 				union perf_event *event,
2338115d60cSArnaldo Carvalho de Melo 				struct perf_sample *sample,
234fcf65bf1SArnaldo Carvalho de Melo 				struct perf_evsel *evsel,
235743eb868SArnaldo Carvalho de Melo 				struct machine *machine)
236ba77c9e1SLi Zefan {
237ef89325fSAdrian Hunter 	struct thread *thread = machine__findnew_thread(machine, sample->pid,
23813ce34dfSNamhyung Kim 							sample->tid);
239ba77c9e1SLi Zefan 
240ba77c9e1SLi Zefan 	if (thread == NULL) {
241ba77c9e1SLi Zefan 		pr_debug("problem processing %d event, skipping it.\n",
242ba77c9e1SLi Zefan 			 event->header.type);
243ba77c9e1SLi Zefan 		return -1;
244ba77c9e1SLi Zefan 	}
245ba77c9e1SLi Zefan 
246b9c5143aSFrederic Weisbecker 	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
247ba77c9e1SLi Zefan 
248744a9719SArnaldo Carvalho de Melo 	if (evsel->handler != NULL) {
249744a9719SArnaldo Carvalho de Melo 		tracepoint_handler f = evsel->handler;
2500f7d2f1bSArnaldo Carvalho de Melo 		return f(evsel, sample);
2510f7d2f1bSArnaldo Carvalho de Melo 	}
2520f7d2f1bSArnaldo Carvalho de Melo 
2530f7d2f1bSArnaldo Carvalho de Melo 	return 0;
254ba77c9e1SLi Zefan }
255ba77c9e1SLi Zefan 
256fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = {
25755aa640fSArnaldo Carvalho de Melo 	.sample		 = process_sample_event,
2588115d60cSArnaldo Carvalho de Melo 	.comm		 = perf_event__process_comm,
25964c40908SNamhyung Kim 	.mmap		 = perf_event__process_mmap,
26064c40908SNamhyung Kim 	.mmap2		 = perf_event__process_mmap2,
2610a8cb85cSJiri Olsa 	.ordered_events	 = true,
262ba77c9e1SLi Zefan };
263ba77c9e1SLi Zefan 
264ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc)
265ba77c9e1SLi Zefan {
266ba77c9e1SLi Zefan 	if (n_alloc == 0)
267ba77c9e1SLi Zefan 		return 0.0;
268ba77c9e1SLi Zefan 	else
269ba77c9e1SLi Zefan 		return 100.0 - (100.0 * n_req / n_alloc);
270ba77c9e1SLi Zefan }
271ba77c9e1SLi Zefan 
2724aa65636SArnaldo Carvalho de Melo static void __print_result(struct rb_root *root, struct perf_session *session,
2734aa65636SArnaldo Carvalho de Melo 			   int n_lines, int is_caller)
274ba77c9e1SLi Zefan {
275ba77c9e1SLi Zefan 	struct rb_node *next;
27634ba5122SArnaldo Carvalho de Melo 	struct machine *machine = &session->machines.host;
277ba77c9e1SLi Zefan 
278079d3f65SLi Zefan 	printf("%.102s\n", graph_dotted_line);
279079d3f65SLi Zefan 	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
280079d3f65SLi Zefan 	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
281079d3f65SLi Zefan 	printf("%.102s\n", graph_dotted_line);
282ba77c9e1SLi Zefan 
283ba77c9e1SLi Zefan 	next = rb_first(root);
284ba77c9e1SLi Zefan 
285ba77c9e1SLi Zefan 	while (next && n_lines--) {
2861b145ae5SArnaldo Carvalho de Melo 		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
2871b145ae5SArnaldo Carvalho de Melo 						   node);
2881b145ae5SArnaldo Carvalho de Melo 		struct symbol *sym = NULL;
28971cf8b8fSArnaldo Carvalho de Melo 		struct map *map;
290079d3f65SLi Zefan 		char buf[BUFSIZ];
2911b145ae5SArnaldo Carvalho de Melo 		u64 addr;
292ba77c9e1SLi Zefan 
2931b145ae5SArnaldo Carvalho de Melo 		if (is_caller) {
2941b145ae5SArnaldo Carvalho de Melo 			addr = data->call_site;
2957707b6b6SLi Zefan 			if (!raw_ip)
2965c0541d5SArnaldo Carvalho de Melo 				sym = machine__find_kernel_function(machine, addr, &map, NULL);
2971b145ae5SArnaldo Carvalho de Melo 		} else
2981b145ae5SArnaldo Carvalho de Melo 			addr = data->ptr;
299ba77c9e1SLi Zefan 
3001b145ae5SArnaldo Carvalho de Melo 		if (sym != NULL)
3019486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
30271cf8b8fSArnaldo Carvalho de Melo 				 addr - map->unmap_ip(map, sym->start));
3031b145ae5SArnaldo Carvalho de Melo 		else
3049486aa38SArnaldo Carvalho de Melo 			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
305079d3f65SLi Zefan 		printf(" %-34s |", buf);
3061b145ae5SArnaldo Carvalho de Melo 
30747103277SPekka Enberg 		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
308079d3f65SLi Zefan 		       (unsigned long long)data->bytes_alloc,
309ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_alloc / data->hit,
310ba77c9e1SLi Zefan 		       (unsigned long long)data->bytes_req,
311ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_req / data->hit,
312ba77c9e1SLi Zefan 		       (unsigned long)data->hit,
313079d3f65SLi Zefan 		       (unsigned long)data->pingpong,
314ba77c9e1SLi Zefan 		       fragmentation(data->bytes_req, data->bytes_alloc));
315ba77c9e1SLi Zefan 
316ba77c9e1SLi Zefan 		next = rb_next(next);
317ba77c9e1SLi Zefan 	}
318ba77c9e1SLi Zefan 
319ba77c9e1SLi Zefan 	if (n_lines == -1)
320079d3f65SLi Zefan 		printf(" ...                                | ...             | ...             | ...    | ...      | ...   \n");
321ba77c9e1SLi Zefan 
322079d3f65SLi Zefan 	printf("%.102s\n", graph_dotted_line);
323ba77c9e1SLi Zefan }
324ba77c9e1SLi Zefan 
325ba77c9e1SLi Zefan static void print_summary(void)
326ba77c9e1SLi Zefan {
327ba77c9e1SLi Zefan 	printf("\nSUMMARY\n=======\n");
328ba77c9e1SLi Zefan 	printf("Total bytes requested: %lu\n", total_requested);
329ba77c9e1SLi Zefan 	printf("Total bytes allocated: %lu\n", total_allocated);
330ba77c9e1SLi Zefan 	printf("Total bytes wasted on internal fragmentation: %lu\n",
331ba77c9e1SLi Zefan 	       total_allocated - total_requested);
332ba77c9e1SLi Zefan 	printf("Internal fragmentation: %f%%\n",
333ba77c9e1SLi Zefan 	       fragmentation(total_requested, total_allocated));
3347d0d3945SLi Zefan 	printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
335ba77c9e1SLi Zefan }
336ba77c9e1SLi Zefan 
3374aa65636SArnaldo Carvalho de Melo static void print_result(struct perf_session *session)
338ba77c9e1SLi Zefan {
339ba77c9e1SLi Zefan 	if (caller_flag)
3404aa65636SArnaldo Carvalho de Melo 		__print_result(&root_caller_sorted, session, caller_lines, 1);
341ba77c9e1SLi Zefan 	if (alloc_flag)
3424aa65636SArnaldo Carvalho de Melo 		__print_result(&root_alloc_sorted, session, alloc_lines, 0);
343ba77c9e1SLi Zefan 	print_summary();
344ba77c9e1SLi Zefan }
345ba77c9e1SLi Zefan 
34629b3e152SLi Zefan struct sort_dimension {
34729b3e152SLi Zefan 	const char		name[20];
34829b3e152SLi Zefan 	sort_fn_t		cmp;
34929b3e152SLi Zefan 	struct list_head	list;
35029b3e152SLi Zefan };
35129b3e152SLi Zefan 
35229b3e152SLi Zefan static LIST_HEAD(caller_sort);
35329b3e152SLi Zefan static LIST_HEAD(alloc_sort);
35429b3e152SLi Zefan 
355ba77c9e1SLi Zefan static void sort_insert(struct rb_root *root, struct alloc_stat *data,
35629b3e152SLi Zefan 			struct list_head *sort_list)
357ba77c9e1SLi Zefan {
358ba77c9e1SLi Zefan 	struct rb_node **new = &(root->rb_node);
359ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
36029b3e152SLi Zefan 	struct sort_dimension *sort;
361ba77c9e1SLi Zefan 
362ba77c9e1SLi Zefan 	while (*new) {
363ba77c9e1SLi Zefan 		struct alloc_stat *this;
36429b3e152SLi Zefan 		int cmp = 0;
365ba77c9e1SLi Zefan 
366ba77c9e1SLi Zefan 		this = rb_entry(*new, struct alloc_stat, node);
367ba77c9e1SLi Zefan 		parent = *new;
368ba77c9e1SLi Zefan 
36929b3e152SLi Zefan 		list_for_each_entry(sort, sort_list, list) {
37029b3e152SLi Zefan 			cmp = sort->cmp(data, this);
37129b3e152SLi Zefan 			if (cmp)
37229b3e152SLi Zefan 				break;
37329b3e152SLi Zefan 		}
374ba77c9e1SLi Zefan 
375ba77c9e1SLi Zefan 		if (cmp > 0)
376ba77c9e1SLi Zefan 			new = &((*new)->rb_left);
377ba77c9e1SLi Zefan 		else
378ba77c9e1SLi Zefan 			new = &((*new)->rb_right);
379ba77c9e1SLi Zefan 	}
380ba77c9e1SLi Zefan 
381ba77c9e1SLi Zefan 	rb_link_node(&data->node, parent, new);
382ba77c9e1SLi Zefan 	rb_insert_color(&data->node, root);
383ba77c9e1SLi Zefan }
384ba77c9e1SLi Zefan 
385ba77c9e1SLi Zefan static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
38629b3e152SLi Zefan 			  struct list_head *sort_list)
387ba77c9e1SLi Zefan {
388ba77c9e1SLi Zefan 	struct rb_node *node;
389ba77c9e1SLi Zefan 	struct alloc_stat *data;
390ba77c9e1SLi Zefan 
391ba77c9e1SLi Zefan 	for (;;) {
392ba77c9e1SLi Zefan 		node = rb_first(root);
393ba77c9e1SLi Zefan 		if (!node)
394ba77c9e1SLi Zefan 			break;
395ba77c9e1SLi Zefan 
396ba77c9e1SLi Zefan 		rb_erase(node, root);
397ba77c9e1SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
39829b3e152SLi Zefan 		sort_insert(root_sorted, data, sort_list);
399ba77c9e1SLi Zefan 	}
400ba77c9e1SLi Zefan }
401ba77c9e1SLi Zefan 
402ba77c9e1SLi Zefan static void sort_result(void)
403ba77c9e1SLi Zefan {
40429b3e152SLi Zefan 	__sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
40529b3e152SLi Zefan 	__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
406ba77c9e1SLi Zefan }
407ba77c9e1SLi Zefan 
4082b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session)
409ba77c9e1SLi Zefan {
410d549c769SArnaldo Carvalho de Melo 	int err = -EINVAL;
4110f7d2f1bSArnaldo Carvalho de Melo 	const struct perf_evsel_str_handler kmem_tracepoints[] = {
4120f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kmalloc",		perf_evsel__process_alloc_event, },
4130f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_alloc",	perf_evsel__process_alloc_event, },
4140f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kmalloc_node",		perf_evsel__process_alloc_node_event, },
4150f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
4160f7d2f1bSArnaldo Carvalho de Melo 		{ "kmem:kfree",			perf_evsel__process_free_event, },
4170f7d2f1bSArnaldo Carvalho de Melo     		{ "kmem:kmem_cache_free",	perf_evsel__process_free_event, },
4180f7d2f1bSArnaldo Carvalho de Melo 	};
419ba77c9e1SLi Zefan 
420d549c769SArnaldo Carvalho de Melo 	if (!perf_session__has_traces(session, "kmem record"))
4212b2b2c68SNamhyung Kim 		goto out;
422d549c769SArnaldo Carvalho de Melo 
4230f7d2f1bSArnaldo Carvalho de Melo 	if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
4240f7d2f1bSArnaldo Carvalho de Melo 		pr_err("Initializing perf session tracepoint handlers failed\n");
4252b2b2c68SNamhyung Kim 		goto out;
4260f7d2f1bSArnaldo Carvalho de Melo 	}
4270f7d2f1bSArnaldo Carvalho de Melo 
4284aa65636SArnaldo Carvalho de Melo 	setup_pager();
429*b7b61cbeSArnaldo Carvalho de Melo 	err = perf_session__process_events(session);
4304aa65636SArnaldo Carvalho de Melo 	if (err != 0)
4312b2b2c68SNamhyung Kim 		goto out;
4324aa65636SArnaldo Carvalho de Melo 	sort_result();
4334aa65636SArnaldo Carvalho de Melo 	print_result(session);
4342b2b2c68SNamhyung Kim out:
4354aa65636SArnaldo Carvalho de Melo 	return err;
436ba77c9e1SLi Zefan }
437ba77c9e1SLi Zefan 
438ba77c9e1SLi Zefan static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
439ba77c9e1SLi Zefan {
440ba77c9e1SLi Zefan 	if (l->ptr < r->ptr)
441ba77c9e1SLi Zefan 		return -1;
442ba77c9e1SLi Zefan 	else if (l->ptr > r->ptr)
443ba77c9e1SLi Zefan 		return 1;
444ba77c9e1SLi Zefan 	return 0;
445ba77c9e1SLi Zefan }
446ba77c9e1SLi Zefan 
44729b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = {
44829b3e152SLi Zefan 	.name	= "ptr",
44929b3e152SLi Zefan 	.cmp	= ptr_cmp,
45029b3e152SLi Zefan };
45129b3e152SLi Zefan 
452ba77c9e1SLi Zefan static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
453ba77c9e1SLi Zefan {
454ba77c9e1SLi Zefan 	if (l->call_site < r->call_site)
455ba77c9e1SLi Zefan 		return -1;
456ba77c9e1SLi Zefan 	else if (l->call_site > r->call_site)
457ba77c9e1SLi Zefan 		return 1;
458ba77c9e1SLi Zefan 	return 0;
459ba77c9e1SLi Zefan }
460ba77c9e1SLi Zefan 
46129b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = {
46229b3e152SLi Zefan 	.name	= "callsite",
46329b3e152SLi Zefan 	.cmp	= callsite_cmp,
46429b3e152SLi Zefan };
46529b3e152SLi Zefan 
466f3ced7cdSPekka Enberg static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
467f3ced7cdSPekka Enberg {
468f3ced7cdSPekka Enberg 	if (l->hit < r->hit)
469f3ced7cdSPekka Enberg 		return -1;
470f3ced7cdSPekka Enberg 	else if (l->hit > r->hit)
471f3ced7cdSPekka Enberg 		return 1;
472f3ced7cdSPekka Enberg 	return 0;
473f3ced7cdSPekka Enberg }
474f3ced7cdSPekka Enberg 
47529b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = {
47629b3e152SLi Zefan 	.name	= "hit",
47729b3e152SLi Zefan 	.cmp	= hit_cmp,
47829b3e152SLi Zefan };
47929b3e152SLi Zefan 
480ba77c9e1SLi Zefan static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
481ba77c9e1SLi Zefan {
482ba77c9e1SLi Zefan 	if (l->bytes_alloc < r->bytes_alloc)
483ba77c9e1SLi Zefan 		return -1;
484ba77c9e1SLi Zefan 	else if (l->bytes_alloc > r->bytes_alloc)
485ba77c9e1SLi Zefan 		return 1;
486ba77c9e1SLi Zefan 	return 0;
487ba77c9e1SLi Zefan }
488ba77c9e1SLi Zefan 
48929b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = {
49029b3e152SLi Zefan 	.name	= "bytes",
49129b3e152SLi Zefan 	.cmp	= bytes_cmp,
49229b3e152SLi Zefan };
49329b3e152SLi Zefan 
494f3ced7cdSPekka Enberg static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
495f3ced7cdSPekka Enberg {
496f3ced7cdSPekka Enberg 	double x, y;
497f3ced7cdSPekka Enberg 
498f3ced7cdSPekka Enberg 	x = fragmentation(l->bytes_req, l->bytes_alloc);
499f3ced7cdSPekka Enberg 	y = fragmentation(r->bytes_req, r->bytes_alloc);
500f3ced7cdSPekka Enberg 
501f3ced7cdSPekka Enberg 	if (x < y)
502f3ced7cdSPekka Enberg 		return -1;
503f3ced7cdSPekka Enberg 	else if (x > y)
504f3ced7cdSPekka Enberg 		return 1;
505f3ced7cdSPekka Enberg 	return 0;
506f3ced7cdSPekka Enberg }
507f3ced7cdSPekka Enberg 
50829b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = {
50929b3e152SLi Zefan 	.name	= "frag",
51029b3e152SLi Zefan 	.cmp	= frag_cmp,
51129b3e152SLi Zefan };
51229b3e152SLi Zefan 
513079d3f65SLi Zefan static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
514079d3f65SLi Zefan {
515079d3f65SLi Zefan 	if (l->pingpong < r->pingpong)
516079d3f65SLi Zefan 		return -1;
517079d3f65SLi Zefan 	else if (l->pingpong > r->pingpong)
518079d3f65SLi Zefan 		return 1;
519079d3f65SLi Zefan 	return 0;
520079d3f65SLi Zefan }
521079d3f65SLi Zefan 
522079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = {
523079d3f65SLi Zefan 	.name	= "pingpong",
524079d3f65SLi Zefan 	.cmp	= pingpong_cmp,
525079d3f65SLi Zefan };
526079d3f65SLi Zefan 
52729b3e152SLi Zefan static struct sort_dimension *avail_sorts[] = {
52829b3e152SLi Zefan 	&ptr_sort_dimension,
52929b3e152SLi Zefan 	&callsite_sort_dimension,
53029b3e152SLi Zefan 	&hit_sort_dimension,
53129b3e152SLi Zefan 	&bytes_sort_dimension,
53229b3e152SLi Zefan 	&frag_sort_dimension,
533079d3f65SLi Zefan 	&pingpong_sort_dimension,
53429b3e152SLi Zefan };
53529b3e152SLi Zefan 
53649e4ba54SSasha Levin #define NUM_AVAIL_SORTS	((int)ARRAY_SIZE(avail_sorts))
53729b3e152SLi Zefan 
53829b3e152SLi Zefan static int sort_dimension__add(const char *tok, struct list_head *list)
53929b3e152SLi Zefan {
54029b3e152SLi Zefan 	struct sort_dimension *sort;
54129b3e152SLi Zefan 	int i;
54229b3e152SLi Zefan 
54329b3e152SLi Zefan 	for (i = 0; i < NUM_AVAIL_SORTS; i++) {
54429b3e152SLi Zefan 		if (!strcmp(avail_sorts[i]->name, tok)) {
5458d9233f2SArnaldo Carvalho de Melo 			sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
5462814eb05SArnaldo Carvalho de Melo 			if (!sort) {
5478d9233f2SArnaldo Carvalho de Melo 				pr_err("%s: memdup failed\n", __func__);
5482814eb05SArnaldo Carvalho de Melo 				return -1;
5492814eb05SArnaldo Carvalho de Melo 			}
55029b3e152SLi Zefan 			list_add_tail(&sort->list, list);
55129b3e152SLi Zefan 			return 0;
55229b3e152SLi Zefan 		}
55329b3e152SLi Zefan 	}
55429b3e152SLi Zefan 
55529b3e152SLi Zefan 	return -1;
55629b3e152SLi Zefan }
55729b3e152SLi Zefan 
55829b3e152SLi Zefan static int setup_sorting(struct list_head *sort_list, const char *arg)
55929b3e152SLi Zefan {
56029b3e152SLi Zefan 	char *tok;
56129b3e152SLi Zefan 	char *str = strdup(arg);
56229b3e152SLi Zefan 
5632814eb05SArnaldo Carvalho de Melo 	if (!str) {
5642814eb05SArnaldo Carvalho de Melo 		pr_err("%s: strdup failed\n", __func__);
5652814eb05SArnaldo Carvalho de Melo 		return -1;
5662814eb05SArnaldo Carvalho de Melo 	}
56729b3e152SLi Zefan 
56829b3e152SLi Zefan 	while (true) {
56929b3e152SLi Zefan 		tok = strsep(&str, ",");
57029b3e152SLi Zefan 		if (!tok)
57129b3e152SLi Zefan 			break;
57229b3e152SLi Zefan 		if (sort_dimension__add(tok, sort_list) < 0) {
57329b3e152SLi Zefan 			error("Unknown --sort key: '%s'", tok);
5741b22859dSNamhyung Kim 			free(str);
57529b3e152SLi Zefan 			return -1;
57629b3e152SLi Zefan 		}
57729b3e152SLi Zefan 	}
57829b3e152SLi Zefan 
57929b3e152SLi Zefan 	free(str);
58029b3e152SLi Zefan 	return 0;
58129b3e152SLi Zefan }
58229b3e152SLi Zefan 
5831d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused,
5841d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
585ba77c9e1SLi Zefan {
586ba77c9e1SLi Zefan 	if (!arg)
587ba77c9e1SLi Zefan 		return -1;
588ba77c9e1SLi Zefan 
589ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
59029b3e152SLi Zefan 		return setup_sorting(&caller_sort, arg);
591ba77c9e1SLi Zefan 	else
59229b3e152SLi Zefan 		return setup_sorting(&alloc_sort, arg);
593ba77c9e1SLi Zefan 
594ba77c9e1SLi Zefan 	return 0;
595ba77c9e1SLi Zefan }
596ba77c9e1SLi Zefan 
5971d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused,
5981d037ca1SIrina Tirdea 			    const char *arg __maybe_unused,
5991d037ca1SIrina Tirdea 			    int unset __maybe_unused)
600ba77c9e1SLi Zefan {
601ba77c9e1SLi Zefan 	caller_flag = (alloc_flag + 1);
60290b86a9fSLi Zefan 	return 0;
60390b86a9fSLi Zefan }
60490b86a9fSLi Zefan 
6051d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused,
6061d037ca1SIrina Tirdea 			   const char *arg __maybe_unused,
6071d037ca1SIrina Tirdea 			   int unset __maybe_unused)
60890b86a9fSLi Zefan {
60990b86a9fSLi Zefan 	alloc_flag = (caller_flag + 1);
610ba77c9e1SLi Zefan 	return 0;
611ba77c9e1SLi Zefan }
612ba77c9e1SLi Zefan 
6131d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused,
6141d037ca1SIrina Tirdea 			  const char *arg, int unset __maybe_unused)
615ba77c9e1SLi Zefan {
616ba77c9e1SLi Zefan 	int lines;
617ba77c9e1SLi Zefan 
618ba77c9e1SLi Zefan 	if (!arg)
619ba77c9e1SLi Zefan 		return -1;
620ba77c9e1SLi Zefan 
621ba77c9e1SLi Zefan 	lines = strtoul(arg, NULL, 10);
622ba77c9e1SLi Zefan 
623ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
624ba77c9e1SLi Zefan 		caller_lines = lines;
625ba77c9e1SLi Zefan 	else
626ba77c9e1SLi Zefan 		alloc_lines = lines;
627ba77c9e1SLi Zefan 
628ba77c9e1SLi Zefan 	return 0;
629ba77c9e1SLi Zefan }
630ba77c9e1SLi Zefan 
6310433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv)
6320433ffbeSArnaldo Carvalho de Melo {
6330433ffbeSArnaldo Carvalho de Melo 	const char * const record_args[] = {
6344a4d371aSJiri Olsa 	"record", "-a", "-R", "-c", "1",
635ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc",
636ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc_node",
637ba77c9e1SLi Zefan 	"-e", "kmem:kfree",
638ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc",
639ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc_node",
640ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_free",
641ba77c9e1SLi Zefan 	};
642ba77c9e1SLi Zefan 	unsigned int rec_argc, i, j;
643ba77c9e1SLi Zefan 	const char **rec_argv;
644ba77c9e1SLi Zefan 
645ba77c9e1SLi Zefan 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
646ba77c9e1SLi Zefan 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
647ba77c9e1SLi Zefan 
648ce47dc56SChris Samuel 	if (rec_argv == NULL)
649ce47dc56SChris Samuel 		return -ENOMEM;
650ce47dc56SChris Samuel 
651ba77c9e1SLi Zefan 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
652ba77c9e1SLi Zefan 		rec_argv[i] = strdup(record_args[i]);
653ba77c9e1SLi Zefan 
654ba77c9e1SLi Zefan 	for (j = 1; j < (unsigned int)argc; j++, i++)
655ba77c9e1SLi Zefan 		rec_argv[i] = argv[j];
656ba77c9e1SLi Zefan 
657ba77c9e1SLi Zefan 	return cmd_record(i, rec_argv, NULL);
658ba77c9e1SLi Zefan }
659ba77c9e1SLi Zefan 
6601d037ca1SIrina Tirdea int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
661ba77c9e1SLi Zefan {
6620433ffbeSArnaldo Carvalho de Melo 	const char * const default_sort_order = "frag,hit,bytes";
6630433ffbeSArnaldo Carvalho de Melo 	const struct option kmem_options[] = {
6640433ffbeSArnaldo Carvalho de Melo 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
6650433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
6660433ffbeSArnaldo Carvalho de Melo 			   "show per-callsite statistics", parse_caller_opt),
6670433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
6680433ffbeSArnaldo Carvalho de Melo 			   "show per-allocation statistics", parse_alloc_opt),
6690433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
6700433ffbeSArnaldo Carvalho de Melo 		     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
6710433ffbeSArnaldo Carvalho de Melo 		     parse_sort_opt),
6720433ffbeSArnaldo Carvalho de Melo 	OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
6730433ffbeSArnaldo Carvalho de Melo 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
6740433ffbeSArnaldo Carvalho de Melo 	OPT_END()
6750433ffbeSArnaldo Carvalho de Melo 	};
6763bca2354SRamkumar Ramachandra 	const char *const kmem_subcommands[] = { "record", "stat", NULL };
6773bca2354SRamkumar Ramachandra 	const char *kmem_usage[] = {
6783bca2354SRamkumar Ramachandra 		NULL,
6790433ffbeSArnaldo Carvalho de Melo 		NULL
6800433ffbeSArnaldo Carvalho de Melo 	};
6812b2b2c68SNamhyung Kim 	struct perf_session *session;
6822b2b2c68SNamhyung Kim 	struct perf_data_file file = {
6832b2b2c68SNamhyung Kim 		.path = input_name,
6842b2b2c68SNamhyung Kim 		.mode = PERF_DATA_MODE_READ,
6852b2b2c68SNamhyung Kim 	};
6862b2b2c68SNamhyung Kim 	int ret = -1;
6872b2b2c68SNamhyung Kim 
6883bca2354SRamkumar Ramachandra 	argc = parse_options_subcommand(argc, argv, kmem_options,
6893bca2354SRamkumar Ramachandra 					kmem_subcommands, kmem_usage, 0);
690ba77c9e1SLi Zefan 
69190b86a9fSLi Zefan 	if (!argc)
692ba77c9e1SLi Zefan 		usage_with_options(kmem_usage, kmem_options);
693ba77c9e1SLi Zefan 
6942b2b2c68SNamhyung Kim 	if (!strncmp(argv[0], "rec", 3)) {
6950a7e6d1bSNamhyung Kim 		symbol__init(NULL);
6962b2b2c68SNamhyung Kim 		return __cmd_record(argc, argv);
6972b2b2c68SNamhyung Kim 	}
6982b2b2c68SNamhyung Kim 
6992b2b2c68SNamhyung Kim 	session = perf_session__new(&file, false, &perf_kmem);
7002b2b2c68SNamhyung Kim 	if (session == NULL)
70152e02834STaeung Song 		return -1;
7022b2b2c68SNamhyung Kim 
7030a7e6d1bSNamhyung Kim 	symbol__init(&session->header.env);
704655000e7SArnaldo Carvalho de Melo 
7052b2b2c68SNamhyung Kim 	if (!strcmp(argv[0], "stat")) {
7064b627957SDon Zickus 		if (cpu__setup_cpunode_map())
7072b2b2c68SNamhyung Kim 			goto out_delete;
70890b86a9fSLi Zefan 
70929b3e152SLi Zefan 		if (list_empty(&caller_sort))
71029b3e152SLi Zefan 			setup_sorting(&caller_sort, default_sort_order);
71129b3e152SLi Zefan 		if (list_empty(&alloc_sort))
71229b3e152SLi Zefan 			setup_sorting(&alloc_sort, default_sort_order);
713ba77c9e1SLi Zefan 
7142b2b2c68SNamhyung Kim 		ret = __cmd_kmem(session);
715b00eca8cSPekka Enberg 	} else
716b00eca8cSPekka Enberg 		usage_with_options(kmem_usage, kmem_options);
717ba77c9e1SLi Zefan 
7182b2b2c68SNamhyung Kim out_delete:
7192b2b2c68SNamhyung Kim 	perf_session__delete(session);
7202b2b2c68SNamhyung Kim 
7212b2b2c68SNamhyung Kim 	return ret;
72290b86a9fSLi Zefan }
72390b86a9fSLi Zefan 
724