1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ba77c9e1SLi Zefan #include "builtin.h"
3ba77c9e1SLi Zefan
44a3cec84SArnaldo Carvalho de Melo #include "util/dso.h"
50f7d2f1bSArnaldo Carvalho de Melo #include "util/evlist.h"
6fcf65bf1SArnaldo Carvalho de Melo #include "util/evsel.h"
741840d21STaeung Song #include "util/config.h"
81101f69aSArnaldo Carvalho de Melo #include "util/map.h"
9ba77c9e1SLi Zefan #include "util/symbol.h"
10ba77c9e1SLi Zefan #include "util/thread.h"
11ba77c9e1SLi Zefan #include "util/header.h"
1294c744b6SArnaldo Carvalho de Melo #include "util/session.h"
1345694aa7SArnaldo Carvalho de Melo #include "util/tool.h"
14c9758cc4SNamhyung Kim #include "util/callchain.h"
152a865bd8SDavid Ahern #include "util/time-utils.h"
166ef81c55SMamatha Inamdar #include <linux/err.h>
17ba77c9e1SLi Zefan
188520a98dSArnaldo Carvalho de Melo #include <subcmd/pager.h>
194b6ab94eSJosh Poimboeuf #include <subcmd/parse-options.h>
20ba77c9e1SLi Zefan #include "util/trace-event.h"
21f5fc1412SJiri Olsa #include "util/data.h"
224b627957SDon Zickus #include "util/cpumap.h"
23ba77c9e1SLi Zefan
24ba77c9e1SLi Zefan #include "util/debug.h"
256a9fa4e3SArnaldo Carvalho de Melo #include "util/string2.h"
26f12ad272SIan Rogers #include "util/util.h"
27ba77c9e1SLi Zefan
28877a7a11SArnaldo Carvalho de Melo #include <linux/kernel.h>
29dce088abSLeo Yan #include <linux/numa.h>
30ba77c9e1SLi Zefan #include <linux/rbtree.h>
318d9233f2SArnaldo Carvalho de Melo #include <linux/string.h>
327f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
33a43783aeSArnaldo Carvalho de Melo #include <errno.h>
34fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
3577cfe388SNamhyung Kim #include <locale.h>
36c9758cc4SNamhyung Kim #include <regex.h>
37ba77c9e1SLi Zefan
383052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
39378ef0f5SIan Rogers #include <traceevent/event-parse.h>
403d689ed6SArnaldo Carvalho de Melo
410d68bc92SNamhyung Kim static int kmem_slab;
420d68bc92SNamhyung Kim static int kmem_page;
430d68bc92SNamhyung Kim
440d68bc92SNamhyung Kim static long kmem_page_size;
450c160d49SNamhyung Kim static enum {
460c160d49SNamhyung Kim KMEM_SLAB,
470c160d49SNamhyung Kim KMEM_PAGE,
480c160d49SNamhyung Kim } kmem_default = KMEM_SLAB; /* for backward compatibility */
490d68bc92SNamhyung Kim
50ba77c9e1SLi Zefan struct alloc_stat;
51fb4f313dSNamhyung Kim typedef int (*sort_fn_t)(void *, void *);
52ba77c9e1SLi Zefan
53ba77c9e1SLi Zefan static int alloc_flag;
54ba77c9e1SLi Zefan static int caller_flag;
55ba77c9e1SLi Zefan
56ba77c9e1SLi Zefan static int alloc_lines = -1;
57ba77c9e1SLi Zefan static int caller_lines = -1;
58ba77c9e1SLi Zefan
597707b6b6SLi Zefan static bool raw_ip;
607707b6b6SLi Zefan
61ba77c9e1SLi Zefan struct alloc_stat {
62ba77c9e1SLi Zefan u64 call_site;
63ba77c9e1SLi Zefan u64 ptr;
64ba77c9e1SLi Zefan u64 bytes_req;
65ba77c9e1SLi Zefan u64 bytes_alloc;
66aa58e9afSDavid Ahern u64 last_alloc;
67ba77c9e1SLi Zefan u32 hit;
68079d3f65SLi Zefan u32 pingpong;
69079d3f65SLi Zefan
70079d3f65SLi Zefan short alloc_cpu;
71ba77c9e1SLi Zefan
72ba77c9e1SLi Zefan struct rb_node node;
73ba77c9e1SLi Zefan };
74ba77c9e1SLi Zefan
75ba77c9e1SLi Zefan static struct rb_root root_alloc_stat;
76ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted;
77ba77c9e1SLi Zefan static struct rb_root root_caller_stat;
78ba77c9e1SLi Zefan static struct rb_root root_caller_sorted;
79ba77c9e1SLi Zefan
80aa58e9afSDavid Ahern static unsigned long total_requested, total_allocated, total_freed;
817d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs;
82ba77c9e1SLi Zefan
832a865bd8SDavid Ahern /* filters for controlling start and stop of time of analysis */
842a865bd8SDavid Ahern static struct perf_time_interval ptime;
852a865bd8SDavid Ahern const char *time_str;
862a865bd8SDavid Ahern
insert_alloc_stat(unsigned long call_site,unsigned long ptr,int bytes_req,int bytes_alloc,int cpu)872814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
88079d3f65SLi Zefan int bytes_req, int bytes_alloc, int cpu)
89ba77c9e1SLi Zefan {
90ba77c9e1SLi Zefan struct rb_node **node = &root_alloc_stat.rb_node;
91ba77c9e1SLi Zefan struct rb_node *parent = NULL;
92ba77c9e1SLi Zefan struct alloc_stat *data = NULL;
93ba77c9e1SLi Zefan
94ba77c9e1SLi Zefan while (*node) {
95ba77c9e1SLi Zefan parent = *node;
96ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node);
97ba77c9e1SLi Zefan
98ba77c9e1SLi Zefan if (ptr > data->ptr)
99ba77c9e1SLi Zefan node = &(*node)->rb_right;
100ba77c9e1SLi Zefan else if (ptr < data->ptr)
101ba77c9e1SLi Zefan node = &(*node)->rb_left;
102ba77c9e1SLi Zefan else
103ba77c9e1SLi Zefan break;
104ba77c9e1SLi Zefan }
105ba77c9e1SLi Zefan
106ba77c9e1SLi Zefan if (data && data->ptr == ptr) {
107ba77c9e1SLi Zefan data->hit++;
108ba77c9e1SLi Zefan data->bytes_req += bytes_req;
1094efb5290SWenji Huang data->bytes_alloc += bytes_alloc;
110ba77c9e1SLi Zefan } else {
111ba77c9e1SLi Zefan data = malloc(sizeof(*data));
1122814eb05SArnaldo Carvalho de Melo if (!data) {
1132814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__);
1142814eb05SArnaldo Carvalho de Melo return -1;
1152814eb05SArnaldo Carvalho de Melo }
116ba77c9e1SLi Zefan data->ptr = ptr;
117079d3f65SLi Zefan data->pingpong = 0;
118ba77c9e1SLi Zefan data->hit = 1;
119ba77c9e1SLi Zefan data->bytes_req = bytes_req;
120ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc;
121ba77c9e1SLi Zefan
122ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node);
123ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_alloc_stat);
124ba77c9e1SLi Zefan }
125079d3f65SLi Zefan data->call_site = call_site;
126079d3f65SLi Zefan data->alloc_cpu = cpu;
127aa58e9afSDavid Ahern data->last_alloc = bytes_alloc;
128aa58e9afSDavid Ahern
1292814eb05SArnaldo Carvalho de Melo return 0;
130ba77c9e1SLi Zefan }
131ba77c9e1SLi Zefan
insert_caller_stat(unsigned long call_site,int bytes_req,int bytes_alloc)1322814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site,
133ba77c9e1SLi Zefan int bytes_req, int bytes_alloc)
134ba77c9e1SLi Zefan {
135ba77c9e1SLi Zefan struct rb_node **node = &root_caller_stat.rb_node;
136ba77c9e1SLi Zefan struct rb_node *parent = NULL;
137ba77c9e1SLi Zefan struct alloc_stat *data = NULL;
138ba77c9e1SLi Zefan
139ba77c9e1SLi Zefan while (*node) {
140ba77c9e1SLi Zefan parent = *node;
141ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node);
142ba77c9e1SLi Zefan
143ba77c9e1SLi Zefan if (call_site > data->call_site)
144ba77c9e1SLi Zefan node = &(*node)->rb_right;
145ba77c9e1SLi Zefan else if (call_site < data->call_site)
146ba77c9e1SLi Zefan node = &(*node)->rb_left;
147ba77c9e1SLi Zefan else
148ba77c9e1SLi Zefan break;
149ba77c9e1SLi Zefan }
150ba77c9e1SLi Zefan
151ba77c9e1SLi Zefan if (data && data->call_site == call_site) {
152ba77c9e1SLi Zefan data->hit++;
153ba77c9e1SLi Zefan data->bytes_req += bytes_req;
1544efb5290SWenji Huang data->bytes_alloc += bytes_alloc;
155ba77c9e1SLi Zefan } else {
156ba77c9e1SLi Zefan data = malloc(sizeof(*data));
1572814eb05SArnaldo Carvalho de Melo if (!data) {
1582814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__);
1592814eb05SArnaldo Carvalho de Melo return -1;
1602814eb05SArnaldo Carvalho de Melo }
161ba77c9e1SLi Zefan data->call_site = call_site;
162079d3f65SLi Zefan data->pingpong = 0;
163ba77c9e1SLi Zefan data->hit = 1;
164ba77c9e1SLi Zefan data->bytes_req = bytes_req;
165ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc;
166ba77c9e1SLi Zefan
167ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node);
168ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_caller_stat);
169ba77c9e1SLi Zefan }
1702814eb05SArnaldo Carvalho de Melo
1712814eb05SArnaldo Carvalho de Melo return 0;
172ba77c9e1SLi Zefan }
173ba77c9e1SLi Zefan
evsel__process_alloc_event(struct evsel * evsel,struct perf_sample * sample)1748cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample)
175ba77c9e1SLi Zefan {
176efc0cdc9SArnaldo Carvalho de Melo unsigned long ptr = evsel__intval(evsel, sample, "ptr"),
177efc0cdc9SArnaldo Carvalho de Melo call_site = evsel__intval(evsel, sample, "call_site");
178efc0cdc9SArnaldo Carvalho de Melo int bytes_req = evsel__intval(evsel, sample, "bytes_req"),
179efc0cdc9SArnaldo Carvalho de Melo bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc");
180ba77c9e1SLi Zefan
1810f7d2f1bSArnaldo Carvalho de Melo if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
1822814eb05SArnaldo Carvalho de Melo insert_caller_stat(call_site, bytes_req, bytes_alloc))
1832814eb05SArnaldo Carvalho de Melo return -1;
184ba77c9e1SLi Zefan
185ba77c9e1SLi Zefan total_requested += bytes_req;
186ba77c9e1SLi Zefan total_allocated += bytes_alloc;
1877d0d3945SLi Zefan
1880f7d2f1bSArnaldo Carvalho de Melo nr_allocs++;
1890f7d2f1bSArnaldo Carvalho de Melo
190dce088abSLeo Yan /*
191dce088abSLeo Yan * Commit 11e9734bcb6a ("mm/slab_common: unify NUMA and UMA
192dce088abSLeo Yan * version of tracepoints") adds the field "node" into the
193dce088abSLeo Yan * tracepoints 'kmalloc' and 'kmem_cache_alloc'.
194dce088abSLeo Yan *
195dce088abSLeo Yan * The legacy tracepoints 'kmalloc_node' and 'kmem_cache_alloc_node'
196dce088abSLeo Yan * also contain the field "node".
197dce088abSLeo Yan *
198dce088abSLeo Yan * If the tracepoint contains the field "node" the tool stats the
199dce088abSLeo Yan * cross allocation.
200dce088abSLeo Yan */
201dce088abSLeo Yan if (evsel__field(evsel, "node")) {
202dce088abSLeo Yan int node1, node2;
2030f7d2f1bSArnaldo Carvalho de Melo
204dce088abSLeo Yan node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu});
205efc0cdc9SArnaldo Carvalho de Melo node2 = evsel__intval(evsel, sample, "node");
2060f7d2f1bSArnaldo Carvalho de Melo
207dce088abSLeo Yan /*
208dce088abSLeo Yan * If the field "node" is NUMA_NO_NODE (-1), we don't take it
209dce088abSLeo Yan * as a cross allocation.
210dce088abSLeo Yan */
211dce088abSLeo Yan if ((node2 != NUMA_NO_NODE) && (node1 != node2))
2127d0d3945SLi Zefan nr_cross_allocs++;
2137d0d3945SLi Zefan }
2140f7d2f1bSArnaldo Carvalho de Melo
215dce088abSLeo Yan return 0;
216ba77c9e1SLi Zefan }
217ba77c9e1SLi Zefan
218fb4f313dSNamhyung Kim static int ptr_cmp(void *, void *);
219fb4f313dSNamhyung Kim static int slab_callsite_cmp(void *, void *);
220079d3f65SLi Zefan
search_alloc_stat(unsigned long ptr,unsigned long call_site,struct rb_root * root,sort_fn_t sort_fn)221079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr,
222079d3f65SLi Zefan unsigned long call_site,
223079d3f65SLi Zefan struct rb_root *root,
224079d3f65SLi Zefan sort_fn_t sort_fn)
225079d3f65SLi Zefan {
226079d3f65SLi Zefan struct rb_node *node = root->rb_node;
227079d3f65SLi Zefan struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
228079d3f65SLi Zefan
229079d3f65SLi Zefan while (node) {
230079d3f65SLi Zefan struct alloc_stat *data;
231079d3f65SLi Zefan int cmp;
232079d3f65SLi Zefan
233079d3f65SLi Zefan data = rb_entry(node, struct alloc_stat, node);
234079d3f65SLi Zefan
235079d3f65SLi Zefan cmp = sort_fn(&key, data);
236079d3f65SLi Zefan if (cmp < 0)
237079d3f65SLi Zefan node = node->rb_left;
238079d3f65SLi Zefan else if (cmp > 0)
239079d3f65SLi Zefan node = node->rb_right;
240079d3f65SLi Zefan else
241079d3f65SLi Zefan return data;
242079d3f65SLi Zefan }
243079d3f65SLi Zefan return NULL;
244079d3f65SLi Zefan }
245079d3f65SLi Zefan
evsel__process_free_event(struct evsel * evsel,struct perf_sample * sample)2468cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample)
247ba77c9e1SLi Zefan {
248efc0cdc9SArnaldo Carvalho de Melo unsigned long ptr = evsel__intval(evsel, sample, "ptr");
249079d3f65SLi Zefan struct alloc_stat *s_alloc, *s_caller;
250079d3f65SLi Zefan
251079d3f65SLi Zefan s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
252079d3f65SLi Zefan if (!s_alloc)
2532814eb05SArnaldo Carvalho de Melo return 0;
254079d3f65SLi Zefan
255aa58e9afSDavid Ahern total_freed += s_alloc->last_alloc;
256aa58e9afSDavid Ahern
25722ad798cSArnaldo Carvalho de Melo if ((short)sample->cpu != s_alloc->alloc_cpu) {
258079d3f65SLi Zefan s_alloc->pingpong++;
259079d3f65SLi Zefan
260079d3f65SLi Zefan s_caller = search_alloc_stat(0, s_alloc->call_site,
261fb4f313dSNamhyung Kim &root_caller_stat,
262fb4f313dSNamhyung Kim slab_callsite_cmp);
2632814eb05SArnaldo Carvalho de Melo if (!s_caller)
2642814eb05SArnaldo Carvalho de Melo return -1;
265079d3f65SLi Zefan s_caller->pingpong++;
266079d3f65SLi Zefan }
267079d3f65SLi Zefan s_alloc->alloc_cpu = -1;
2682814eb05SArnaldo Carvalho de Melo
2692814eb05SArnaldo Carvalho de Melo return 0;
270ba77c9e1SLi Zefan }
271ba77c9e1SLi Zefan
2720d68bc92SNamhyung Kim static u64 total_page_alloc_bytes;
2730d68bc92SNamhyung Kim static u64 total_page_free_bytes;
2740d68bc92SNamhyung Kim static u64 total_page_nomatch_bytes;
2750d68bc92SNamhyung Kim static u64 total_page_fail_bytes;
2760d68bc92SNamhyung Kim static unsigned long nr_page_allocs;
2770d68bc92SNamhyung Kim static unsigned long nr_page_frees;
2780d68bc92SNamhyung Kim static unsigned long nr_page_fails;
2790d68bc92SNamhyung Kim static unsigned long nr_page_nomatch;
2800d68bc92SNamhyung Kim
2810d68bc92SNamhyung Kim static bool use_pfn;
2822a7ef02cSNamhyung Kim static bool live_page;
283c9758cc4SNamhyung Kim static struct perf_session *kmem_session;
2840d68bc92SNamhyung Kim
2850d68bc92SNamhyung Kim #define MAX_MIGRATE_TYPES 6
2860d68bc92SNamhyung Kim #define MAX_PAGE_ORDER 11
2870d68bc92SNamhyung Kim
2880d68bc92SNamhyung Kim static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
2890d68bc92SNamhyung Kim
2900d68bc92SNamhyung Kim struct page_stat {
2910d68bc92SNamhyung Kim struct rb_node node;
2920d68bc92SNamhyung Kim u64 page;
293c9758cc4SNamhyung Kim u64 callsite;
2940d68bc92SNamhyung Kim int order;
2950d68bc92SNamhyung Kim unsigned gfp_flags;
2960d68bc92SNamhyung Kim unsigned migrate_type;
2970d68bc92SNamhyung Kim u64 alloc_bytes;
2980d68bc92SNamhyung Kim u64 free_bytes;
2990d68bc92SNamhyung Kim int nr_alloc;
3000d68bc92SNamhyung Kim int nr_free;
3010d68bc92SNamhyung Kim };
3020d68bc92SNamhyung Kim
3032a7ef02cSNamhyung Kim static struct rb_root page_live_tree;
3040d68bc92SNamhyung Kim static struct rb_root page_alloc_tree;
3050d68bc92SNamhyung Kim static struct rb_root page_alloc_sorted;
306c9758cc4SNamhyung Kim static struct rb_root page_caller_tree;
307c9758cc4SNamhyung Kim static struct rb_root page_caller_sorted;
3080d68bc92SNamhyung Kim
309c9758cc4SNamhyung Kim struct alloc_func {
310c9758cc4SNamhyung Kim u64 start;
311c9758cc4SNamhyung Kim u64 end;
312c9758cc4SNamhyung Kim char *name;
313c9758cc4SNamhyung Kim };
314c9758cc4SNamhyung Kim
315c9758cc4SNamhyung Kim static int nr_alloc_funcs;
316c9758cc4SNamhyung Kim static struct alloc_func *alloc_func_list;
317c9758cc4SNamhyung Kim
funcmp(const void * a,const void * b)318c9758cc4SNamhyung Kim static int funcmp(const void *a, const void *b)
319c9758cc4SNamhyung Kim {
320c9758cc4SNamhyung Kim const struct alloc_func *fa = a;
321c9758cc4SNamhyung Kim const struct alloc_func *fb = b;
322c9758cc4SNamhyung Kim
323c9758cc4SNamhyung Kim if (fa->start > fb->start)
324c9758cc4SNamhyung Kim return 1;
325c9758cc4SNamhyung Kim else
326c9758cc4SNamhyung Kim return -1;
327c9758cc4SNamhyung Kim }
328c9758cc4SNamhyung Kim
callcmp(const void * a,const void * b)329c9758cc4SNamhyung Kim static int callcmp(const void *a, const void *b)
330c9758cc4SNamhyung Kim {
331c9758cc4SNamhyung Kim const struct alloc_func *fa = a;
332c9758cc4SNamhyung Kim const struct alloc_func *fb = b;
333c9758cc4SNamhyung Kim
334c9758cc4SNamhyung Kim if (fb->start <= fa->start && fa->end < fb->end)
335c9758cc4SNamhyung Kim return 0;
336c9758cc4SNamhyung Kim
337c9758cc4SNamhyung Kim if (fa->start > fb->start)
338c9758cc4SNamhyung Kim return 1;
339c9758cc4SNamhyung Kim else
340c9758cc4SNamhyung Kim return -1;
341c9758cc4SNamhyung Kim }
342c9758cc4SNamhyung Kim
build_alloc_func_list(void)343c9758cc4SNamhyung Kim static int build_alloc_func_list(void)
344c9758cc4SNamhyung Kim {
345c9758cc4SNamhyung Kim int ret;
346c9758cc4SNamhyung Kim struct map *kernel_map;
347c9758cc4SNamhyung Kim struct symbol *sym;
348c9758cc4SNamhyung Kim struct rb_node *node;
349c9758cc4SNamhyung Kim struct alloc_func *func;
350c9758cc4SNamhyung Kim struct machine *machine = &kmem_session->machines.host;
351c9758cc4SNamhyung Kim regex_t alloc_func_regex;
35249b8e2beSRasmus Villemoes static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
353c9758cc4SNamhyung Kim
354c9758cc4SNamhyung Kim ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
355c9758cc4SNamhyung Kim if (ret) {
356c9758cc4SNamhyung Kim char err[BUFSIZ];
357c9758cc4SNamhyung Kim
358c9758cc4SNamhyung Kim regerror(ret, &alloc_func_regex, err, sizeof(err));
359c9758cc4SNamhyung Kim pr_err("Invalid regex: %s\n%s", pattern, err);
360c9758cc4SNamhyung Kim return -EINVAL;
361c9758cc4SNamhyung Kim }
362c9758cc4SNamhyung Kim
363a5e813c6SArnaldo Carvalho de Melo kernel_map = machine__kernel_map(machine);
364be39db9fSArnaldo Carvalho de Melo if (map__load(kernel_map) < 0) {
365c9758cc4SNamhyung Kim pr_err("cannot load kernel map\n");
366c9758cc4SNamhyung Kim return -ENOENT;
367c9758cc4SNamhyung Kim }
368c9758cc4SNamhyung Kim
369c9758cc4SNamhyung Kim map__for_each_symbol(kernel_map, sym, node) {
370c9758cc4SNamhyung Kim if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
371c9758cc4SNamhyung Kim continue;
372c9758cc4SNamhyung Kim
373c9758cc4SNamhyung Kim func = realloc(alloc_func_list,
374c9758cc4SNamhyung Kim (nr_alloc_funcs + 1) * sizeof(*func));
375c9758cc4SNamhyung Kim if (func == NULL)
376c9758cc4SNamhyung Kim return -ENOMEM;
377c9758cc4SNamhyung Kim
378c9758cc4SNamhyung Kim pr_debug("alloc func: %s\n", sym->name);
379c9758cc4SNamhyung Kim func[nr_alloc_funcs].start = sym->start;
380c9758cc4SNamhyung Kim func[nr_alloc_funcs].end = sym->end;
381c9758cc4SNamhyung Kim func[nr_alloc_funcs].name = sym->name;
382c9758cc4SNamhyung Kim
383c9758cc4SNamhyung Kim alloc_func_list = func;
384c9758cc4SNamhyung Kim nr_alloc_funcs++;
385c9758cc4SNamhyung Kim }
386c9758cc4SNamhyung Kim
387c9758cc4SNamhyung Kim qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
388c9758cc4SNamhyung Kim
389c9758cc4SNamhyung Kim regfree(&alloc_func_regex);
390c9758cc4SNamhyung Kim return 0;
391c9758cc4SNamhyung Kim }
392c9758cc4SNamhyung Kim
393c9758cc4SNamhyung Kim /*
394c9758cc4SNamhyung Kim * Find first non-memory allocation function from callchain.
395c9758cc4SNamhyung Kim * The allocation functions are in the 'alloc_func_list'.
396c9758cc4SNamhyung Kim */
find_callsite(struct evsel * evsel,struct perf_sample * sample)39732dcd021SJiri Olsa static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
398c9758cc4SNamhyung Kim {
399c9758cc4SNamhyung Kim struct addr_location al;
400c9758cc4SNamhyung Kim struct machine *machine = &kmem_session->machines.host;
401c9758cc4SNamhyung Kim struct callchain_cursor_node *node;
4028ab12a20SIan Rogers struct callchain_cursor *cursor;
4030dd5041cSIan Rogers u64 result = sample->ip;
404c9758cc4SNamhyung Kim
4050dd5041cSIan Rogers addr_location__init(&al);
406c9758cc4SNamhyung Kim if (alloc_func_list == NULL) {
407c9758cc4SNamhyung Kim if (build_alloc_func_list() < 0)
408c9758cc4SNamhyung Kim goto out;
409c9758cc4SNamhyung Kim }
410c9758cc4SNamhyung Kim
411c9758cc4SNamhyung Kim al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
412c9758cc4SNamhyung Kim
4138ab12a20SIan Rogers cursor = get_tls_callchain_cursor();
4148ab12a20SIan Rogers if (cursor == NULL)
4158ab12a20SIan Rogers goto out;
4168ab12a20SIan Rogers
4178ab12a20SIan Rogers sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16);
4188ab12a20SIan Rogers
4198ab12a20SIan Rogers callchain_cursor_commit(cursor);
420c9758cc4SNamhyung Kim while (true) {
421c9758cc4SNamhyung Kim struct alloc_func key, *caller;
422c9758cc4SNamhyung Kim u64 addr;
423c9758cc4SNamhyung Kim
4248ab12a20SIan Rogers node = callchain_cursor_current(cursor);
425c9758cc4SNamhyung Kim if (node == NULL)
426c9758cc4SNamhyung Kim break;
427c9758cc4SNamhyung Kim
428c9758cc4SNamhyung Kim key.start = key.end = node->ip;
429c9758cc4SNamhyung Kim caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
430c9758cc4SNamhyung Kim sizeof(key), callcmp);
431c9758cc4SNamhyung Kim if (!caller) {
432c9758cc4SNamhyung Kim /* found */
4335f0fef8aSArnaldo Carvalho de Melo if (node->ms.map)
4340e6aa013SIan Rogers addr = map__dso_unmap_ip(node->ms.map, node->ip);
435c9758cc4SNamhyung Kim else
436c9758cc4SNamhyung Kim addr = node->ip;
437c9758cc4SNamhyung Kim
4380dd5041cSIan Rogers result = addr;
4390dd5041cSIan Rogers goto out;
440c9758cc4SNamhyung Kim } else
441c9758cc4SNamhyung Kim pr_debug3("skipping alloc function: %s\n", caller->name);
442c9758cc4SNamhyung Kim
4438ab12a20SIan Rogers callchain_cursor_advance(cursor);
444c9758cc4SNamhyung Kim }
445c9758cc4SNamhyung Kim
446c9758cc4SNamhyung Kim pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
4470dd5041cSIan Rogers out:
4480dd5041cSIan Rogers addr_location__exit(&al);
4490dd5041cSIan Rogers return result;
450c9758cc4SNamhyung Kim }
451c9758cc4SNamhyung Kim
4522a7ef02cSNamhyung Kim struct sort_dimension {
4532a7ef02cSNamhyung Kim const char name[20];
4542a7ef02cSNamhyung Kim sort_fn_t cmp;
4552a7ef02cSNamhyung Kim struct list_head list;
4562a7ef02cSNamhyung Kim };
4572a7ef02cSNamhyung Kim
4582a7ef02cSNamhyung Kim static LIST_HEAD(page_alloc_sort_input);
4592a7ef02cSNamhyung Kim static LIST_HEAD(page_caller_sort_input);
4602a7ef02cSNamhyung Kim
461c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_page(struct page_stat * pstat,bool create)4622a7ef02cSNamhyung Kim __page_stat__findnew_page(struct page_stat *pstat, bool create)
4630d68bc92SNamhyung Kim {
4642a7ef02cSNamhyung Kim struct rb_node **node = &page_live_tree.rb_node;
4650d68bc92SNamhyung Kim struct rb_node *parent = NULL;
4660d68bc92SNamhyung Kim struct page_stat *data;
4670d68bc92SNamhyung Kim
4680d68bc92SNamhyung Kim while (*node) {
4690d68bc92SNamhyung Kim s64 cmp;
4700d68bc92SNamhyung Kim
4710d68bc92SNamhyung Kim parent = *node;
4720d68bc92SNamhyung Kim data = rb_entry(*node, struct page_stat, node);
4730d68bc92SNamhyung Kim
4742a7ef02cSNamhyung Kim cmp = data->page - pstat->page;
4750d68bc92SNamhyung Kim if (cmp < 0)
4760d68bc92SNamhyung Kim node = &parent->rb_left;
4770d68bc92SNamhyung Kim else if (cmp > 0)
4780d68bc92SNamhyung Kim node = &parent->rb_right;
4790d68bc92SNamhyung Kim else
4800d68bc92SNamhyung Kim return data;
4810d68bc92SNamhyung Kim }
4820d68bc92SNamhyung Kim
4830d68bc92SNamhyung Kim if (!create)
4840d68bc92SNamhyung Kim return NULL;
4850d68bc92SNamhyung Kim
4860d68bc92SNamhyung Kim data = zalloc(sizeof(*data));
4870d68bc92SNamhyung Kim if (data != NULL) {
4882a7ef02cSNamhyung Kim data->page = pstat->page;
4892a7ef02cSNamhyung Kim data->order = pstat->order;
4902a7ef02cSNamhyung Kim data->gfp_flags = pstat->gfp_flags;
4912a7ef02cSNamhyung Kim data->migrate_type = pstat->migrate_type;
4920d68bc92SNamhyung Kim
4930d68bc92SNamhyung Kim rb_link_node(&data->node, parent, node);
4942a7ef02cSNamhyung Kim rb_insert_color(&data->node, &page_live_tree);
4950d68bc92SNamhyung Kim }
4960d68bc92SNamhyung Kim
4970d68bc92SNamhyung Kim return data;
4980d68bc92SNamhyung Kim }
4990d68bc92SNamhyung Kim
page_stat__find_page(struct page_stat * pstat)5002a7ef02cSNamhyung Kim static struct page_stat *page_stat__find_page(struct page_stat *pstat)
501c9758cc4SNamhyung Kim {
5022a7ef02cSNamhyung Kim return __page_stat__findnew_page(pstat, false);
503c9758cc4SNamhyung Kim }
504c9758cc4SNamhyung Kim
page_stat__findnew_page(struct page_stat * pstat)5052a7ef02cSNamhyung Kim static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
506c9758cc4SNamhyung Kim {
5072a7ef02cSNamhyung Kim return __page_stat__findnew_page(pstat, true);
508c9758cc4SNamhyung Kim }
509c9758cc4SNamhyung Kim
510c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_alloc(struct page_stat * pstat,bool create)511c9758cc4SNamhyung Kim __page_stat__findnew_alloc(struct page_stat *pstat, bool create)
5120d68bc92SNamhyung Kim {
5130d68bc92SNamhyung Kim struct rb_node **node = &page_alloc_tree.rb_node;
5140d68bc92SNamhyung Kim struct rb_node *parent = NULL;
5150d68bc92SNamhyung Kim struct page_stat *data;
516fb4f313dSNamhyung Kim struct sort_dimension *sort;
5170d68bc92SNamhyung Kim
5180d68bc92SNamhyung Kim while (*node) {
519fb4f313dSNamhyung Kim int cmp = 0;
5200d68bc92SNamhyung Kim
5210d68bc92SNamhyung Kim parent = *node;
5220d68bc92SNamhyung Kim data = rb_entry(*node, struct page_stat, node);
5230d68bc92SNamhyung Kim
524fb4f313dSNamhyung Kim list_for_each_entry(sort, &page_alloc_sort_input, list) {
525fb4f313dSNamhyung Kim cmp = sort->cmp(pstat, data);
526fb4f313dSNamhyung Kim if (cmp)
527fb4f313dSNamhyung Kim break;
528fb4f313dSNamhyung Kim }
529fb4f313dSNamhyung Kim
5300d68bc92SNamhyung Kim if (cmp < 0)
5310d68bc92SNamhyung Kim node = &parent->rb_left;
5320d68bc92SNamhyung Kim else if (cmp > 0)
5330d68bc92SNamhyung Kim node = &parent->rb_right;
5340d68bc92SNamhyung Kim else
5350d68bc92SNamhyung Kim return data;
5360d68bc92SNamhyung Kim }
5370d68bc92SNamhyung Kim
5380d68bc92SNamhyung Kim if (!create)
5390d68bc92SNamhyung Kim return NULL;
5400d68bc92SNamhyung Kim
5410d68bc92SNamhyung Kim data = zalloc(sizeof(*data));
5420d68bc92SNamhyung Kim if (data != NULL) {
5436b1a2752SDavid Ahern data->page = pstat->page;
5446b1a2752SDavid Ahern data->order = pstat->order;
5456b1a2752SDavid Ahern data->gfp_flags = pstat->gfp_flags;
5466b1a2752SDavid Ahern data->migrate_type = pstat->migrate_type;
5470d68bc92SNamhyung Kim
5480d68bc92SNamhyung Kim rb_link_node(&data->node, parent, node);
5490d68bc92SNamhyung Kim rb_insert_color(&data->node, &page_alloc_tree);
5500d68bc92SNamhyung Kim }
5510d68bc92SNamhyung Kim
5520d68bc92SNamhyung Kim return data;
5530d68bc92SNamhyung Kim }
5540d68bc92SNamhyung Kim
page_stat__find_alloc(struct page_stat * pstat)555c9758cc4SNamhyung Kim static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
556c9758cc4SNamhyung Kim {
557c9758cc4SNamhyung Kim return __page_stat__findnew_alloc(pstat, false);
558c9758cc4SNamhyung Kim }
559c9758cc4SNamhyung Kim
page_stat__findnew_alloc(struct page_stat * pstat)560c9758cc4SNamhyung Kim static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
561c9758cc4SNamhyung Kim {
562c9758cc4SNamhyung Kim return __page_stat__findnew_alloc(pstat, true);
563c9758cc4SNamhyung Kim }
564c9758cc4SNamhyung Kim
565c9758cc4SNamhyung Kim static struct page_stat *
__page_stat__findnew_caller(struct page_stat * pstat,bool create)566fb4f313dSNamhyung Kim __page_stat__findnew_caller(struct page_stat *pstat, bool create)
567c9758cc4SNamhyung Kim {
568c9758cc4SNamhyung Kim struct rb_node **node = &page_caller_tree.rb_node;
569c9758cc4SNamhyung Kim struct rb_node *parent = NULL;
570c9758cc4SNamhyung Kim struct page_stat *data;
571fb4f313dSNamhyung Kim struct sort_dimension *sort;
572c9758cc4SNamhyung Kim
573c9758cc4SNamhyung Kim while (*node) {
574fb4f313dSNamhyung Kim int cmp = 0;
575c9758cc4SNamhyung Kim
576c9758cc4SNamhyung Kim parent = *node;
577c9758cc4SNamhyung Kim data = rb_entry(*node, struct page_stat, node);
578c9758cc4SNamhyung Kim
579fb4f313dSNamhyung Kim list_for_each_entry(sort, &page_caller_sort_input, list) {
580fb4f313dSNamhyung Kim cmp = sort->cmp(pstat, data);
581fb4f313dSNamhyung Kim if (cmp)
582fb4f313dSNamhyung Kim break;
583fb4f313dSNamhyung Kim }
584fb4f313dSNamhyung Kim
585c9758cc4SNamhyung Kim if (cmp < 0)
586c9758cc4SNamhyung Kim node = &parent->rb_left;
587c9758cc4SNamhyung Kim else if (cmp > 0)
588c9758cc4SNamhyung Kim node = &parent->rb_right;
589c9758cc4SNamhyung Kim else
590c9758cc4SNamhyung Kim return data;
591c9758cc4SNamhyung Kim }
592c9758cc4SNamhyung Kim
593c9758cc4SNamhyung Kim if (!create)
594c9758cc4SNamhyung Kim return NULL;
595c9758cc4SNamhyung Kim
596c9758cc4SNamhyung Kim data = zalloc(sizeof(*data));
597c9758cc4SNamhyung Kim if (data != NULL) {
598fb4f313dSNamhyung Kim data->callsite = pstat->callsite;
599fb4f313dSNamhyung Kim data->order = pstat->order;
600fb4f313dSNamhyung Kim data->gfp_flags = pstat->gfp_flags;
601fb4f313dSNamhyung Kim data->migrate_type = pstat->migrate_type;
602c9758cc4SNamhyung Kim
603c9758cc4SNamhyung Kim rb_link_node(&data->node, parent, node);
604c9758cc4SNamhyung Kim rb_insert_color(&data->node, &page_caller_tree);
605c9758cc4SNamhyung Kim }
606c9758cc4SNamhyung Kim
607c9758cc4SNamhyung Kim return data;
608c9758cc4SNamhyung Kim }
609c9758cc4SNamhyung Kim
page_stat__find_caller(struct page_stat * pstat)610fb4f313dSNamhyung Kim static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
611c9758cc4SNamhyung Kim {
612fb4f313dSNamhyung Kim return __page_stat__findnew_caller(pstat, false);
613c9758cc4SNamhyung Kim }
614c9758cc4SNamhyung Kim
page_stat__findnew_caller(struct page_stat * pstat)615fb4f313dSNamhyung Kim static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
616c9758cc4SNamhyung Kim {
617fb4f313dSNamhyung Kim return __page_stat__findnew_caller(pstat, true);
618c9758cc4SNamhyung Kim }
619c9758cc4SNamhyung Kim
valid_page(u64 pfn_or_page)6200d68bc92SNamhyung Kim static bool valid_page(u64 pfn_or_page)
6210d68bc92SNamhyung Kim {
6220d68bc92SNamhyung Kim if (use_pfn && pfn_or_page == -1UL)
6230d68bc92SNamhyung Kim return false;
6240d68bc92SNamhyung Kim if (!use_pfn && pfn_or_page == 0)
6250d68bc92SNamhyung Kim return false;
6260d68bc92SNamhyung Kim return true;
6270d68bc92SNamhyung Kim }
6280d68bc92SNamhyung Kim
6290e111156SNamhyung Kim struct gfp_flag {
6300e111156SNamhyung Kim unsigned int flags;
6310e111156SNamhyung Kim char *compact_str;
6320e111156SNamhyung Kim char *human_readable;
6330e111156SNamhyung Kim };
6340e111156SNamhyung Kim
6350e111156SNamhyung Kim static struct gfp_flag *gfps;
6360e111156SNamhyung Kim static int nr_gfps;
6370e111156SNamhyung Kim
gfpcmp(const void * a,const void * b)6380e111156SNamhyung Kim static int gfpcmp(const void *a, const void *b)
6390e111156SNamhyung Kim {
6400e111156SNamhyung Kim const struct gfp_flag *fa = a;
6410e111156SNamhyung Kim const struct gfp_flag *fb = b;
6420e111156SNamhyung Kim
6430e111156SNamhyung Kim return fa->flags - fb->flags;
6440e111156SNamhyung Kim }
6450e111156SNamhyung Kim
646420adbe9SVlastimil Babka /* see include/trace/events/mmflags.h */
6470e111156SNamhyung Kim static const struct {
6480e111156SNamhyung Kim const char *original;
6490e111156SNamhyung Kim const char *compact;
6500e111156SNamhyung Kim } gfp_compact_table[] = {
6510e111156SNamhyung Kim { "GFP_TRANSHUGE", "THP" },
65225160354SVlastimil Babka { "GFP_TRANSHUGE_LIGHT", "THL" },
6530e111156SNamhyung Kim { "GFP_HIGHUSER_MOVABLE", "HUM" },
6540e111156SNamhyung Kim { "GFP_HIGHUSER", "HU" },
6550e111156SNamhyung Kim { "GFP_USER", "U" },
65614e0a214SVlastimil Babka { "GFP_KERNEL_ACCOUNT", "KAC" },
6570e111156SNamhyung Kim { "GFP_KERNEL", "K" },
6580e111156SNamhyung Kim { "GFP_NOFS", "NF" },
6590e111156SNamhyung Kim { "GFP_ATOMIC", "A" },
6600e111156SNamhyung Kim { "GFP_NOIO", "NI" },
6610e111156SNamhyung Kim { "GFP_NOWAIT", "NW" },
66214e0a214SVlastimil Babka { "GFP_DMA", "D" },
66314e0a214SVlastimil Babka { "__GFP_HIGHMEM", "HM" },
66414e0a214SVlastimil Babka { "GFP_DMA32", "D32" },
66514e0a214SVlastimil Babka { "__GFP_HIGH", "H" },
66614e0a214SVlastimil Babka { "__GFP_IO", "I" },
66714e0a214SVlastimil Babka { "__GFP_FS", "F" },
66814e0a214SVlastimil Babka { "__GFP_NOWARN", "NWR" },
669dcda9b04SMichal Hocko { "__GFP_RETRY_MAYFAIL", "R" },
67014e0a214SVlastimil Babka { "__GFP_NOFAIL", "NF" },
67114e0a214SVlastimil Babka { "__GFP_NORETRY", "NR" },
67214e0a214SVlastimil Babka { "__GFP_COMP", "C" },
67314e0a214SVlastimil Babka { "__GFP_ZERO", "Z" },
67414e0a214SVlastimil Babka { "__GFP_NOMEMALLOC", "NMA" },
67514e0a214SVlastimil Babka { "__GFP_MEMALLOC", "MA" },
67614e0a214SVlastimil Babka { "__GFP_HARDWALL", "HW" },
67714e0a214SVlastimil Babka { "__GFP_THISNODE", "TN" },
67814e0a214SVlastimil Babka { "__GFP_RECLAIMABLE", "RC" },
67914e0a214SVlastimil Babka { "__GFP_MOVABLE", "M" },
68014e0a214SVlastimil Babka { "__GFP_ACCOUNT", "AC" },
68114e0a214SVlastimil Babka { "__GFP_WRITE", "WR" },
68214e0a214SVlastimil Babka { "__GFP_RECLAIM", "R" },
68314e0a214SVlastimil Babka { "__GFP_DIRECT_RECLAIM", "DR" },
68414e0a214SVlastimil Babka { "__GFP_KSWAPD_RECLAIM", "KR" },
6850e111156SNamhyung Kim };
6860e111156SNamhyung Kim
6870e111156SNamhyung Kim static size_t max_gfp_len;
6880e111156SNamhyung Kim
compact_gfp_flags(char * gfp_flags)6890e111156SNamhyung Kim static char *compact_gfp_flags(char *gfp_flags)
6900e111156SNamhyung Kim {
6910e111156SNamhyung Kim char *orig_flags = strdup(gfp_flags);
6920e111156SNamhyung Kim char *new_flags = NULL;
693b2365122SArnaldo Carvalho de Melo char *str, *pos = NULL;
6940e111156SNamhyung Kim size_t len = 0;
6950e111156SNamhyung Kim
6960e111156SNamhyung Kim if (orig_flags == NULL)
6970e111156SNamhyung Kim return NULL;
6980e111156SNamhyung Kim
6990e111156SNamhyung Kim str = strtok_r(orig_flags, "|", &pos);
7000e111156SNamhyung Kim while (str) {
7010e111156SNamhyung Kim size_t i;
7020e111156SNamhyung Kim char *new;
7030e111156SNamhyung Kim const char *cpt;
7040e111156SNamhyung Kim
7050e111156SNamhyung Kim for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
7060e111156SNamhyung Kim if (strcmp(gfp_compact_table[i].original, str))
7070e111156SNamhyung Kim continue;
7080e111156SNamhyung Kim
7090e111156SNamhyung Kim cpt = gfp_compact_table[i].compact;
7100e111156SNamhyung Kim new = realloc(new_flags, len + strlen(cpt) + 2);
7110e111156SNamhyung Kim if (new == NULL) {
7120e111156SNamhyung Kim free(new_flags);
7131abecfcaSYunfeng Ye free(orig_flags);
7140e111156SNamhyung Kim return NULL;
7150e111156SNamhyung Kim }
7160e111156SNamhyung Kim
7170e111156SNamhyung Kim new_flags = new;
7180e111156SNamhyung Kim
7190e111156SNamhyung Kim if (!len) {
7200e111156SNamhyung Kim strcpy(new_flags, cpt);
7210e111156SNamhyung Kim } else {
7220e111156SNamhyung Kim strcat(new_flags, "|");
7230e111156SNamhyung Kim strcat(new_flags, cpt);
7240e111156SNamhyung Kim len++;
7250e111156SNamhyung Kim }
7260e111156SNamhyung Kim
7270e111156SNamhyung Kim len += strlen(cpt);
7280e111156SNamhyung Kim }
7290e111156SNamhyung Kim
7300e111156SNamhyung Kim str = strtok_r(NULL, "|", &pos);
7310e111156SNamhyung Kim }
7320e111156SNamhyung Kim
7330e111156SNamhyung Kim if (max_gfp_len < len)
7340e111156SNamhyung Kim max_gfp_len = len;
7350e111156SNamhyung Kim
7360e111156SNamhyung Kim free(orig_flags);
7370e111156SNamhyung Kim return new_flags;
7380e111156SNamhyung Kim }
7390e111156SNamhyung Kim
compact_gfp_string(unsigned long gfp_flags)7400e111156SNamhyung Kim static char *compact_gfp_string(unsigned long gfp_flags)
7410e111156SNamhyung Kim {
7420e111156SNamhyung Kim struct gfp_flag key = {
7430e111156SNamhyung Kim .flags = gfp_flags,
7440e111156SNamhyung Kim };
7450e111156SNamhyung Kim struct gfp_flag *gfp;
7460e111156SNamhyung Kim
7470e111156SNamhyung Kim gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
7480e111156SNamhyung Kim if (gfp)
7490e111156SNamhyung Kim return gfp->compact_str;
7500e111156SNamhyung Kim
7510e111156SNamhyung Kim return NULL;
7520e111156SNamhyung Kim }
7530e111156SNamhyung Kim
parse_gfp_flags(struct evsel * evsel,struct perf_sample * sample,unsigned int gfp_flags)75432dcd021SJiri Olsa static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
7550e111156SNamhyung Kim unsigned int gfp_flags)
7560e111156SNamhyung Kim {
757cbc49b25STzvetomir Stoyanov (VMware) struct tep_record record = {
7580e111156SNamhyung Kim .cpu = sample->cpu,
7590e111156SNamhyung Kim .data = sample->raw_data,
7600e111156SNamhyung Kim .size = sample->raw_size,
7610e111156SNamhyung Kim };
7620e111156SNamhyung Kim struct trace_seq seq;
76308a9b985SArnaldo Carvalho de Melo char *str, *pos = NULL;
7640e111156SNamhyung Kim
7650e111156SNamhyung Kim if (nr_gfps) {
7660e111156SNamhyung Kim struct gfp_flag key = {
7670e111156SNamhyung Kim .flags = gfp_flags,
7680e111156SNamhyung Kim };
7690e111156SNamhyung Kim
7700e111156SNamhyung Kim if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
7710e111156SNamhyung Kim return 0;
7720e111156SNamhyung Kim }
7730e111156SNamhyung Kim
7740e111156SNamhyung Kim trace_seq_init(&seq);
77538847db9STzvetomir Stoyanov tep_print_event(evsel->tp_format->tep,
77638847db9STzvetomir Stoyanov &seq, &record, "%s", TEP_PRINT_INFO);
7770e111156SNamhyung Kim
7780e111156SNamhyung Kim str = strtok_r(seq.buffer, " ", &pos);
7790e111156SNamhyung Kim while (str) {
7800e111156SNamhyung Kim if (!strncmp(str, "gfp_flags=", 10)) {
7810e111156SNamhyung Kim struct gfp_flag *new;
7820e111156SNamhyung Kim
7830e111156SNamhyung Kim new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
7840e111156SNamhyung Kim if (new == NULL)
7850e111156SNamhyung Kim return -ENOMEM;
7860e111156SNamhyung Kim
7870e111156SNamhyung Kim gfps = new;
7880e111156SNamhyung Kim new += nr_gfps++;
7890e111156SNamhyung Kim
7900e111156SNamhyung Kim new->flags = gfp_flags;
7910e111156SNamhyung Kim new->human_readable = strdup(str + 10);
7920e111156SNamhyung Kim new->compact_str = compact_gfp_flags(str + 10);
7930e111156SNamhyung Kim if (!new->human_readable || !new->compact_str)
7940e111156SNamhyung Kim return -ENOMEM;
7950e111156SNamhyung Kim
7960e111156SNamhyung Kim qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
7970e111156SNamhyung Kim }
7980e111156SNamhyung Kim
7990e111156SNamhyung Kim str = strtok_r(NULL, " ", &pos);
8000e111156SNamhyung Kim }
8010e111156SNamhyung Kim
8020e111156SNamhyung Kim trace_seq_destroy(&seq);
8030e111156SNamhyung Kim return 0;
8040e111156SNamhyung Kim }
8050e111156SNamhyung Kim
evsel__process_page_alloc_event(struct evsel * evsel,struct perf_sample * sample)8068cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample)
8070d68bc92SNamhyung Kim {
8080d68bc92SNamhyung Kim u64 page;
809efc0cdc9SArnaldo Carvalho de Melo unsigned int order = evsel__intval(evsel, sample, "order");
810efc0cdc9SArnaldo Carvalho de Melo unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags");
811efc0cdc9SArnaldo Carvalho de Melo unsigned int migrate_type = evsel__intval(evsel, sample,
8120d68bc92SNamhyung Kim "migratetype");
8130d68bc92SNamhyung Kim u64 bytes = kmem_page_size << order;
814c9758cc4SNamhyung Kim u64 callsite;
8156b1a2752SDavid Ahern struct page_stat *pstat;
8160d68bc92SNamhyung Kim struct page_stat this = {
8170d68bc92SNamhyung Kim .order = order,
8180d68bc92SNamhyung Kim .gfp_flags = gfp_flags,
8190d68bc92SNamhyung Kim .migrate_type = migrate_type,
8200d68bc92SNamhyung Kim };
8210d68bc92SNamhyung Kim
8220d68bc92SNamhyung Kim if (use_pfn)
823efc0cdc9SArnaldo Carvalho de Melo page = evsel__intval(evsel, sample, "pfn");
8240d68bc92SNamhyung Kim else
825efc0cdc9SArnaldo Carvalho de Melo page = evsel__intval(evsel, sample, "page");
8260d68bc92SNamhyung Kim
8270d68bc92SNamhyung Kim nr_page_allocs++;
8280d68bc92SNamhyung Kim total_page_alloc_bytes += bytes;
8290d68bc92SNamhyung Kim
8300d68bc92SNamhyung Kim if (!valid_page(page)) {
8310d68bc92SNamhyung Kim nr_page_fails++;
8320d68bc92SNamhyung Kim total_page_fail_bytes += bytes;
8330d68bc92SNamhyung Kim
8340d68bc92SNamhyung Kim return 0;
8350d68bc92SNamhyung Kim }
8360d68bc92SNamhyung Kim
8370e111156SNamhyung Kim if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
8380e111156SNamhyung Kim return -1;
8390e111156SNamhyung Kim
840c9758cc4SNamhyung Kim callsite = find_callsite(evsel, sample);
841c9758cc4SNamhyung Kim
8420d68bc92SNamhyung Kim /*
8430d68bc92SNamhyung Kim * This is to find the current page (with correct gfp flags and
8440d68bc92SNamhyung Kim * migrate type) at free event.
8450d68bc92SNamhyung Kim */
8462a7ef02cSNamhyung Kim this.page = page;
8472a7ef02cSNamhyung Kim pstat = page_stat__findnew_page(&this);
8486b1a2752SDavid Ahern if (pstat == NULL)
8490d68bc92SNamhyung Kim return -ENOMEM;
8500d68bc92SNamhyung Kim
8512a7ef02cSNamhyung Kim pstat->nr_alloc++;
8522a7ef02cSNamhyung Kim pstat->alloc_bytes += bytes;
853c9758cc4SNamhyung Kim pstat->callsite = callsite;
8540d68bc92SNamhyung Kim
8552a7ef02cSNamhyung Kim if (!live_page) {
856c9758cc4SNamhyung Kim pstat = page_stat__findnew_alloc(&this);
8576b1a2752SDavid Ahern if (pstat == NULL)
8580d68bc92SNamhyung Kim return -ENOMEM;
8590d68bc92SNamhyung Kim
8606b1a2752SDavid Ahern pstat->nr_alloc++;
8616b1a2752SDavid Ahern pstat->alloc_bytes += bytes;
862c9758cc4SNamhyung Kim pstat->callsite = callsite;
8632a7ef02cSNamhyung Kim }
864c9758cc4SNamhyung Kim
865fb4f313dSNamhyung Kim this.callsite = callsite;
866fb4f313dSNamhyung Kim pstat = page_stat__findnew_caller(&this);
867c9758cc4SNamhyung Kim if (pstat == NULL)
868c9758cc4SNamhyung Kim return -ENOMEM;
869c9758cc4SNamhyung Kim
870c9758cc4SNamhyung Kim pstat->nr_alloc++;
871c9758cc4SNamhyung Kim pstat->alloc_bytes += bytes;
8720d68bc92SNamhyung Kim
8730d68bc92SNamhyung Kim order_stats[order][migrate_type]++;
8740d68bc92SNamhyung Kim
8750d68bc92SNamhyung Kim return 0;
8760d68bc92SNamhyung Kim }
8770d68bc92SNamhyung Kim
evsel__process_page_free_event(struct evsel * evsel,struct perf_sample * sample)8788cf5d0e0SArnaldo Carvalho de Melo static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample)
8790d68bc92SNamhyung Kim {
8800d68bc92SNamhyung Kim u64 page;
881efc0cdc9SArnaldo Carvalho de Melo unsigned int order = evsel__intval(evsel, sample, "order");
8820d68bc92SNamhyung Kim u64 bytes = kmem_page_size << order;
8836b1a2752SDavid Ahern struct page_stat *pstat;
8840d68bc92SNamhyung Kim struct page_stat this = {
8850d68bc92SNamhyung Kim .order = order,
8860d68bc92SNamhyung Kim };
8870d68bc92SNamhyung Kim
8880d68bc92SNamhyung Kim if (use_pfn)
889efc0cdc9SArnaldo Carvalho de Melo page = evsel__intval(evsel, sample, "pfn");
8900d68bc92SNamhyung Kim else
891efc0cdc9SArnaldo Carvalho de Melo page = evsel__intval(evsel, sample, "page");
8920d68bc92SNamhyung Kim
8930d68bc92SNamhyung Kim nr_page_frees++;
8940d68bc92SNamhyung Kim total_page_free_bytes += bytes;
8950d68bc92SNamhyung Kim
8962a7ef02cSNamhyung Kim this.page = page;
8972a7ef02cSNamhyung Kim pstat = page_stat__find_page(&this);
8986b1a2752SDavid Ahern if (pstat == NULL) {
8990d68bc92SNamhyung Kim pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
9000d68bc92SNamhyung Kim page, order);
9010d68bc92SNamhyung Kim
9020d68bc92SNamhyung Kim nr_page_nomatch++;
9030d68bc92SNamhyung Kim total_page_nomatch_bytes += bytes;
9040d68bc92SNamhyung Kim
9050d68bc92SNamhyung Kim return 0;
9060d68bc92SNamhyung Kim }
9070d68bc92SNamhyung Kim
9086b1a2752SDavid Ahern this.gfp_flags = pstat->gfp_flags;
9096b1a2752SDavid Ahern this.migrate_type = pstat->migrate_type;
910c9758cc4SNamhyung Kim this.callsite = pstat->callsite;
9110d68bc92SNamhyung Kim
9122a7ef02cSNamhyung Kim rb_erase(&pstat->node, &page_live_tree);
9136b1a2752SDavid Ahern free(pstat);
9140d68bc92SNamhyung Kim
9152a7ef02cSNamhyung Kim if (live_page) {
9162a7ef02cSNamhyung Kim order_stats[this.order][this.migrate_type]--;
9172a7ef02cSNamhyung Kim } else {
918c9758cc4SNamhyung Kim pstat = page_stat__find_alloc(&this);
919c9758cc4SNamhyung Kim if (pstat == NULL)
9202a7ef02cSNamhyung Kim return -ENOMEM;
921c9758cc4SNamhyung Kim
922c9758cc4SNamhyung Kim pstat->nr_free++;
923c9758cc4SNamhyung Kim pstat->free_bytes += bytes;
9242a7ef02cSNamhyung Kim }
925c9758cc4SNamhyung Kim
926fb4f313dSNamhyung Kim pstat = page_stat__find_caller(&this);
9276b1a2752SDavid Ahern if (pstat == NULL)
9280d68bc92SNamhyung Kim return -ENOENT;
9290d68bc92SNamhyung Kim
9306b1a2752SDavid Ahern pstat->nr_free++;
9316b1a2752SDavid Ahern pstat->free_bytes += bytes;
9320d68bc92SNamhyung Kim
9332a7ef02cSNamhyung Kim if (live_page) {
9342a7ef02cSNamhyung Kim pstat->nr_alloc--;
9352a7ef02cSNamhyung Kim pstat->alloc_bytes -= bytes;
9362a7ef02cSNamhyung Kim
9372a7ef02cSNamhyung Kim if (pstat->nr_alloc == 0) {
9382a7ef02cSNamhyung Kim rb_erase(&pstat->node, &page_caller_tree);
9392a7ef02cSNamhyung Kim free(pstat);
9402a7ef02cSNamhyung Kim }
9412a7ef02cSNamhyung Kim }
9422a7ef02cSNamhyung Kim
9430d68bc92SNamhyung Kim return 0;
9440d68bc92SNamhyung Kim }
9450d68bc92SNamhyung Kim
perf_kmem__skip_sample(struct perf_sample * sample)9462a865bd8SDavid Ahern static bool perf_kmem__skip_sample(struct perf_sample *sample)
9472a865bd8SDavid Ahern {
9482a865bd8SDavid Ahern /* skip sample based on time? */
9492a865bd8SDavid Ahern if (perf_time__skip_sample(&ptime, sample->time))
9502a865bd8SDavid Ahern return true;
9512a865bd8SDavid Ahern
9522a865bd8SDavid Ahern return false;
9532a865bd8SDavid Ahern }
9542a865bd8SDavid Ahern
95532dcd021SJiri Olsa typedef int (*tracepoint_handler)(struct evsel *evsel,
9560f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample);
957ba77c9e1SLi Zefan
process_sample_event(struct perf_tool * tool __maybe_unused,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine)9581d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused,
959d20deb64SArnaldo Carvalho de Melo union perf_event *event,
9608115d60cSArnaldo Carvalho de Melo struct perf_sample *sample,
96132dcd021SJiri Olsa struct evsel *evsel,
962743eb868SArnaldo Carvalho de Melo struct machine *machine)
963ba77c9e1SLi Zefan {
964b91fc39fSArnaldo Carvalho de Melo int err = 0;
965ef89325fSAdrian Hunter struct thread *thread = machine__findnew_thread(machine, sample->pid,
96613ce34dfSNamhyung Kim sample->tid);
967ba77c9e1SLi Zefan
968ba77c9e1SLi Zefan if (thread == NULL) {
969ba77c9e1SLi Zefan pr_debug("problem processing %d event, skipping it.\n",
970ba77c9e1SLi Zefan event->header.type);
971ba77c9e1SLi Zefan return -1;
972ba77c9e1SLi Zefan }
973ba77c9e1SLi Zefan
9742a865bd8SDavid Ahern if (perf_kmem__skip_sample(sample))
9752a865bd8SDavid Ahern return 0;
9762a865bd8SDavid Ahern
977ee84a303SIan Rogers dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread));
978ba77c9e1SLi Zefan
979744a9719SArnaldo Carvalho de Melo if (evsel->handler != NULL) {
980744a9719SArnaldo Carvalho de Melo tracepoint_handler f = evsel->handler;
981b91fc39fSArnaldo Carvalho de Melo err = f(evsel, sample);
9820f7d2f1bSArnaldo Carvalho de Melo }
9830f7d2f1bSArnaldo Carvalho de Melo
984b91fc39fSArnaldo Carvalho de Melo thread__put(thread);
985b91fc39fSArnaldo Carvalho de Melo
986b91fc39fSArnaldo Carvalho de Melo return err;
987ba77c9e1SLi Zefan }
988ba77c9e1SLi Zefan
989fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = {
99055aa640fSArnaldo Carvalho de Melo .sample = process_sample_event,
9918115d60cSArnaldo Carvalho de Melo .comm = perf_event__process_comm,
99264c40908SNamhyung Kim .mmap = perf_event__process_mmap,
99364c40908SNamhyung Kim .mmap2 = perf_event__process_mmap2,
994f3b3614aSHari Bathini .namespaces = perf_event__process_namespaces,
9950a8cb85cSJiri Olsa .ordered_events = true,
996ba77c9e1SLi Zefan };
997ba77c9e1SLi Zefan
fragmentation(unsigned long n_req,unsigned long n_alloc)998ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc)
999ba77c9e1SLi Zefan {
1000ba77c9e1SLi Zefan if (n_alloc == 0)
1001ba77c9e1SLi Zefan return 0.0;
1002ba77c9e1SLi Zefan else
1003ba77c9e1SLi Zefan return 100.0 - (100.0 * n_req / n_alloc);
1004ba77c9e1SLi Zefan }
1005ba77c9e1SLi Zefan
__print_slab_result(struct rb_root * root,struct perf_session * session,int n_lines,int is_caller)10060d68bc92SNamhyung Kim static void __print_slab_result(struct rb_root *root,
10070d68bc92SNamhyung Kim struct perf_session *session,
10084aa65636SArnaldo Carvalho de Melo int n_lines, int is_caller)
1009ba77c9e1SLi Zefan {
1010ba77c9e1SLi Zefan struct rb_node *next;
101134ba5122SArnaldo Carvalho de Melo struct machine *machine = &session->machines.host;
1012ba77c9e1SLi Zefan
101365f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line);
1014079d3f65SLi Zefan printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
1015079d3f65SLi Zefan printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
101665f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line);
1017ba77c9e1SLi Zefan
1018ba77c9e1SLi Zefan next = rb_first(root);
1019ba77c9e1SLi Zefan
1020ba77c9e1SLi Zefan while (next && n_lines--) {
10211b145ae5SArnaldo Carvalho de Melo struct alloc_stat *data = rb_entry(next, struct alloc_stat,
10221b145ae5SArnaldo Carvalho de Melo node);
10231b145ae5SArnaldo Carvalho de Melo struct symbol *sym = NULL;
102471cf8b8fSArnaldo Carvalho de Melo struct map *map;
1025079d3f65SLi Zefan char buf[BUFSIZ];
10261b145ae5SArnaldo Carvalho de Melo u64 addr;
1027ba77c9e1SLi Zefan
10281b145ae5SArnaldo Carvalho de Melo if (is_caller) {
10291b145ae5SArnaldo Carvalho de Melo addr = data->call_site;
10307707b6b6SLi Zefan if (!raw_ip)
1031107cad95SArnaldo Carvalho de Melo sym = machine__find_kernel_symbol(machine, addr, &map);
10321b145ae5SArnaldo Carvalho de Melo } else
10331b145ae5SArnaldo Carvalho de Melo addr = data->ptr;
1034ba77c9e1SLi Zefan
10351b145ae5SArnaldo Carvalho de Melo if (sym != NULL)
10369486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
103778a1f7cdSIan Rogers addr - map__unmap_ip(map, sym->start));
10381b145ae5SArnaldo Carvalho de Melo else
10399486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
1040079d3f65SLi Zefan printf(" %-34s |", buf);
10411b145ae5SArnaldo Carvalho de Melo
104265f46e02SNamhyung Kim printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
1043079d3f65SLi Zefan (unsigned long long)data->bytes_alloc,
1044ba77c9e1SLi Zefan (unsigned long)data->bytes_alloc / data->hit,
1045ba77c9e1SLi Zefan (unsigned long long)data->bytes_req,
1046ba77c9e1SLi Zefan (unsigned long)data->bytes_req / data->hit,
1047ba77c9e1SLi Zefan (unsigned long)data->hit,
1048079d3f65SLi Zefan (unsigned long)data->pingpong,
1049ba77c9e1SLi Zefan fragmentation(data->bytes_req, data->bytes_alloc));
1050ba77c9e1SLi Zefan
1051ba77c9e1SLi Zefan next = rb_next(next);
1052ba77c9e1SLi Zefan }
1053ba77c9e1SLi Zefan
1054ba77c9e1SLi Zefan if (n_lines == -1)
1055079d3f65SLi Zefan printf(" ... | ... | ... | ... | ... | ... \n");
1056ba77c9e1SLi Zefan
105765f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line);
1058ba77c9e1SLi Zefan }
1059ba77c9e1SLi Zefan
10600d68bc92SNamhyung Kim static const char * const migrate_type_str[] = {
10610d68bc92SNamhyung Kim "UNMOVABL",
10620d68bc92SNamhyung Kim "RECLAIM",
10630d68bc92SNamhyung Kim "MOVABLE",
10640d68bc92SNamhyung Kim "RESERVED",
10650d68bc92SNamhyung Kim "CMA/ISLT",
10660d68bc92SNamhyung Kim "UNKNOWN",
10670d68bc92SNamhyung Kim };
10680d68bc92SNamhyung Kim
__print_page_alloc_result(struct perf_session * session,int n_lines)1069c9758cc4SNamhyung Kim static void __print_page_alloc_result(struct perf_session *session, int n_lines)
1070ba77c9e1SLi Zefan {
1071c9758cc4SNamhyung Kim struct rb_node *next = rb_first(&page_alloc_sorted);
1072c9758cc4SNamhyung Kim struct machine *machine = &session->machines.host;
10730d68bc92SNamhyung Kim const char *format;
10740e111156SNamhyung Kim int gfp_len = max(strlen("GFP flags"), max_gfp_len);
10750d68bc92SNamhyung Kim
1076c9758cc4SNamhyung Kim printf("\n%.105s\n", graph_dotted_line);
10770e111156SNamhyung Kim printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
10780e111156SNamhyung Kim use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
10790e111156SNamhyung Kim gfp_len, "GFP flags");
1080c9758cc4SNamhyung Kim printf("%.105s\n", graph_dotted_line);
10810d68bc92SNamhyung Kim
10820d68bc92SNamhyung Kim if (use_pfn)
10830e111156SNamhyung Kim format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
10840d68bc92SNamhyung Kim else
10850e111156SNamhyung Kim format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
10860d68bc92SNamhyung Kim
10870d68bc92SNamhyung Kim while (next && n_lines--) {
10880d68bc92SNamhyung Kim struct page_stat *data;
1089c9758cc4SNamhyung Kim struct symbol *sym;
1090c9758cc4SNamhyung Kim struct map *map;
1091c9758cc4SNamhyung Kim char buf[32];
1092c9758cc4SNamhyung Kim char *caller = buf;
10930d68bc92SNamhyung Kim
10940d68bc92SNamhyung Kim data = rb_entry(next, struct page_stat, node);
1095107cad95SArnaldo Carvalho de Melo sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1096a7c3899cSArnaldo Carvalho de Melo if (sym)
1097c9758cc4SNamhyung Kim caller = sym->name;
1098c9758cc4SNamhyung Kim else
1099c9758cc4SNamhyung Kim scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
11000d68bc92SNamhyung Kim
11010d68bc92SNamhyung Kim printf(format, (unsigned long long)data->page,
11020d68bc92SNamhyung Kim (unsigned long long)data->alloc_bytes / 1024,
11030d68bc92SNamhyung Kim data->nr_alloc, data->order,
11040d68bc92SNamhyung Kim migrate_type_str[data->migrate_type],
11050e111156SNamhyung Kim gfp_len, compact_gfp_string(data->gfp_flags), caller);
1106c9758cc4SNamhyung Kim
1107c9758cc4SNamhyung Kim next = rb_next(next);
1108c9758cc4SNamhyung Kim }
1109c9758cc4SNamhyung Kim
11100e111156SNamhyung Kim if (n_lines == -1) {
11110e111156SNamhyung Kim printf(" ... | ... | ... | ... | ... | %-*s | ...\n",
11120e111156SNamhyung Kim gfp_len, "...");
11130e111156SNamhyung Kim }
1114c9758cc4SNamhyung Kim
1115c9758cc4SNamhyung Kim printf("%.105s\n", graph_dotted_line);
1116c9758cc4SNamhyung Kim }
1117c9758cc4SNamhyung Kim
__print_page_caller_result(struct perf_session * session,int n_lines)1118c9758cc4SNamhyung Kim static void __print_page_caller_result(struct perf_session *session, int n_lines)
1119c9758cc4SNamhyung Kim {
1120c9758cc4SNamhyung Kim struct rb_node *next = rb_first(&page_caller_sorted);
1121c9758cc4SNamhyung Kim struct machine *machine = &session->machines.host;
11220e111156SNamhyung Kim int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1123c9758cc4SNamhyung Kim
1124c9758cc4SNamhyung Kim printf("\n%.105s\n", graph_dotted_line);
11250e111156SNamhyung Kim printf(" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
11260e111156SNamhyung Kim live_page ? "Live" : "Total", gfp_len, "GFP flags");
1127c9758cc4SNamhyung Kim printf("%.105s\n", graph_dotted_line);
1128c9758cc4SNamhyung Kim
1129c9758cc4SNamhyung Kim while (next && n_lines--) {
1130c9758cc4SNamhyung Kim struct page_stat *data;
1131c9758cc4SNamhyung Kim struct symbol *sym;
1132c9758cc4SNamhyung Kim struct map *map;
1133c9758cc4SNamhyung Kim char buf[32];
1134c9758cc4SNamhyung Kim char *caller = buf;
1135c9758cc4SNamhyung Kim
1136c9758cc4SNamhyung Kim data = rb_entry(next, struct page_stat, node);
1137107cad95SArnaldo Carvalho de Melo sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1138a7c3899cSArnaldo Carvalho de Melo if (sym)
1139c9758cc4SNamhyung Kim caller = sym->name;
1140c9758cc4SNamhyung Kim else
1141c9758cc4SNamhyung Kim scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1142c9758cc4SNamhyung Kim
11430e111156SNamhyung Kim printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
1144c9758cc4SNamhyung Kim (unsigned long long)data->alloc_bytes / 1024,
1145c9758cc4SNamhyung Kim data->nr_alloc, data->order,
1146c9758cc4SNamhyung Kim migrate_type_str[data->migrate_type],
11470e111156SNamhyung Kim gfp_len, compact_gfp_string(data->gfp_flags), caller);
11480d68bc92SNamhyung Kim
11490d68bc92SNamhyung Kim next = rb_next(next);
11500d68bc92SNamhyung Kim }
11510d68bc92SNamhyung Kim
11520e111156SNamhyung Kim if (n_lines == -1) {
11530e111156SNamhyung Kim printf(" ... | ... | ... | ... | %-*s | ...\n",
11540e111156SNamhyung Kim gfp_len, "...");
11550e111156SNamhyung Kim }
11560d68bc92SNamhyung Kim
1157c9758cc4SNamhyung Kim printf("%.105s\n", graph_dotted_line);
11580d68bc92SNamhyung Kim }
11590d68bc92SNamhyung Kim
print_gfp_flags(void)11600e111156SNamhyung Kim static void print_gfp_flags(void)
11610e111156SNamhyung Kim {
11620e111156SNamhyung Kim int i;
11630e111156SNamhyung Kim
11640e111156SNamhyung Kim printf("#\n");
11650e111156SNamhyung Kim printf("# GFP flags\n");
11660e111156SNamhyung Kim printf("# ---------\n");
11670e111156SNamhyung Kim for (i = 0; i < nr_gfps; i++) {
11680e111156SNamhyung Kim printf("# %08x: %*s: %s\n", gfps[i].flags,
11690e111156SNamhyung Kim (int) max_gfp_len, gfps[i].compact_str,
11700e111156SNamhyung Kim gfps[i].human_readable);
11710e111156SNamhyung Kim }
11720e111156SNamhyung Kim }
11730e111156SNamhyung Kim
print_slab_summary(void)11740d68bc92SNamhyung Kim static void print_slab_summary(void)
11750d68bc92SNamhyung Kim {
11760d68bc92SNamhyung Kim printf("\nSUMMARY (SLAB allocator)");
11770d68bc92SNamhyung Kim printf("\n========================\n");
117877cfe388SNamhyung Kim printf("Total bytes requested: %'lu\n", total_requested);
117977cfe388SNamhyung Kim printf("Total bytes allocated: %'lu\n", total_allocated);
1180aa58e9afSDavid Ahern printf("Total bytes freed: %'lu\n", total_freed);
1181aa58e9afSDavid Ahern if (total_allocated > total_freed) {
1182aa58e9afSDavid Ahern printf("Net total bytes allocated: %'lu\n",
1183aa58e9afSDavid Ahern total_allocated - total_freed);
1184aa58e9afSDavid Ahern }
118577cfe388SNamhyung Kim printf("Total bytes wasted on internal fragmentation: %'lu\n",
1186ba77c9e1SLi Zefan total_allocated - total_requested);
1187ba77c9e1SLi Zefan printf("Internal fragmentation: %f%%\n",
1188ba77c9e1SLi Zefan fragmentation(total_requested, total_allocated));
118977cfe388SNamhyung Kim printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
1190ba77c9e1SLi Zefan }
1191ba77c9e1SLi Zefan
print_page_summary(void)11920d68bc92SNamhyung Kim static void print_page_summary(void)
11930d68bc92SNamhyung Kim {
11940d68bc92SNamhyung Kim int o, m;
11950d68bc92SNamhyung Kim u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
11960d68bc92SNamhyung Kim u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
11970d68bc92SNamhyung Kim
11980d68bc92SNamhyung Kim printf("\nSUMMARY (page allocator)");
11990d68bc92SNamhyung Kim printf("\n========================\n");
12000d68bc92SNamhyung Kim printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation requests",
12010d68bc92SNamhyung Kim nr_page_allocs, total_page_alloc_bytes / 1024);
12020d68bc92SNamhyung Kim printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free requests",
12030d68bc92SNamhyung Kim nr_page_frees, total_page_free_bytes / 1024);
12040d68bc92SNamhyung Kim printf("\n");
12050d68bc92SNamhyung Kim
12066145c259SWill Deacon printf("%-30s: %'16"PRIu64" [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
12070d68bc92SNamhyung Kim nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
12086145c259SWill Deacon printf("%-30s: %'16"PRIu64" [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
12090d68bc92SNamhyung Kim nr_page_allocs - nr_alloc_freed,
12100d68bc92SNamhyung Kim (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
12110d68bc92SNamhyung Kim printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free-only requests",
12120d68bc92SNamhyung Kim nr_page_nomatch, total_page_nomatch_bytes / 1024);
12130d68bc92SNamhyung Kim printf("\n");
12140d68bc92SNamhyung Kim
12150d68bc92SNamhyung Kim printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation failures",
12160d68bc92SNamhyung Kim nr_page_fails, total_page_fail_bytes / 1024);
12170d68bc92SNamhyung Kim printf("\n");
12180d68bc92SNamhyung Kim
12190d68bc92SNamhyung Kim printf("%5s %12s %12s %12s %12s %12s\n", "Order", "Unmovable",
12200d68bc92SNamhyung Kim "Reclaimable", "Movable", "Reserved", "CMA/Isolated");
12210d68bc92SNamhyung Kim printf("%.5s %.12s %.12s %.12s %.12s %.12s\n", graph_dotted_line,
12220d68bc92SNamhyung Kim graph_dotted_line, graph_dotted_line, graph_dotted_line,
12230d68bc92SNamhyung Kim graph_dotted_line, graph_dotted_line);
12240d68bc92SNamhyung Kim
12250d68bc92SNamhyung Kim for (o = 0; o < MAX_PAGE_ORDER; o++) {
12260d68bc92SNamhyung Kim printf("%5d", o);
12270d68bc92SNamhyung Kim for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
12280d68bc92SNamhyung Kim if (order_stats[o][m])
12290d68bc92SNamhyung Kim printf(" %'12d", order_stats[o][m]);
12300d68bc92SNamhyung Kim else
12310d68bc92SNamhyung Kim printf(" %12c", '.');
12320d68bc92SNamhyung Kim }
12330d68bc92SNamhyung Kim printf("\n");
12340d68bc92SNamhyung Kim }
12350d68bc92SNamhyung Kim }
12360d68bc92SNamhyung Kim
print_slab_result(struct perf_session * session)12370d68bc92SNamhyung Kim static void print_slab_result(struct perf_session *session)
1238ba77c9e1SLi Zefan {
1239ba77c9e1SLi Zefan if (caller_flag)
12400d68bc92SNamhyung Kim __print_slab_result(&root_caller_sorted, session, caller_lines, 1);
1241ba77c9e1SLi Zefan if (alloc_flag)
12420d68bc92SNamhyung Kim __print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
12430d68bc92SNamhyung Kim print_slab_summary();
12440d68bc92SNamhyung Kim }
12450d68bc92SNamhyung Kim
print_page_result(struct perf_session * session)12460d68bc92SNamhyung Kim static void print_page_result(struct perf_session *session)
12470d68bc92SNamhyung Kim {
12480e111156SNamhyung Kim if (caller_flag || alloc_flag)
12490e111156SNamhyung Kim print_gfp_flags();
1250c9758cc4SNamhyung Kim if (caller_flag)
1251c9758cc4SNamhyung Kim __print_page_caller_result(session, caller_lines);
12520d68bc92SNamhyung Kim if (alloc_flag)
1253c9758cc4SNamhyung Kim __print_page_alloc_result(session, alloc_lines);
12540d68bc92SNamhyung Kim print_page_summary();
12550d68bc92SNamhyung Kim }
12560d68bc92SNamhyung Kim
print_result(struct perf_session * session)12570d68bc92SNamhyung Kim static void print_result(struct perf_session *session)
12580d68bc92SNamhyung Kim {
12590d68bc92SNamhyung Kim if (kmem_slab)
12600d68bc92SNamhyung Kim print_slab_result(session);
12610d68bc92SNamhyung Kim if (kmem_page)
12620d68bc92SNamhyung Kim print_page_result(session);
1263ba77c9e1SLi Zefan }
1264ba77c9e1SLi Zefan
1265fb4f313dSNamhyung Kim static LIST_HEAD(slab_caller_sort);
1266fb4f313dSNamhyung Kim static LIST_HEAD(slab_alloc_sort);
1267fb4f313dSNamhyung Kim static LIST_HEAD(page_caller_sort);
1268fb4f313dSNamhyung Kim static LIST_HEAD(page_alloc_sort);
126929b3e152SLi Zefan
sort_slab_insert(struct rb_root * root,struct alloc_stat * data,struct list_head * sort_list)12700d68bc92SNamhyung Kim static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
127129b3e152SLi Zefan struct list_head *sort_list)
1272ba77c9e1SLi Zefan {
1273ba77c9e1SLi Zefan struct rb_node **new = &(root->rb_node);
1274ba77c9e1SLi Zefan struct rb_node *parent = NULL;
127529b3e152SLi Zefan struct sort_dimension *sort;
1276ba77c9e1SLi Zefan
1277ba77c9e1SLi Zefan while (*new) {
1278ba77c9e1SLi Zefan struct alloc_stat *this;
127929b3e152SLi Zefan int cmp = 0;
1280ba77c9e1SLi Zefan
1281ba77c9e1SLi Zefan this = rb_entry(*new, struct alloc_stat, node);
1282ba77c9e1SLi Zefan parent = *new;
1283ba77c9e1SLi Zefan
128429b3e152SLi Zefan list_for_each_entry(sort, sort_list, list) {
128529b3e152SLi Zefan cmp = sort->cmp(data, this);
128629b3e152SLi Zefan if (cmp)
128729b3e152SLi Zefan break;
128829b3e152SLi Zefan }
1289ba77c9e1SLi Zefan
1290ba77c9e1SLi Zefan if (cmp > 0)
1291ba77c9e1SLi Zefan new = &((*new)->rb_left);
1292ba77c9e1SLi Zefan else
1293ba77c9e1SLi Zefan new = &((*new)->rb_right);
1294ba77c9e1SLi Zefan }
1295ba77c9e1SLi Zefan
1296ba77c9e1SLi Zefan rb_link_node(&data->node, parent, new);
1297ba77c9e1SLi Zefan rb_insert_color(&data->node, root);
1298ba77c9e1SLi Zefan }
1299ba77c9e1SLi Zefan
__sort_slab_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)13000d68bc92SNamhyung Kim static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
130129b3e152SLi Zefan struct list_head *sort_list)
1302ba77c9e1SLi Zefan {
1303ba77c9e1SLi Zefan struct rb_node *node;
1304ba77c9e1SLi Zefan struct alloc_stat *data;
1305ba77c9e1SLi Zefan
1306ba77c9e1SLi Zefan for (;;) {
1307ba77c9e1SLi Zefan node = rb_first(root);
1308ba77c9e1SLi Zefan if (!node)
1309ba77c9e1SLi Zefan break;
1310ba77c9e1SLi Zefan
1311ba77c9e1SLi Zefan rb_erase(node, root);
1312ba77c9e1SLi Zefan data = rb_entry(node, struct alloc_stat, node);
13130d68bc92SNamhyung Kim sort_slab_insert(root_sorted, data, sort_list);
13140d68bc92SNamhyung Kim }
13150d68bc92SNamhyung Kim }
13160d68bc92SNamhyung Kim
sort_page_insert(struct rb_root * root,struct page_stat * data,struct list_head * sort_list)1317fb4f313dSNamhyung Kim static void sort_page_insert(struct rb_root *root, struct page_stat *data,
1318fb4f313dSNamhyung Kim struct list_head *sort_list)
13190d68bc92SNamhyung Kim {
13200d68bc92SNamhyung Kim struct rb_node **new = &root->rb_node;
13210d68bc92SNamhyung Kim struct rb_node *parent = NULL;
1322fb4f313dSNamhyung Kim struct sort_dimension *sort;
13230d68bc92SNamhyung Kim
13240d68bc92SNamhyung Kim while (*new) {
13250d68bc92SNamhyung Kim struct page_stat *this;
13260d68bc92SNamhyung Kim int cmp = 0;
13270d68bc92SNamhyung Kim
13280d68bc92SNamhyung Kim this = rb_entry(*new, struct page_stat, node);
13290d68bc92SNamhyung Kim parent = *new;
13300d68bc92SNamhyung Kim
1331fb4f313dSNamhyung Kim list_for_each_entry(sort, sort_list, list) {
1332fb4f313dSNamhyung Kim cmp = sort->cmp(data, this);
1333fb4f313dSNamhyung Kim if (cmp)
1334fb4f313dSNamhyung Kim break;
1335fb4f313dSNamhyung Kim }
13360d68bc92SNamhyung Kim
13370d68bc92SNamhyung Kim if (cmp > 0)
13380d68bc92SNamhyung Kim new = &parent->rb_left;
13390d68bc92SNamhyung Kim else
13400d68bc92SNamhyung Kim new = &parent->rb_right;
13410d68bc92SNamhyung Kim }
13420d68bc92SNamhyung Kim
13430d68bc92SNamhyung Kim rb_link_node(&data->node, parent, new);
13440d68bc92SNamhyung Kim rb_insert_color(&data->node, root);
13450d68bc92SNamhyung Kim }
13460d68bc92SNamhyung Kim
__sort_page_result(struct rb_root * root,struct rb_root * root_sorted,struct list_head * sort_list)1347fb4f313dSNamhyung Kim static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
1348fb4f313dSNamhyung Kim struct list_head *sort_list)
13490d68bc92SNamhyung Kim {
13500d68bc92SNamhyung Kim struct rb_node *node;
13510d68bc92SNamhyung Kim struct page_stat *data;
13520d68bc92SNamhyung Kim
13530d68bc92SNamhyung Kim for (;;) {
13540d68bc92SNamhyung Kim node = rb_first(root);
13550d68bc92SNamhyung Kim if (!node)
13560d68bc92SNamhyung Kim break;
13570d68bc92SNamhyung Kim
13580d68bc92SNamhyung Kim rb_erase(node, root);
13590d68bc92SNamhyung Kim data = rb_entry(node, struct page_stat, node);
1360fb4f313dSNamhyung Kim sort_page_insert(root_sorted, data, sort_list);
1361ba77c9e1SLi Zefan }
1362ba77c9e1SLi Zefan }
1363ba77c9e1SLi Zefan
sort_result(void)1364ba77c9e1SLi Zefan static void sort_result(void)
1365ba77c9e1SLi Zefan {
13660d68bc92SNamhyung Kim if (kmem_slab) {
13670d68bc92SNamhyung Kim __sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
1368fb4f313dSNamhyung Kim &slab_alloc_sort);
13690d68bc92SNamhyung Kim __sort_slab_result(&root_caller_stat, &root_caller_sorted,
1370fb4f313dSNamhyung Kim &slab_caller_sort);
13710d68bc92SNamhyung Kim }
13720d68bc92SNamhyung Kim if (kmem_page) {
13732a7ef02cSNamhyung Kim if (live_page)
13742a7ef02cSNamhyung Kim __sort_page_result(&page_live_tree, &page_alloc_sorted,
13752a7ef02cSNamhyung Kim &page_alloc_sort);
13762a7ef02cSNamhyung Kim else
1377fb4f313dSNamhyung Kim __sort_page_result(&page_alloc_tree, &page_alloc_sorted,
1378fb4f313dSNamhyung Kim &page_alloc_sort);
13792a7ef02cSNamhyung Kim
1380fb4f313dSNamhyung Kim __sort_page_result(&page_caller_tree, &page_caller_sorted,
1381fb4f313dSNamhyung Kim &page_caller_sort);
13820d68bc92SNamhyung Kim }
1383ba77c9e1SLi Zefan }
1384ba77c9e1SLi Zefan
__cmd_kmem(struct perf_session * session)13852b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session)
1386ba77c9e1SLi Zefan {
1387d549c769SArnaldo Carvalho de Melo int err = -EINVAL;
138832dcd021SJiri Olsa struct evsel *evsel;
138932dcd021SJiri Olsa const struct evsel_str_handler kmem_tracepoints[] = {
13900d68bc92SNamhyung Kim /* slab allocator */
13918cf5d0e0SArnaldo Carvalho de Melo { "kmem:kmalloc", evsel__process_alloc_event, },
13928cf5d0e0SArnaldo Carvalho de Melo { "kmem:kmem_cache_alloc", evsel__process_alloc_event, },
1393dce088abSLeo Yan { "kmem:kmalloc_node", evsel__process_alloc_event, },
1394dce088abSLeo Yan { "kmem:kmem_cache_alloc_node", evsel__process_alloc_event, },
13958cf5d0e0SArnaldo Carvalho de Melo { "kmem:kfree", evsel__process_free_event, },
13968cf5d0e0SArnaldo Carvalho de Melo { "kmem:kmem_cache_free", evsel__process_free_event, },
13970d68bc92SNamhyung Kim /* page allocator */
13988cf5d0e0SArnaldo Carvalho de Melo { "kmem:mm_page_alloc", evsel__process_page_alloc_event, },
13998cf5d0e0SArnaldo Carvalho de Melo { "kmem:mm_page_free", evsel__process_page_free_event, },
14000f7d2f1bSArnaldo Carvalho de Melo };
1401ba77c9e1SLi Zefan
1402d549c769SArnaldo Carvalho de Melo if (!perf_session__has_traces(session, "kmem record"))
14032b2b2c68SNamhyung Kim goto out;
1404d549c769SArnaldo Carvalho de Melo
14050f7d2f1bSArnaldo Carvalho de Melo if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
14060f7d2f1bSArnaldo Carvalho de Melo pr_err("Initializing perf session tracepoint handlers failed\n");
14072b2b2c68SNamhyung Kim goto out;
14080f7d2f1bSArnaldo Carvalho de Melo }
14090f7d2f1bSArnaldo Carvalho de Melo
1410e5cadb93SArnaldo Carvalho de Melo evlist__for_each_entry(session->evlist, evsel) {
14118ab2e96dSArnaldo Carvalho de Melo if (!strcmp(evsel__name(evsel), "kmem:mm_page_alloc") &&
1412efc0cdc9SArnaldo Carvalho de Melo evsel__field(evsel, "pfn")) {
14130d68bc92SNamhyung Kim use_pfn = true;
14140d68bc92SNamhyung Kim break;
14150d68bc92SNamhyung Kim }
14160d68bc92SNamhyung Kim }
14170d68bc92SNamhyung Kim
14184aa65636SArnaldo Carvalho de Melo setup_pager();
1419b7b61cbeSArnaldo Carvalho de Melo err = perf_session__process_events(session);
14200d68bc92SNamhyung Kim if (err != 0) {
14210d68bc92SNamhyung Kim pr_err("error during process events: %d\n", err);
14222b2b2c68SNamhyung Kim goto out;
14230d68bc92SNamhyung Kim }
14244aa65636SArnaldo Carvalho de Melo sort_result();
14254aa65636SArnaldo Carvalho de Melo print_result(session);
14262b2b2c68SNamhyung Kim out:
14274aa65636SArnaldo Carvalho de Melo return err;
1428ba77c9e1SLi Zefan }
1429ba77c9e1SLi Zefan
1430fb4f313dSNamhyung Kim /* slab sort keys */
ptr_cmp(void * a,void * b)1431fb4f313dSNamhyung Kim static int ptr_cmp(void *a, void *b)
1432ba77c9e1SLi Zefan {
1433fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1434fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1435fb4f313dSNamhyung Kim
1436ba77c9e1SLi Zefan if (l->ptr < r->ptr)
1437ba77c9e1SLi Zefan return -1;
1438ba77c9e1SLi Zefan else if (l->ptr > r->ptr)
1439ba77c9e1SLi Zefan return 1;
1440ba77c9e1SLi Zefan return 0;
1441ba77c9e1SLi Zefan }
1442ba77c9e1SLi Zefan
144329b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = {
144429b3e152SLi Zefan .name = "ptr",
144529b3e152SLi Zefan .cmp = ptr_cmp,
144629b3e152SLi Zefan };
144729b3e152SLi Zefan
slab_callsite_cmp(void * a,void * b)1448fb4f313dSNamhyung Kim static int slab_callsite_cmp(void *a, void *b)
1449ba77c9e1SLi Zefan {
1450fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1451fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1452fb4f313dSNamhyung Kim
1453ba77c9e1SLi Zefan if (l->call_site < r->call_site)
1454ba77c9e1SLi Zefan return -1;
1455ba77c9e1SLi Zefan else if (l->call_site > r->call_site)
1456ba77c9e1SLi Zefan return 1;
1457ba77c9e1SLi Zefan return 0;
1458ba77c9e1SLi Zefan }
1459ba77c9e1SLi Zefan
146029b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = {
146129b3e152SLi Zefan .name = "callsite",
1462fb4f313dSNamhyung Kim .cmp = slab_callsite_cmp,
146329b3e152SLi Zefan };
146429b3e152SLi Zefan
hit_cmp(void * a,void * b)1465fb4f313dSNamhyung Kim static int hit_cmp(void *a, void *b)
1466f3ced7cdSPekka Enberg {
1467fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1468fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1469fb4f313dSNamhyung Kim
1470f3ced7cdSPekka Enberg if (l->hit < r->hit)
1471f3ced7cdSPekka Enberg return -1;
1472f3ced7cdSPekka Enberg else if (l->hit > r->hit)
1473f3ced7cdSPekka Enberg return 1;
1474f3ced7cdSPekka Enberg return 0;
1475f3ced7cdSPekka Enberg }
1476f3ced7cdSPekka Enberg
147729b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = {
147829b3e152SLi Zefan .name = "hit",
147929b3e152SLi Zefan .cmp = hit_cmp,
148029b3e152SLi Zefan };
148129b3e152SLi Zefan
bytes_cmp(void * a,void * b)1482fb4f313dSNamhyung Kim static int bytes_cmp(void *a, void *b)
1483ba77c9e1SLi Zefan {
1484fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1485fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1486fb4f313dSNamhyung Kim
1487ba77c9e1SLi Zefan if (l->bytes_alloc < r->bytes_alloc)
1488ba77c9e1SLi Zefan return -1;
1489ba77c9e1SLi Zefan else if (l->bytes_alloc > r->bytes_alloc)
1490ba77c9e1SLi Zefan return 1;
1491ba77c9e1SLi Zefan return 0;
1492ba77c9e1SLi Zefan }
1493ba77c9e1SLi Zefan
149429b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = {
149529b3e152SLi Zefan .name = "bytes",
149629b3e152SLi Zefan .cmp = bytes_cmp,
149729b3e152SLi Zefan };
149829b3e152SLi Zefan
frag_cmp(void * a,void * b)1499fb4f313dSNamhyung Kim static int frag_cmp(void *a, void *b)
1500f3ced7cdSPekka Enberg {
1501f3ced7cdSPekka Enberg double x, y;
1502fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1503fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1504f3ced7cdSPekka Enberg
1505f3ced7cdSPekka Enberg x = fragmentation(l->bytes_req, l->bytes_alloc);
1506f3ced7cdSPekka Enberg y = fragmentation(r->bytes_req, r->bytes_alloc);
1507f3ced7cdSPekka Enberg
1508f3ced7cdSPekka Enberg if (x < y)
1509f3ced7cdSPekka Enberg return -1;
1510f3ced7cdSPekka Enberg else if (x > y)
1511f3ced7cdSPekka Enberg return 1;
1512f3ced7cdSPekka Enberg return 0;
1513f3ced7cdSPekka Enberg }
1514f3ced7cdSPekka Enberg
151529b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = {
151629b3e152SLi Zefan .name = "frag",
151729b3e152SLi Zefan .cmp = frag_cmp,
151829b3e152SLi Zefan };
151929b3e152SLi Zefan
pingpong_cmp(void * a,void * b)1520fb4f313dSNamhyung Kim static int pingpong_cmp(void *a, void *b)
1521079d3f65SLi Zefan {
1522fb4f313dSNamhyung Kim struct alloc_stat *l = a;
1523fb4f313dSNamhyung Kim struct alloc_stat *r = b;
1524fb4f313dSNamhyung Kim
1525079d3f65SLi Zefan if (l->pingpong < r->pingpong)
1526079d3f65SLi Zefan return -1;
1527079d3f65SLi Zefan else if (l->pingpong > r->pingpong)
1528079d3f65SLi Zefan return 1;
1529079d3f65SLi Zefan return 0;
1530079d3f65SLi Zefan }
1531079d3f65SLi Zefan
1532079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = {
1533079d3f65SLi Zefan .name = "pingpong",
1534079d3f65SLi Zefan .cmp = pingpong_cmp,
1535079d3f65SLi Zefan };
1536079d3f65SLi Zefan
1537fb4f313dSNamhyung Kim /* page sort keys */
page_cmp(void * a,void * b)1538fb4f313dSNamhyung Kim static int page_cmp(void *a, void *b)
1539fb4f313dSNamhyung Kim {
1540fb4f313dSNamhyung Kim struct page_stat *l = a;
1541fb4f313dSNamhyung Kim struct page_stat *r = b;
1542fb4f313dSNamhyung Kim
1543fb4f313dSNamhyung Kim if (l->page < r->page)
1544fb4f313dSNamhyung Kim return -1;
1545fb4f313dSNamhyung Kim else if (l->page > r->page)
1546fb4f313dSNamhyung Kim return 1;
1547fb4f313dSNamhyung Kim return 0;
1548fb4f313dSNamhyung Kim }
1549fb4f313dSNamhyung Kim
1550fb4f313dSNamhyung Kim static struct sort_dimension page_sort_dimension = {
1551fb4f313dSNamhyung Kim .name = "page",
1552fb4f313dSNamhyung Kim .cmp = page_cmp,
1553fb4f313dSNamhyung Kim };
1554fb4f313dSNamhyung Kim
page_callsite_cmp(void * a,void * b)1555fb4f313dSNamhyung Kim static int page_callsite_cmp(void *a, void *b)
1556fb4f313dSNamhyung Kim {
1557fb4f313dSNamhyung Kim struct page_stat *l = a;
1558fb4f313dSNamhyung Kim struct page_stat *r = b;
1559fb4f313dSNamhyung Kim
1560fb4f313dSNamhyung Kim if (l->callsite < r->callsite)
1561fb4f313dSNamhyung Kim return -1;
1562fb4f313dSNamhyung Kim else if (l->callsite > r->callsite)
1563fb4f313dSNamhyung Kim return 1;
1564fb4f313dSNamhyung Kim return 0;
1565fb4f313dSNamhyung Kim }
1566fb4f313dSNamhyung Kim
1567fb4f313dSNamhyung Kim static struct sort_dimension page_callsite_sort_dimension = {
1568fb4f313dSNamhyung Kim .name = "callsite",
1569fb4f313dSNamhyung Kim .cmp = page_callsite_cmp,
1570fb4f313dSNamhyung Kim };
1571fb4f313dSNamhyung Kim
page_hit_cmp(void * a,void * b)1572fb4f313dSNamhyung Kim static int page_hit_cmp(void *a, void *b)
1573fb4f313dSNamhyung Kim {
1574fb4f313dSNamhyung Kim struct page_stat *l = a;
1575fb4f313dSNamhyung Kim struct page_stat *r = b;
1576fb4f313dSNamhyung Kim
1577fb4f313dSNamhyung Kim if (l->nr_alloc < r->nr_alloc)
1578fb4f313dSNamhyung Kim return -1;
1579fb4f313dSNamhyung Kim else if (l->nr_alloc > r->nr_alloc)
1580fb4f313dSNamhyung Kim return 1;
1581fb4f313dSNamhyung Kim return 0;
1582fb4f313dSNamhyung Kim }
1583fb4f313dSNamhyung Kim
1584fb4f313dSNamhyung Kim static struct sort_dimension page_hit_sort_dimension = {
1585fb4f313dSNamhyung Kim .name = "hit",
1586fb4f313dSNamhyung Kim .cmp = page_hit_cmp,
1587fb4f313dSNamhyung Kim };
1588fb4f313dSNamhyung Kim
page_bytes_cmp(void * a,void * b)1589fb4f313dSNamhyung Kim static int page_bytes_cmp(void *a, void *b)
1590fb4f313dSNamhyung Kim {
1591fb4f313dSNamhyung Kim struct page_stat *l = a;
1592fb4f313dSNamhyung Kim struct page_stat *r = b;
1593fb4f313dSNamhyung Kim
1594fb4f313dSNamhyung Kim if (l->alloc_bytes < r->alloc_bytes)
1595fb4f313dSNamhyung Kim return -1;
1596fb4f313dSNamhyung Kim else if (l->alloc_bytes > r->alloc_bytes)
1597fb4f313dSNamhyung Kim return 1;
1598fb4f313dSNamhyung Kim return 0;
1599fb4f313dSNamhyung Kim }
1600fb4f313dSNamhyung Kim
1601fb4f313dSNamhyung Kim static struct sort_dimension page_bytes_sort_dimension = {
1602fb4f313dSNamhyung Kim .name = "bytes",
1603fb4f313dSNamhyung Kim .cmp = page_bytes_cmp,
1604fb4f313dSNamhyung Kim };
1605fb4f313dSNamhyung Kim
page_order_cmp(void * a,void * b)1606fb4f313dSNamhyung Kim static int page_order_cmp(void *a, void *b)
1607fb4f313dSNamhyung Kim {
1608fb4f313dSNamhyung Kim struct page_stat *l = a;
1609fb4f313dSNamhyung Kim struct page_stat *r = b;
1610fb4f313dSNamhyung Kim
1611fb4f313dSNamhyung Kim if (l->order < r->order)
1612fb4f313dSNamhyung Kim return -1;
1613fb4f313dSNamhyung Kim else if (l->order > r->order)
1614fb4f313dSNamhyung Kim return 1;
1615fb4f313dSNamhyung Kim return 0;
1616fb4f313dSNamhyung Kim }
1617fb4f313dSNamhyung Kim
1618fb4f313dSNamhyung Kim static struct sort_dimension page_order_sort_dimension = {
1619fb4f313dSNamhyung Kim .name = "order",
1620fb4f313dSNamhyung Kim .cmp = page_order_cmp,
1621fb4f313dSNamhyung Kim };
1622fb4f313dSNamhyung Kim
migrate_type_cmp(void * a,void * b)1623fb4f313dSNamhyung Kim static int migrate_type_cmp(void *a, void *b)
1624fb4f313dSNamhyung Kim {
1625fb4f313dSNamhyung Kim struct page_stat *l = a;
1626fb4f313dSNamhyung Kim struct page_stat *r = b;
1627fb4f313dSNamhyung Kim
1628fb4f313dSNamhyung Kim /* for internal use to find free'd page */
1629fb4f313dSNamhyung Kim if (l->migrate_type == -1U)
1630fb4f313dSNamhyung Kim return 0;
1631fb4f313dSNamhyung Kim
1632fb4f313dSNamhyung Kim if (l->migrate_type < r->migrate_type)
1633fb4f313dSNamhyung Kim return -1;
1634fb4f313dSNamhyung Kim else if (l->migrate_type > r->migrate_type)
1635fb4f313dSNamhyung Kim return 1;
1636fb4f313dSNamhyung Kim return 0;
1637fb4f313dSNamhyung Kim }
1638fb4f313dSNamhyung Kim
1639fb4f313dSNamhyung Kim static struct sort_dimension migrate_type_sort_dimension = {
1640fb4f313dSNamhyung Kim .name = "migtype",
1641fb4f313dSNamhyung Kim .cmp = migrate_type_cmp,
1642fb4f313dSNamhyung Kim };
1643fb4f313dSNamhyung Kim
gfp_flags_cmp(void * a,void * b)1644fb4f313dSNamhyung Kim static int gfp_flags_cmp(void *a, void *b)
1645fb4f313dSNamhyung Kim {
1646fb4f313dSNamhyung Kim struct page_stat *l = a;
1647fb4f313dSNamhyung Kim struct page_stat *r = b;
1648fb4f313dSNamhyung Kim
1649fb4f313dSNamhyung Kim /* for internal use to find free'd page */
1650fb4f313dSNamhyung Kim if (l->gfp_flags == -1U)
1651fb4f313dSNamhyung Kim return 0;
1652fb4f313dSNamhyung Kim
1653fb4f313dSNamhyung Kim if (l->gfp_flags < r->gfp_flags)
1654fb4f313dSNamhyung Kim return -1;
1655fb4f313dSNamhyung Kim else if (l->gfp_flags > r->gfp_flags)
1656fb4f313dSNamhyung Kim return 1;
1657fb4f313dSNamhyung Kim return 0;
1658fb4f313dSNamhyung Kim }
1659fb4f313dSNamhyung Kim
1660fb4f313dSNamhyung Kim static struct sort_dimension gfp_flags_sort_dimension = {
1661fb4f313dSNamhyung Kim .name = "gfp",
1662fb4f313dSNamhyung Kim .cmp = gfp_flags_cmp,
1663fb4f313dSNamhyung Kim };
1664fb4f313dSNamhyung Kim
1665fb4f313dSNamhyung Kim static struct sort_dimension *slab_sorts[] = {
166629b3e152SLi Zefan &ptr_sort_dimension,
166729b3e152SLi Zefan &callsite_sort_dimension,
166829b3e152SLi Zefan &hit_sort_dimension,
166929b3e152SLi Zefan &bytes_sort_dimension,
167029b3e152SLi Zefan &frag_sort_dimension,
1671079d3f65SLi Zefan &pingpong_sort_dimension,
167229b3e152SLi Zefan };
167329b3e152SLi Zefan
1674fb4f313dSNamhyung Kim static struct sort_dimension *page_sorts[] = {
1675fb4f313dSNamhyung Kim &page_sort_dimension,
1676fb4f313dSNamhyung Kim &page_callsite_sort_dimension,
1677fb4f313dSNamhyung Kim &page_hit_sort_dimension,
1678fb4f313dSNamhyung Kim &page_bytes_sort_dimension,
1679fb4f313dSNamhyung Kim &page_order_sort_dimension,
1680fb4f313dSNamhyung Kim &migrate_type_sort_dimension,
1681fb4f313dSNamhyung Kim &gfp_flags_sort_dimension,
1682fb4f313dSNamhyung Kim };
168329b3e152SLi Zefan
slab_sort_dimension__add(const char * tok,struct list_head * list)1684fb4f313dSNamhyung Kim static int slab_sort_dimension__add(const char *tok, struct list_head *list)
168529b3e152SLi Zefan {
168629b3e152SLi Zefan struct sort_dimension *sort;
168729b3e152SLi Zefan int i;
168829b3e152SLi Zefan
1689fb4f313dSNamhyung Kim for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
1690fb4f313dSNamhyung Kim if (!strcmp(slab_sorts[i]->name, tok)) {
1691fb4f313dSNamhyung Kim sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
16922814eb05SArnaldo Carvalho de Melo if (!sort) {
16938d9233f2SArnaldo Carvalho de Melo pr_err("%s: memdup failed\n", __func__);
16942814eb05SArnaldo Carvalho de Melo return -1;
16952814eb05SArnaldo Carvalho de Melo }
169629b3e152SLi Zefan list_add_tail(&sort->list, list);
169729b3e152SLi Zefan return 0;
169829b3e152SLi Zefan }
169929b3e152SLi Zefan }
170029b3e152SLi Zefan
170129b3e152SLi Zefan return -1;
170229b3e152SLi Zefan }
170329b3e152SLi Zefan
page_sort_dimension__add(const char * tok,struct list_head * list)1704fb4f313dSNamhyung Kim static int page_sort_dimension__add(const char *tok, struct list_head *list)
1705fb4f313dSNamhyung Kim {
1706fb4f313dSNamhyung Kim struct sort_dimension *sort;
1707fb4f313dSNamhyung Kim int i;
1708fb4f313dSNamhyung Kim
1709fb4f313dSNamhyung Kim for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
1710fb4f313dSNamhyung Kim if (!strcmp(page_sorts[i]->name, tok)) {
1711fb4f313dSNamhyung Kim sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
1712fb4f313dSNamhyung Kim if (!sort) {
1713fb4f313dSNamhyung Kim pr_err("%s: memdup failed\n", __func__);
1714fb4f313dSNamhyung Kim return -1;
1715fb4f313dSNamhyung Kim }
1716fb4f313dSNamhyung Kim list_add_tail(&sort->list, list);
1717fb4f313dSNamhyung Kim return 0;
1718fb4f313dSNamhyung Kim }
1719fb4f313dSNamhyung Kim }
1720fb4f313dSNamhyung Kim
1721fb4f313dSNamhyung Kim return -1;
1722fb4f313dSNamhyung Kim }
1723fb4f313dSNamhyung Kim
setup_slab_sorting(struct list_head * sort_list,const char * arg)1724fb4f313dSNamhyung Kim static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
172529b3e152SLi Zefan {
172629b3e152SLi Zefan char *tok;
172729b3e152SLi Zefan char *str = strdup(arg);
1728405f8755SNamhyung Kim char *pos = str;
172929b3e152SLi Zefan
17302814eb05SArnaldo Carvalho de Melo if (!str) {
17312814eb05SArnaldo Carvalho de Melo pr_err("%s: strdup failed\n", __func__);
17322814eb05SArnaldo Carvalho de Melo return -1;
17332814eb05SArnaldo Carvalho de Melo }
173429b3e152SLi Zefan
173529b3e152SLi Zefan while (true) {
1736405f8755SNamhyung Kim tok = strsep(&pos, ",");
173729b3e152SLi Zefan if (!tok)
173829b3e152SLi Zefan break;
1739fb4f313dSNamhyung Kim if (slab_sort_dimension__add(tok, sort_list) < 0) {
174062d94b00SArnaldo Carvalho de Melo pr_err("Unknown slab --sort key: '%s'", tok);
1741fb4f313dSNamhyung Kim free(str);
1742fb4f313dSNamhyung Kim return -1;
1743fb4f313dSNamhyung Kim }
1744fb4f313dSNamhyung Kim }
1745fb4f313dSNamhyung Kim
1746fb4f313dSNamhyung Kim free(str);
1747fb4f313dSNamhyung Kim return 0;
1748fb4f313dSNamhyung Kim }
1749fb4f313dSNamhyung Kim
setup_page_sorting(struct list_head * sort_list,const char * arg)1750fb4f313dSNamhyung Kim static int setup_page_sorting(struct list_head *sort_list, const char *arg)
1751fb4f313dSNamhyung Kim {
1752fb4f313dSNamhyung Kim char *tok;
1753fb4f313dSNamhyung Kim char *str = strdup(arg);
1754fb4f313dSNamhyung Kim char *pos = str;
1755fb4f313dSNamhyung Kim
1756fb4f313dSNamhyung Kim if (!str) {
1757fb4f313dSNamhyung Kim pr_err("%s: strdup failed\n", __func__);
1758fb4f313dSNamhyung Kim return -1;
1759fb4f313dSNamhyung Kim }
1760fb4f313dSNamhyung Kim
1761fb4f313dSNamhyung Kim while (true) {
1762fb4f313dSNamhyung Kim tok = strsep(&pos, ",");
1763fb4f313dSNamhyung Kim if (!tok)
1764fb4f313dSNamhyung Kim break;
1765fb4f313dSNamhyung Kim if (page_sort_dimension__add(tok, sort_list) < 0) {
176662d94b00SArnaldo Carvalho de Melo pr_err("Unknown page --sort key: '%s'", tok);
17671b22859dSNamhyung Kim free(str);
176829b3e152SLi Zefan return -1;
176929b3e152SLi Zefan }
177029b3e152SLi Zefan }
177129b3e152SLi Zefan
177229b3e152SLi Zefan free(str);
177329b3e152SLi Zefan return 0;
177429b3e152SLi Zefan }
177529b3e152SLi Zefan
parse_sort_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)17761d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused,
17771d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused)
1778ba77c9e1SLi Zefan {
1779ba77c9e1SLi Zefan if (!arg)
1780ba77c9e1SLi Zefan return -1;
1781ba77c9e1SLi Zefan
17820c160d49SNamhyung Kim if (kmem_page > kmem_slab ||
17830c160d49SNamhyung Kim (kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) {
1784ba77c9e1SLi Zefan if (caller_flag > alloc_flag)
1785fb4f313dSNamhyung Kim return setup_page_sorting(&page_caller_sort, arg);
1786ba77c9e1SLi Zefan else
1787fb4f313dSNamhyung Kim return setup_page_sorting(&page_alloc_sort, arg);
1788fb4f313dSNamhyung Kim } else {
1789fb4f313dSNamhyung Kim if (caller_flag > alloc_flag)
1790fb4f313dSNamhyung Kim return setup_slab_sorting(&slab_caller_sort, arg);
1791fb4f313dSNamhyung Kim else
1792fb4f313dSNamhyung Kim return setup_slab_sorting(&slab_alloc_sort, arg);
1793fb4f313dSNamhyung Kim }
1794ba77c9e1SLi Zefan
1795ba77c9e1SLi Zefan return 0;
1796ba77c9e1SLi Zefan }
1797ba77c9e1SLi Zefan
parse_caller_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)17981d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused,
17991d037ca1SIrina Tirdea const char *arg __maybe_unused,
18001d037ca1SIrina Tirdea int unset __maybe_unused)
1801ba77c9e1SLi Zefan {
1802ba77c9e1SLi Zefan caller_flag = (alloc_flag + 1);
180390b86a9fSLi Zefan return 0;
180490b86a9fSLi Zefan }
180590b86a9fSLi Zefan
parse_alloc_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18061d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused,
18071d037ca1SIrina Tirdea const char *arg __maybe_unused,
18081d037ca1SIrina Tirdea int unset __maybe_unused)
180990b86a9fSLi Zefan {
181090b86a9fSLi Zefan alloc_flag = (caller_flag + 1);
1811ba77c9e1SLi Zefan return 0;
1812ba77c9e1SLi Zefan }
1813ba77c9e1SLi Zefan
parse_slab_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18140d68bc92SNamhyung Kim static int parse_slab_opt(const struct option *opt __maybe_unused,
18150d68bc92SNamhyung Kim const char *arg __maybe_unused,
18160d68bc92SNamhyung Kim int unset __maybe_unused)
18170d68bc92SNamhyung Kim {
18180d68bc92SNamhyung Kim kmem_slab = (kmem_page + 1);
18190d68bc92SNamhyung Kim return 0;
18200d68bc92SNamhyung Kim }
18210d68bc92SNamhyung Kim
parse_page_opt(const struct option * opt __maybe_unused,const char * arg __maybe_unused,int unset __maybe_unused)18220d68bc92SNamhyung Kim static int parse_page_opt(const struct option *opt __maybe_unused,
18230d68bc92SNamhyung Kim const char *arg __maybe_unused,
18240d68bc92SNamhyung Kim int unset __maybe_unused)
18250d68bc92SNamhyung Kim {
18260d68bc92SNamhyung Kim kmem_page = (kmem_slab + 1);
18270d68bc92SNamhyung Kim return 0;
18280d68bc92SNamhyung Kim }
18290d68bc92SNamhyung Kim
parse_line_opt(const struct option * opt __maybe_unused,const char * arg,int unset __maybe_unused)18301d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused,
18311d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused)
1832ba77c9e1SLi Zefan {
1833ba77c9e1SLi Zefan int lines;
1834ba77c9e1SLi Zefan
1835ba77c9e1SLi Zefan if (!arg)
1836ba77c9e1SLi Zefan return -1;
1837ba77c9e1SLi Zefan
1838ba77c9e1SLi Zefan lines = strtoul(arg, NULL, 10);
1839ba77c9e1SLi Zefan
1840ba77c9e1SLi Zefan if (caller_flag > alloc_flag)
1841ba77c9e1SLi Zefan caller_lines = lines;
1842ba77c9e1SLi Zefan else
1843ba77c9e1SLi Zefan alloc_lines = lines;
1844ba77c9e1SLi Zefan
1845ba77c9e1SLi Zefan return 0;
1846ba77c9e1SLi Zefan }
1847ba77c9e1SLi Zefan
slab_legacy_tp_is_exposed(void)1848b3719108SLeo Yan static bool slab_legacy_tp_is_exposed(void)
1849b3719108SLeo Yan {
1850b3719108SLeo Yan /*
1851b3719108SLeo Yan * The tracepoints "kmem:kmalloc_node" and
1852b3719108SLeo Yan * "kmem:kmem_cache_alloc_node" have been removed on the latest
1853b3719108SLeo Yan * kernel, if the tracepoint "kmem:kmalloc_node" is existed it
1854b3719108SLeo Yan * means the tool is running on an old kernel, we need to
1855b3719108SLeo Yan * rollback to support these legacy tracepoints.
1856b3719108SLeo Yan */
1857b3719108SLeo Yan return IS_ERR(trace_event__tp_format("kmem", "kmalloc_node")) ?
1858b3719108SLeo Yan false : true;
1859b3719108SLeo Yan }
1860b3719108SLeo Yan
__cmd_record(int argc,const char ** argv)18610433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv)
18620433ffbeSArnaldo Carvalho de Melo {
18630433ffbeSArnaldo Carvalho de Melo const char * const record_args[] = {
18644a4d371aSJiri Olsa "record", "-a", "-R", "-c", "1",
18650d68bc92SNamhyung Kim };
18660d68bc92SNamhyung Kim const char * const slab_events[] = {
1867ba77c9e1SLi Zefan "-e", "kmem:kmalloc",
1868ba77c9e1SLi Zefan "-e", "kmem:kfree",
1869ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_alloc",
1870ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_free",
1871ba77c9e1SLi Zefan };
1872b3719108SLeo Yan const char * const slab_legacy_events[] = {
1873b3719108SLeo Yan "-e", "kmem:kmalloc_node",
1874b3719108SLeo Yan "-e", "kmem:kmem_cache_alloc_node",
1875b3719108SLeo Yan };
18760d68bc92SNamhyung Kim const char * const page_events[] = {
18770d68bc92SNamhyung Kim "-e", "kmem:mm_page_alloc",
18780d68bc92SNamhyung Kim "-e", "kmem:mm_page_free",
18790d68bc92SNamhyung Kim };
1880ba77c9e1SLi Zefan unsigned int rec_argc, i, j;
1881ba77c9e1SLi Zefan const char **rec_argv;
1882b3719108SLeo Yan unsigned int slab_legacy_tp_exposed = slab_legacy_tp_is_exposed();
1883ba77c9e1SLi Zefan
1884ba77c9e1SLi Zefan rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1885b3719108SLeo Yan if (kmem_slab) {
18860d68bc92SNamhyung Kim rec_argc += ARRAY_SIZE(slab_events);
1887b3719108SLeo Yan if (slab_legacy_tp_exposed)
1888b3719108SLeo Yan rec_argc += ARRAY_SIZE(slab_legacy_events);
1889b3719108SLeo Yan }
18900d68bc92SNamhyung Kim if (kmem_page)
1891c9758cc4SNamhyung Kim rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
18920d68bc92SNamhyung Kim
1893ba77c9e1SLi Zefan rec_argv = calloc(rec_argc + 1, sizeof(char *));
1894ba77c9e1SLi Zefan
1895ce47dc56SChris Samuel if (rec_argv == NULL)
1896ce47dc56SChris Samuel return -ENOMEM;
1897ce47dc56SChris Samuel
1898ba77c9e1SLi Zefan for (i = 0; i < ARRAY_SIZE(record_args); i++)
1899ba77c9e1SLi Zefan rec_argv[i] = strdup(record_args[i]);
1900ba77c9e1SLi Zefan
19010d68bc92SNamhyung Kim if (kmem_slab) {
19020d68bc92SNamhyung Kim for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
19030d68bc92SNamhyung Kim rec_argv[i] = strdup(slab_events[j]);
1904b3719108SLeo Yan if (slab_legacy_tp_exposed) {
1905b3719108SLeo Yan for (j = 0; j < ARRAY_SIZE(slab_legacy_events); j++, i++)
1906b3719108SLeo Yan rec_argv[i] = strdup(slab_legacy_events[j]);
1907b3719108SLeo Yan }
19080d68bc92SNamhyung Kim }
19090d68bc92SNamhyung Kim if (kmem_page) {
1910c9758cc4SNamhyung Kim rec_argv[i++] = strdup("-g");
1911c9758cc4SNamhyung Kim
19120d68bc92SNamhyung Kim for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
19130d68bc92SNamhyung Kim rec_argv[i] = strdup(page_events[j]);
19140d68bc92SNamhyung Kim }
19150d68bc92SNamhyung Kim
1916ba77c9e1SLi Zefan for (j = 1; j < (unsigned int)argc; j++, i++)
1917ba77c9e1SLi Zefan rec_argv[i] = argv[j];
1918ba77c9e1SLi Zefan
1919b0ad8ea6SArnaldo Carvalho de Melo return cmd_record(i, rec_argv);
1920ba77c9e1SLi Zefan }
1921ba77c9e1SLi Zefan
kmem_config(const char * var,const char * value,void * cb __maybe_unused)1922b8cbb349SWang Nan static int kmem_config(const char *var, const char *value, void *cb __maybe_unused)
19230c160d49SNamhyung Kim {
19240c160d49SNamhyung Kim if (!strcmp(var, "kmem.default")) {
19250c160d49SNamhyung Kim if (!strcmp(value, "slab"))
19260c160d49SNamhyung Kim kmem_default = KMEM_SLAB;
19270c160d49SNamhyung Kim else if (!strcmp(value, "page"))
19280c160d49SNamhyung Kim kmem_default = KMEM_PAGE;
19290c160d49SNamhyung Kim else
19300c160d49SNamhyung Kim pr_err("invalid default value ('slab' or 'page' required): %s\n",
19310c160d49SNamhyung Kim value);
19320c160d49SNamhyung Kim return 0;
19330c160d49SNamhyung Kim }
19340c160d49SNamhyung Kim
1935b8cbb349SWang Nan return 0;
19360c160d49SNamhyung Kim }
19370c160d49SNamhyung Kim
cmd_kmem(int argc,const char ** argv)1938b0ad8ea6SArnaldo Carvalho de Melo int cmd_kmem(int argc, const char **argv)
1939ba77c9e1SLi Zefan {
1940fb4f313dSNamhyung Kim const char * const default_slab_sort = "frag,hit,bytes";
1941fb4f313dSNamhyung Kim const char * const default_page_sort = "bytes,hit";
19428ceb41d7SJiri Olsa struct perf_data data = {
1943d1eeb77cSYunlong Song .mode = PERF_DATA_MODE_READ,
1944d1eeb77cSYunlong Song };
19450433ffbeSArnaldo Carvalho de Melo const struct option kmem_options[] = {
19460433ffbeSArnaldo Carvalho de Melo OPT_STRING('i', "input", &input_name, "file", "input file name"),
1947bd72a33eSNamhyung Kim OPT_INCR('v', "verbose", &verbose,
1948bd72a33eSNamhyung Kim "be more verbose (show symbol address, etc)"),
19490433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
19500433ffbeSArnaldo Carvalho de Melo "show per-callsite statistics", parse_caller_opt),
19510433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
19520433ffbeSArnaldo Carvalho de Melo "show per-allocation statistics", parse_alloc_opt),
19530433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
1954fb4f313dSNamhyung Kim "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
1955fb4f313dSNamhyung Kim "page, order, migtype, gfp", parse_sort_opt),
19560433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
19570433ffbeSArnaldo Carvalho de Melo OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
19588ceb41d7SJiri Olsa OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"),
19590d68bc92SNamhyung Kim OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
19600d68bc92SNamhyung Kim parse_slab_opt),
19610d68bc92SNamhyung Kim OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
19620d68bc92SNamhyung Kim parse_page_opt),
19632a7ef02cSNamhyung Kim OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
19642a865bd8SDavid Ahern OPT_STRING(0, "time", &time_str, "str",
19652a865bd8SDavid Ahern "Time span of interest (start,stop)"),
19660433ffbeSArnaldo Carvalho de Melo OPT_END()
19670433ffbeSArnaldo Carvalho de Melo };
19683bca2354SRamkumar Ramachandra const char *const kmem_subcommands[] = { "record", "stat", NULL };
19693bca2354SRamkumar Ramachandra const char *kmem_usage[] = {
19703bca2354SRamkumar Ramachandra NULL,
19710433ffbeSArnaldo Carvalho de Melo NULL
19720433ffbeSArnaldo Carvalho de Melo };
19732b2b2c68SNamhyung Kim struct perf_session *session;
197449b8e2beSRasmus Villemoes static const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n";
1975ecc4c561SArnaldo Carvalho de Melo int ret = perf_config(kmem_config, NULL);
19762b2b2c68SNamhyung Kim
1977ecc4c561SArnaldo Carvalho de Melo if (ret)
1978ecc4c561SArnaldo Carvalho de Melo return ret;
1979ecc4c561SArnaldo Carvalho de Melo
19803bca2354SRamkumar Ramachandra argc = parse_options_subcommand(argc, argv, kmem_options,
1981be8299e4SIan Rogers kmem_subcommands, kmem_usage,
1982be8299e4SIan Rogers PARSE_OPT_STOP_AT_NON_OPTION);
1983ba77c9e1SLi Zefan
198490b86a9fSLi Zefan if (!argc)
1985ba77c9e1SLi Zefan usage_with_options(kmem_usage, kmem_options);
1986ba77c9e1SLi Zefan
19870c160d49SNamhyung Kim if (kmem_slab == 0 && kmem_page == 0) {
19880c160d49SNamhyung Kim if (kmem_default == KMEM_SLAB)
19890c160d49SNamhyung Kim kmem_slab = 1;
19900c160d49SNamhyung Kim else
19910c160d49SNamhyung Kim kmem_page = 1;
19920c160d49SNamhyung Kim }
19930d68bc92SNamhyung Kim
1994ae0f4eb3SWei Li if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
19950a7e6d1bSNamhyung Kim symbol__init(NULL);
19962b2b2c68SNamhyung Kim return __cmd_record(argc, argv);
19972b2b2c68SNamhyung Kim }
19982b2b2c68SNamhyung Kim
19992d4f2799SJiri Olsa data.path = input_name;
200028939e1aSJiri Olsa
20012681bd85SNamhyung Kim kmem_session = session = perf_session__new(&data, &perf_kmem);
20026ef81c55SMamatha Inamdar if (IS_ERR(session))
20036ef81c55SMamatha Inamdar return PTR_ERR(session);
20042b2b2c68SNamhyung Kim
2005ecc4c561SArnaldo Carvalho de Melo ret = -1;
2006ecc4c561SArnaldo Carvalho de Melo
2007a923e2c4SNamhyung Kim if (kmem_slab) {
2008b02736f7SArnaldo Carvalho de Melo if (!evlist__find_tracepoint_by_name(session->evlist, "kmem:kmalloc")) {
2009a923e2c4SNamhyung Kim pr_err(errmsg, "slab", "slab");
2010249ca1a8STaeung Song goto out_delete;
2011a923e2c4SNamhyung Kim }
2012a923e2c4SNamhyung Kim }
20130d68bc92SNamhyung Kim
2014a923e2c4SNamhyung Kim if (kmem_page) {
2015b02736f7SArnaldo Carvalho de Melo struct evsel *evsel = evlist__find_tracepoint_by_name(session->evlist, "kmem:mm_page_alloc");
2016a923e2c4SNamhyung Kim
2017a923e2c4SNamhyung Kim if (evsel == NULL) {
2018a923e2c4SNamhyung Kim pr_err(errmsg, "page", "page");
2019249ca1a8STaeung Song goto out_delete;
20200d68bc92SNamhyung Kim }
20210d68bc92SNamhyung Kim
202269769ce1STzvetomir Stoyanov kmem_page_size = tep_get_page_size(evsel->tp_format->tep);
2023c9758cc4SNamhyung Kim symbol_conf.use_callchain = true;
20240d68bc92SNamhyung Kim }
20250d68bc92SNamhyung Kim
20260a7e6d1bSNamhyung Kim symbol__init(&session->header.env);
2027655000e7SArnaldo Carvalho de Melo
20282a865bd8SDavid Ahern if (perf_time__parse_str(&ptime, time_str) != 0) {
20292a865bd8SDavid Ahern pr_err("Invalid time string\n");
203079f56ebeSChristophe JAILLET ret = -EINVAL;
203179f56ebeSChristophe JAILLET goto out_delete;
20322a865bd8SDavid Ahern }
20332a865bd8SDavid Ahern
20342b2b2c68SNamhyung Kim if (!strcmp(argv[0], "stat")) {
203577cfe388SNamhyung Kim setlocale(LC_ALL, "");
203677cfe388SNamhyung Kim
20374b627957SDon Zickus if (cpu__setup_cpunode_map())
20382b2b2c68SNamhyung Kim goto out_delete;
203990b86a9fSLi Zefan
2040fb4f313dSNamhyung Kim if (list_empty(&slab_caller_sort))
2041fb4f313dSNamhyung Kim setup_slab_sorting(&slab_caller_sort, default_slab_sort);
2042fb4f313dSNamhyung Kim if (list_empty(&slab_alloc_sort))
2043fb4f313dSNamhyung Kim setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
2044fb4f313dSNamhyung Kim if (list_empty(&page_caller_sort))
2045fb4f313dSNamhyung Kim setup_page_sorting(&page_caller_sort, default_page_sort);
2046fb4f313dSNamhyung Kim if (list_empty(&page_alloc_sort))
2047fb4f313dSNamhyung Kim setup_page_sorting(&page_alloc_sort, default_page_sort);
2048ba77c9e1SLi Zefan
2049fb4f313dSNamhyung Kim if (kmem_page) {
2050fb4f313dSNamhyung Kim setup_page_sorting(&page_alloc_sort_input,
2051fb4f313dSNamhyung Kim "page,order,migtype,gfp");
2052fb4f313dSNamhyung Kim setup_page_sorting(&page_caller_sort_input,
2053fb4f313dSNamhyung Kim "callsite,order,migtype,gfp");
2054fb4f313dSNamhyung Kim }
20552b2b2c68SNamhyung Kim ret = __cmd_kmem(session);
2056b00eca8cSPekka Enberg } else
2057b00eca8cSPekka Enberg usage_with_options(kmem_usage, kmem_options);
2058ba77c9e1SLi Zefan
20592b2b2c68SNamhyung Kim out_delete:
20602b2b2c68SNamhyung Kim perf_session__delete(session);
2061*ab656dd9SAditya Gupta /* free usage string allocated by parse_options_subcommand */
2062*ab656dd9SAditya Gupta free((void *)kmem_usage[0]);
20632b2b2c68SNamhyung Kim
20642b2b2c68SNamhyung Kim return ret;
206590b86a9fSLi Zefan }
206690b86a9fSLi Zefan
2067