Blame view

tools/perf/builtin-kmem.c 17.2 KB
ba77c9e11   Li Zefan   perf: Add 'perf k...
1
2
3
4
5
6
7
8
  #include "builtin.h"
  #include "perf.h"
  
  #include "util/util.h"
  #include "util/cache.h"
  #include "util/symbol.h"
  #include "util/thread.h"
  #include "util/header.h"
94c744b6c   Arnaldo Carvalho de Melo   perf tools: Intro...
9
  #include "util/session.h"
ba77c9e11   Li Zefan   perf: Add 'perf k...
10
11
12
13
14
  
  #include "util/parse-options.h"
  #include "util/trace-event.h"
  
  #include "util/debug.h"
ba77c9e11   Li Zefan   perf: Add 'perf k...
15
16
17
18
19
20
21
  
  #include <linux/rbtree.h>
  
  struct alloc_stat;
  typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
  
  static char const		*input_name = "perf.data";
ba77c9e11   Li Zefan   perf: Add 'perf k...
22
23
  static int			alloc_flag;
  static int			caller_flag;
ba77c9e11   Li Zefan   perf: Add 'perf k...
24
25
  static int			alloc_lines = -1;
  static int			caller_lines = -1;
7707b6b6f   Li Zefan   perf kmem: Add ne...
26
  static bool			raw_ip;
29b3e1528   Li Zefan   perf kmem: Defaul...
27
  static char			default_sort_order[] = "frag,hit,bytes";
7d0d39459   Li Zefan   perf kmem: Collec...
28
29
  static int			*cpunode_map;
  static int			max_cpu_num;
ba77c9e11   Li Zefan   perf: Add 'perf k...
30
  struct alloc_stat {
079d3f653   Li Zefan   perf kmem: Measur...
31
32
  	u64	call_site;
  	u64	ptr;
ba77c9e11   Li Zefan   perf: Add 'perf k...
33
34
35
  	u64	bytes_req;
  	u64	bytes_alloc;
  	u32	hit;
079d3f653   Li Zefan   perf kmem: Measur...
36
37
38
  	u32	pingpong;
  
  	short	alloc_cpu;
ba77c9e11   Li Zefan   perf: Add 'perf k...
39
40
41
42
43
44
45
46
47
48
  
  	struct rb_node node;
  };
  
  static struct rb_root root_alloc_stat;
  static struct rb_root root_alloc_sorted;
  static struct rb_root root_caller_stat;
  static struct rb_root root_caller_sorted;
  
  static unsigned long total_requested, total_allocated;
7d0d39459   Li Zefan   perf kmem: Collec...
49
  static unsigned long nr_allocs, nr_cross_allocs;
ba77c9e11   Li Zefan   perf: Add 'perf k...
50

7d0d39459   Li Zefan   perf kmem: Collec...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  #define PATH_SYS_NODE	"/sys/devices/system/node"
  
  static void init_cpunode_map(void)
  {
  	FILE *fp;
  	int i;
  
  	fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
  	if (!fp) {
  		max_cpu_num = 4096;
  		return;
  	}
  
  	if (fscanf(fp, "%d", &max_cpu_num) < 1)
  		die("Failed to read 'kernel_max' from sysfs");
  	max_cpu_num++;
  
  	cpunode_map = calloc(max_cpu_num, sizeof(int));
  	if (!cpunode_map)
  		die("calloc");
  	for (i = 0; i < max_cpu_num; i++)
  		cpunode_map[i] = -1;
  	fclose(fp);
  }
  
  static void setup_cpunode_map(void)
  {
  	struct dirent *dent1, *dent2;
  	DIR *dir1, *dir2;
  	unsigned int cpu, mem;
  	char buf[PATH_MAX];
  
  	init_cpunode_map();
  
  	dir1 = opendir(PATH_SYS_NODE);
  	if (!dir1)
  		return;
659d8cfbb   Ulrich Drepper   perf tools: Do a ...
88
89
90
  	while ((dent1 = readdir(dir1)) != NULL) {
  		if (dent1->d_type != DT_DIR ||
  		    sscanf(dent1->d_name, "node%u", &mem) < 1)
7d0d39459   Li Zefan   perf kmem: Collec...
91
92
93
94
95
96
  			continue;
  
  		snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
  		dir2 = opendir(buf);
  		if (!dir2)
  			continue;
659d8cfbb   Ulrich Drepper   perf tools: Do a ...
97
98
99
  		while ((dent2 = readdir(dir2)) != NULL) {
  			if (dent2->d_type != DT_LNK ||
  			    sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
7d0d39459   Li Zefan   perf kmem: Collec...
100
101
102
103
104
  				continue;
  			cpunode_map[cpu] = mem;
  		}
  	}
  }
079d3f653   Li Zefan   perf kmem: Measur...
105
106
  static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
  			      int bytes_req, int bytes_alloc, int cpu)
