xref: /openbmc/linux/tools/perf/builtin-kmem.c (revision 29b3e15289eb66788a0bf5ea4903f9fbeb1ec751)
1ba77c9e1SLi Zefan #include "builtin.h"
2ba77c9e1SLi Zefan #include "perf.h"
3ba77c9e1SLi Zefan 
4ba77c9e1SLi Zefan #include "util/util.h"
5ba77c9e1SLi Zefan #include "util/cache.h"
6ba77c9e1SLi Zefan #include "util/symbol.h"
7ba77c9e1SLi Zefan #include "util/thread.h"
8ba77c9e1SLi Zefan #include "util/header.h"
9ba77c9e1SLi Zefan 
10ba77c9e1SLi Zefan #include "util/parse-options.h"
11ba77c9e1SLi Zefan #include "util/trace-event.h"
12ba77c9e1SLi Zefan 
13ba77c9e1SLi Zefan #include "util/debug.h"
14ba77c9e1SLi Zefan #include "util/data_map.h"
15ba77c9e1SLi Zefan 
16ba77c9e1SLi Zefan #include <linux/rbtree.h>
17ba77c9e1SLi Zefan 
18ba77c9e1SLi Zefan struct alloc_stat;
19ba77c9e1SLi Zefan typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
20ba77c9e1SLi Zefan 
21ba77c9e1SLi Zefan static char const		*input_name = "perf.data";
22ba77c9e1SLi Zefan 
23ba77c9e1SLi Zefan static struct perf_header	*header;
24ba77c9e1SLi Zefan static u64			sample_type;
25ba77c9e1SLi Zefan 
26ba77c9e1SLi Zefan static int			alloc_flag;
27ba77c9e1SLi Zefan static int			caller_flag;
28ba77c9e1SLi Zefan 
29ba77c9e1SLi Zefan static int			alloc_lines = -1;
30ba77c9e1SLi Zefan static int			caller_lines = -1;
31ba77c9e1SLi Zefan 
327707b6b6SLi Zefan static bool			raw_ip;
337707b6b6SLi Zefan 
34*29b3e152SLi Zefan static char			default_sort_order[] = "frag,hit,bytes";
35*29b3e152SLi Zefan 
36ba77c9e1SLi Zefan static char			*cwd;
37ba77c9e1SLi Zefan static int			cwdlen;
38ba77c9e1SLi Zefan 
39ba77c9e1SLi Zefan struct alloc_stat {
40ba77c9e1SLi Zefan 	union {
41ba77c9e1SLi Zefan 		u64	call_site;
42ba77c9e1SLi Zefan 		u64	ptr;
43ba77c9e1SLi Zefan 	};
44ba77c9e1SLi Zefan 	u64	bytes_req;
45ba77c9e1SLi Zefan 	u64	bytes_alloc;
46ba77c9e1SLi Zefan 	u32	hit;
47ba77c9e1SLi Zefan 
48ba77c9e1SLi Zefan 	struct rb_node node;
49ba77c9e1SLi Zefan };
50ba77c9e1SLi Zefan 
51ba77c9e1SLi Zefan static struct rb_root root_alloc_stat;
52ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted;
53ba77c9e1SLi Zefan static struct rb_root root_caller_stat;
54ba77c9e1SLi Zefan static struct rb_root root_caller_sorted;
55ba77c9e1SLi Zefan 
56ba77c9e1SLi Zefan static unsigned long total_requested, total_allocated;
57ba77c9e1SLi Zefan 
58ba77c9e1SLi Zefan struct raw_event_sample {
59ba77c9e1SLi Zefan 	u32 size;
60ba77c9e1SLi Zefan 	char data[0];
61ba77c9e1SLi Zefan };
62ba77c9e1SLi Zefan 
63ba77c9e1SLi Zefan static int
64ba77c9e1SLi Zefan process_comm_event(event_t *event, unsigned long offset, unsigned long head)
65ba77c9e1SLi Zefan {
66ba77c9e1SLi Zefan 	struct thread *thread = threads__findnew(event->comm.pid);
67ba77c9e1SLi Zefan 
68ba77c9e1SLi Zefan 	dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
69ba77c9e1SLi Zefan 		(void *)(offset + head),
70ba77c9e1SLi Zefan 		(void *)(long)(event->header.size),
71ba77c9e1SLi Zefan 		event->comm.comm, event->comm.pid);
72ba77c9e1SLi Zefan 
73ba77c9e1SLi Zefan 	if (thread == NULL ||
74ba77c9e1SLi Zefan 	    thread__set_comm(thread, event->comm.comm)) {
75ba77c9e1SLi Zefan 		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
76ba77c9e1SLi Zefan 		return -1;
77ba77c9e1SLi Zefan 	}
78ba77c9e1SLi Zefan 
79ba77c9e1SLi Zefan 	return 0;
80ba77c9e1SLi Zefan }
81ba77c9e1SLi Zefan 
82ba77c9e1SLi Zefan static void insert_alloc_stat(unsigned long ptr,
83ba77c9e1SLi Zefan 			      int bytes_req, int bytes_alloc)
84ba77c9e1SLi Zefan {
85ba77c9e1SLi Zefan 	struct rb_node **node = &root_alloc_stat.rb_node;
86ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
87ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
88ba77c9e1SLi Zefan 
89ba77c9e1SLi Zefan 	if (!alloc_flag)
90ba77c9e1SLi Zefan 		return;
91ba77c9e1SLi Zefan 
92ba77c9e1SLi Zefan 	while (*node) {
93ba77c9e1SLi Zefan 		parent = *node;
94ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
95ba77c9e1SLi Zefan 
96ba77c9e1SLi Zefan 		if (ptr > data->ptr)
97ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
98ba77c9e1SLi Zefan 		else if (ptr < data->ptr)
99ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
100ba77c9e1SLi Zefan 		else
101ba77c9e1SLi Zefan 			break;
102ba77c9e1SLi Zefan 	}
103ba77c9e1SLi Zefan 
104ba77c9e1SLi Zefan 	if (data && data->ptr == ptr) {
105ba77c9e1SLi Zefan 		data->hit++;
106ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
107ba77c9e1SLi Zefan 		data->bytes_alloc += bytes_req;
108ba77c9e1SLi Zefan 	} else {
109ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
110ba77c9e1SLi Zefan 		data->ptr = ptr;
111ba77c9e1SLi Zefan 		data->hit = 1;
112ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
113ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
114ba77c9e1SLi Zefan 
115ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
116ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_alloc_stat);
117ba77c9e1SLi Zefan 	}
118ba77c9e1SLi Zefan }
119ba77c9e1SLi Zefan 
120ba77c9e1SLi Zefan static void insert_caller_stat(unsigned long call_site,
121ba77c9e1SLi Zefan 			      int bytes_req, int bytes_alloc)
122ba77c9e1SLi Zefan {
123ba77c9e1SLi Zefan 	struct rb_node **node = &root_caller_stat.rb_node;
124ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
125ba77c9e1SLi Zefan 	struct alloc_stat *data = NULL;
126ba77c9e1SLi Zefan 
127ba77c9e1SLi Zefan 	if (!caller_flag)
128ba77c9e1SLi Zefan 		return;
129ba77c9e1SLi Zefan 
130ba77c9e1SLi Zefan 	while (*node) {
131ba77c9e1SLi Zefan 		parent = *node;
132ba77c9e1SLi Zefan 		data = rb_entry(*node, struct alloc_stat, node);
133ba77c9e1SLi Zefan 
134ba77c9e1SLi Zefan 		if (call_site > data->call_site)
135ba77c9e1SLi Zefan 			node = &(*node)->rb_right;
136ba77c9e1SLi Zefan 		else if (call_site < data->call_site)
137ba77c9e1SLi Zefan 			node = &(*node)->rb_left;
138ba77c9e1SLi Zefan 		else
139ba77c9e1SLi Zefan 			break;
140ba77c9e1SLi Zefan 	}
141ba77c9e1SLi Zefan 
142ba77c9e1SLi Zefan 	if (data && data->call_site == call_site) {
143ba77c9e1SLi Zefan 		data->hit++;
144ba77c9e1SLi Zefan 		data->bytes_req += bytes_req;
145ba77c9e1SLi Zefan 		data->bytes_alloc += bytes_req;
146ba77c9e1SLi Zefan 	} else {
147ba77c9e1SLi Zefan 		data = malloc(sizeof(*data));
148ba77c9e1SLi Zefan 		data->call_site = call_site;
149ba77c9e1SLi Zefan 		data->hit = 1;
150ba77c9e1SLi Zefan 		data->bytes_req = bytes_req;
151ba77c9e1SLi Zefan 		data->bytes_alloc = bytes_alloc;
152ba77c9e1SLi Zefan 
153ba77c9e1SLi Zefan 		rb_link_node(&data->node, parent, node);
154ba77c9e1SLi Zefan 		rb_insert_color(&data->node, &root_caller_stat);
155ba77c9e1SLi Zefan 	}
156ba77c9e1SLi Zefan }
157ba77c9e1SLi Zefan 
158ba77c9e1SLi Zefan static void process_alloc_event(struct raw_event_sample *raw,
159ba77c9e1SLi Zefan 				struct event *event,
160ba77c9e1SLi Zefan 				int cpu __used,
161ba77c9e1SLi Zefan 				u64 timestamp __used,
162ba77c9e1SLi Zefan 				struct thread *thread __used,
163ba77c9e1SLi Zefan 				int node __used)
164ba77c9e1SLi Zefan {
165ba77c9e1SLi Zefan 	unsigned long call_site;
166ba77c9e1SLi Zefan 	unsigned long ptr;
167ba77c9e1SLi Zefan 	int bytes_req;
168ba77c9e1SLi Zefan 	int bytes_alloc;
169ba77c9e1SLi Zefan 
170ba77c9e1SLi Zefan 	ptr = raw_field_value(event, "ptr", raw->data);
171ba77c9e1SLi Zefan 	call_site = raw_field_value(event, "call_site", raw->data);
172ba77c9e1SLi Zefan 	bytes_req = raw_field_value(event, "bytes_req", raw->data);
173ba77c9e1SLi Zefan 	bytes_alloc = raw_field_value(event, "bytes_alloc", raw->data);
174ba77c9e1SLi Zefan 
175ba77c9e1SLi Zefan 	insert_alloc_stat(ptr, bytes_req, bytes_alloc);
176ba77c9e1SLi Zefan 	insert_caller_stat(call_site, bytes_req, bytes_alloc);
177ba77c9e1SLi Zefan 
178ba77c9e1SLi Zefan 	total_requested += bytes_req;
179ba77c9e1SLi Zefan 	total_allocated += bytes_alloc;
180ba77c9e1SLi Zefan }
181ba77c9e1SLi Zefan 
182ba77c9e1SLi Zefan static void process_free_event(struct raw_event_sample *raw __used,
183ba77c9e1SLi Zefan 			       struct event *event __used,
184ba77c9e1SLi Zefan 			       int cpu __used,
185ba77c9e1SLi Zefan 			       u64 timestamp __used,
186ba77c9e1SLi Zefan 			       struct thread *thread __used)
187ba77c9e1SLi Zefan {
188ba77c9e1SLi Zefan }
189ba77c9e1SLi Zefan 
190ba77c9e1SLi Zefan static void
191ba77c9e1SLi Zefan process_raw_event(event_t *raw_event __used, void *more_data,
192ba77c9e1SLi Zefan 		  int cpu, u64 timestamp, struct thread *thread)
193ba77c9e1SLi Zefan {
194ba77c9e1SLi Zefan 	struct raw_event_sample *raw = more_data;
195ba77c9e1SLi Zefan 	struct event *event;
196ba77c9e1SLi Zefan 	int type;
197ba77c9e1SLi Zefan 
198ba77c9e1SLi Zefan 	type = trace_parse_common_type(raw->data);
199ba77c9e1SLi Zefan 	event = trace_find_event(type);
200ba77c9e1SLi Zefan 
201ba77c9e1SLi Zefan 	if (!strcmp(event->name, "kmalloc") ||
202ba77c9e1SLi Zefan 	    !strcmp(event->name, "kmem_cache_alloc")) {
203ba77c9e1SLi Zefan 		process_alloc_event(raw, event, cpu, timestamp, thread, 0);
204ba77c9e1SLi Zefan 		return;
205ba77c9e1SLi Zefan 	}
206ba77c9e1SLi Zefan 
207ba77c9e1SLi Zefan 	if (!strcmp(event->name, "kmalloc_node") ||
208ba77c9e1SLi Zefan 	    !strcmp(event->name, "kmem_cache_alloc_node")) {
209ba77c9e1SLi Zefan 		process_alloc_event(raw, event, cpu, timestamp, thread, 1);
210ba77c9e1SLi Zefan 		return;
211ba77c9e1SLi Zefan 	}
212ba77c9e1SLi Zefan 
213ba77c9e1SLi Zefan 	if (!strcmp(event->name, "kfree") ||
214ba77c9e1SLi Zefan 	    !strcmp(event->name, "kmem_cache_free")) {
215ba77c9e1SLi Zefan 		process_free_event(raw, event, cpu, timestamp, thread);
216ba77c9e1SLi Zefan 		return;
217ba77c9e1SLi Zefan 	}
218ba77c9e1SLi Zefan }
219ba77c9e1SLi Zefan 
220ba77c9e1SLi Zefan static int
221ba77c9e1SLi Zefan process_sample_event(event_t *event, unsigned long offset, unsigned long head)
222ba77c9e1SLi Zefan {
223ba77c9e1SLi Zefan 	u64 ip = event->ip.ip;
224ba77c9e1SLi Zefan 	u64 timestamp = -1;
225ba77c9e1SLi Zefan 	u32 cpu = -1;
226ba77c9e1SLi Zefan 	u64 period = 1;
227ba77c9e1SLi Zefan 	void *more_data = event->ip.__more_data;
228ba77c9e1SLi Zefan 	struct thread *thread = threads__findnew(event->ip.pid);
229ba77c9e1SLi Zefan 
230ba77c9e1SLi Zefan 	if (sample_type & PERF_SAMPLE_TIME) {
231ba77c9e1SLi Zefan 		timestamp = *(u64 *)more_data;
232ba77c9e1SLi Zefan 		more_data += sizeof(u64);
233ba77c9e1SLi Zefan 	}
234ba77c9e1SLi Zefan 
235ba77c9e1SLi Zefan 	if (sample_type & PERF_SAMPLE_CPU) {
236ba77c9e1SLi Zefan 		cpu = *(u32 *)more_data;
237ba77c9e1SLi Zefan 		more_data += sizeof(u32);
238ba77c9e1SLi Zefan 		more_data += sizeof(u32); /* reserved */
239ba77c9e1SLi Zefan 	}
240ba77c9e1SLi Zefan 
241ba77c9e1SLi Zefan 	if (sample_type & PERF_SAMPLE_PERIOD) {
242ba77c9e1SLi Zefan 		period = *(u64 *)more_data;
243ba77c9e1SLi Zefan 		more_data += sizeof(u64);
244ba77c9e1SLi Zefan 	}
245ba77c9e1SLi Zefan 
246ba77c9e1SLi Zefan 	dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
247ba77c9e1SLi Zefan 		(void *)(offset + head),
248ba77c9e1SLi Zefan 		(void *)(long)(event->header.size),
249ba77c9e1SLi Zefan 		event->header.misc,
250ba77c9e1SLi Zefan 		event->ip.pid, event->ip.tid,
251ba77c9e1SLi Zefan 		(void *)(long)ip,
252ba77c9e1SLi Zefan 		(long long)period);
253ba77c9e1SLi Zefan 
254ba77c9e1SLi Zefan 	if (thread == NULL) {
255ba77c9e1SLi Zefan 		pr_debug("problem processing %d event, skipping it.\n",
256ba77c9e1SLi Zefan 			 event->header.type);
257ba77c9e1SLi Zefan 		return -1;
258ba77c9e1SLi Zefan 	}
259ba77c9e1SLi Zefan 
260ba77c9e1SLi Zefan 	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
261ba77c9e1SLi Zefan 
262ba77c9e1SLi Zefan 	process_raw_event(event, more_data, cpu, timestamp, thread);
263ba77c9e1SLi Zefan 
264ba77c9e1SLi Zefan 	return 0;
265ba77c9e1SLi Zefan }
266ba77c9e1SLi Zefan 
267ba77c9e1SLi Zefan static int sample_type_check(u64 type)
268ba77c9e1SLi Zefan {
269ba77c9e1SLi Zefan 	sample_type = type;
270ba77c9e1SLi Zefan 
271ba77c9e1SLi Zefan 	if (!(sample_type & PERF_SAMPLE_RAW)) {
272ba77c9e1SLi Zefan 		fprintf(stderr,
273ba77c9e1SLi Zefan 			"No trace sample to read. Did you call perf record "
274ba77c9e1SLi Zefan 			"without -R?");
275ba77c9e1SLi Zefan 		return -1;
276ba77c9e1SLi Zefan 	}
277ba77c9e1SLi Zefan 
278ba77c9e1SLi Zefan 	return 0;
279ba77c9e1SLi Zefan }
280ba77c9e1SLi Zefan 
281ba77c9e1SLi Zefan static struct perf_file_handler file_handler = {
282ba77c9e1SLi Zefan 	.process_sample_event	= process_sample_event,
283ba77c9e1SLi Zefan 	.process_comm_event	= process_comm_event,
284ba77c9e1SLi Zefan 	.sample_type_check	= sample_type_check,
285ba77c9e1SLi Zefan };
286ba77c9e1SLi Zefan 
287ba77c9e1SLi Zefan static int read_events(void)
288ba77c9e1SLi Zefan {
289ba77c9e1SLi Zefan 	register_idle_thread();
290ba77c9e1SLi Zefan 	register_perf_file_handler(&file_handler);
291ba77c9e1SLi Zefan 
292cc612d81SArnaldo Carvalho de Melo 	return mmap_dispatch_perf_file(&header, input_name, NULL, false, 0, 0,
293ba77c9e1SLi Zefan 				       &cwdlen, &cwd);
294ba77c9e1SLi Zefan }
295ba77c9e1SLi Zefan 
296ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc)
297ba77c9e1SLi Zefan {
298ba77c9e1SLi Zefan 	if (n_alloc == 0)
299ba77c9e1SLi Zefan 		return 0.0;
300ba77c9e1SLi Zefan 	else
301ba77c9e1SLi Zefan 		return 100.0 - (100.0 * n_req / n_alloc);
302ba77c9e1SLi Zefan }
303ba77c9e1SLi Zefan 
304ba77c9e1SLi Zefan static void __print_result(struct rb_root *root, int n_lines, int is_caller)
305ba77c9e1SLi Zefan {
306ba77c9e1SLi Zefan 	struct rb_node *next;
307ba77c9e1SLi Zefan 
3081b145ae5SArnaldo Carvalho de Melo 	printf("%.78s\n", graph_dotted_line);
3091b145ae5SArnaldo Carvalho de Melo 	printf("%-28s|",  is_caller ? "Callsite": "Alloc Ptr");
3101b145ae5SArnaldo Carvalho de Melo 	printf("Total_alloc/Per | Total_req/Per | Hit  | Frag\n");
3111b145ae5SArnaldo Carvalho de Melo 	printf("%.78s\n", graph_dotted_line);
312ba77c9e1SLi Zefan 
313ba77c9e1SLi Zefan 	next = rb_first(root);
314ba77c9e1SLi Zefan 
315ba77c9e1SLi Zefan 	while (next && n_lines--) {
3161b145ae5SArnaldo Carvalho de Melo 		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
3171b145ae5SArnaldo Carvalho de Melo 						   node);
3181b145ae5SArnaldo Carvalho de Melo 		struct symbol *sym = NULL;
3191b145ae5SArnaldo Carvalho de Melo 		char bf[BUFSIZ];
3201b145ae5SArnaldo Carvalho de Melo 		u64 addr;
321ba77c9e1SLi Zefan 
3221b145ae5SArnaldo Carvalho de Melo 		if (is_caller) {
3231b145ae5SArnaldo Carvalho de Melo 			addr = data->call_site;
3247707b6b6SLi Zefan 			if (!raw_ip)
3257707b6b6SLi Zefan 				sym = kernel_maps__find_symbol(addr,
3267707b6b6SLi Zefan 							       NULL, NULL);
3271b145ae5SArnaldo Carvalho de Melo 		} else
3281b145ae5SArnaldo Carvalho de Melo 			addr = data->ptr;
329ba77c9e1SLi Zefan 
3301b145ae5SArnaldo Carvalho de Melo 		if (sym != NULL)
3317707b6b6SLi Zefan 			snprintf(bf, sizeof(bf), "%s+%Lx", sym->name,
3321b145ae5SArnaldo Carvalho de Melo 				 addr - sym->start);
3331b145ae5SArnaldo Carvalho de Melo 		else
3341b145ae5SArnaldo Carvalho de Melo 			snprintf(bf, sizeof(bf), "%#Lx", addr);
3351b145ae5SArnaldo Carvalho de Melo 
3361b145ae5SArnaldo Carvalho de Melo 		printf("%-28s|%8llu/%-6lu |%8llu/%-6lu|%6lu|%8.3f%%\n",
3371b145ae5SArnaldo Carvalho de Melo 		       bf, (unsigned long long)data->bytes_alloc,
338ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_alloc / data->hit,
339ba77c9e1SLi Zefan 		       (unsigned long long)data->bytes_req,
340ba77c9e1SLi Zefan 		       (unsigned long)data->bytes_req / data->hit,
341ba77c9e1SLi Zefan 		       (unsigned long)data->hit,
342ba77c9e1SLi Zefan 		       fragmentation(data->bytes_req, data->bytes_alloc));
343ba77c9e1SLi Zefan 
344ba77c9e1SLi Zefan 		next = rb_next(next);
345ba77c9e1SLi Zefan 	}
346ba77c9e1SLi Zefan 
347ba77c9e1SLi Zefan 	if (n_lines == -1)
348ba77c9e1SLi Zefan 		printf(" ...                        | ...            | ...           | ...    | ...   \n");
349ba77c9e1SLi Zefan 
3507707b6b6SLi Zefan 	printf("%.78s\n", graph_dotted_line);
351ba77c9e1SLi Zefan }
352ba77c9e1SLi Zefan 
353ba77c9e1SLi Zefan static void print_summary(void)
354ba77c9e1SLi Zefan {
355ba77c9e1SLi Zefan 	printf("\nSUMMARY\n=======\n");
356ba77c9e1SLi Zefan 	printf("Total bytes requested: %lu\n", total_requested);
357ba77c9e1SLi Zefan 	printf("Total bytes allocated: %lu\n", total_allocated);
358ba77c9e1SLi Zefan 	printf("Total bytes wasted on internal fragmentation: %lu\n",
359ba77c9e1SLi Zefan 	       total_allocated - total_requested);
360ba77c9e1SLi Zefan 	printf("Internal fragmentation: %f%%\n",
361ba77c9e1SLi Zefan 	       fragmentation(total_requested, total_allocated));
362ba77c9e1SLi Zefan }
363ba77c9e1SLi Zefan 
364ba77c9e1SLi Zefan static void print_result(void)
365ba77c9e1SLi Zefan {
366ba77c9e1SLi Zefan 	if (caller_flag)
367ba77c9e1SLi Zefan 		__print_result(&root_caller_sorted, caller_lines, 1);
368ba77c9e1SLi Zefan 	if (alloc_flag)
369ba77c9e1SLi Zefan 		__print_result(&root_alloc_sorted, alloc_lines, 0);
370ba77c9e1SLi Zefan 	print_summary();
371ba77c9e1SLi Zefan }
372ba77c9e1SLi Zefan 
373*29b3e152SLi Zefan struct sort_dimension {
374*29b3e152SLi Zefan 	const char		name[20];
375*29b3e152SLi Zefan 	sort_fn_t		cmp;
376*29b3e152SLi Zefan 	struct list_head	list;
377*29b3e152SLi Zefan };
378*29b3e152SLi Zefan 
379*29b3e152SLi Zefan static LIST_HEAD(caller_sort);
380*29b3e152SLi Zefan static LIST_HEAD(alloc_sort);
381*29b3e152SLi Zefan 
382ba77c9e1SLi Zefan static void sort_insert(struct rb_root *root, struct alloc_stat *data,
383*29b3e152SLi Zefan 			struct list_head *sort_list)
384ba77c9e1SLi Zefan {
385ba77c9e1SLi Zefan 	struct rb_node **new = &(root->rb_node);
386ba77c9e1SLi Zefan 	struct rb_node *parent = NULL;
387*29b3e152SLi Zefan 	struct sort_dimension *sort;
388ba77c9e1SLi Zefan 
389ba77c9e1SLi Zefan 	while (*new) {
390ba77c9e1SLi Zefan 		struct alloc_stat *this;
391*29b3e152SLi Zefan 		int cmp = 0;
392ba77c9e1SLi Zefan 
393ba77c9e1SLi Zefan 		this = rb_entry(*new, struct alloc_stat, node);
394ba77c9e1SLi Zefan 		parent = *new;
395ba77c9e1SLi Zefan 
396*29b3e152SLi Zefan 		list_for_each_entry(sort, sort_list, list) {
397*29b3e152SLi Zefan 			cmp = sort->cmp(data, this);
398*29b3e152SLi Zefan 			if (cmp)
399*29b3e152SLi Zefan 				break;
400*29b3e152SLi Zefan 		}
401ba77c9e1SLi Zefan 
402ba77c9e1SLi Zefan 		if (cmp > 0)
403ba77c9e1SLi Zefan 			new = &((*new)->rb_left);
404ba77c9e1SLi Zefan 		else
405ba77c9e1SLi Zefan 			new = &((*new)->rb_right);
406ba77c9e1SLi Zefan 	}
407ba77c9e1SLi Zefan 
408ba77c9e1SLi Zefan 	rb_link_node(&data->node, parent, new);
409ba77c9e1SLi Zefan 	rb_insert_color(&data->node, root);
410ba77c9e1SLi Zefan }
411ba77c9e1SLi Zefan 
412ba77c9e1SLi Zefan static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
413*29b3e152SLi Zefan 			  struct list_head *sort_list)
414ba77c9e1SLi Zefan {
415ba77c9e1SLi Zefan 	struct rb_node *node;
416ba77c9e1SLi Zefan 	struct alloc_stat *data;
417ba77c9e1SLi Zefan 
418ba77c9e1SLi Zefan 	for (;;) {
419ba77c9e1SLi Zefan 		node = rb_first(root);
420ba77c9e1SLi Zefan 		if (!node)
421ba77c9e1SLi Zefan 			break;
422ba77c9e1SLi Zefan 
423ba77c9e1SLi Zefan 		rb_erase(node, root);
424ba77c9e1SLi Zefan 		data = rb_entry(node, struct alloc_stat, node);
425*29b3e152SLi Zefan 		sort_insert(root_sorted, data, sort_list);
426ba77c9e1SLi Zefan 	}
427ba77c9e1SLi Zefan }
428ba77c9e1SLi Zefan 
429ba77c9e1SLi Zefan static void sort_result(void)
430ba77c9e1SLi Zefan {
431*29b3e152SLi Zefan 	__sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
432*29b3e152SLi Zefan 	__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
433ba77c9e1SLi Zefan }
434ba77c9e1SLi Zefan 
435ba77c9e1SLi Zefan static int __cmd_kmem(void)
436ba77c9e1SLi Zefan {
437ba77c9e1SLi Zefan 	setup_pager();
438ba77c9e1SLi Zefan 	read_events();
439ba77c9e1SLi Zefan 	sort_result();
440ba77c9e1SLi Zefan 	print_result();
441ba77c9e1SLi Zefan 
442ba77c9e1SLi Zefan 	return 0;
443ba77c9e1SLi Zefan }
444ba77c9e1SLi Zefan 
445ba77c9e1SLi Zefan static const char * const kmem_usage[] = {
446ba77c9e1SLi Zefan 	"perf kmem [<options>] {record}",
447ba77c9e1SLi Zefan 	NULL
448ba77c9e1SLi Zefan };
449ba77c9e1SLi Zefan 
450ba77c9e1SLi Zefan static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
451ba77c9e1SLi Zefan {
452ba77c9e1SLi Zefan 	if (l->ptr < r->ptr)
453ba77c9e1SLi Zefan 		return -1;
454ba77c9e1SLi Zefan 	else if (l->ptr > r->ptr)
455ba77c9e1SLi Zefan 		return 1;
456ba77c9e1SLi Zefan 	return 0;
457ba77c9e1SLi Zefan }
458ba77c9e1SLi Zefan 
459*29b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = {
460*29b3e152SLi Zefan 	.name	= "ptr",
461*29b3e152SLi Zefan 	.cmp	= ptr_cmp,
462*29b3e152SLi Zefan };
463*29b3e152SLi Zefan 
464ba77c9e1SLi Zefan static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
465ba77c9e1SLi Zefan {
466ba77c9e1SLi Zefan 	if (l->call_site < r->call_site)
467ba77c9e1SLi Zefan 		return -1;
468ba77c9e1SLi Zefan 	else if (l->call_site > r->call_site)
469ba77c9e1SLi Zefan 		return 1;
470ba77c9e1SLi Zefan 	return 0;
471ba77c9e1SLi Zefan }
472ba77c9e1SLi Zefan 
473*29b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = {
474*29b3e152SLi Zefan 	.name	= "callsite",
475*29b3e152SLi Zefan 	.cmp	= callsite_cmp,
476*29b3e152SLi Zefan };
477*29b3e152SLi Zefan 
478f3ced7cdSPekka Enberg static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
479f3ced7cdSPekka Enberg {
480f3ced7cdSPekka Enberg 	if (l->hit < r->hit)
481f3ced7cdSPekka Enberg 		return -1;
482f3ced7cdSPekka Enberg 	else if (l->hit > r->hit)
483f3ced7cdSPekka Enberg 		return 1;
484f3ced7cdSPekka Enberg 	return 0;
485f3ced7cdSPekka Enberg }
486f3ced7cdSPekka Enberg 
487*29b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = {
488*29b3e152SLi Zefan 	.name	= "hit",
489*29b3e152SLi Zefan 	.cmp	= hit_cmp,
490*29b3e152SLi Zefan };
491*29b3e152SLi Zefan 
492ba77c9e1SLi Zefan static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
493ba77c9e1SLi Zefan {
494ba77c9e1SLi Zefan 	if (l->bytes_alloc < r->bytes_alloc)
495ba77c9e1SLi Zefan 		return -1;
496ba77c9e1SLi Zefan 	else if (l->bytes_alloc > r->bytes_alloc)
497ba77c9e1SLi Zefan 		return 1;
498ba77c9e1SLi Zefan 	return 0;
499ba77c9e1SLi Zefan }
500ba77c9e1SLi Zefan 
501*29b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = {
502*29b3e152SLi Zefan 	.name	= "bytes",
503*29b3e152SLi Zefan 	.cmp	= bytes_cmp,
504*29b3e152SLi Zefan };
505*29b3e152SLi Zefan 
506f3ced7cdSPekka Enberg static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
507f3ced7cdSPekka Enberg {
508f3ced7cdSPekka Enberg 	double x, y;
509f3ced7cdSPekka Enberg 
510f3ced7cdSPekka Enberg 	x = fragmentation(l->bytes_req, l->bytes_alloc);
511f3ced7cdSPekka Enberg 	y = fragmentation(r->bytes_req, r->bytes_alloc);
512f3ced7cdSPekka Enberg 
513f3ced7cdSPekka Enberg 	if (x < y)
514f3ced7cdSPekka Enberg 		return -1;
515f3ced7cdSPekka Enberg 	else if (x > y)
516f3ced7cdSPekka Enberg 		return 1;
517f3ced7cdSPekka Enberg 	return 0;
518f3ced7cdSPekka Enberg }
519f3ced7cdSPekka Enberg 
520*29b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = {
521*29b3e152SLi Zefan 	.name	= "frag",
522*29b3e152SLi Zefan 	.cmp	= frag_cmp,
523*29b3e152SLi Zefan };
524*29b3e152SLi Zefan 
525*29b3e152SLi Zefan static struct sort_dimension *avail_sorts[] = {
526*29b3e152SLi Zefan 	&ptr_sort_dimension,
527*29b3e152SLi Zefan 	&callsite_sort_dimension,
528*29b3e152SLi Zefan 	&hit_sort_dimension,
529*29b3e152SLi Zefan 	&bytes_sort_dimension,
530*29b3e152SLi Zefan 	&frag_sort_dimension,
531*29b3e152SLi Zefan };
532*29b3e152SLi Zefan 
533*29b3e152SLi Zefan #define NUM_AVAIL_SORTS	\
534*29b3e152SLi Zefan 	(int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
535*29b3e152SLi Zefan 
536*29b3e152SLi Zefan static int sort_dimension__add(const char *tok, struct list_head *list)
537*29b3e152SLi Zefan {
538*29b3e152SLi Zefan 	struct sort_dimension *sort;
539*29b3e152SLi Zefan 	int i;
540*29b3e152SLi Zefan 
541*29b3e152SLi Zefan 	for (i = 0; i < NUM_AVAIL_SORTS; i++) {
542*29b3e152SLi Zefan 		if (!strcmp(avail_sorts[i]->name, tok)) {
543*29b3e152SLi Zefan 			sort = malloc(sizeof(*sort));
544*29b3e152SLi Zefan 			if (!sort)
545*29b3e152SLi Zefan 				die("malloc");
546*29b3e152SLi Zefan 			memcpy(sort, avail_sorts[i], sizeof(*sort));
547*29b3e152SLi Zefan 			list_add_tail(&sort->list, list);
548*29b3e152SLi Zefan 			return 0;
549*29b3e152SLi Zefan 		}
550*29b3e152SLi Zefan 	}
551*29b3e152SLi Zefan 
552*29b3e152SLi Zefan 	return -1;
553*29b3e152SLi Zefan }
554*29b3e152SLi Zefan 
555*29b3e152SLi Zefan static int setup_sorting(struct list_head *sort_list, const char *arg)
556*29b3e152SLi Zefan {
557*29b3e152SLi Zefan 	char *tok;
558*29b3e152SLi Zefan 	char *str = strdup(arg);
559*29b3e152SLi Zefan 
560*29b3e152SLi Zefan 	if (!str)
561*29b3e152SLi Zefan 		die("strdup");
562*29b3e152SLi Zefan 
563*29b3e152SLi Zefan 	while (true) {
564*29b3e152SLi Zefan 		tok = strsep(&str, ",");
565*29b3e152SLi Zefan 		if (!tok)
566*29b3e152SLi Zefan 			break;
567*29b3e152SLi Zefan 		if (sort_dimension__add(tok, sort_list) < 0) {
568*29b3e152SLi Zefan 			error("Unknown --sort key: '%s'", tok);
569*29b3e152SLi Zefan 			return -1;
570*29b3e152SLi Zefan 		}
571*29b3e152SLi Zefan 	}
572*29b3e152SLi Zefan 
573*29b3e152SLi Zefan 	free(str);
574*29b3e152SLi Zefan 	return 0;
575*29b3e152SLi Zefan }
576*29b3e152SLi Zefan 
577ba77c9e1SLi Zefan static int parse_sort_opt(const struct option *opt __used,
578ba77c9e1SLi Zefan 			  const char *arg, int unset __used)
579ba77c9e1SLi Zefan {
580ba77c9e1SLi Zefan 	if (!arg)
581ba77c9e1SLi Zefan 		return -1;
582ba77c9e1SLi Zefan 
583ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
584*29b3e152SLi Zefan 		return setup_sorting(&caller_sort, arg);
585ba77c9e1SLi Zefan 	else
586*29b3e152SLi Zefan 		return setup_sorting(&alloc_sort, arg);
587ba77c9e1SLi Zefan 
588ba77c9e1SLi Zefan 	return 0;
589ba77c9e1SLi Zefan }
590ba77c9e1SLi Zefan 
591ba77c9e1SLi Zefan static int parse_stat_opt(const struct option *opt __used,
592ba77c9e1SLi Zefan 			  const char *arg, int unset __used)
593ba77c9e1SLi Zefan {
594ba77c9e1SLi Zefan 	if (!arg)
595ba77c9e1SLi Zefan 		return -1;
596ba77c9e1SLi Zefan 
597ba77c9e1SLi Zefan 	if (strcmp(arg, "alloc") == 0)
598ba77c9e1SLi Zefan 		alloc_flag = (caller_flag + 1);
599ba77c9e1SLi Zefan 	else if (strcmp(arg, "caller") == 0)
600ba77c9e1SLi Zefan 		caller_flag = (alloc_flag + 1);
601ba77c9e1SLi Zefan 	else
602ba77c9e1SLi Zefan 		return -1;
603ba77c9e1SLi Zefan 	return 0;
604ba77c9e1SLi Zefan }
605ba77c9e1SLi Zefan 
606ba77c9e1SLi Zefan static int parse_line_opt(const struct option *opt __used,
607ba77c9e1SLi Zefan 			  const char *arg, int unset __used)
608ba77c9e1SLi Zefan {
609ba77c9e1SLi Zefan 	int lines;
610ba77c9e1SLi Zefan 
611ba77c9e1SLi Zefan 	if (!arg)
612ba77c9e1SLi Zefan 		return -1;
613ba77c9e1SLi Zefan 
614ba77c9e1SLi Zefan 	lines = strtoul(arg, NULL, 10);
615ba77c9e1SLi Zefan 
616ba77c9e1SLi Zefan 	if (caller_flag > alloc_flag)
617ba77c9e1SLi Zefan 		caller_lines = lines;
618ba77c9e1SLi Zefan 	else
619ba77c9e1SLi Zefan 		alloc_lines = lines;
620ba77c9e1SLi Zefan 
621ba77c9e1SLi Zefan 	return 0;
622ba77c9e1SLi Zefan }
623ba77c9e1SLi Zefan 
624ba77c9e1SLi Zefan static const struct option kmem_options[] = {
625ba77c9e1SLi Zefan 	OPT_STRING('i', "input", &input_name, "file",
626ba77c9e1SLi Zefan 		   "input file name"),
627ba77c9e1SLi Zefan 	OPT_CALLBACK(0, "stat", NULL, "<alloc>|<caller>",
628ba77c9e1SLi Zefan 		     "stat selector, Pass 'alloc' or 'caller'.",
629ba77c9e1SLi Zefan 		     parse_stat_opt),
630*29b3e152SLi Zefan 	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
631*29b3e152SLi Zefan 		     "sort by key(s): ptr, call_site, bytes, hit, frag",
632ba77c9e1SLi Zefan 		     parse_sort_opt),
633ba77c9e1SLi Zefan 	OPT_CALLBACK('l', "line", NULL, "num",
634ba77c9e1SLi Zefan 		     "show n lins",
635ba77c9e1SLi Zefan 		     parse_line_opt),
6367707b6b6SLi Zefan 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
637ba77c9e1SLi Zefan 	OPT_END()
638ba77c9e1SLi Zefan };
639ba77c9e1SLi Zefan 
640ba77c9e1SLi Zefan static const char *record_args[] = {
641ba77c9e1SLi Zefan 	"record",
642ba77c9e1SLi Zefan 	"-a",
643ba77c9e1SLi Zefan 	"-R",
644ba77c9e1SLi Zefan 	"-M",
645ba77c9e1SLi Zefan 	"-f",
646ba77c9e1SLi Zefan 	"-c", "1",
647ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc",
648ba77c9e1SLi Zefan 	"-e", "kmem:kmalloc_node",
649ba77c9e1SLi Zefan 	"-e", "kmem:kfree",
650ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc",
651ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_alloc_node",
652ba77c9e1SLi Zefan 	"-e", "kmem:kmem_cache_free",
653ba77c9e1SLi Zefan };
654ba77c9e1SLi Zefan 
655ba77c9e1SLi Zefan static int __cmd_record(int argc, const char **argv)
656ba77c9e1SLi Zefan {
657ba77c9e1SLi Zefan 	unsigned int rec_argc, i, j;
658ba77c9e1SLi Zefan 	const char **rec_argv;
659ba77c9e1SLi Zefan 
660ba77c9e1SLi Zefan 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
661ba77c9e1SLi Zefan 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
662ba77c9e1SLi Zefan 
663ba77c9e1SLi Zefan 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
664ba77c9e1SLi Zefan 		rec_argv[i] = strdup(record_args[i]);
665ba77c9e1SLi Zefan 
666ba77c9e1SLi Zefan 	for (j = 1; j < (unsigned int)argc; j++, i++)
667ba77c9e1SLi Zefan 		rec_argv[i] = argv[j];
668ba77c9e1SLi Zefan 
669ba77c9e1SLi Zefan 	return cmd_record(i, rec_argv, NULL);
670ba77c9e1SLi Zefan }
671ba77c9e1SLi Zefan 
672ba77c9e1SLi Zefan int cmd_kmem(int argc, const char **argv, const char *prefix __used)
673ba77c9e1SLi Zefan {
674ba77c9e1SLi Zefan 	symbol__init(0);
675ba77c9e1SLi Zefan 
676ba77c9e1SLi Zefan 	argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
677ba77c9e1SLi Zefan 
678ba77c9e1SLi Zefan 	if (argc && !strncmp(argv[0], "rec", 3))
679ba77c9e1SLi Zefan 		return __cmd_record(argc, argv);
680ba77c9e1SLi Zefan 	else if (argc)
681ba77c9e1SLi Zefan 		usage_with_options(kmem_usage, kmem_options);
682ba77c9e1SLi Zefan 
683*29b3e152SLi Zefan 	if (list_empty(&caller_sort))
684*29b3e152SLi Zefan 		setup_sorting(&caller_sort, default_sort_order);
685*29b3e152SLi Zefan 	if (list_empty(&alloc_sort))
686*29b3e152SLi Zefan 		setup_sorting(&alloc_sort, default_sort_order);
687ba77c9e1SLi Zefan 
688ba77c9e1SLi Zefan 	return __cmd_kmem();
689ba77c9e1SLi Zefan }
690ba77c9e1SLi Zefan 
691