blob: 1c668953c7ec90f9b6ad8fb776841b59fe270a9e [file] [log] [blame]
Li Zefanba77c9e2009-11-20 15:53:25 +08001#include "builtin.h"
2#include "perf.h"
3
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03004#include "util/evlist.h"
Arnaldo Carvalho de Melofcf65bf2012-08-07 09:58:03 -03005#include "util/evsel.h"
Li Zefanba77c9e2009-11-20 15:53:25 +08006#include "util/util.h"
7#include "util/cache.h"
8#include "util/symbol.h"
9#include "util/thread.h"
10#include "util/header.h"
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -020011#include "util/session.h"
Arnaldo Carvalho de Melo45694aa2011-11-28 08:30:20 -020012#include "util/tool.h"
Namhyung Kimc9758cc2015-04-21 13:55:02 +090013#include "util/callchain.h"
Li Zefanba77c9e2009-11-20 15:53:25 +080014
15#include "util/parse-options.h"
16#include "util/trace-event.h"
Jiri Olsaf5fc1412013-10-15 16:27:32 +020017#include "util/data.h"
Don Zickus4b627952014-04-07 14:55:23 -040018#include "util/cpumap.h"
Li Zefanba77c9e2009-11-20 15:53:25 +080019
20#include "util/debug.h"
Li Zefanba77c9e2009-11-20 15:53:25 +080021
22#include <linux/rbtree.h>
Arnaldo Carvalho de Melo8d9233f2013-01-24 22:24:57 -030023#include <linux/string.h>
Namhyung Kim77cfe382015-03-23 15:30:40 +090024#include <locale.h>
Namhyung Kimc9758cc2015-04-21 13:55:02 +090025#include <regex.h>
Li Zefanba77c9e2009-11-20 15:53:25 +080026
Namhyung Kim0d68bc92015-04-06 14:36:10 +090027static int kmem_slab;
28static int kmem_page;
29
30static long kmem_page_size;
31
Li Zefanba77c9e2009-11-20 15:53:25 +080032struct alloc_stat;
Namhyung Kimfb4f3132015-04-21 13:55:03 +090033typedef int (*sort_fn_t)(void *, void *);
Li Zefanba77c9e2009-11-20 15:53:25 +080034
Li Zefanba77c9e2009-11-20 15:53:25 +080035static int alloc_flag;
36static int caller_flag;
37
Li Zefanba77c9e2009-11-20 15:53:25 +080038static int alloc_lines = -1;
39static int caller_lines = -1;
40
Li Zefan7707b6b2009-11-24 13:25:48 +080041static bool raw_ip;
42
Li Zefanba77c9e2009-11-20 15:53:25 +080043struct alloc_stat {
Li Zefan079d3f62009-11-24 13:26:55 +080044 u64 call_site;
45 u64 ptr;
Li Zefanba77c9e2009-11-20 15:53:25 +080046 u64 bytes_req;
47 u64 bytes_alloc;
48 u32 hit;
Li Zefan079d3f62009-11-24 13:26:55 +080049 u32 pingpong;
50
51 short alloc_cpu;
Li Zefanba77c9e2009-11-20 15:53:25 +080052
53 struct rb_node node;
54};
55
56static struct rb_root root_alloc_stat;
57static struct rb_root root_alloc_sorted;
58static struct rb_root root_caller_stat;
59static struct rb_root root_caller_sorted;
60
61static unsigned long total_requested, total_allocated;
Li Zefan7d0d3942009-11-24 13:26:31 +080062static unsigned long nr_allocs, nr_cross_allocs;
Li Zefanba77c9e2009-11-20 15:53:25 +080063
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -030064static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
65 int bytes_req, int bytes_alloc, int cpu)
Li Zefanba77c9e2009-11-20 15:53:25 +080066{
67 struct rb_node **node = &root_alloc_stat.rb_node;
68 struct rb_node *parent = NULL;
69 struct alloc_stat *data = NULL;
70
Li Zefanba77c9e2009-11-20 15:53:25 +080071 while (*node) {
72 parent = *node;
73 data = rb_entry(*node, struct alloc_stat, node);
74
75 if (ptr > data->ptr)
76 node = &(*node)->rb_right;
77 else if (ptr < data->ptr)
78 node = &(*node)->rb_left;
79 else
80 break;
81 }
82
83 if (data && data->ptr == ptr) {
84 data->hit++;
85 data->bytes_req += bytes_req;
Wenji Huang4efb5292009-12-21 17:52:55 +080086 data->bytes_alloc += bytes_alloc;
Li Zefanba77c9e2009-11-20 15:53:25 +080087 } else {
88 data = malloc(sizeof(*data));
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -030089 if (!data) {
90 pr_err("%s: malloc failed\n", __func__);
91 return -1;
92 }
Li Zefanba77c9e2009-11-20 15:53:25 +080093 data->ptr = ptr;
Li Zefan079d3f62009-11-24 13:26:55 +080094 data->pingpong = 0;
Li Zefanba77c9e2009-11-20 15:53:25 +080095 data->hit = 1;
96 data->bytes_req = bytes_req;
97 data->bytes_alloc = bytes_alloc;
98
99 rb_link_node(&data->node, parent, node);
100 rb_insert_color(&data->node, &root_alloc_stat);
101 }
Li Zefan079d3f62009-11-24 13:26:55 +0800102 data->call_site = call_site;
103 data->alloc_cpu = cpu;
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300104 return 0;
Li Zefanba77c9e2009-11-20 15:53:25 +0800105}
106
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300107static int insert_caller_stat(unsigned long call_site,
Li Zefanba77c9e2009-11-20 15:53:25 +0800108 int bytes_req, int bytes_alloc)
109{
110 struct rb_node **node = &root_caller_stat.rb_node;
111 struct rb_node *parent = NULL;
112 struct alloc_stat *data = NULL;
113
Li Zefanba77c9e2009-11-20 15:53:25 +0800114 while (*node) {
115 parent = *node;
116 data = rb_entry(*node, struct alloc_stat, node);
117
118 if (call_site > data->call_site)
119 node = &(*node)->rb_right;
120 else if (call_site < data->call_site)
121 node = &(*node)->rb_left;
122 else
123 break;
124 }
125
126 if (data && data->call_site == call_site) {
127 data->hit++;
128 data->bytes_req += bytes_req;
Wenji Huang4efb5292009-12-21 17:52:55 +0800129 data->bytes_alloc += bytes_alloc;
Li Zefanba77c9e2009-11-20 15:53:25 +0800130 } else {
131 data = malloc(sizeof(*data));
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300132 if (!data) {
133 pr_err("%s: malloc failed\n", __func__);
134 return -1;
135 }
Li Zefanba77c9e2009-11-20 15:53:25 +0800136 data->call_site = call_site;
Li Zefan079d3f62009-11-24 13:26:55 +0800137 data->pingpong = 0;
Li Zefanba77c9e2009-11-20 15:53:25 +0800138 data->hit = 1;
139 data->bytes_req = bytes_req;
140 data->bytes_alloc = bytes_alloc;
141
142 rb_link_node(&data->node, parent, node);
143 rb_insert_color(&data->node, &root_caller_stat);
144 }
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300145
146 return 0;
Li Zefanba77c9e2009-11-20 15:53:25 +0800147}
148
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300149static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300150 struct perf_sample *sample)
Li Zefanba77c9e2009-11-20 15:53:25 +0800151{
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300152 unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
153 call_site = perf_evsel__intval(evsel, sample, "call_site");
154 int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
155 bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
Li Zefanba77c9e2009-11-20 15:53:25 +0800156
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300157 if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300158 insert_caller_stat(call_site, bytes_req, bytes_alloc))
159 return -1;
Li Zefanba77c9e2009-11-20 15:53:25 +0800160
161 total_requested += bytes_req;
162 total_allocated += bytes_alloc;
Li Zefan7d0d3942009-11-24 13:26:31 +0800163
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300164 nr_allocs++;
165 return 0;
166}
167
168static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
169 struct perf_sample *sample)
170{
171 int ret = perf_evsel__process_alloc_event(evsel, sample);
172
173 if (!ret) {
Don Zickus4b627952014-04-07 14:55:23 -0400174 int node1 = cpu__get_node(sample->cpu),
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300175 node2 = perf_evsel__intval(evsel, sample, "node");
176
Li Zefan7d0d3942009-11-24 13:26:31 +0800177 if (node1 != node2)
178 nr_cross_allocs++;
179 }
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300180
181 return ret;
Li Zefanba77c9e2009-11-20 15:53:25 +0800182}
183
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900184static int ptr_cmp(void *, void *);
185static int slab_callsite_cmp(void *, void *);
Li Zefan079d3f62009-11-24 13:26:55 +0800186
187static struct alloc_stat *search_alloc_stat(unsigned long ptr,
188 unsigned long call_site,
189 struct rb_root *root,
190 sort_fn_t sort_fn)
191{
192 struct rb_node *node = root->rb_node;
193 struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
194
195 while (node) {
196 struct alloc_stat *data;
197 int cmp;
198
199 data = rb_entry(node, struct alloc_stat, node);
200
201 cmp = sort_fn(&key, data);
202 if (cmp < 0)
203 node = node->rb_left;
204 else if (cmp > 0)
205 node = node->rb_right;
206 else
207 return data;
208 }
209 return NULL;
210}
211
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300212static int perf_evsel__process_free_event(struct perf_evsel *evsel,
213 struct perf_sample *sample)
Li Zefanba77c9e2009-11-20 15:53:25 +0800214{
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300215 unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
Li Zefan079d3f62009-11-24 13:26:55 +0800216 struct alloc_stat *s_alloc, *s_caller;
217
Li Zefan079d3f62009-11-24 13:26:55 +0800218 s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
219 if (!s_alloc)
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300220 return 0;
Li Zefan079d3f62009-11-24 13:26:55 +0800221
Arnaldo Carvalho de Melo22ad7982012-08-07 10:56:43 -0300222 if ((short)sample->cpu != s_alloc->alloc_cpu) {
Li Zefan079d3f62009-11-24 13:26:55 +0800223 s_alloc->pingpong++;
224
225 s_caller = search_alloc_stat(0, s_alloc->call_site,
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900226 &root_caller_stat,
227 slab_callsite_cmp);
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300228 if (!s_caller)
229 return -1;
Li Zefan079d3f62009-11-24 13:26:55 +0800230 s_caller->pingpong++;
231 }
232 s_alloc->alloc_cpu = -1;
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -0300233
234 return 0;
Li Zefanba77c9e2009-11-20 15:53:25 +0800235}
236
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900237static u64 total_page_alloc_bytes;
238static u64 total_page_free_bytes;
239static u64 total_page_nomatch_bytes;
240static u64 total_page_fail_bytes;
241static unsigned long nr_page_allocs;
242static unsigned long nr_page_frees;
243static unsigned long nr_page_fails;
244static unsigned long nr_page_nomatch;
245
246static bool use_pfn;
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900247static bool live_page;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900248static struct perf_session *kmem_session;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900249
250#define MAX_MIGRATE_TYPES 6
251#define MAX_PAGE_ORDER 11
252
253static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
254
255struct page_stat {
256 struct rb_node node;
257 u64 page;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900258 u64 callsite;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900259 int order;
260 unsigned gfp_flags;
261 unsigned migrate_type;
262 u64 alloc_bytes;
263 u64 free_bytes;
264 int nr_alloc;
265 int nr_free;
266};
267
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900268static struct rb_root page_live_tree;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900269static struct rb_root page_alloc_tree;
270static struct rb_root page_alloc_sorted;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900271static struct rb_root page_caller_tree;
272static struct rb_root page_caller_sorted;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900273
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900274struct alloc_func {
275 u64 start;
276 u64 end;
277 char *name;
278};
279
280static int nr_alloc_funcs;
281static struct alloc_func *alloc_func_list;
282
283static int funcmp(const void *a, const void *b)
284{
285 const struct alloc_func *fa = a;
286 const struct alloc_func *fb = b;
287
288 if (fa->start > fb->start)
289 return 1;
290 else
291 return -1;
292}
293
294static int callcmp(const void *a, const void *b)
295{
296 const struct alloc_func *fa = a;
297 const struct alloc_func *fb = b;
298
299 if (fb->start <= fa->start && fa->end < fb->end)
300 return 0;
301
302 if (fa->start > fb->start)
303 return 1;
304 else
305 return -1;
306}
307
308static int build_alloc_func_list(void)
309{
310 int ret;
311 struct map *kernel_map;
312 struct symbol *sym;
313 struct rb_node *node;
314 struct alloc_func *func;
315 struct machine *machine = &kmem_session->machines.host;
316 regex_t alloc_func_regex;
317 const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
318
319 ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
320 if (ret) {
321 char err[BUFSIZ];
322
323 regerror(ret, &alloc_func_regex, err, sizeof(err));
324 pr_err("Invalid regex: %s\n%s", pattern, err);
325 return -EINVAL;
326 }
327
328 kernel_map = machine->vmlinux_maps[MAP__FUNCTION];
329 if (map__load(kernel_map, NULL) < 0) {
330 pr_err("cannot load kernel map\n");
331 return -ENOENT;
332 }
333
334 map__for_each_symbol(kernel_map, sym, node) {
335 if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
336 continue;
337
338 func = realloc(alloc_func_list,
339 (nr_alloc_funcs + 1) * sizeof(*func));
340 if (func == NULL)
341 return -ENOMEM;
342
343 pr_debug("alloc func: %s\n", sym->name);
344 func[nr_alloc_funcs].start = sym->start;
345 func[nr_alloc_funcs].end = sym->end;
346 func[nr_alloc_funcs].name = sym->name;
347
348 alloc_func_list = func;
349 nr_alloc_funcs++;
350 }
351
352 qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
353
354 regfree(&alloc_func_regex);
355 return 0;
356}
357
358/*
359 * Find first non-memory allocation function from callchain.
360 * The allocation functions are in the 'alloc_func_list'.
361 */
362static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
363{
364 struct addr_location al;
365 struct machine *machine = &kmem_session->machines.host;
366 struct callchain_cursor_node *node;
367
368 if (alloc_func_list == NULL) {
369 if (build_alloc_func_list() < 0)
370 goto out;
371 }
372
373 al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
374 sample__resolve_callchain(sample, NULL, evsel, &al, 16);
375
376 callchain_cursor_commit(&callchain_cursor);
377 while (true) {
378 struct alloc_func key, *caller;
379 u64 addr;
380
381 node = callchain_cursor_current(&callchain_cursor);
382 if (node == NULL)
383 break;
384
385 key.start = key.end = node->ip;
386 caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
387 sizeof(key), callcmp);
388 if (!caller) {
389 /* found */
390 if (node->map)
391 addr = map__unmap_ip(node->map, node->ip);
392 else
393 addr = node->ip;
394
395 return addr;
396 } else
397 pr_debug3("skipping alloc function: %s\n", caller->name);
398
399 callchain_cursor_advance(&callchain_cursor);
400 }
401
402out:
403 pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
404 return sample->ip;
405}
406
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900407struct sort_dimension {
408 const char name[20];
409 sort_fn_t cmp;
410 struct list_head list;
411};
412
413static LIST_HEAD(page_alloc_sort_input);
414static LIST_HEAD(page_caller_sort_input);
415
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900416static struct page_stat *
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900417__page_stat__findnew_page(struct page_stat *pstat, bool create)
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900418{
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900419 struct rb_node **node = &page_live_tree.rb_node;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900420 struct rb_node *parent = NULL;
421 struct page_stat *data;
422
423 while (*node) {
424 s64 cmp;
425
426 parent = *node;
427 data = rb_entry(*node, struct page_stat, node);
428
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900429 cmp = data->page - pstat->page;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900430 if (cmp < 0)
431 node = &parent->rb_left;
432 else if (cmp > 0)
433 node = &parent->rb_right;
434 else
435 return data;
436 }
437
438 if (!create)
439 return NULL;
440
441 data = zalloc(sizeof(*data));
442 if (data != NULL) {
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900443 data->page = pstat->page;
444 data->order = pstat->order;
445 data->gfp_flags = pstat->gfp_flags;
446 data->migrate_type = pstat->migrate_type;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900447
448 rb_link_node(&data->node, parent, node);
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900449 rb_insert_color(&data->node, &page_live_tree);
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900450 }
451
452 return data;
453}
454
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900455static struct page_stat *page_stat__find_page(struct page_stat *pstat)
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900456{
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900457 return __page_stat__findnew_page(pstat, false);
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900458}
459
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900460static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900461{
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900462 return __page_stat__findnew_page(pstat, true);
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900463}
464
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900465static struct page_stat *
466__page_stat__findnew_alloc(struct page_stat *pstat, bool create)
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900467{
468 struct rb_node **node = &page_alloc_tree.rb_node;
469 struct rb_node *parent = NULL;
470 struct page_stat *data;
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900471 struct sort_dimension *sort;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900472
473 while (*node) {
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900474 int cmp = 0;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900475
476 parent = *node;
477 data = rb_entry(*node, struct page_stat, node);
478
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900479 list_for_each_entry(sort, &page_alloc_sort_input, list) {
480 cmp = sort->cmp(pstat, data);
481 if (cmp)
482 break;
483 }
484
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900485 if (cmp < 0)
486 node = &parent->rb_left;
487 else if (cmp > 0)
488 node = &parent->rb_right;
489 else
490 return data;
491 }
492
493 if (!create)
494 return NULL;
495
496 data = zalloc(sizeof(*data));
497 if (data != NULL) {
David Ahern6b1a2752015-04-14 13:49:33 -0400498 data->page = pstat->page;
499 data->order = pstat->order;
500 data->gfp_flags = pstat->gfp_flags;
501 data->migrate_type = pstat->migrate_type;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900502
503 rb_link_node(&data->node, parent, node);
504 rb_insert_color(&data->node, &page_alloc_tree);
505 }
506
507 return data;
508}
509
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900510static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
511{
512 return __page_stat__findnew_alloc(pstat, false);
513}
514
515static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
516{
517 return __page_stat__findnew_alloc(pstat, true);
518}
519
520static struct page_stat *
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900521__page_stat__findnew_caller(struct page_stat *pstat, bool create)
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900522{
523 struct rb_node **node = &page_caller_tree.rb_node;
524 struct rb_node *parent = NULL;
525 struct page_stat *data;
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900526 struct sort_dimension *sort;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900527
528 while (*node) {
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900529 int cmp = 0;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900530
531 parent = *node;
532 data = rb_entry(*node, struct page_stat, node);
533
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900534 list_for_each_entry(sort, &page_caller_sort_input, list) {
535 cmp = sort->cmp(pstat, data);
536 if (cmp)
537 break;
538 }
539
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900540 if (cmp < 0)
541 node = &parent->rb_left;
542 else if (cmp > 0)
543 node = &parent->rb_right;
544 else
545 return data;
546 }
547
548 if (!create)
549 return NULL;
550
551 data = zalloc(sizeof(*data));
552 if (data != NULL) {
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900553 data->callsite = pstat->callsite;
554 data->order = pstat->order;
555 data->gfp_flags = pstat->gfp_flags;
556 data->migrate_type = pstat->migrate_type;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900557
558 rb_link_node(&data->node, parent, node);
559 rb_insert_color(&data->node, &page_caller_tree);
560 }
561
562 return data;
563}
564
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900565static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900566{
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900567 return __page_stat__findnew_caller(pstat, false);
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900568}
569
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900570static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900571{
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900572 return __page_stat__findnew_caller(pstat, true);
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900573}
574
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900575static bool valid_page(u64 pfn_or_page)
576{
577 if (use_pfn && pfn_or_page == -1UL)
578 return false;
579 if (!use_pfn && pfn_or_page == 0)
580 return false;
581 return true;
582}
583
Namhyung Kim0e111152015-04-21 13:55:05 +0900584struct gfp_flag {
585 unsigned int flags;
586 char *compact_str;
587 char *human_readable;
588};
589
590static struct gfp_flag *gfps;
591static int nr_gfps;
592
593static int gfpcmp(const void *a, const void *b)
594{
595 const struct gfp_flag *fa = a;
596 const struct gfp_flag *fb = b;
597
598 return fa->flags - fb->flags;
599}
600
601/* see include/trace/events/gfpflags.h */
602static const struct {
603 const char *original;
604 const char *compact;
605} gfp_compact_table[] = {
606 { "GFP_TRANSHUGE", "THP" },
607 { "GFP_HIGHUSER_MOVABLE", "HUM" },
608 { "GFP_HIGHUSER", "HU" },
609 { "GFP_USER", "U" },
610 { "GFP_TEMPORARY", "TMP" },
611 { "GFP_KERNEL", "K" },
612 { "GFP_NOFS", "NF" },
613 { "GFP_ATOMIC", "A" },
614 { "GFP_NOIO", "NI" },
615 { "GFP_HIGH", "H" },
616 { "GFP_WAIT", "W" },
617 { "GFP_IO", "I" },
618 { "GFP_COLD", "CO" },
619 { "GFP_NOWARN", "NWR" },
620 { "GFP_REPEAT", "R" },
621 { "GFP_NOFAIL", "NF" },
622 { "GFP_NORETRY", "NR" },
623 { "GFP_COMP", "C" },
624 { "GFP_ZERO", "Z" },
625 { "GFP_NOMEMALLOC", "NMA" },
626 { "GFP_MEMALLOC", "MA" },
627 { "GFP_HARDWALL", "HW" },
628 { "GFP_THISNODE", "TN" },
629 { "GFP_RECLAIMABLE", "RC" },
630 { "GFP_MOVABLE", "M" },
631 { "GFP_NOTRACK", "NT" },
632 { "GFP_NO_KSWAPD", "NK" },
633 { "GFP_OTHER_NODE", "ON" },
634 { "GFP_NOWAIT", "NW" },
635};
636
637static size_t max_gfp_len;
638
639static char *compact_gfp_flags(char *gfp_flags)
640{
641 char *orig_flags = strdup(gfp_flags);
642 char *new_flags = NULL;
643 char *str, *pos;
644 size_t len = 0;
645
646 if (orig_flags == NULL)
647 return NULL;
648
649 str = strtok_r(orig_flags, "|", &pos);
650 while (str) {
651 size_t i;
652 char *new;
653 const char *cpt;
654
655 for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
656 if (strcmp(gfp_compact_table[i].original, str))
657 continue;
658
659 cpt = gfp_compact_table[i].compact;
660 new = realloc(new_flags, len + strlen(cpt) + 2);
661 if (new == NULL) {
662 free(new_flags);
663 return NULL;
664 }
665
666 new_flags = new;
667
668 if (!len) {
669 strcpy(new_flags, cpt);
670 } else {
671 strcat(new_flags, "|");
672 strcat(new_flags, cpt);
673 len++;
674 }
675
676 len += strlen(cpt);
677 }
678
679 str = strtok_r(NULL, "|", &pos);
680 }
681
682 if (max_gfp_len < len)
683 max_gfp_len = len;
684
685 free(orig_flags);
686 return new_flags;
687}
688
689static char *compact_gfp_string(unsigned long gfp_flags)
690{
691 struct gfp_flag key = {
692 .flags = gfp_flags,
693 };
694 struct gfp_flag *gfp;
695
696 gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
697 if (gfp)
698 return gfp->compact_str;
699
700 return NULL;
701}
702
703static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
704 unsigned int gfp_flags)
705{
706 struct pevent_record record = {
707 .cpu = sample->cpu,
708 .data = sample->raw_data,
709 .size = sample->raw_size,
710 };
711 struct trace_seq seq;
712 char *str, *pos;
713
714 if (nr_gfps) {
715 struct gfp_flag key = {
716 .flags = gfp_flags,
717 };
718
719 if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
720 return 0;
721 }
722
723 trace_seq_init(&seq);
724 pevent_event_info(&seq, evsel->tp_format, &record);
725
726 str = strtok_r(seq.buffer, " ", &pos);
727 while (str) {
728 if (!strncmp(str, "gfp_flags=", 10)) {
729 struct gfp_flag *new;
730
731 new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
732 if (new == NULL)
733 return -ENOMEM;
734
735 gfps = new;
736 new += nr_gfps++;
737
738 new->flags = gfp_flags;
739 new->human_readable = strdup(str + 10);
740 new->compact_str = compact_gfp_flags(str + 10);
741 if (!new->human_readable || !new->compact_str)
742 return -ENOMEM;
743
744 qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
745 }
746
747 str = strtok_r(NULL, " ", &pos);
748 }
749
750 trace_seq_destroy(&seq);
751 return 0;
752}
753
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900754static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
755 struct perf_sample *sample)
756{
757 u64 page;
758 unsigned int order = perf_evsel__intval(evsel, sample, "order");
759 unsigned int gfp_flags = perf_evsel__intval(evsel, sample, "gfp_flags");
760 unsigned int migrate_type = perf_evsel__intval(evsel, sample,
761 "migratetype");
762 u64 bytes = kmem_page_size << order;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900763 u64 callsite;
David Ahern6b1a2752015-04-14 13:49:33 -0400764 struct page_stat *pstat;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900765 struct page_stat this = {
766 .order = order,
767 .gfp_flags = gfp_flags,
768 .migrate_type = migrate_type,
769 };
770
771 if (use_pfn)
772 page = perf_evsel__intval(evsel, sample, "pfn");
773 else
774 page = perf_evsel__intval(evsel, sample, "page");
775
776 nr_page_allocs++;
777 total_page_alloc_bytes += bytes;
778
779 if (!valid_page(page)) {
780 nr_page_fails++;
781 total_page_fail_bytes += bytes;
782
783 return 0;
784 }
785
Namhyung Kim0e111152015-04-21 13:55:05 +0900786 if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
787 return -1;
788
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900789 callsite = find_callsite(evsel, sample);
790
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900791 /*
792 * This is to find the current page (with correct gfp flags and
793 * migrate type) at free event.
794 */
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900795 this.page = page;
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900796 pstat = page_stat__findnew_page(&this);
David Ahern6b1a2752015-04-14 13:49:33 -0400797 if (pstat == NULL)
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900798 return -ENOMEM;
799
David Ahern6b1a2752015-04-14 13:49:33 -0400800 pstat->nr_alloc++;
801 pstat->alloc_bytes += bytes;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900802 pstat->callsite = callsite;
803
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900804 if (!live_page) {
805 pstat = page_stat__findnew_alloc(&this);
806 if (pstat == NULL)
807 return -ENOMEM;
808
809 pstat->nr_alloc++;
810 pstat->alloc_bytes += bytes;
811 pstat->callsite = callsite;
812 }
813
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900814 this.callsite = callsite;
815 pstat = page_stat__findnew_caller(&this);
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900816 if (pstat == NULL)
817 return -ENOMEM;
818
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900819 pstat->nr_alloc++;
820 pstat->alloc_bytes += bytes;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900821
822 order_stats[order][migrate_type]++;
823
824 return 0;
825}
826
827static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
828 struct perf_sample *sample)
829{
830 u64 page;
831 unsigned int order = perf_evsel__intval(evsel, sample, "order");
832 u64 bytes = kmem_page_size << order;
David Ahern6b1a2752015-04-14 13:49:33 -0400833 struct page_stat *pstat;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900834 struct page_stat this = {
835 .order = order,
836 };
837
838 if (use_pfn)
839 page = perf_evsel__intval(evsel, sample, "pfn");
840 else
841 page = perf_evsel__intval(evsel, sample, "page");
842
843 nr_page_frees++;
844 total_page_free_bytes += bytes;
845
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900846 this.page = page;
847 pstat = page_stat__find_page(&this);
David Ahern6b1a2752015-04-14 13:49:33 -0400848 if (pstat == NULL) {
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900849 pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
850 page, order);
851
852 nr_page_nomatch++;
853 total_page_nomatch_bytes += bytes;
854
855 return 0;
856 }
857
David Ahern6b1a2752015-04-14 13:49:33 -0400858 this.gfp_flags = pstat->gfp_flags;
859 this.migrate_type = pstat->migrate_type;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900860 this.callsite = pstat->callsite;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900861
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900862 rb_erase(&pstat->node, &page_live_tree);
David Ahern6b1a2752015-04-14 13:49:33 -0400863 free(pstat);
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900864
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900865 if (live_page) {
866 order_stats[this.order][this.migrate_type]--;
867 } else {
868 pstat = page_stat__find_alloc(&this);
869 if (pstat == NULL)
870 return -ENOMEM;
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900871
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900872 pstat->nr_free++;
873 pstat->free_bytes += bytes;
874 }
Namhyung Kimc9758cc2015-04-21 13:55:02 +0900875
Namhyung Kimfb4f3132015-04-21 13:55:03 +0900876 pstat = page_stat__find_caller(&this);
David Ahern6b1a2752015-04-14 13:49:33 -0400877 if (pstat == NULL)
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900878 return -ENOENT;
879
David Ahern6b1a2752015-04-14 13:49:33 -0400880 pstat->nr_free++;
881 pstat->free_bytes += bytes;
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900882
Namhyung Kim2a7ef022015-04-21 13:55:04 +0900883 if (live_page) {
884 pstat->nr_alloc--;
885 pstat->alloc_bytes -= bytes;
886
887 if (pstat->nr_alloc == 0) {
888 rb_erase(&pstat->node, &page_caller_tree);
889 free(pstat);
890 }
891 }
892
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900893 return 0;
894}
895
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300896typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
897 struct perf_sample *sample);
Li Zefanba77c9e2009-11-20 15:53:25 +0800898
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300899static int process_sample_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200900 union perf_event *event,
Arnaldo Carvalho de Melo8115d602011-01-29 14:01:45 -0200901 struct perf_sample *sample,
Arnaldo Carvalho de Melofcf65bf2012-08-07 09:58:03 -0300902 struct perf_evsel *evsel,
Arnaldo Carvalho de Melo743eb862011-11-28 07:56:39 -0200903 struct machine *machine)
Li Zefanba77c9e2009-11-20 15:53:25 +0800904{
Adrian Hunteref893252013-08-27 11:23:06 +0300905 struct thread *thread = machine__findnew_thread(machine, sample->pid,
Namhyung Kim13ce34d2014-05-12 09:56:42 +0900906 sample->tid);
Li Zefanba77c9e2009-11-20 15:53:25 +0800907
Li Zefanba77c9e2009-11-20 15:53:25 +0800908 if (thread == NULL) {
909 pr_debug("problem processing %d event, skipping it.\n",
910 event->header.type);
911 return -1;
912 }
913
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +0200914 dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
Li Zefanba77c9e2009-11-20 15:53:25 +0800915
Arnaldo Carvalho de Melo744a9712013-11-06 10:17:38 -0300916 if (evsel->handler != NULL) {
917 tracepoint_handler f = evsel->handler;
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -0300918 return f(evsel, sample);
919 }
920
921 return 0;
Li Zefanba77c9e2009-11-20 15:53:25 +0800922}
923
Arnaldo Carvalho de Melofcf65bf2012-08-07 09:58:03 -0300924static struct perf_tool perf_kmem = {
925 .sample = process_sample_event,
926 .comm = perf_event__process_comm,
Namhyung Kim64c40902014-08-01 14:59:31 +0900927 .mmap = perf_event__process_mmap,
928 .mmap2 = perf_event__process_mmap2,
Jiri Olsa0a8cb852014-07-06 14:18:21 +0200929 .ordered_events = true,
Li Zefanba77c9e2009-11-20 15:53:25 +0800930};
931
Li Zefanba77c9e2009-11-20 15:53:25 +0800932static double fragmentation(unsigned long n_req, unsigned long n_alloc)
933{
934 if (n_alloc == 0)
935 return 0.0;
936 else
937 return 100.0 - (100.0 * n_req / n_alloc);
938}
939
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900940static void __print_slab_result(struct rb_root *root,
941 struct perf_session *session,
942 int n_lines, int is_caller)
Li Zefanba77c9e2009-11-20 15:53:25 +0800943{
944 struct rb_node *next;
Arnaldo Carvalho de Melo34ba5122012-12-19 09:04:24 -0300945 struct machine *machine = &session->machines.host;
Li Zefanba77c9e2009-11-20 15:53:25 +0800946
Namhyung Kim65f46e02015-03-12 16:32:48 +0900947 printf("%.105s\n", graph_dotted_line);
Li Zefan079d3f62009-11-24 13:26:55 +0800948 printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
Pekka Enberg47103272010-01-19 19:23:23 +0200949 printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
Namhyung Kim65f46e02015-03-12 16:32:48 +0900950 printf("%.105s\n", graph_dotted_line);
Li Zefanba77c9e2009-11-20 15:53:25 +0800951
952 next = rb_first(root);
953
954 while (next && n_lines--) {
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200955 struct alloc_stat *data = rb_entry(next, struct alloc_stat,
956 node);
957 struct symbol *sym = NULL;
Arnaldo Carvalho de Melo71cf8b82010-04-01 21:24:38 -0300958 struct map *map;
Li Zefan079d3f62009-11-24 13:26:55 +0800959 char buf[BUFSIZ];
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200960 u64 addr;
Li Zefanba77c9e2009-11-20 15:53:25 +0800961
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200962 if (is_caller) {
963 addr = data->call_site;
Li Zefan7707b6b2009-11-24 13:25:48 +0800964 if (!raw_ip)
Arnaldo Carvalho de Melo5c0541d2010-04-29 15:25:23 -0300965 sym = machine__find_kernel_function(machine, addr, &map, NULL);
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200966 } else
967 addr = data->ptr;
Li Zefanba77c9e2009-11-20 15:53:25 +0800968
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200969 if (sym != NULL)
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200970 snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
Arnaldo Carvalho de Melo71cf8b82010-04-01 21:24:38 -0300971 addr - map->unmap_ip(map, sym->start));
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200972 else
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200973 snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
Li Zefan079d3f62009-11-24 13:26:55 +0800974 printf(" %-34s |", buf);
Arnaldo Carvalho de Melo1b145ae2009-11-23 17:51:09 -0200975
Namhyung Kim65f46e02015-03-12 16:32:48 +0900976 printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
Li Zefan079d3f62009-11-24 13:26:55 +0800977 (unsigned long long)data->bytes_alloc,
Li Zefanba77c9e2009-11-20 15:53:25 +0800978 (unsigned long)data->bytes_alloc / data->hit,
979 (unsigned long long)data->bytes_req,
980 (unsigned long)data->bytes_req / data->hit,
981 (unsigned long)data->hit,
Li Zefan079d3f62009-11-24 13:26:55 +0800982 (unsigned long)data->pingpong,
Li Zefanba77c9e2009-11-20 15:53:25 +0800983 fragmentation(data->bytes_req, data->bytes_alloc));
984
985 next = rb_next(next);
986 }
987
988 if (n_lines == -1)
Namhyung Kim65f46e02015-03-12 16:32:48 +0900989 printf(" ... | ... | ... | ... | ... | ... \n");
Li Zefanba77c9e2009-11-20 15:53:25 +0800990
Namhyung Kim65f46e02015-03-12 16:32:48 +0900991 printf("%.105s\n", graph_dotted_line);
Li Zefanba77c9e2009-11-20 15:53:25 +0800992}
993
Namhyung Kim0d68bc92015-04-06 14:36:10 +0900994static const char * const migrate_type_str[] = {
995 "UNMOVABL",
996 "RECLAIM",
997 "MOVABLE",
998 "RESERVED",
999 "CMA/ISLT",
1000 "UNKNOWN",
1001};
1002
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001003static void __print_page_alloc_result(struct perf_session *session, int n_lines)
Li Zefanba77c9e2009-11-20 15:53:25 +08001004{
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001005 struct rb_node *next = rb_first(&page_alloc_sorted);
1006 struct machine *machine = &session->machines.host;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001007 const char *format;
Namhyung Kim0e111152015-04-21 13:55:05 +09001008 int gfp_len = max(strlen("GFP flags"), max_gfp_len);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001009
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001010 printf("\n%.105s\n", graph_dotted_line);
Namhyung Kim0e111152015-04-21 13:55:05 +09001011 printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
1012 use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
1013 gfp_len, "GFP flags");
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001014 printf("%.105s\n", graph_dotted_line);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001015
1016 if (use_pfn)
Namhyung Kim0e111152015-04-21 13:55:05 +09001017 format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001018 else
Namhyung Kim0e111152015-04-21 13:55:05 +09001019 format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001020
1021 while (next && n_lines--) {
1022 struct page_stat *data;
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001023 struct symbol *sym;
1024 struct map *map;
1025 char buf[32];
1026 char *caller = buf;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001027
1028 data = rb_entry(next, struct page_stat, node);
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001029 sym = machine__find_kernel_function(machine, data->callsite,
1030 &map, NULL);
1031 if (sym && sym->name)
1032 caller = sym->name;
1033 else
1034 scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001035
1036 printf(format, (unsigned long long)data->page,
1037 (unsigned long long)data->alloc_bytes / 1024,
1038 data->nr_alloc, data->order,
1039 migrate_type_str[data->migrate_type],
Namhyung Kim0e111152015-04-21 13:55:05 +09001040 gfp_len, compact_gfp_string(data->gfp_flags), caller);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001041
1042 next = rb_next(next);
1043 }
1044
Namhyung Kim0e111152015-04-21 13:55:05 +09001045 if (n_lines == -1) {
1046 printf(" ... | ... | ... | ... | ... | %-*s | ...\n",
1047 gfp_len, "...");
1048 }
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001049
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001050 printf("%.105s\n", graph_dotted_line);
1051}
1052
1053static void __print_page_caller_result(struct perf_session *session, int n_lines)
1054{
1055 struct rb_node *next = rb_first(&page_caller_sorted);
1056 struct machine *machine = &session->machines.host;
Namhyung Kim0e111152015-04-21 13:55:05 +09001057 int gfp_len = max(strlen("GFP flags"), max_gfp_len);
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001058
1059 printf("\n%.105s\n", graph_dotted_line);
Namhyung Kim0e111152015-04-21 13:55:05 +09001060 printf(" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
1061 live_page ? "Live" : "Total", gfp_len, "GFP flags");
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001062 printf("%.105s\n", graph_dotted_line);
1063
1064 while (next && n_lines--) {
1065 struct page_stat *data;
1066 struct symbol *sym;
1067 struct map *map;
1068 char buf[32];
1069 char *caller = buf;
1070
1071 data = rb_entry(next, struct page_stat, node);
1072 sym = machine__find_kernel_function(machine, data->callsite,
1073 &map, NULL);
1074 if (sym && sym->name)
1075 caller = sym->name;
1076 else
1077 scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1078
Namhyung Kim0e111152015-04-21 13:55:05 +09001079 printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001080 (unsigned long long)data->alloc_bytes / 1024,
1081 data->nr_alloc, data->order,
1082 migrate_type_str[data->migrate_type],
Namhyung Kim0e111152015-04-21 13:55:05 +09001083 gfp_len, compact_gfp_string(data->gfp_flags), caller);
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001084
1085 next = rb_next(next);
1086 }
1087
Namhyung Kim0e111152015-04-21 13:55:05 +09001088 if (n_lines == -1) {
1089 printf(" ... | ... | ... | ... | %-*s | ...\n",
1090 gfp_len, "...");
1091 }
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001092
1093 printf("%.105s\n", graph_dotted_line);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001094}
1095
Namhyung Kim0e111152015-04-21 13:55:05 +09001096static void print_gfp_flags(void)
1097{
1098 int i;
1099
1100 printf("#\n");
1101 printf("# GFP flags\n");
1102 printf("# ---------\n");
1103 for (i = 0; i < nr_gfps; i++) {
1104 printf("# %08x: %*s: %s\n", gfps[i].flags,
1105 (int) max_gfp_len, gfps[i].compact_str,
1106 gfps[i].human_readable);
1107 }
1108}
1109
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001110static void print_slab_summary(void)
1111{
1112 printf("\nSUMMARY (SLAB allocator)");
1113 printf("\n========================\n");
Namhyung Kim77cfe382015-03-23 15:30:40 +09001114 printf("Total bytes requested: %'lu\n", total_requested);
1115 printf("Total bytes allocated: %'lu\n", total_allocated);
1116 printf("Total bytes wasted on internal fragmentation: %'lu\n",
Li Zefanba77c9e2009-11-20 15:53:25 +08001117 total_allocated - total_requested);
1118 printf("Internal fragmentation: %f%%\n",
1119 fragmentation(total_requested, total_allocated));
Namhyung Kim77cfe382015-03-23 15:30:40 +09001120 printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
Li Zefanba77c9e2009-11-20 15:53:25 +08001121}
1122
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001123static void print_page_summary(void)
1124{
1125 int o, m;
1126 u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
1127 u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
1128
1129 printf("\nSUMMARY (page allocator)");
1130 printf("\n========================\n");
1131 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation requests",
1132 nr_page_allocs, total_page_alloc_bytes / 1024);
1133 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free requests",
1134 nr_page_frees, total_page_free_bytes / 1024);
1135 printf("\n");
1136
1137 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
1138 nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
1139 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
1140 nr_page_allocs - nr_alloc_freed,
1141 (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
1142 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free-only requests",
1143 nr_page_nomatch, total_page_nomatch_bytes / 1024);
1144 printf("\n");
1145
1146 printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation failures",
1147 nr_page_fails, total_page_fail_bytes / 1024);
1148 printf("\n");
1149
1150 printf("%5s %12s %12s %12s %12s %12s\n", "Order", "Unmovable",
1151 "Reclaimable", "Movable", "Reserved", "CMA/Isolated");
1152 printf("%.5s %.12s %.12s %.12s %.12s %.12s\n", graph_dotted_line,
1153 graph_dotted_line, graph_dotted_line, graph_dotted_line,
1154 graph_dotted_line, graph_dotted_line);
1155
1156 for (o = 0; o < MAX_PAGE_ORDER; o++) {
1157 printf("%5d", o);
1158 for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
1159 if (order_stats[o][m])
1160 printf(" %'12d", order_stats[o][m]);
1161 else
1162 printf(" %12c", '.');
1163 }
1164 printf("\n");
1165 }
1166}
1167
1168static void print_slab_result(struct perf_session *session)
Li Zefanba77c9e2009-11-20 15:53:25 +08001169{
1170 if (caller_flag)
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001171 __print_slab_result(&root_caller_sorted, session, caller_lines, 1);
Li Zefanba77c9e2009-11-20 15:53:25 +08001172 if (alloc_flag)
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001173 __print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
1174 print_slab_summary();
1175}
1176
1177static void print_page_result(struct perf_session *session)
1178{
Namhyung Kim0e111152015-04-21 13:55:05 +09001179 if (caller_flag || alloc_flag)
1180 print_gfp_flags();
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001181 if (caller_flag)
1182 __print_page_caller_result(session, caller_lines);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001183 if (alloc_flag)
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001184 __print_page_alloc_result(session, alloc_lines);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001185 print_page_summary();
1186}
1187
1188static void print_result(struct perf_session *session)
1189{
1190 if (kmem_slab)
1191 print_slab_result(session);
1192 if (kmem_page)
1193 print_page_result(session);
Li Zefanba77c9e2009-11-20 15:53:25 +08001194}
1195
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001196static LIST_HEAD(slab_caller_sort);
1197static LIST_HEAD(slab_alloc_sort);
1198static LIST_HEAD(page_caller_sort);
1199static LIST_HEAD(page_alloc_sort);
Li Zefan29b3e152009-11-24 13:26:10 +08001200
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001201static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
1202 struct list_head *sort_list)
Li Zefanba77c9e2009-11-20 15:53:25 +08001203{
1204 struct rb_node **new = &(root->rb_node);
1205 struct rb_node *parent = NULL;
Li Zefan29b3e152009-11-24 13:26:10 +08001206 struct sort_dimension *sort;
Li Zefanba77c9e2009-11-20 15:53:25 +08001207
1208 while (*new) {
1209 struct alloc_stat *this;
Li Zefan29b3e152009-11-24 13:26:10 +08001210 int cmp = 0;
Li Zefanba77c9e2009-11-20 15:53:25 +08001211
1212 this = rb_entry(*new, struct alloc_stat, node);
1213 parent = *new;
1214
Li Zefan29b3e152009-11-24 13:26:10 +08001215 list_for_each_entry(sort, sort_list, list) {
1216 cmp = sort->cmp(data, this);
1217 if (cmp)
1218 break;
1219 }
Li Zefanba77c9e2009-11-20 15:53:25 +08001220
1221 if (cmp > 0)
1222 new = &((*new)->rb_left);
1223 else
1224 new = &((*new)->rb_right);
1225 }
1226
1227 rb_link_node(&data->node, parent, new);
1228 rb_insert_color(&data->node, root);
1229}
1230
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001231static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
1232 struct list_head *sort_list)
Li Zefanba77c9e2009-11-20 15:53:25 +08001233{
1234 struct rb_node *node;
1235 struct alloc_stat *data;
1236
1237 for (;;) {
1238 node = rb_first(root);
1239 if (!node)
1240 break;
1241
1242 rb_erase(node, root);
1243 data = rb_entry(node, struct alloc_stat, node);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001244 sort_slab_insert(root_sorted, data, sort_list);
1245 }
1246}
1247
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001248static void sort_page_insert(struct rb_root *root, struct page_stat *data,
1249 struct list_head *sort_list)
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001250{
1251 struct rb_node **new = &root->rb_node;
1252 struct rb_node *parent = NULL;
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001253 struct sort_dimension *sort;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001254
1255 while (*new) {
1256 struct page_stat *this;
1257 int cmp = 0;
1258
1259 this = rb_entry(*new, struct page_stat, node);
1260 parent = *new;
1261
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001262 list_for_each_entry(sort, sort_list, list) {
1263 cmp = sort->cmp(data, this);
1264 if (cmp)
1265 break;
1266 }
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001267
1268 if (cmp > 0)
1269 new = &parent->rb_left;
1270 else
1271 new = &parent->rb_right;
1272 }
1273
1274 rb_link_node(&data->node, parent, new);
1275 rb_insert_color(&data->node, root);
1276}
1277
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001278static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
1279 struct list_head *sort_list)
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001280{
1281 struct rb_node *node;
1282 struct page_stat *data;
1283
1284 for (;;) {
1285 node = rb_first(root);
1286 if (!node)
1287 break;
1288
1289 rb_erase(node, root);
1290 data = rb_entry(node, struct page_stat, node);
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001291 sort_page_insert(root_sorted, data, sort_list);
Li Zefanba77c9e2009-11-20 15:53:25 +08001292 }
1293}
1294
1295static void sort_result(void)
1296{
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001297 if (kmem_slab) {
1298 __sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001299 &slab_alloc_sort);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001300 __sort_slab_result(&root_caller_stat, &root_caller_sorted,
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001301 &slab_caller_sort);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001302 }
1303 if (kmem_page) {
Namhyung Kim2a7ef022015-04-21 13:55:04 +09001304 if (live_page)
1305 __sort_page_result(&page_live_tree, &page_alloc_sorted,
1306 &page_alloc_sort);
1307 else
1308 __sort_page_result(&page_alloc_tree, &page_alloc_sorted,
1309 &page_alloc_sort);
1310
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001311 __sort_page_result(&page_caller_tree, &page_caller_sorted,
1312 &page_caller_sort);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001313 }
Li Zefanba77c9e2009-11-20 15:53:25 +08001314}
1315
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001316static int __cmd_kmem(struct perf_session *session)
Li Zefanba77c9e2009-11-20 15:53:25 +08001317{
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001318 int err = -EINVAL;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001319 struct perf_evsel *evsel;
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03001320 const struct perf_evsel_str_handler kmem_tracepoints[] = {
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001321 /* slab allocator */
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03001322 { "kmem:kmalloc", perf_evsel__process_alloc_event, },
1323 { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, },
1324 { "kmem:kmalloc_node", perf_evsel__process_alloc_node_event, },
1325 { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
1326 { "kmem:kfree", perf_evsel__process_free_event, },
1327 { "kmem:kmem_cache_free", perf_evsel__process_free_event, },
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001328 /* page allocator */
1329 { "kmem:mm_page_alloc", perf_evsel__process_page_alloc_event, },
1330 { "kmem:mm_page_free", perf_evsel__process_page_free_event, },
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03001331 };
Li Zefanba77c9e2009-11-20 15:53:25 +08001332
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001333 if (!perf_session__has_traces(session, "kmem record"))
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001334 goto out;
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001335
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03001336 if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
1337 pr_err("Initializing perf session tracepoint handlers failed\n");
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001338 goto out;
Arnaldo Carvalho de Melo0f7d2f12012-09-24 10:46:54 -03001339 }
1340
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001341 evlist__for_each(session->evlist, evsel) {
1342 if (!strcmp(perf_evsel__name(evsel), "kmem:mm_page_alloc") &&
1343 perf_evsel__field(evsel, "pfn")) {
1344 use_pfn = true;
1345 break;
1346 }
1347 }
1348
Arnaldo Carvalho de Melo4aa65632009-12-13 19:50:29 -02001349 setup_pager();
Arnaldo Carvalho de Melob7b61cb2015-03-03 11:58:45 -03001350 err = perf_session__process_events(session);
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001351 if (err != 0) {
1352 pr_err("error during process events: %d\n", err);
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001353 goto out;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001354 }
Arnaldo Carvalho de Melo4aa65632009-12-13 19:50:29 -02001355 sort_result();
1356 print_result(session);
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001357out:
Arnaldo Carvalho de Melo4aa65632009-12-13 19:50:29 -02001358 return err;
Li Zefanba77c9e2009-11-20 15:53:25 +08001359}
1360
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001361/* slab sort keys */
1362static int ptr_cmp(void *a, void *b)
Li Zefanba77c9e2009-11-20 15:53:25 +08001363{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001364 struct alloc_stat *l = a;
1365 struct alloc_stat *r = b;
1366
Li Zefanba77c9e2009-11-20 15:53:25 +08001367 if (l->ptr < r->ptr)
1368 return -1;
1369 else if (l->ptr > r->ptr)
1370 return 1;
1371 return 0;
1372}
1373
Li Zefan29b3e152009-11-24 13:26:10 +08001374static struct sort_dimension ptr_sort_dimension = {
1375 .name = "ptr",
1376 .cmp = ptr_cmp,
1377};
1378
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001379static int slab_callsite_cmp(void *a, void *b)
Li Zefanba77c9e2009-11-20 15:53:25 +08001380{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001381 struct alloc_stat *l = a;
1382 struct alloc_stat *r = b;
1383
Li Zefanba77c9e2009-11-20 15:53:25 +08001384 if (l->call_site < r->call_site)
1385 return -1;
1386 else if (l->call_site > r->call_site)
1387 return 1;
1388 return 0;
1389}
1390
Li Zefan29b3e152009-11-24 13:26:10 +08001391static struct sort_dimension callsite_sort_dimension = {
1392 .name = "callsite",
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001393 .cmp = slab_callsite_cmp,
Li Zefan29b3e152009-11-24 13:26:10 +08001394};
1395
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001396static int hit_cmp(void *a, void *b)
Pekka Enbergf3ced7c2009-11-22 11:58:00 +02001397{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001398 struct alloc_stat *l = a;
1399 struct alloc_stat *r = b;
1400
Pekka Enbergf3ced7c2009-11-22 11:58:00 +02001401 if (l->hit < r->hit)
1402 return -1;
1403 else if (l->hit > r->hit)
1404 return 1;
1405 return 0;
1406}
1407
Li Zefan29b3e152009-11-24 13:26:10 +08001408static struct sort_dimension hit_sort_dimension = {
1409 .name = "hit",
1410 .cmp = hit_cmp,
1411};
1412
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001413static int bytes_cmp(void *a, void *b)
Li Zefanba77c9e2009-11-20 15:53:25 +08001414{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001415 struct alloc_stat *l = a;
1416 struct alloc_stat *r = b;
1417
Li Zefanba77c9e2009-11-20 15:53:25 +08001418 if (l->bytes_alloc < r->bytes_alloc)
1419 return -1;
1420 else if (l->bytes_alloc > r->bytes_alloc)
1421 return 1;
1422 return 0;
1423}
1424
Li Zefan29b3e152009-11-24 13:26:10 +08001425static struct sort_dimension bytes_sort_dimension = {
1426 .name = "bytes",
1427 .cmp = bytes_cmp,
1428};
1429
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001430static int frag_cmp(void *a, void *b)
Pekka Enbergf3ced7c2009-11-22 11:58:00 +02001431{
1432 double x, y;
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001433 struct alloc_stat *l = a;
1434 struct alloc_stat *r = b;
Pekka Enbergf3ced7c2009-11-22 11:58:00 +02001435
1436 x = fragmentation(l->bytes_req, l->bytes_alloc);
1437 y = fragmentation(r->bytes_req, r->bytes_alloc);
1438
1439 if (x < y)
1440 return -1;
1441 else if (x > y)
1442 return 1;
1443 return 0;
1444}
1445
Li Zefan29b3e152009-11-24 13:26:10 +08001446static struct sort_dimension frag_sort_dimension = {
1447 .name = "frag",
1448 .cmp = frag_cmp,
1449};
1450
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001451static int pingpong_cmp(void *a, void *b)
Li Zefan079d3f62009-11-24 13:26:55 +08001452{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001453 struct alloc_stat *l = a;
1454 struct alloc_stat *r = b;
1455
Li Zefan079d3f62009-11-24 13:26:55 +08001456 if (l->pingpong < r->pingpong)
1457 return -1;
1458 else if (l->pingpong > r->pingpong)
1459 return 1;
1460 return 0;
1461}
1462
1463static struct sort_dimension pingpong_sort_dimension = {
1464 .name = "pingpong",
1465 .cmp = pingpong_cmp,
1466};
1467
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001468/* page sort keys */
1469static int page_cmp(void *a, void *b)
1470{
1471 struct page_stat *l = a;
1472 struct page_stat *r = b;
1473
1474 if (l->page < r->page)
1475 return -1;
1476 else if (l->page > r->page)
1477 return 1;
1478 return 0;
1479}
1480
1481static struct sort_dimension page_sort_dimension = {
1482 .name = "page",
1483 .cmp = page_cmp,
1484};
1485
1486static int page_callsite_cmp(void *a, void *b)
1487{
1488 struct page_stat *l = a;
1489 struct page_stat *r = b;
1490
1491 if (l->callsite < r->callsite)
1492 return -1;
1493 else if (l->callsite > r->callsite)
1494 return 1;
1495 return 0;
1496}
1497
1498static struct sort_dimension page_callsite_sort_dimension = {
1499 .name = "callsite",
1500 .cmp = page_callsite_cmp,
1501};
1502
1503static int page_hit_cmp(void *a, void *b)
1504{
1505 struct page_stat *l = a;
1506 struct page_stat *r = b;
1507
1508 if (l->nr_alloc < r->nr_alloc)
1509 return -1;
1510 else if (l->nr_alloc > r->nr_alloc)
1511 return 1;
1512 return 0;
1513}
1514
1515static struct sort_dimension page_hit_sort_dimension = {
1516 .name = "hit",
1517 .cmp = page_hit_cmp,
1518};
1519
1520static int page_bytes_cmp(void *a, void *b)
1521{
1522 struct page_stat *l = a;
1523 struct page_stat *r = b;
1524
1525 if (l->alloc_bytes < r->alloc_bytes)
1526 return -1;
1527 else if (l->alloc_bytes > r->alloc_bytes)
1528 return 1;
1529 return 0;
1530}
1531
1532static struct sort_dimension page_bytes_sort_dimension = {
1533 .name = "bytes",
1534 .cmp = page_bytes_cmp,
1535};
1536
1537static int page_order_cmp(void *a, void *b)
1538{
1539 struct page_stat *l = a;
1540 struct page_stat *r = b;
1541
1542 if (l->order < r->order)
1543 return -1;
1544 else if (l->order > r->order)
1545 return 1;
1546 return 0;
1547}
1548
1549static struct sort_dimension page_order_sort_dimension = {
1550 .name = "order",
1551 .cmp = page_order_cmp,
1552};
1553
1554static int migrate_type_cmp(void *a, void *b)
1555{
1556 struct page_stat *l = a;
1557 struct page_stat *r = b;
1558
1559 /* for internal use to find free'd page */
1560 if (l->migrate_type == -1U)
1561 return 0;
1562
1563 if (l->migrate_type < r->migrate_type)
1564 return -1;
1565 else if (l->migrate_type > r->migrate_type)
1566 return 1;
1567 return 0;
1568}
1569
1570static struct sort_dimension migrate_type_sort_dimension = {
1571 .name = "migtype",
1572 .cmp = migrate_type_cmp,
1573};
1574
1575static int gfp_flags_cmp(void *a, void *b)
1576{
1577 struct page_stat *l = a;
1578 struct page_stat *r = b;
1579
1580 /* for internal use to find free'd page */
1581 if (l->gfp_flags == -1U)
1582 return 0;
1583
1584 if (l->gfp_flags < r->gfp_flags)
1585 return -1;
1586 else if (l->gfp_flags > r->gfp_flags)
1587 return 1;
1588 return 0;
1589}
1590
1591static struct sort_dimension gfp_flags_sort_dimension = {
1592 .name = "gfp",
1593 .cmp = gfp_flags_cmp,
1594};
1595
1596static struct sort_dimension *slab_sorts[] = {
Li Zefan29b3e152009-11-24 13:26:10 +08001597 &ptr_sort_dimension,
1598 &callsite_sort_dimension,
1599 &hit_sort_dimension,
1600 &bytes_sort_dimension,
1601 &frag_sort_dimension,
Li Zefan079d3f62009-11-24 13:26:55 +08001602 &pingpong_sort_dimension,
Li Zefan29b3e152009-11-24 13:26:10 +08001603};
1604
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001605static struct sort_dimension *page_sorts[] = {
1606 &page_sort_dimension,
1607 &page_callsite_sort_dimension,
1608 &page_hit_sort_dimension,
1609 &page_bytes_sort_dimension,
1610 &page_order_sort_dimension,
1611 &migrate_type_sort_dimension,
1612 &gfp_flags_sort_dimension,
1613};
Li Zefan29b3e152009-11-24 13:26:10 +08001614
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001615static int slab_sort_dimension__add(const char *tok, struct list_head *list)
Li Zefan29b3e152009-11-24 13:26:10 +08001616{
1617 struct sort_dimension *sort;
1618 int i;
1619
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001620 for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
1621 if (!strcmp(slab_sorts[i]->name, tok)) {
1622 sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -03001623 if (!sort) {
Arnaldo Carvalho de Melo8d9233f2013-01-24 22:24:57 -03001624 pr_err("%s: memdup failed\n", __func__);
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -03001625 return -1;
1626 }
Li Zefan29b3e152009-11-24 13:26:10 +08001627 list_add_tail(&sort->list, list);
1628 return 0;
1629 }
1630 }
1631
1632 return -1;
1633}
1634
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001635static int page_sort_dimension__add(const char *tok, struct list_head *list)
1636{
1637 struct sort_dimension *sort;
1638 int i;
1639
1640 for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
1641 if (!strcmp(page_sorts[i]->name, tok)) {
1642 sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
1643 if (!sort) {
1644 pr_err("%s: memdup failed\n", __func__);
1645 return -1;
1646 }
1647 list_add_tail(&sort->list, list);
1648 return 0;
1649 }
1650 }
1651
1652 return -1;
1653}
1654
1655static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
Li Zefan29b3e152009-11-24 13:26:10 +08001656{
1657 char *tok;
1658 char *str = strdup(arg);
Namhyung Kim405f8752015-03-12 16:32:46 +09001659 char *pos = str;
Li Zefan29b3e152009-11-24 13:26:10 +08001660
Arnaldo Carvalho de Melo2814eb02012-09-08 22:53:06 -03001661 if (!str) {
1662 pr_err("%s: strdup failed\n", __func__);
1663 return -1;
1664 }
Li Zefan29b3e152009-11-24 13:26:10 +08001665
1666 while (true) {
Namhyung Kim405f8752015-03-12 16:32:46 +09001667 tok = strsep(&pos, ",");
Li Zefan29b3e152009-11-24 13:26:10 +08001668 if (!tok)
1669 break;
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001670 if (slab_sort_dimension__add(tok, sort_list) < 0) {
1671 error("Unknown slab --sort key: '%s'", tok);
1672 free(str);
1673 return -1;
1674 }
1675 }
1676
1677 free(str);
1678 return 0;
1679}
1680
1681static int setup_page_sorting(struct list_head *sort_list, const char *arg)
1682{
1683 char *tok;
1684 char *str = strdup(arg);
1685 char *pos = str;
1686
1687 if (!str) {
1688 pr_err("%s: strdup failed\n", __func__);
1689 return -1;
1690 }
1691
1692 while (true) {
1693 tok = strsep(&pos, ",");
1694 if (!tok)
1695 break;
1696 if (page_sort_dimension__add(tok, sort_list) < 0) {
1697 error("Unknown page --sort key: '%s'", tok);
Namhyung Kim1b228592012-01-08 02:25:29 +09001698 free(str);
Li Zefan29b3e152009-11-24 13:26:10 +08001699 return -1;
1700 }
1701 }
1702
1703 free(str);
1704 return 0;
1705}
1706
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001707static int parse_sort_opt(const struct option *opt __maybe_unused,
1708 const char *arg, int unset __maybe_unused)
Li Zefanba77c9e2009-11-20 15:53:25 +08001709{
Li Zefanba77c9e2009-11-20 15:53:25 +08001710 if (!arg)
1711 return -1;
1712
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001713 if (kmem_page > kmem_slab) {
1714 if (caller_flag > alloc_flag)
1715 return setup_page_sorting(&page_caller_sort, arg);
1716 else
1717 return setup_page_sorting(&page_alloc_sort, arg);
1718 } else {
1719 if (caller_flag > alloc_flag)
1720 return setup_slab_sorting(&slab_caller_sort, arg);
1721 else
1722 return setup_slab_sorting(&slab_alloc_sort, arg);
1723 }
Li Zefanba77c9e2009-11-20 15:53:25 +08001724
1725 return 0;
1726}
1727
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001728static int parse_caller_opt(const struct option *opt __maybe_unused,
1729 const char *arg __maybe_unused,
1730 int unset __maybe_unused)
Li Zefanba77c9e2009-11-20 15:53:25 +08001731{
Li Zefan90b86a92009-12-10 15:21:57 +08001732 caller_flag = (alloc_flag + 1);
1733 return 0;
1734}
Li Zefanba77c9e2009-11-20 15:53:25 +08001735
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001736static int parse_alloc_opt(const struct option *opt __maybe_unused,
1737 const char *arg __maybe_unused,
1738 int unset __maybe_unused)
Li Zefan90b86a92009-12-10 15:21:57 +08001739{
1740 alloc_flag = (caller_flag + 1);
Li Zefanba77c9e2009-11-20 15:53:25 +08001741 return 0;
1742}
1743
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001744static int parse_slab_opt(const struct option *opt __maybe_unused,
1745 const char *arg __maybe_unused,
1746 int unset __maybe_unused)
1747{
1748 kmem_slab = (kmem_page + 1);
1749 return 0;
1750}
1751
1752static int parse_page_opt(const struct option *opt __maybe_unused,
1753 const char *arg __maybe_unused,
1754 int unset __maybe_unused)
1755{
1756 kmem_page = (kmem_slab + 1);
1757 return 0;
1758}
1759
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001760static int parse_line_opt(const struct option *opt __maybe_unused,
1761 const char *arg, int unset __maybe_unused)
Li Zefanba77c9e2009-11-20 15:53:25 +08001762{
1763 int lines;
1764
1765 if (!arg)
1766 return -1;
1767
1768 lines = strtoul(arg, NULL, 10);
1769
1770 if (caller_flag > alloc_flag)
1771 caller_lines = lines;
1772 else
1773 alloc_lines = lines;
1774
1775 return 0;
1776}
1777
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001778static int __cmd_record(int argc, const char **argv)
1779{
1780 const char * const record_args[] = {
Jiri Olsa4a4d3712013-06-05 13:37:21 +02001781 "record", "-a", "-R", "-c", "1",
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001782 };
1783 const char * const slab_events[] = {
Li Zefanba77c9e2009-11-20 15:53:25 +08001784 "-e", "kmem:kmalloc",
1785 "-e", "kmem:kmalloc_node",
1786 "-e", "kmem:kfree",
1787 "-e", "kmem:kmem_cache_alloc",
1788 "-e", "kmem:kmem_cache_alloc_node",
1789 "-e", "kmem:kmem_cache_free",
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001790 };
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001791 const char * const page_events[] = {
1792 "-e", "kmem:mm_page_alloc",
1793 "-e", "kmem:mm_page_free",
1794 };
Li Zefanba77c9e2009-11-20 15:53:25 +08001795 unsigned int rec_argc, i, j;
1796 const char **rec_argv;
1797
1798 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001799 if (kmem_slab)
1800 rec_argc += ARRAY_SIZE(slab_events);
1801 if (kmem_page)
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001802 rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001803
Li Zefanba77c9e2009-11-20 15:53:25 +08001804 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1805
Chris Samuelce47dc52010-11-13 13:35:06 +11001806 if (rec_argv == NULL)
1807 return -ENOMEM;
1808
Li Zefanba77c9e2009-11-20 15:53:25 +08001809 for (i = 0; i < ARRAY_SIZE(record_args); i++)
1810 rec_argv[i] = strdup(record_args[i]);
1811
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001812 if (kmem_slab) {
1813 for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
1814 rec_argv[i] = strdup(slab_events[j]);
1815 }
1816 if (kmem_page) {
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001817 rec_argv[i++] = strdup("-g");
1818
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001819 for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
1820 rec_argv[i] = strdup(page_events[j]);
1821 }
1822
Li Zefanba77c9e2009-11-20 15:53:25 +08001823 for (j = 1; j < (unsigned int)argc; j++, i++)
1824 rec_argv[i] = argv[j];
1825
1826 return cmd_record(i, rec_argv, NULL);
1827}
1828
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001829int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
Li Zefanba77c9e2009-11-20 15:53:25 +08001830{
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001831 const char * const default_slab_sort = "frag,hit,bytes";
1832 const char * const default_page_sort = "bytes,hit";
Yunlong Songd1eeb772015-04-02 21:47:12 +08001833 struct perf_data_file file = {
Yunlong Songd1eeb772015-04-02 21:47:12 +08001834 .mode = PERF_DATA_MODE_READ,
1835 };
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001836 const struct option kmem_options[] = {
1837 OPT_STRING('i', "input", &input_name, "file", "input file name"),
Namhyung Kimbd72a332015-03-12 16:32:47 +09001838 OPT_INCR('v', "verbose", &verbose,
1839 "be more verbose (show symbol address, etc)"),
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001840 OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
1841 "show per-callsite statistics", parse_caller_opt),
1842 OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
1843 "show per-allocation statistics", parse_alloc_opt),
1844 OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001845 "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
1846 "page, order, migtype, gfp", parse_sort_opt),
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001847 OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
1848 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
Yunlong Songd1eeb772015-04-02 21:47:12 +08001849 OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001850 OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
1851 parse_slab_opt),
1852 OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
1853 parse_page_opt),
Namhyung Kim2a7ef022015-04-21 13:55:04 +09001854 OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001855 OPT_END()
1856 };
Ramkumar Ramachandra3bca2352014-03-14 23:17:51 -04001857 const char *const kmem_subcommands[] = { "record", "stat", NULL };
1858 const char *kmem_usage[] = {
1859 NULL,
Arnaldo Carvalho de Melo0433ffb2012-10-01 15:20:58 -03001860 NULL
1861 };
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001862 struct perf_session *session;
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001863 int ret = -1;
1864
Ramkumar Ramachandra3bca2352014-03-14 23:17:51 -04001865 argc = parse_options_subcommand(argc, argv, kmem_options,
1866 kmem_subcommands, kmem_usage, 0);
Li Zefanba77c9e2009-11-20 15:53:25 +08001867
Li Zefan90b86a92009-12-10 15:21:57 +08001868 if (!argc)
Li Zefanba77c9e2009-11-20 15:53:25 +08001869 usage_with_options(kmem_usage, kmem_options);
1870
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001871 if (kmem_slab == 0 && kmem_page == 0)
1872 kmem_slab = 1; /* for backward compatibility */
1873
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001874 if (!strncmp(argv[0], "rec", 3)) {
Namhyung Kim0a7e6d12014-08-12 15:40:45 +09001875 symbol__init(NULL);
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001876 return __cmd_record(argc, argv);
1877 }
1878
Jiri Olsa28939e12015-04-06 14:36:08 +09001879 file.path = input_name;
1880
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001881 kmem_session = session = perf_session__new(&file, false, &perf_kmem);
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001882 if (session == NULL)
Taeung Song52e028342014-09-24 10:33:37 +09001883 return -1;
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001884
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001885 if (kmem_page) {
1886 struct perf_evsel *evsel = perf_evlist__first(session->evlist);
1887
1888 if (evsel == NULL || evsel->tp_format == NULL) {
1889 pr_err("invalid event found.. aborting\n");
1890 return -1;
1891 }
1892
1893 kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
Namhyung Kimc9758cc2015-04-21 13:55:02 +09001894 symbol_conf.use_callchain = true;
Namhyung Kim0d68bc92015-04-06 14:36:10 +09001895 }
1896
Namhyung Kim0a7e6d12014-08-12 15:40:45 +09001897 symbol__init(&session->header.env);
Arnaldo Carvalho de Melo655000e2009-12-15 20:04:40 -02001898
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001899 if (!strcmp(argv[0], "stat")) {
Namhyung Kim77cfe382015-03-23 15:30:40 +09001900 setlocale(LC_ALL, "");
1901
Don Zickus4b627952014-04-07 14:55:23 -04001902 if (cpu__setup_cpunode_map())
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001903 goto out_delete;
Li Zefanba77c9e2009-11-20 15:53:25 +08001904
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001905 if (list_empty(&slab_caller_sort))
1906 setup_slab_sorting(&slab_caller_sort, default_slab_sort);
1907 if (list_empty(&slab_alloc_sort))
1908 setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
1909 if (list_empty(&page_caller_sort))
1910 setup_page_sorting(&page_caller_sort, default_page_sort);
1911 if (list_empty(&page_alloc_sort))
1912 setup_page_sorting(&page_alloc_sort, default_page_sort);
Li Zefan7d0d3942009-11-24 13:26:31 +08001913
Namhyung Kimfb4f3132015-04-21 13:55:03 +09001914 if (kmem_page) {
1915 setup_page_sorting(&page_alloc_sort_input,
1916 "page,order,migtype,gfp");
1917 setup_page_sorting(&page_caller_sort_input,
1918 "callsite,order,migtype,gfp");
1919 }
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001920 ret = __cmd_kmem(session);
Pekka Enbergb00eca82010-01-19 19:26:11 +02001921 } else
1922 usage_with_options(kmem_usage, kmem_options);
Li Zefan90b86a92009-12-10 15:21:57 +08001923
Namhyung Kim2b2b2c62014-08-12 15:40:38 +09001924out_delete:
1925 perf_session__delete(session);
1926
1927 return ret;
Li Zefanba77c9e2009-11-20 15:53:25 +08001928}
1929