ba77c9e11   Li Zefan   perf: Add 'perf k...
107
108
109
110
  {
  	struct rb_node **node = &root_alloc_stat.rb_node;
  	struct rb_node *parent = NULL;
  	struct alloc_stat *data = NULL;
ba77c9e11   Li Zefan   perf: Add 'perf k...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  	while (*node) {
  		parent = *node;
  		data = rb_entry(*node, struct alloc_stat, node);
  
  		if (ptr > data->ptr)
  			node = &(*node)->rb_right;
  		else if (ptr < data->ptr)
  			node = &(*node)->rb_left;
  		else
  			break;
  	}
  
  	if (data && data->ptr == ptr) {
  		data->hit++;
  		data->bytes_req += bytes_req;
4efb5290a   Wenji Huang   perf kmem: Fix st...
126
  		data->bytes_alloc += bytes_alloc;
ba77c9e11   Li Zefan   perf: Add 'perf k...
127
128
  	} else {
  		data = malloc(sizeof(*data));
079d3f653   Li Zefan   perf kmem: Measur...
129
130
  		if (!data)
  			die("malloc");
ba77c9e11   Li Zefan   perf: Add 'perf k...
131
  		data->ptr = ptr;
079d3f653   Li Zefan   perf kmem: Measur...
132
  		data->pingpong = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
133
134
135
136
137
138
139
  		data->hit = 1;
  		data->bytes_req = bytes_req;
  		data->bytes_alloc = bytes_alloc;
  
  		rb_link_node(&data->node, parent, node);
  		rb_insert_color(&data->node, &root_alloc_stat);
  	}
079d3f653   Li Zefan   perf kmem: Measur...
140
141
  	data->call_site = call_site;
  	data->alloc_cpu = cpu;
ba77c9e11   Li Zefan   perf: Add 'perf k...
142
143
144
145
146
147
148
149
  }
  
  static void insert_caller_stat(unsigned long call_site,
  			      int bytes_req, int bytes_alloc)
  {
  	struct rb_node **node = &root_caller_stat.rb_node;
  	struct rb_node *parent = NULL;
  	struct alloc_stat *data = NULL;
ba77c9e11   Li Zefan   perf: Add 'perf k...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  	while (*node) {
  		parent = *node;
  		data = rb_entry(*node, struct alloc_stat, node);
  
  		if (call_site > data->call_site)
  			node = &(*node)->rb_right;
  		else if (call_site < data->call_site)
  			node = &(*node)->rb_left;
  		else
  			break;
  	}
  
  	if (data && data->call_site == call_site) {
  		data->hit++;
  		data->bytes_req += bytes_req;
4efb5290a   Wenji Huang   perf kmem: Fix st...
165
  		data->bytes_alloc += bytes_alloc;
ba77c9e11   Li Zefan   perf: Add 'perf k...
166
167
  	} else {
  		data = malloc(sizeof(*data));
079d3f653   Li Zefan   perf kmem: Measur...
168
169
  		if (!data)
  			die("malloc");
ba77c9e11   Li Zefan   perf: Add 'perf k...
170
  		data->call_site = call_site;
079d3f653   Li Zefan   perf kmem: Measur...
171
  		data->pingpong = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
172
173
174
175
176
177
178
179
  		data->hit = 1;
  		data->bytes_req = bytes_req;
  		data->bytes_alloc = bytes_alloc;
  
  		rb_link_node(&data->node, parent, node);
  		rb_insert_color(&data->node, &root_caller_stat);
  	}
  }
f48f669d4   Xiao Guangrong   perf_event: Elimi...
180
  static void process_alloc_event(void *data,
ba77c9e11   Li Zefan   perf: Add 'perf k...
181
  				struct event *event,
7d0d39459   Li Zefan   perf kmem: Collec...
182
  				int cpu,
ba77c9e11   Li Zefan   perf: Add 'perf k...
183
184
  				u64 timestamp __used,
  				struct thread *thread __used,
7d0d39459   Li Zefan   perf kmem: Collec...
185
  				int node)
