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"
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
10
  #include "util/tool.h"
ba77c9e11   Li Zefan   perf: Add 'perf k...
11
12
13
14
15
  
  #include "util/parse-options.h"
  #include "util/trace-event.h"
  
  #include "util/debug.h"
ba77c9e11   Li Zefan   perf: Add 'perf k...
16
17
18
19
20
  
  #include <linux/rbtree.h>
  
  struct alloc_stat;
  typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
efad14150   Robert Richter   perf report: Acce...
21
  static const char		*input_name;
ba77c9e11   Li Zefan   perf: Add 'perf k...
22

ba77c9e11   Li Zefan   perf: Add 'perf k...
23
24
  static int			alloc_flag;
  static int			caller_flag;
ba77c9e11   Li Zefan   perf: Add 'perf k...
25
26
  static int			alloc_lines = -1;
  static int			caller_lines = -1;
7707b6b6f   Li Zefan   perf kmem: Add ne...
27
  static bool			raw_ip;
29b3e1528   Li Zefan   perf kmem: Defaul...
28
  static char			default_sort_order[] = "frag,hit,bytes";
7d0d39459   Li Zefan   perf kmem: Collec...
29
30
  static int			*cpunode_map;
  static int			max_cpu_num;
ba77c9e11   Li Zefan   perf: Add 'perf k...
31
  struct alloc_stat {
079d3f653   Li Zefan   perf kmem: Measur...
32
33
  	u64	call_site;
  	u64	ptr;
ba77c9e11   Li Zefan   perf: Add 'perf k...
34
35
36
  	u64	bytes_req;
  	u64	bytes_alloc;
  	u32	hit;
079d3f653   Li Zefan   perf kmem: Measur...
37
38
39
  	u32	pingpong;
  
  	short	alloc_cpu;
ba77c9e11   Li Zefan   perf: Add 'perf k...
40
41
42
43
44
45
46
47
48
49
  
  	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...
50
  static unsigned long nr_allocs, nr_cross_allocs;
ba77c9e11   Li Zefan   perf: Add 'perf k...
51

7d0d39459   Li Zefan   perf kmem: Collec...
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
88
  #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 ...
89
90
91
  	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...
92
93
94
95
96
97
  			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 ...
98
99
100
  		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...
101
102
103
  				continue;
  			cpunode_map[cpu] = mem;
  		}
8442da1d9   Namhyung Kim   perf kmem: Add mi...
104
  		closedir(dir2);
7d0d39459   Li Zefan   perf kmem: Collec...
105
  	}
8442da1d9   Namhyung Kim   perf kmem: Add mi...
106
  	closedir(dir1);
7d0d39459   Li Zefan   perf kmem: Collec...
107
  }
079d3f653   Li Zefan   perf kmem: Measur...
108
109
  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...
110
111
112
113
  {
  	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...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  	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...
129
  		data->bytes_alloc += bytes_alloc;
ba77c9e11   Li Zefan   perf: Add 'perf k...
130
131
  	} else {
  		data = malloc(sizeof(*data));
079d3f653   Li Zefan   perf kmem: Measur...
132
133
  		if (!data)
  			die("malloc");
ba77c9e11   Li Zefan   perf: Add 'perf k...
134
  		data->ptr = ptr;
079d3f653   Li Zefan   perf kmem: Measur...
135
  		data->pingpong = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
136
137
138
139
140
141
142
  		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...
143
144
  	data->call_site = call_site;
  	data->alloc_cpu = cpu;
ba77c9e11   Li Zefan   perf: Add 'perf k...
145
146
147
148
149
150
151
152
  }
  
  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...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  	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...
168
  		data->bytes_alloc += bytes_alloc;