ba77c9e11   Li Zefan   perf: Add 'perf k...
186
187
188
189
190
  {
  	unsigned long call_site;
  	unsigned long ptr;
  	int bytes_req;
  	int bytes_alloc;
7d0d39459   Li Zefan   perf kmem: Collec...
191
  	int node1, node2;
ba77c9e11   Li Zefan   perf: Add 'perf k...
192

f48f669d4   Xiao Guangrong   perf_event: Elimi...
193
194
195
196
  	ptr = raw_field_value(event, "ptr", data);
  	call_site = raw_field_value(event, "call_site", data);
  	bytes_req = raw_field_value(event, "bytes_req", data);
  	bytes_alloc = raw_field_value(event, "bytes_alloc", data);
ba77c9e11   Li Zefan   perf: Add 'perf k...
197

079d3f653   Li Zefan   perf kmem: Measur...
198
  	insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
ba77c9e11   Li Zefan   perf: Add 'perf k...
199
200
201
202
  	insert_caller_stat(call_site, bytes_req, bytes_alloc);
  
  	total_requested += bytes_req;
  	total_allocated += bytes_alloc;
7d0d39459   Li Zefan   perf kmem: Collec...
203
204
205
  
  	if (node) {
  		node1 = cpunode_map[cpu];
f48f669d4   Xiao Guangrong   perf_event: Elimi...
206
  		node2 = raw_field_value(event, "node", data);
7d0d39459   Li Zefan   perf kmem: Collec...
207
208
209
210
  		if (node1 != node2)
  			nr_cross_allocs++;
  	}
  	nr_allocs++;
ba77c9e11   Li Zefan   perf: Add 'perf k...
211
  }
079d3f653   Li Zefan   perf kmem: Measur...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
  static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
  
  static struct alloc_stat *search_alloc_stat(unsigned long ptr,
  					    unsigned long call_site,
  					    struct rb_root *root,
  					    sort_fn_t sort_fn)
  {
  	struct rb_node *node = root->rb_node;
  	struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
  
  	while (node) {
  		struct alloc_stat *data;
  		int cmp;
  
  		data = rb_entry(node, struct alloc_stat, node);
  
  		cmp = sort_fn(&key, data);
  		if (cmp < 0)
  			node = node->rb_left;
  		else if (cmp > 0)
  			node = node->rb_right;
  		else
  			return data;
  	}
  	return NULL;
  }
f48f669d4   Xiao Guangrong   perf_event: Elimi...
239
  static void process_free_event(void *data,
079d3f653   Li Zefan   perf kmem: Measur...
240
241
  			       struct event *event,
  			       int cpu,
ba77c9e11   Li Zefan   perf: Add 'perf k...
242
243
244
  			       u64 timestamp __used,
  			       struct thread *thread __used)
  {
079d3f653   Li Zefan   perf kmem: Measur...
245
246
  	unsigned long ptr;
  	struct alloc_stat *s_alloc, *s_caller;
f48f669d4   Xiao Guangrong   perf_event: Elimi...
247
  	ptr = raw_field_value(event, "ptr", data);
079d3f653   Li Zefan   perf kmem: Measur...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  
  	s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
  	if (!s_alloc)
  		return;
  
  	if (cpu != s_alloc->alloc_cpu) {
  		s_alloc->pingpong++;
  
  		s_caller = search_alloc_stat(0, s_alloc->call_site,
  					     &root_caller_stat, callsite_cmp);
  		assert(s_caller);
  		s_caller->pingpong++;
  	}
  	s_alloc->alloc_cpu = -1;
ba77c9e11   Li Zefan   perf: Add 'perf k...
262
  }
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
263
264
  static void process_raw_event(union perf_event *raw_event __used, void *data,
  			      int cpu, u64 timestamp, struct thread *thread)
ba77c9e11   Li Zefan   perf: Add 'perf k...
265
  {
ba77c9e11   Li Zefan   perf: Add 'perf k...
266
267
  	struct event *event;
  	int type;
f48f669d4   Xiao Guangrong   perf_event: Elimi...
268
  	type = trace_parse_common_type(data);
ba77c9e11   Li Zefan   perf: Add 'perf k...
269
270
271
272
  	event = trace_find_event(type);
  
  	if (!strcmp(event->name, "kmalloc") ||
  	    !strcmp(event->name, "kmem_cache_alloc")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
273
  		process_alloc_event(data, event, cpu, timestamp, thread, 0);
ba77c9e11   Li Zefan   perf: Add 'perf k...
274
275
276
277
278
  		return;
  	}
  
  	if (!strcmp(event->name, "kmalloc_node") ||
  	    !strcmp(event->name, "kmem_cache_alloc_node")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
279
  		process_alloc_event(data, event, cpu, timestamp, thread, 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
280
281
282
283
284
  		return;
  	}
  
  	if (!strcmp(event->name, "kfree") ||
  	    !strcmp(event->name, "kmem_cache_free")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
285
  		process_free_event(data, event, cpu, timestamp, thread);
ba77c9e11   Li Zefan   perf: Add 'perf k...
286
287
288
  		return;
  	}
  }
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
289
290
  static int process_sample_event(union perf_event *event,
  				struct perf_sample *sample,
9e69c2108   Arnaldo Carvalho de Melo   perf session: Pas...
291
  				struct perf_evsel *evsel __used,
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
292
  				struct perf_session *session)
ba77c9e11   Li Zefan   perf: Add 'perf k...
293
  {
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
294
  	struct thread *thread = perf_session__findnew(session, event->ip.pid);
ba77c9e11   Li Zefan   perf: Add 'perf k...
295

ba77c9e11   Li Zefan   perf: Add 'perf k...
296
297
298
299
300
301
302
303
304
  	if (thread == NULL) {
  		pr_debug("problem processing %d event, skipping it.
  ",
  			 event->header.type);
  		return -1;
  	}
  
  	dump_printf(" ... thread: %s:%d
  ", thread->comm, thread->pid);
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
305
306
  	process_raw_event(event, sample->raw_data, sample->cpu,
  			  sample->time, thread);
ba77c9e11   Li Zefan   perf: Add 'perf k...
307
308
309
  
  	return 0;
  }
301a0b020   Arnaldo Carvalho de Melo   perf session: Dit...
310
  static struct perf_event_ops event_ops = {
587570d4c   Frederic Weisbecker   perf: Use generic...
311
  	.sample			= process_sample_event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
312
  	.comm			= perf_event__process_comm,
587570d4c   Frederic Weisbecker   perf: Use generic...
313
  	.ordered_samples	= true,
ba77c9e11   Li Zefan   perf: Add 'perf k...
314
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
315
316
317
318
319
320
321
  static double fragmentation(unsigned long n_req, unsigned long n_alloc)
  {
  	if (n_alloc == 0)
  		return 0.0;
  	else
  		return 100.0 - (100.0 * n_req / n_alloc);
  }
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
322
323
  static void __print_result(struct rb_root *root, struct perf_session *session,
  			   int n_lines, int is_caller)
ba77c9e11   Li Zefan   perf: Add 'perf k...
324
325
  {
  	struct rb_node *next;
23346f21b   Arnaldo Carvalho de Melo   perf tools: Renam...
326
  	struct machine *machine;
ba77c9e11   Li Zefan   perf: Add 'perf k...
327

079d3f653   Li Zefan   perf kmem: Measur...
328
329
330
  	printf("%.102s
  ", graph_dotted_line);
  	printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
47103277f   Pekka Enberg   perf kmem: Increa...
331
332
  	printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag
  ");
079d3f653   Li Zefan   perf kmem: Measur...
333
334
  	printf("%.102s
  ", graph_dotted_line);
ba77c9e11   Li Zefan   perf: Add 'perf k...
335
336
  
  	next = rb_first(root);
23346f21b   Arnaldo Carvalho de Melo   perf tools: Renam...
337
338
  	machine = perf_session__find_host_machine(session);
  	if (!machine) {
a1645ce12   Zhang, Yanmin   perf: 'perf kvm' ...
339
340
341
342
  		pr_err("__print_result: couldn't find kernel information
  ");
  		return;
  	}
ba77c9e11   Li Zefan   perf: Add 'perf k...
343
  	while (next && n_lines--) {
1b145ae58   Arnaldo Carvalho de Melo   perf kmem: Resolv...
344
345
346
  		struct alloc_stat *data = rb_entry(next, struct alloc_stat,
  						   node);
  		struct symbol *sym = NULL;
71cf8b8ff   Arnaldo Carvalho de Melo   perf kmem: Fixup ...
347
  		struct map *map;
079d3f653   Li Zefan   perf kmem: Measur...
348
  		char buf[BUFSIZ];
1b145ae58   Arnaldo Carvalho de Melo   perf kmem: Resolv...
349
350
351
352
  		u64 addr;
  
  		if (is_caller) {
  			addr = data->call_site;
7707b6b6f   Li Zefan   perf kmem: Add ne...
353
  			if (!raw_ip)
5c0541d53   Arnaldo Carvalho de Melo   perf symbols: Add...
354
  				sym = machine__find_kernel_function(machine, addr, &map, NULL);
1b145ae58   Arnaldo Carvalho de Melo   perf kmem: Resolv...
355
356
357
358
  		} else
  			addr = data->ptr;
  
  		if (sym != NULL)
9486aa387   Arnaldo Carvalho de Melo   perf tools: Fix 6...
359
  			snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
71cf8b8ff   Arnaldo Carvalho de Melo   perf kmem: Fixup ...
360
  				 addr - map->unmap_ip(map, sym->start));
1b145ae58   Arnaldo Carvalho de Melo   perf kmem: Resolv...
361
  		else
9486aa387   Arnaldo Carvalho de Melo   perf tools: Fix 6...
362
  			snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
079d3f653   Li Zefan   perf kmem: Measur...
363
  		printf(" %-34s |", buf);
ba77c9e11   Li Zefan   perf: Add 'perf k...
364

47103277f   Pekka Enberg   perf kmem: Increa...
365
366
  		printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%
  ",
079d3f653   Li Zefan   perf kmem: Measur...
367
  		       (unsigned long long)data->bytes_alloc,
ba77c9e11   Li Zefan   perf: Add 'perf k...
368
369
370
371
  		       (unsigned long)data->bytes_alloc / data->hit,
  		       (unsigned long long)data->bytes_req,
  		       (unsigned long)data->bytes_req / data->hit,
  		       (unsigned long)data->hit,
079d3f653   Li Zefan   perf kmem: Measur...
372
  		       (unsigned long)data->pingpong,
ba77c9e11   Li Zefan   perf: Add 'perf k...
373
374
375
376
377
378
  		       fragmentation(data->bytes_req, data->bytes_alloc));
  
  		next = rb_next(next);
  	}
  
  	if (n_lines == -1)
079d3f653   Li Zefan   perf kmem: Measur...
379
380
  		printf(" ...                                | ...             | ...             | ...    | ...      | ...   
  ");
ba77c9e11   Li Zefan   perf: Add 'perf k...
381

079d3f653   Li Zefan   perf kmem: Measur...
382
383
  	printf("%.102s
  ", graph_dotted_line);
ba77c9e11   Li Zefan   perf: Add 'perf k...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  }
  
  static void print_summary(void)
  {
  	printf("
  SUMMARY
  =======
  ");
  	printf("Total bytes requested: %lu
  ", total_requested);
  	printf("Total bytes allocated: %lu
  ", total_allocated);
  	printf("Total bytes wasted on internal fragmentation: %lu
  ",
  	       total_allocated - total_requested);
  	printf("Internal fragmentation: %f%%
  ",
  	       fragmentation(total_requested, total_allocated));
7d0d39459   Li Zefan   perf kmem: Collec...
402
403
  	printf("Cross CPU allocations: %lu/%lu
  ", nr_cross_allocs, nr_allocs);
ba77c9e11   Li Zefan   perf: Add 'perf k...
404
  }
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
405
  static void print_result(struct perf_session *session)
ba77c9e11   Li Zefan   perf: Add 'perf k...
406
407
  {
  	if (caller_flag)
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
408
  		__print_result(&root_caller_sorted, session, caller_lines, 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
409
  	if (alloc_flag)
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
410
  		__print_result(&root_alloc_sorted, session, alloc_lines, 0);
ba77c9e11   Li Zefan   perf: Add 'perf k...
411
412
  	print_summary();
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
413
414
415
416
417
418
419
420
  struct sort_dimension {
  	const char		name[20];
  	sort_fn_t		cmp;
  	struct list_head	list;
  };
  
  static LIST_HEAD(caller_sort);
  static LIST_HEAD(alloc_sort);
ba77c9e11   Li Zefan   perf: Add 'perf k...
421
  static void sort_insert(struct rb_root *root, struct alloc_stat *data,
29b3e1528   Li Zefan   perf kmem: Defaul...
422
  			struct list_head *sort_list)
ba77c9e11   Li Zefan   perf: Add 'perf k...
423
424
425
  {
  	struct rb_node **new = &(root->rb_node);
  	struct rb_node *parent = NULL;
29b3e1528   Li Zefan   perf kmem: Defaul...
426
  	struct sort_dimension *sort;
ba77c9e11   Li Zefan   perf: Add 'perf k...
427
428
429
  
  	while (*new) {
  		struct alloc_stat *this;
29b3e1528   Li Zefan   perf kmem: Defaul...
430
  		int cmp = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
431
432
433
  
  		this = rb_entry(*new, struct alloc_stat, node);
  		parent = *new;
29b3e1528   Li Zefan   perf kmem: Defaul...
434
435
436
437
438
  		list_for_each_entry(sort, sort_list, list) {
  			cmp = sort->cmp(data, this);
  			if (cmp)
  				break;
  		}
ba77c9e11   Li Zefan   perf: Add 'perf k...
439
440
441
442
443
444
445
446
447
448
449
450
  
  		if (cmp > 0)
  			new = &((*new)->rb_left);
  		else
  			new = &((*new)->rb_right);
  	}
  
  	rb_link_node(&data->node, parent, new);
  	rb_insert_color(&data->node, root);
  }
  
  static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
29b3e1528   Li Zefan   perf kmem: Defaul...
451
  			  struct list_head *sort_list)
ba77c9e11   Li Zefan   perf: Add 'perf k...
452
453
454
455
456
457
458
459
460
461
462
  {
  	struct rb_node *node;
  	struct alloc_stat *data;
  
  	for (;;) {
  		node = rb_first(root);
  		if (!node)
  			break;
  
  		rb_erase(node, root);
  		data = rb_entry(node, struct alloc_stat, node);
29b3e1528   Li Zefan   perf kmem: Defaul...
463
  		sort_insert(root_sorted, data, sort_list);
ba77c9e11   Li Zefan   perf: Add 'perf k...
464
465
466
467
468
  	}
  }
  
  static void sort_result(void)
  {
29b3e1528   Li Zefan   perf kmem: Defaul...
469
470
  	__sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
  	__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
ba77c9e11   Li Zefan   perf: Add 'perf k...
471
472
473
474
  }
  
  static int __cmd_kmem(void)
  {
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
475
  	int err = -EINVAL;
21ef97f05   Ian Munsie   perf session: Fal...
476
477
  	struct perf_session *session = perf_session__new(input_name, O_RDONLY,
  							 0, false, &event_ops);
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
478
479
  	if (session == NULL)
  		return -ENOMEM;
e727ca73f   Arnaldo Carvalho de Melo   perf kmem: Resolv...
480
481
  	if (perf_session__create_kernel_maps(session) < 0)
  		goto out_delete;
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
482
483
  	if (!perf_session__has_traces(session, "kmem record"))
  		goto out_delete;
ba77c9e11   Li Zefan   perf: Add 'perf k...
484
  	setup_pager();
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
485
486
487
  	err = perf_session__process_events(session, &event_ops);
  	if (err != 0)
  		goto out_delete;
ba77c9e11   Li Zefan   perf: Add 'perf k...
488
  	sort_result();
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
489
490
491
492
  	print_result(session);
  out_delete:
  	perf_session__delete(session);
  	return err;
ba77c9e11   Li Zefan   perf: Add 'perf k...
493
494
495
  }
  
  static const char * const kmem_usage[] = {
90b86a9f7   Li Zefan   perf kmem: Show u...
496
  	"perf kmem [<options>] {record|stat}",
ba77c9e11   Li Zefan   perf: Add 'perf k...
497
498
  	NULL
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
499
500
501
502
503
504
505
506
  static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	if (l->ptr < r->ptr)
  		return -1;
  	else if (l->ptr > r->ptr)
  		return 1;
  	return 0;
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
507
508
509
510
  static struct sort_dimension ptr_sort_dimension = {
  	.name	= "ptr",
  	.cmp	= ptr_cmp,
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
511
512
513
514
515
516
517
518
  static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	if (l->call_site < r->call_site)
  		return -1;
  	else if (l->call_site > r->call_site)
  		return 1;
  	return 0;
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
519
520
521
522
  static struct sort_dimension callsite_sort_dimension = {
  	.name	= "callsite",
  	.cmp	= callsite_cmp,
  };
f3ced7cdb   Pekka Enberg   perf kmem: Add --...
523
524
525
526
527
528
529
530
  static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	if (l->hit < r->hit)
  		return -1;
  	else if (l->hit > r->hit)
  		return 1;
  	return 0;
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
531
532
533
534
  static struct sort_dimension hit_sort_dimension = {
  	.name	= "hit",
  	.cmp	= hit_cmp,
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
535
536
537
538
539
540
541
542
  static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	if (l->bytes_alloc < r->bytes_alloc)
  		return -1;
  	else if (l->bytes_alloc > r->bytes_alloc)
  		return 1;
  	return 0;
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
543
544
545
546
  static struct sort_dimension bytes_sort_dimension = {
  	.name	= "bytes",
  	.cmp	= bytes_cmp,
  };
f3ced7cdb   Pekka Enberg   perf kmem: Add --...
547
548
549
550
551
552
553
554
555
556
557
558
559
  static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	double x, y;
  
  	x = fragmentation(l->bytes_req, l->bytes_alloc);
  	y = fragmentation(r->bytes_req, r->bytes_alloc);
  
  	if (x < y)
  		return -1;
  	else if (x > y)
  		return 1;
  	return 0;
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
560
561
562
563
  static struct sort_dimension frag_sort_dimension = {
  	.name	= "frag",
  	.cmp	= frag_cmp,
  };
079d3f653   Li Zefan   perf kmem: Measur...
564
565
566
567
568
569
570
571
572
573
574
575
576
  static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
  {
  	if (l->pingpong < r->pingpong)
  		return -1;
  	else if (l->pingpong > r->pingpong)
  		return 1;
  	return 0;
  }
  
  static struct sort_dimension pingpong_sort_dimension = {
  	.name	= "pingpong",
  	.cmp	= pingpong_cmp,
  };
29b3e1528   Li Zefan   perf kmem: Defaul...
577
578
579
580
581
582
  static struct sort_dimension *avail_sorts[] = {
  	&ptr_sort_dimension,
  	&callsite_sort_dimension,
  	&hit_sort_dimension,
  	&bytes_sort_dimension,
  	&frag_sort_dimension,
079d3f653   Li Zefan   perf kmem: Measur...
583
  	&pingpong_sort_dimension,
29b3e1528   Li Zefan   perf kmem: Defaul...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
  };
  
  #define NUM_AVAIL_SORTS	\
  	(int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
  
  static int sort_dimension__add(const char *tok, struct list_head *list)
  {
  	struct sort_dimension *sort;
  	int i;
  
  	for (i = 0; i < NUM_AVAIL_SORTS; i++) {
  		if (!strcmp(avail_sorts[i]->name, tok)) {
  			sort = malloc(sizeof(*sort));
  			if (!sort)
  				die("malloc");
  			memcpy(sort, avail_sorts[i], sizeof(*sort));
  			list_add_tail(&sort->list, list);
  			return 0;
  		}
  	}
  
  	return -1;
  }
  
  static int setup_sorting(struct list_head *sort_list, const char *arg)
  {
  	char *tok;
  	char *str = strdup(arg);
  
  	if (!str)
  		die("strdup");
  
  	while (true) {
  		tok = strsep(&str, ",");
  		if (!tok)
  			break;
  		if (sort_dimension__add(tok, sort_list) < 0) {
  			error("Unknown --sort key: '%s'", tok);
  			return -1;
  		}
  	}
  
  	free(str);
  	return 0;
  }
ba77c9e11   Li Zefan   perf: Add 'perf k...
629
630
631
  static int parse_sort_opt(const struct option *opt __used,
  			  const char *arg, int unset __used)
  {
ba77c9e11   Li Zefan   perf: Add 'perf k...
632
633
  	if (!arg)
  		return -1;
ba77c9e11   Li Zefan   perf: Add 'perf k...
634
  	if (caller_flag > alloc_flag)
29b3e1528   Li Zefan   perf kmem: Defaul...
635
  		return setup_sorting(&caller_sort, arg);
ba77c9e11   Li Zefan   perf: Add 'perf k...
636
  	else
29b3e1528   Li Zefan   perf kmem: Defaul...
637
  		return setup_sorting(&alloc_sort, arg);
ba77c9e11   Li Zefan   perf: Add 'perf k...
638
639
640
  
  	return 0;
  }
90b86a9f7   Li Zefan   perf kmem: Show u...
641
  static int parse_caller_opt(const struct option *opt __used,
793124169   Ingo Molnar   perf kmem: Fix un...
642
  			  const char *arg __used, int unset __used)
ba77c9e11   Li Zefan   perf: Add 'perf k...
643
  {
90b86a9f7   Li Zefan   perf kmem: Show u...
644
645
646
  	caller_flag = (alloc_flag + 1);
  	return 0;
  }
ba77c9e11   Li Zefan   perf: Add 'perf k...
647

90b86a9f7   Li Zefan   perf kmem: Show u...
648
  static int parse_alloc_opt(const struct option *opt __used,
793124169   Ingo Molnar   perf kmem: Fix un...
649
  			  const char *arg __used, int unset __used)
90b86a9f7   Li Zefan   perf kmem: Show u...
650
651
  {
  	alloc_flag = (caller_flag + 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  	return 0;
  }
  
  static int parse_line_opt(const struct option *opt __used,
  			  const char *arg, int unset __used)
  {
  	int lines;
  
  	if (!arg)
  		return -1;
  
  	lines = strtoul(arg, NULL, 10);
  
  	if (caller_flag > alloc_flag)
  		caller_lines = lines;
  	else
  		alloc_lines = lines;
  
  	return 0;
  }
  
  static const struct option kmem_options[] = {
  	OPT_STRING('i', "input", &input_name, "file",
  		   "input file name"),
90b86a9f7   Li Zefan   perf kmem: Show u...
676
677
678
679
680
681
  	OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
  			   "show per-callsite statistics",
  			   parse_caller_opt),
  	OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
  			   "show per-allocation statistics",
  			   parse_alloc_opt),
29b3e1528   Li Zefan   perf kmem: Defaul...
682
  	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
079d3f653   Li Zefan   perf kmem: Measur...
683
  		     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
ba77c9e11   Li Zefan   perf: Add 'perf k...
684
685
  		     parse_sort_opt),
  	OPT_CALLBACK('l', "line", NULL, "num",
90b86a9f7   Li Zefan   perf kmem: Show u...
686
  		     "show n lines",
ba77c9e11   Li Zefan   perf: Add 'perf k...
687
  		     parse_line_opt),
7707b6b6f   Li Zefan   perf kmem: Add ne...
688
  	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
ba77c9e11   Li Zefan   perf: Add 'perf k...
689
690
691
692
693
694
695
  	OPT_END()
  };
  
  static const char *record_args[] = {
  	"record",
  	"-a",
  	"-R",
ba77c9e11   Li Zefan   perf: Add 'perf k...
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
  	"-f",
  	"-c", "1",
  	"-e", "kmem:kmalloc",
  	"-e", "kmem:kmalloc_node",
  	"-e", "kmem:kfree",
  	"-e", "kmem:kmem_cache_alloc",
  	"-e", "kmem:kmem_cache_alloc_node",
  	"-e", "kmem:kmem_cache_free",
  };
  
  static int __cmd_record(int argc, const char **argv)
  {
  	unsigned int rec_argc, i, j;
  	const char **rec_argv;
  
  	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
  	rec_argv = calloc(rec_argc + 1, sizeof(char *));
ce47dc56a   Chris Samuel   perf tools: Catch...
713
714
  	if (rec_argv == NULL)
  		return -ENOMEM;
ba77c9e11   Li Zefan   perf: Add 'perf k...
715
716
717
718
719
720
721
722
723
724
725
  	for (i = 0; i < ARRAY_SIZE(record_args); i++)
  		rec_argv[i] = strdup(record_args[i]);
  
  	for (j = 1; j < (unsigned int)argc; j++, i++)
  		rec_argv[i] = argv[j];
  
  	return cmd_record(i, rec_argv, NULL);
  }
  
  int cmd_kmem(int argc, const char **argv, const char *prefix __used)
  {
ba77c9e11   Li Zefan   perf: Add 'perf k...
726
  	argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
90b86a9f7   Li Zefan   perf kmem: Show u...
727
  	if (!argc)
ba77c9e11   Li Zefan   perf: Add 'perf k...
728
  		usage_with_options(kmem_usage, kmem_options);
655000e7c   Arnaldo Carvalho de Melo   perf symbols: Ado...
729
  	symbol__init();
90b86a9f7   Li Zefan   perf kmem: Show u...
730
731
732
733
734
735
736
737
738
  	if (!strncmp(argv[0], "rec", 3)) {
  		return __cmd_record(argc, argv);
  	} else if (!strcmp(argv[0], "stat")) {
  		setup_cpunode_map();
  
  		if (list_empty(&caller_sort))
  			setup_sorting(&caller_sort, default_sort_order);
  		if (list_empty(&alloc_sort))
  			setup_sorting(&alloc_sort, default_sort_order);
ba77c9e11   Li Zefan   perf: Add 'perf k...
739

90b86a9f7   Li Zefan   perf kmem: Show u...
740
  		return __cmd_kmem();
b00eca8cd   Pekka Enberg   perf kmem: Print ...
741
742
  	} else
  		usage_with_options(kmem_usage, kmem_options);
7d0d39459   Li Zefan   perf kmem: Collec...
743

90b86a9f7   Li Zefan   perf kmem: Show u...
744
  	return 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
745
  }