ba77c9e11   Li Zefan   perf: Add 'perf k...
169
170
  	} else {
  		data = malloc(sizeof(*data));
079d3f653   Li Zefan   perf kmem: Measur...
171
172
  		if (!data)
  			die("malloc");
ba77c9e11   Li Zefan   perf: Add 'perf k...
173
  		data->call_site = call_site;
079d3f653   Li Zefan   perf kmem: Measur...
174
  		data->pingpong = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
175
176
177
178
179
180
181
182
  		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...
183
  static void process_alloc_event(void *data,
ba77c9e11   Li Zefan   perf: Add 'perf k...
184
  				struct event *event,
7d0d39459   Li Zefan   perf kmem: Collec...
185
  				int cpu,
ba77c9e11   Li Zefan   perf: Add 'perf k...
186
187
  				u64 timestamp __used,
  				struct thread *thread __used,
7d0d39459   Li Zefan   perf kmem: Collec...
188
  				int node)
ba77c9e11   Li Zefan   perf: Add 'perf k...
189
190
191
192
193
  {
  	unsigned long call_site;
  	unsigned long ptr;
  	int bytes_req;
  	int bytes_alloc;
7d0d39459   Li Zefan   perf kmem: Collec...
194
  	int node1, node2;
ba77c9e11   Li Zefan   perf: Add 'perf k...
195

f48f669d4   Xiao Guangrong   perf_event: Elimi...
196
197
198
199
  	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...
200

079d3f653   Li Zefan   perf kmem: Measur...
201
  	insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
ba77c9e11   Li Zefan   perf: Add 'perf k...
202
203
204
205
  	insert_caller_stat(call_site, bytes_req, bytes_alloc);
  
  	total_requested += bytes_req;
  	total_allocated += bytes_alloc;
7d0d39459   Li Zefan   perf kmem: Collec...
206
207
208
  
  	if (node) {
  		node1 = cpunode_map[cpu];
f48f669d4   Xiao Guangrong   perf_event: Elimi...
209
  		node2 = raw_field_value(event, "node", data);
7d0d39459   Li Zefan   perf kmem: Collec...
210
211
212
213
  		if (node1 != node2)
  			nr_cross_allocs++;
  	}
  	nr_allocs++;
ba77c9e11   Li Zefan   perf: Add 'perf k...
214
  }
079d3f653   Li Zefan   perf kmem: Measur...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  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...
242
  static void process_free_event(void *data,
079d3f653   Li Zefan   perf kmem: Measur...
243
244
  			       struct event *event,
  			       int cpu,
ba77c9e11   Li Zefan   perf: Add 'perf k...
245
246
247
  			       u64 timestamp __used,
  			       struct thread *thread __used)
  {
079d3f653   Li Zefan   perf kmem: Measur...
248
249
  	unsigned long ptr;
  	struct alloc_stat *s_alloc, *s_caller;
f48f669d4   Xiao Guangrong   perf_event: Elimi...
250
  	ptr = raw_field_value(event, "ptr", data);
079d3f653   Li Zefan   perf kmem: Measur...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  
  	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...
265
  }
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
266
267
  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...
268
  {
ba77c9e11   Li Zefan   perf: Add 'perf k...
269
270
  	struct event *event;
  	int type;
f48f669d4   Xiao Guangrong   perf_event: Elimi...
271
  	type = trace_parse_common_type(data);
ba77c9e11   Li Zefan   perf: Add 'perf k...
272
273
274
275
  	event = trace_find_event(type);
  
  	if (!strcmp(event->name, "kmalloc") ||
  	    !strcmp(event->name, "kmem_cache_alloc")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
276
  		process_alloc_event(data, event, cpu, timestamp, thread, 0);
ba77c9e11   Li Zefan   perf: Add 'perf k...
277
278
279
280
281
  		return;
  	}
  
  	if (!strcmp(event->name, "kmalloc_node") ||
  	    !strcmp(event->name, "kmem_cache_alloc_node")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
282
  		process_alloc_event(data, event, cpu, timestamp, thread, 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
283
284
285
286
287
  		return;
  	}
  
  	if (!strcmp(event->name, "kfree") ||
  	    !strcmp(event->name, "kmem_cache_free")) {
f48f669d4   Xiao Guangrong   perf_event: Elimi...
288
  		process_free_event(data, event, cpu, timestamp, thread);
ba77c9e11   Li Zefan   perf: Add 'perf k...
289
290
291
  		return;
  	}
  }
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
292
  static int process_sample_event(struct perf_tool *tool __used,
d20deb64e   Arnaldo Carvalho de Melo   perf tools: Pass ...
293
  				union perf_event *event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
294
  				struct perf_sample *sample,
9e69c2108   Arnaldo Carvalho de Melo   perf session: Pas...
295
  				struct perf_evsel *evsel __used,
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
296
  				struct machine *machine)
ba77c9e11   Li Zefan   perf: Add 'perf k...
297
  {
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
298
  	struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
ba77c9e11   Li Zefan   perf: Add 'perf k...
299

ba77c9e11   Li Zefan   perf: Add 'perf k...
300
301
302
303
304
305
306
307
308
  	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...
309
310
  	process_raw_event(event, sample->raw_data, sample->cpu,
  			  sample->time, thread);
ba77c9e11   Li Zefan   perf: Add 'perf k...
311
312
313
  
  	return 0;
  }
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
314
  static struct perf_tool perf_kmem = {
587570d4c   Frederic Weisbecker   perf: Use generic...
315
  	.sample			= process_sample_event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
316
  	.comm			= perf_event__process_comm,
587570d4c   Frederic Weisbecker   perf: Use generic...
317
  	.ordered_samples	= true,
ba77c9e11   Li Zefan   perf: Add 'perf k...
318
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
319
320
321
322
323
324
325
  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...
326
327
  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...
328
329
  {
  	struct rb_node *next;
23346f21b   Arnaldo Carvalho de Melo   perf tools: Renam...
330
  	struct machine *machine;
ba77c9e11   Li Zefan   perf: Add 'perf k...
331

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

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

079d3f653   Li Zefan   perf kmem: Measur...
386
387
  	printf("%.102s
  ", graph_dotted_line);
ba77c9e11   Li Zefan   perf: Add 'perf k...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  }
  
  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...
406
407
  	printf("Cross CPU allocations: %lu/%lu
  ", nr_cross_allocs, nr_allocs);
ba77c9e11   Li Zefan   perf: Add 'perf k...
408
  }
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
409
  static void print_result(struct perf_session *session)
ba77c9e11   Li Zefan   perf: Add 'perf k...
410
411
  {
  	if (caller_flag)
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
412
  		__print_result(&root_caller_sorted, session, caller_lines, 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
413
  	if (alloc_flag)
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
414
  		__print_result(&root_alloc_sorted, session, alloc_lines, 0);
ba77c9e11   Li Zefan   perf: Add 'perf k...
415
416
  	print_summary();
  }
29b3e1528   Li Zefan   perf kmem: Defaul...
417
418
419
420
421
422
423
424
  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...
425
  static void sort_insert(struct rb_root *root, struct alloc_stat *data,
29b3e1528   Li Zefan   perf kmem: Defaul...
426
  			struct list_head *sort_list)
ba77c9e11   Li Zefan   perf: Add 'perf k...
427
428
429
  {
  	struct rb_node **new = &(root->rb_node);
  	struct rb_node *parent = NULL;
29b3e1528   Li Zefan   perf kmem: Defaul...
430
  	struct sort_dimension *sort;
ba77c9e11   Li Zefan   perf: Add 'perf k...
431
432
433
  
  	while (*new) {
  		struct alloc_stat *this;
29b3e1528   Li Zefan   perf kmem: Defaul...
434
  		int cmp = 0;
ba77c9e11   Li Zefan   perf: Add 'perf k...
435
436
437
  
  		this = rb_entry(*new, struct alloc_stat, node);
  		parent = *new;
29b3e1528   Li Zefan   perf kmem: Defaul...
438
439
440
441
442
  		list_for_each_entry(sort, sort_list, list) {
  			cmp = sort->cmp(data, this);
  			if (cmp)
  				break;
  		}
ba77c9e11   Li Zefan   perf: Add 'perf k...
443
444
445
446
447
448
449
450
451
452
453
454
  
  		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...
455
  			  struct list_head *sort_list)
ba77c9e11   Li Zefan   perf: Add 'perf k...
456
457
458
459
460
461
462
463
464
465
466
  {
  	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...
467
  		sort_insert(root_sorted, data, sort_list);
ba77c9e11   Li Zefan   perf: Add 'perf k...
468
469
470
471
472
  	}
  }
  
  static void sort_result(void)
  {
29b3e1528   Li Zefan   perf kmem: Defaul...
473
474
  	__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...
475
476
477
478
  }
  
  static int __cmd_kmem(void)
  {
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
479
  	int err = -EINVAL;
21ef97f05   Ian Munsie   perf session: Fal...
480
  	struct perf_session *session = perf_session__new(input_name, O_RDONLY,
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
481
  							 0, false, &perf_kmem);
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
482
483
  	if (session == NULL)
  		return -ENOMEM;
e727ca73f   Arnaldo Carvalho de Melo   perf kmem: Resolv...
484
485
  	if (perf_session__create_kernel_maps(session) < 0)
  		goto out_delete;
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
486
487
  	if (!perf_session__has_traces(session, "kmem record"))
  		goto out_delete;
ba77c9e11   Li Zefan   perf: Add 'perf k...
488
  	setup_pager();
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
489
  	err = perf_session__process_events(session, &perf_kmem);
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
490
491
  	if (err != 0)
  		goto out_delete;
ba77c9e11   Li Zefan   perf: Add 'perf k...
492
  	sort_result();
4aa656364   Arnaldo Carvalho de Melo   perf session: Mov...
493
494
495
496
  	print_result(session);
  out_delete:
  	perf_session__delete(session);
  	return err;
ba77c9e11   Li Zefan   perf: Add 'perf k...
497
498
499
  }
  
  static const char * const kmem_usage[] = {
90b86a9f7   Li Zefan   perf kmem: Show u...
500
  	"perf kmem [<options>] {record|stat}",
ba77c9e11   Li Zefan   perf: Add 'perf k...
501
502
  	NULL
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
503
504
505
506
507
508
509
510
  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...
511
512
513
514
  static struct sort_dimension ptr_sort_dimension = {
  	.name	= "ptr",
  	.cmp	= ptr_cmp,
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
515
516
517
518
519
520
521
522
  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...
523
524
525
526
  static struct sort_dimension callsite_sort_dimension = {
  	.name	= "callsite",
  	.cmp	= callsite_cmp,
  };
f3ced7cdb   Pekka Enberg   perf kmem: Add --...
527
528
529
530
531
532
533
534
  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...
535
536
537
538
  static struct sort_dimension hit_sort_dimension = {
  	.name	= "hit",
  	.cmp	= hit_cmp,
  };
ba77c9e11   Li Zefan   perf: Add 'perf k...
539
540
541
542
543
544
545
546
  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...
547
548
549
550
  static struct sort_dimension bytes_sort_dimension = {
  	.name	= "bytes",
  	.cmp	= bytes_cmp,
  };
f3ced7cdb   Pekka Enberg   perf kmem: Add --...
551
552
553
554
555
556
557
558
559
560
561
562
563
  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...
564
565
566
567
  static struct sort_dimension frag_sort_dimension = {
  	.name	= "frag",
  	.cmp	= frag_cmp,
  };
079d3f653   Li Zefan   perf kmem: Measur...
568
569
570
571
572
573
574
575
576
577
578
579
580
  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...
581
582
583
584
585
586
  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...
587
  	&pingpong_sort_dimension,
29b3e1528   Li Zefan   perf kmem: Defaul...
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
  };
  
  #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);
1b22859d4   Namhyung Kim   perf kmem: Fix a ...
626
  			free(str);
29b3e1528   Li Zefan   perf kmem: Defaul...
627
628
629
630
631
632
633
  			return -1;
  		}
  	}
  
  	free(str);
  	return 0;
  }
ba77c9e11   Li Zefan   perf: Add 'perf k...
634
635
636
  static int parse_sort_opt(const struct option *opt __used,
  			  const char *arg, int unset __used)
  {
ba77c9e11   Li Zefan   perf: Add 'perf k...
637
638
  	if (!arg)
  		return -1;
ba77c9e11   Li Zefan   perf: Add 'perf k...
639
  	if (caller_flag > alloc_flag)
29b3e1528   Li Zefan   perf kmem: Defaul...
640
  		return setup_sorting(&caller_sort, arg);
ba77c9e11   Li Zefan   perf: Add 'perf k...
641
  	else
29b3e1528   Li Zefan   perf kmem: Defaul...
642
  		return setup_sorting(&alloc_sort, arg);
ba77c9e11   Li Zefan   perf: Add 'perf k...
643
644
645
  
  	return 0;
  }
90b86a9f7   Li Zefan   perf kmem: Show u...
646
  static int parse_caller_opt(const struct option *opt __used,
793124169   Ingo Molnar   perf kmem: Fix un...
647
  			  const char *arg __used, int unset __used)
ba77c9e11   Li Zefan   perf: Add 'perf k...
648
  {
90b86a9f7   Li Zefan   perf kmem: Show u...
649
650
651
  	caller_flag = (alloc_flag + 1);
  	return 0;
  }
ba77c9e11   Li Zefan   perf: Add 'perf k...
652

90b86a9f7   Li Zefan   perf kmem: Show u...
653
  static int parse_alloc_opt(const struct option *opt __used,
793124169   Ingo Molnar   perf kmem: Fix un...
654
  			  const char *arg __used, int unset __used)
90b86a9f7   Li Zefan   perf kmem: Show u...
655
656
  {
  	alloc_flag = (caller_flag + 1);
ba77c9e11   Li Zefan   perf: Add 'perf k...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
  	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...
681
682
683
684
685
686
  	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...
687
  	OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
079d3f653   Li Zefan   perf kmem: Measur...
688
  		     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
ba77c9e11   Li Zefan   perf: Add 'perf k...
689
690
  		     parse_sort_opt),
  	OPT_CALLBACK('l', "line", NULL, "num",
90b86a9f7   Li Zefan   perf kmem: Show u...
691
  		     "show n lines",
ba77c9e11   Li Zefan   perf: Add 'perf k...
692
  		     parse_line_opt),
7707b6b6f   Li Zefan   perf kmem: Add ne...
693
  	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
ba77c9e11   Li Zefan   perf: Add 'perf k...
694
695
696
697
698
699
700
  	OPT_END()
  };
  
  static const char *record_args[] = {
  	"record",
  	"-a",
  	"-R",
ba77c9e11   Li Zefan   perf: Add 'perf k...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
  	"-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...
718
719
  	if (rec_argv == NULL)
  		return -ENOMEM;
ba77c9e11   Li Zefan   perf: Add 'perf k...
720
721
722
723
724
725
726
727
728
729
730
  	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...
731
  	argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
90b86a9f7   Li Zefan   perf kmem: Show u...
732
  	if (!argc)
ba77c9e11   Li Zefan   perf: Add 'perf k...
733
  		usage_with_options(kmem_usage, kmem_options);
655000e7c   Arnaldo Carvalho de Melo   perf symbols: Ado...
734
  	symbol__init();
90b86a9f7   Li Zefan   perf kmem: Show u...
735
736
737
738
739
740
741
742
743
  	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...
744

90b86a9f7   Li Zefan   perf kmem: Show u...
745
  		return __cmd_kmem();
b00eca8cd   Pekka Enberg   perf kmem: Print ...
746
747
  	} else
  		usage_with_options(kmem_usage, kmem_options);
7d0d39459   Li Zefan   perf kmem: Collec...
748

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