Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/builtin-kmem.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include "builtin.h"
3
4
#include "util/dso.h"
5
#include "util/evlist.h"
6
#include "util/evsel.h"
7
#include "util/config.h"
8
#include "util/map.h"
9
#include "util/symbol.h"
10
#include "util/thread.h"
11
#include "util/header.h"
12
#include "util/session.h"
13
#include "util/tool.h"
14
#include "util/callchain.h"
15
#include "util/time-utils.h"
16
#include <linux/err.h>
17
18
#include <subcmd/pager.h>
19
#include <subcmd/parse-options.h>
20
#include "util/trace-event.h"
21
#include "util/data.h"
22
#include "util/cpumap.h"
23
24
#include "util/debug.h"
25
#include "util/string2.h"
26
#include "util/util.h"
27
28
#include <linux/kernel.h>
29
#include <linux/numa.h>
30
#include <linux/rbtree.h>
31
#include <linux/string.h>
32
#include <linux/zalloc.h>
33
#include <errno.h>
34
#include <inttypes.h>
35
#include <locale.h>
36
#include <regex.h>
37
38
#include <linux/ctype.h>
39
#include <event-parse.h>
40
41
static int kmem_slab;
42
static int kmem_page;
43
44
static long kmem_page_size;
45
static enum {
46
KMEM_SLAB,
47
KMEM_PAGE,
48
} kmem_default = KMEM_SLAB; /* for backward compatibility */
49
50
struct alloc_stat;
51
typedef int (*sort_fn_t)(void *, void *);
52
53
static int alloc_flag;
54
static int caller_flag;
55
56
static int alloc_lines = -1;
57
static int caller_lines = -1;
58
59
static bool raw_ip;
60
61
struct alloc_stat {
62
u64 call_site;
63
u64 ptr;
64
u64 bytes_req;
65
u64 bytes_alloc;
66
u64 last_alloc;
67
u32 hit;
68
u32 pingpong;
69
70
short alloc_cpu;
71
72
struct rb_node node;
73
};
74
75
static struct rb_root root_alloc_stat;
76
static struct rb_root root_alloc_sorted;
77
static struct rb_root root_caller_stat;
78
static struct rb_root root_caller_sorted;
79
80
static unsigned long total_requested, total_allocated, total_freed;
81
static unsigned long nr_allocs, nr_cross_allocs;
82
83
/* filters for controlling start and stop of time of analysis */
84
static struct perf_time_interval ptime;
85
const char *time_str;
86
87
static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
88
int bytes_req, int bytes_alloc, int cpu)
89
{
90
struct rb_node **node = &root_alloc_stat.rb_node;
91
struct rb_node *parent = NULL;
92
struct alloc_stat *data = NULL;
93
94
while (*node) {
95
parent = *node;
96
data = rb_entry(*node, struct alloc_stat, node);
97
98
if (ptr > data->ptr)
99
node = &(*node)->rb_right;
100
else if (ptr < data->ptr)
101
node = &(*node)->rb_left;
102
else
103
break;
104
}
105
106
if (data && data->ptr == ptr) {
107
data->hit++;
108
data->bytes_req += bytes_req;
109
data->bytes_alloc += bytes_alloc;
110
} else {
111
data = malloc(sizeof(*data));
112
if (!data) {
113
pr_err("%s: malloc failed\n", __func__);
114
return -1;
115
}
116
data->ptr = ptr;
117
data->pingpong = 0;
118
data->hit = 1;
119
data->bytes_req = bytes_req;
120
data->bytes_alloc = bytes_alloc;
121
122
rb_link_node(&data->node, parent, node);
123
rb_insert_color(&data->node, &root_alloc_stat);
124
}
125
data->call_site = call_site;
126
data->alloc_cpu = cpu;
127
data->last_alloc = bytes_alloc;
128
129
return 0;
130
}
131
132
static int insert_caller_stat(unsigned long call_site,
133
int bytes_req, int bytes_alloc)
134
{
135
struct rb_node **node = &root_caller_stat.rb_node;
136
struct rb_node *parent = NULL;
137
struct alloc_stat *data = NULL;
138
139
while (*node) {
140
parent = *node;
141
data = rb_entry(*node, struct alloc_stat, node);
142
143
if (call_site > data->call_site)
144
node = &(*node)->rb_right;
145
else if (call_site < data->call_site)
146
node = &(*node)->rb_left;
147
else
148
break;
149
}
150
151
if (data && data->call_site == call_site) {
152
data->hit++;
153
data->bytes_req += bytes_req;
154
data->bytes_alloc += bytes_alloc;
155
} else {
156
data = malloc(sizeof(*data));
157
if (!data) {
158
pr_err("%s: malloc failed\n", __func__);
159
return -1;
160
}
161
data->call_site = call_site;
162
data->pingpong = 0;
163
data->hit = 1;
164
data->bytes_req = bytes_req;
165
data->bytes_alloc = bytes_alloc;
166
167
rb_link_node(&data->node, parent, node);
168
rb_insert_color(&data->node, &root_caller_stat);
169
}
170
171
return 0;
172
}
173
174
static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample)
175
{
176
unsigned long ptr = evsel__intval(evsel, sample, "ptr"),
177
call_site = evsel__intval(evsel, sample, "call_site");
178
int bytes_req = evsel__intval(evsel, sample, "bytes_req"),
179
bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc");
180
181
if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
182
insert_caller_stat(call_site, bytes_req, bytes_alloc))
183
return -1;
184
185
total_requested += bytes_req;
186
total_allocated += bytes_alloc;
187
188
nr_allocs++;
189
190
/*
191
* Commit 11e9734bcb6a ("mm/slab_common: unify NUMA and UMA
192
* version of tracepoints") adds the field "node" into the
193
* tracepoints 'kmalloc' and 'kmem_cache_alloc'.
194
*
195
* The legacy tracepoints 'kmalloc_node' and 'kmem_cache_alloc_node'
196
* also contain the field "node".
197
*
198
* If the tracepoint contains the field "node" the tool stats the
199
* cross allocation.
200
*/
201
if (evsel__field(evsel, "node")) {
202
int node1, node2;
203
204
node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu});
205
node2 = evsel__intval(evsel, sample, "node");
206
207
/*
208
* If the field "node" is NUMA_NO_NODE (-1), we don't take it
209
* as a cross allocation.
210
*/
211
if ((node2 != NUMA_NO_NODE) && (node1 != node2))
212
nr_cross_allocs++;
213
}
214
215
return 0;
216
}
217
218
static int ptr_cmp(void *, void *);
219
static int slab_callsite_cmp(void *, void *);
220
221
static struct alloc_stat *search_alloc_stat(unsigned long ptr,
222
unsigned long call_site,
223
struct rb_root *root,
224
sort_fn_t sort_fn)
225
{
226
struct rb_node *node = root->rb_node;
227
struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
228
229
while (node) {
230
struct alloc_stat *data;
231
int cmp;
232
233
data = rb_entry(node, struct alloc_stat, node);
234
235
cmp = sort_fn(&key, data);
236
if (cmp < 0)
237
node = node->rb_left;
238
else if (cmp > 0)
239
node = node->rb_right;
240
else
241
return data;
242
}
243
return NULL;
244
}
245
246
static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample)
247
{
248
unsigned long ptr = evsel__intval(evsel, sample, "ptr");
249
struct alloc_stat *s_alloc, *s_caller;
250
251
s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
252
if (!s_alloc)
253
return 0;
254
255
total_freed += s_alloc->last_alloc;
256
257
if ((short)sample->cpu != s_alloc->alloc_cpu) {
258
s_alloc->pingpong++;
259
260
s_caller = search_alloc_stat(0, s_alloc->call_site,
261
&root_caller_stat,
262
slab_callsite_cmp);
263
if (!s_caller)
264
return -1;
265
s_caller->pingpong++;
266
}
267
s_alloc->alloc_cpu = -1;
268
269
return 0;
270
}
271
272
static u64 total_page_alloc_bytes;
273
static u64 total_page_free_bytes;
274
static u64 total_page_nomatch_bytes;
275
static u64 total_page_fail_bytes;
276
static unsigned long nr_page_allocs;
277
static unsigned long nr_page_frees;
278
static unsigned long nr_page_fails;
279
static unsigned long nr_page_nomatch;
280
281
static bool use_pfn;
282
static bool live_page;
283
static struct perf_session *kmem_session;
284
285
#define MAX_MIGRATE_TYPES 6
286
#define MAX_PAGE_ORDER 11
287
288
static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
289
290
struct page_stat {
291
struct rb_node node;
292
u64 page;
293
u64 callsite;
294
int order;
295
unsigned gfp_flags;
296
unsigned migrate_type;
297
u64 alloc_bytes;
298
u64 free_bytes;
299
int nr_alloc;
300
int nr_free;
301
};
302
303
static struct rb_root page_live_tree;
304
static struct rb_root page_alloc_tree;
305
static struct rb_root page_alloc_sorted;
306
static struct rb_root page_caller_tree;
307
static struct rb_root page_caller_sorted;
308
309
struct alloc_func {
310
u64 start;
311
u64 end;
312
char *name;
313
};
314
315
static int nr_alloc_funcs;
316
static struct alloc_func *alloc_func_list;
317
318
static int funcmp(const void *a, const void *b)
319
{
320
const struct alloc_func *fa = a;
321
const struct alloc_func *fb = b;
322
323
if (fa->start > fb->start)
324
return 1;
325
else
326
return -1;
327
}
328
329
static int callcmp(const void *a, const void *b)
330
{
331
const struct alloc_func *fa = a;
332
const struct alloc_func *fb = b;
333
334
if (fb->start <= fa->start && fa->end < fb->end)
335
return 0;
336
337
if (fa->start > fb->start)
338
return 1;
339
else
340
return -1;
341
}
342
343
static int build_alloc_func_list(void)
344
{
345
int ret;
346
struct map *kernel_map;
347
struct symbol *sym;
348
struct rb_node *node;
349
struct alloc_func *func;
350
struct machine *machine = &kmem_session->machines.host;
351
regex_t alloc_func_regex;
352
static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
353
354
ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
355
if (ret) {
356
char err[BUFSIZ];
357
358
regerror(ret, &alloc_func_regex, err, sizeof(err));
359
pr_err("Invalid regex: %s\n%s", pattern, err);
360
return -EINVAL;
361
}
362
363
kernel_map = machine__kernel_map(machine);
364
if (map__load(kernel_map) < 0) {
365
pr_err("cannot load kernel map\n");
366
return -ENOENT;
367
}
368
369
map__for_each_symbol(kernel_map, sym, node) {
370
if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
371
continue;
372
373
func = realloc(alloc_func_list,
374
(nr_alloc_funcs + 1) * sizeof(*func));
375
if (func == NULL)
376
return -ENOMEM;
377
378
pr_debug("alloc func: %s\n", sym->name);
379
func[nr_alloc_funcs].start = sym->start;
380
func[nr_alloc_funcs].end = sym->end;
381
func[nr_alloc_funcs].name = sym->name;
382
383
alloc_func_list = func;
384
nr_alloc_funcs++;
385
}
386
387
qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
388
389
regfree(&alloc_func_regex);
390
return 0;
391
}
392
393
/*
394
* Find first non-memory allocation function from callchain.
395
* The allocation functions are in the 'alloc_func_list'.
396
*/
397
static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
398
{
399
struct addr_location al;
400
struct machine *machine = &kmem_session->machines.host;
401
struct callchain_cursor_node *node;
402
struct callchain_cursor *cursor;
403
u64 result = sample->ip;
404
405
addr_location__init(&al);
406
if (alloc_func_list == NULL) {
407
if (build_alloc_func_list() < 0)
408
goto out;
409
}
410
411
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
412
413
cursor = get_tls_callchain_cursor();
414
if (cursor == NULL)
415
goto out;
416
417
sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16);
418
419
callchain_cursor_commit(cursor);
420
while (true) {
421
struct alloc_func key, *caller;
422
u64 addr;
423
424
node = callchain_cursor_current(cursor);
425
if (node == NULL)
426
break;
427
428
key.start = key.end = node->ip;
429
caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
430
sizeof(key), callcmp);
431
if (!caller) {
432
/* found */
433
if (node->ms.map)
434
addr = map__dso_unmap_ip(node->ms.map, node->ip);
435
else
436
addr = node->ip;
437
438
result = addr;
439
goto out;
440
} else
441
pr_debug3("skipping alloc function: %s\n", caller->name);
442
443
callchain_cursor_advance(cursor);
444
}
445
446
pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
447
out:
448
addr_location__exit(&al);
449
return result;
450
}
451
452
struct sort_dimension {
453
const char name[20];
454
sort_fn_t cmp;
455
struct list_head list;
456
};
457
458
static LIST_HEAD(page_alloc_sort_input);
459
static LIST_HEAD(page_caller_sort_input);
460
461
static struct page_stat *
462
__page_stat__findnew_page(struct page_stat *pstat, bool create)
463
{
464
struct rb_node **node = &page_live_tree.rb_node;
465
struct rb_node *parent = NULL;
466
struct page_stat *data;
467
468
while (*node) {
469
s64 cmp;
470
471
parent = *node;
472
data = rb_entry(*node, struct page_stat, node);
473
474
cmp = data->page - pstat->page;
475
if (cmp < 0)
476
node = &parent->rb_left;
477
else if (cmp > 0)
478
node = &parent->rb_right;
479
else
480
return data;
481
}
482
483
if (!create)
484
return NULL;
485
486
data = zalloc(sizeof(*data));
487
if (data != NULL) {
488
data->page = pstat->page;
489
data->order = pstat->order;
490
data->gfp_flags = pstat->gfp_flags;
491
data->migrate_type = pstat->migrate_type;
492
493
rb_link_node(&data->node, parent, node);
494
rb_insert_color(&data->node, &page_live_tree);
495
}
496
497
return data;
498
}
499
500
static struct page_stat *page_stat__find_page(struct page_stat *pstat)
501
{
502
return __page_stat__findnew_page(pstat, false);
503
}
504
505
static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
506
{
507
return __page_stat__findnew_page(pstat, true);
508
}
509
510
static struct page_stat *
511
__page_stat__findnew_alloc(struct page_stat *pstat, bool create)
512
{
513
struct rb_node **node = &page_alloc_tree.rb_node;
514
struct rb_node *parent = NULL;
515
struct page_stat *data;
516
struct sort_dimension *sort;
517
518
while (*node) {
519
int cmp = 0;
520
521
parent = *node;
522
data = rb_entry(*node, struct page_stat, node);
523
524
list_for_each_entry(sort, &page_alloc_sort_input, list) {
525
cmp = sort->cmp(pstat, data);
526
if (cmp)
527
break;
528
}
529
530
if (cmp < 0)
531
node = &parent->rb_left;
532
else if (cmp > 0)
533
node = &parent->rb_right;
534
else
535
return data;
536
}
537
538
if (!create)
539
return NULL;
540
541
data = zalloc(sizeof(*data));
542
if (data != NULL) {
543
data->page = pstat->page;
544
data->order = pstat->order;
545
data->gfp_flags = pstat->gfp_flags;
546
data->migrate_type = pstat->migrate_type;
547
548
rb_link_node(&data->node, parent, node);
549
rb_insert_color(&data->node, &page_alloc_tree);
550
}
551
552
return data;
553
}
554
555
static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
556
{
557
return __page_stat__findnew_alloc(pstat, false);
558
}
559
560
static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
561
{
562
return __page_stat__findnew_alloc(pstat, true);
563
}
564
565
static struct page_stat *
566
__page_stat__findnew_caller(struct page_stat *pstat, bool create)
567
{
568
struct rb_node **node = &page_caller_tree.rb_node;
569
struct rb_node *parent = NULL;
570
struct page_stat *data;
571
struct sort_dimension *sort;
572
573
while (*node) {
574
int cmp = 0;
575
576
parent = *node;
577
data = rb_entry(*node, struct page_stat, node);
578
579
list_for_each_entry(sort, &page_caller_sort_input, list) {
580
cmp = sort->cmp(pstat, data);
581
if (cmp)
582
break;
583
}
584
585
if (cmp < 0)
586
node = &parent->rb_left;
587
else if (cmp > 0)
588
node = &parent->rb_right;
589
else
590
return data;
591
}
592
593
if (!create)
594
return NULL;
595
596
data = zalloc(sizeof(*data));
597
if (data != NULL) {
598
data->callsite = pstat->callsite;
599
data->order = pstat->order;
600
data->gfp_flags = pstat->gfp_flags;
601
data->migrate_type = pstat->migrate_type;
602
603
rb_link_node(&data->node, parent, node);
604
rb_insert_color(&data->node, &page_caller_tree);
605
}
606
607
return data;
608
}
609
610
static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
611
{
612
return __page_stat__findnew_caller(pstat, false);
613
}
614
615
static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
616
{
617
return __page_stat__findnew_caller(pstat, true);
618
}
619
620
static bool valid_page(u64 pfn_or_page)
621
{
622
if (use_pfn && pfn_or_page == -1UL)
623
return false;
624
if (!use_pfn && pfn_or_page == 0)
625
return false;
626
return true;
627
}
628
629
struct gfp_flag {
630
unsigned int flags;
631
char *compact_str;
632
char *human_readable;
633
};
634
635
static struct gfp_flag *gfps;
636
static int nr_gfps;
637
638
static int gfpcmp(const void *a, const void *b)
639
{
640
const struct gfp_flag *fa = a;
641
const struct gfp_flag *fb = b;
642
643
return fa->flags - fb->flags;
644
}
645
646
/* see include/trace/events/mmflags.h */
647
static const struct {
648
const char *original;
649
const char *compact;
650
} gfp_compact_table[] = {
651
{ "GFP_TRANSHUGE", "THP" },
652
{ "GFP_TRANSHUGE_LIGHT", "THL" },
653
{ "GFP_HIGHUSER_MOVABLE", "HUM" },
654
{ "GFP_HIGHUSER", "HU" },
655
{ "GFP_USER", "U" },
656
{ "GFP_KERNEL_ACCOUNT", "KAC" },
657
{ "GFP_KERNEL", "K" },
658
{ "GFP_NOFS", "NF" },
659
{ "GFP_ATOMIC", "A" },
660
{ "GFP_NOIO", "NI" },
661
{ "GFP_NOWAIT", "NW" },
662
{ "GFP_DMA", "D" },
663
{ "__GFP_HIGHMEM", "HM" },
664
{ "GFP_DMA32", "D32" },
665
{ "__GFP_HIGH", "H" },
666
{ "__GFP_IO", "I" },
667
{ "__GFP_FS", "F" },
668
{ "__GFP_NOWARN", "NWR" },
669
{ "__GFP_RETRY_MAYFAIL", "R" },
670
{ "__GFP_NOFAIL", "NF" },
671
{ "__GFP_NORETRY", "NR" },
672
{ "__GFP_COMP", "C" },
673
{ "__GFP_ZERO", "Z" },
674
{ "__GFP_NOMEMALLOC", "NMA" },
675
{ "__GFP_MEMALLOC", "MA" },
676
{ "__GFP_HARDWALL", "HW" },
677
{ "__GFP_THISNODE", "TN" },
678
{ "__GFP_RECLAIMABLE", "RC" },
679
{ "__GFP_MOVABLE", "M" },
680
{ "__GFP_ACCOUNT", "AC" },
681
{ "__GFP_WRITE", "WR" },
682
{ "__GFP_RECLAIM", "R" },
683
{ "__GFP_DIRECT_RECLAIM", "DR" },
684
{ "__GFP_KSWAPD_RECLAIM", "KR" },
685
};
686
687
static size_t max_gfp_len;
688
689
static char *compact_gfp_flags(char *gfp_flags)
690
{
691
char *orig_flags = strdup(gfp_flags);
692
char *new_flags = NULL;
693
char *str, *pos = NULL;
694
size_t len = 0;
695
696
if (orig_flags == NULL)
697
return NULL;
698
699
str = strtok_r(orig_flags, "|", &pos);
700
while (str) {
701
size_t i;
702
char *new;
703
const char *cpt;
704
705
for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
706
if (strcmp(gfp_compact_table[i].original, str))
707
continue;
708
709
cpt = gfp_compact_table[i].compact;
710
new = realloc(new_flags, len + strlen(cpt) + 2);
711
if (new == NULL) {
712
free(new_flags);
713
free(orig_flags);
714
return NULL;
715
}
716
717
new_flags = new;
718
719
if (!len) {
720
strcpy(new_flags, cpt);
721
} else {
722
strcat(new_flags, "|");
723
strcat(new_flags, cpt);
724
len++;
725
}
726
727
len += strlen(cpt);
728
}
729
730
str = strtok_r(NULL, "|", &pos);
731
}
732
733
if (max_gfp_len < len)
734
max_gfp_len = len;
735
736
free(orig_flags);
737
return new_flags;
738
}
739
740
static char *compact_gfp_string(unsigned long gfp_flags)
741
{
742
struct gfp_flag key = {
743
.flags = gfp_flags,
744
};
745
struct gfp_flag *gfp;
746
747
gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
748
if (gfp)
749
return gfp->compact_str;
750
751
return NULL;
752
}
753
754
static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
755
unsigned int gfp_flags)
756
{
757
struct tep_record record = {
758
.cpu = sample->cpu,
759
.data = sample->raw_data,
760
.size = sample->raw_size,
761
};
762
struct trace_seq seq;
763
char *str, *pos = NULL;
764
const struct tep_event *tp_format;
765
766
if (nr_gfps) {
767
struct gfp_flag key = {
768
.flags = gfp_flags,
769
};
770
771
if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
772
return 0;
773
}
774
775
trace_seq_init(&seq);
776
tp_format = evsel__tp_format(evsel);
777
if (tp_format)
778
tep_print_event(tp_format->tep, &seq, &record, "%s", TEP_PRINT_INFO);
779
780
str = strtok_r(seq.buffer, " ", &pos);
781
while (str) {
782
if (!strncmp(str, "gfp_flags=", 10)) {
783
struct gfp_flag *new;
784
785
new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
786
if (new == NULL)
787
return -ENOMEM;
788
789
gfps = new;
790
new += nr_gfps++;
791
792
new->flags = gfp_flags;
793
new->human_readable = strdup(str + 10);
794
new->compact_str = compact_gfp_flags(str + 10);
795
if (!new->human_readable || !new->compact_str)
796
return -ENOMEM;
797
798
qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
799
}
800
801
str = strtok_r(NULL, " ", &pos);
802
}
803
804
trace_seq_destroy(&seq);
805
return 0;
806
}
807
808
static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample)
809
{
810
u64 page;
811
unsigned int order = evsel__intval(evsel, sample, "order");
812
unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags");
813
unsigned int migrate_type = evsel__intval(evsel, sample,
814
"migratetype");
815
u64 bytes = kmem_page_size << order;
816
u64 callsite;
817
struct page_stat *pstat;
818
struct page_stat this = {
819
.order = order,
820
.gfp_flags = gfp_flags,
821
.migrate_type = migrate_type,
822
};
823
824
if (use_pfn)
825
page = evsel__intval(evsel, sample, "pfn");
826
else
827
page = evsel__intval(evsel, sample, "page");
828
829
nr_page_allocs++;
830
total_page_alloc_bytes += bytes;
831
832
if (!valid_page(page)) {
833
nr_page_fails++;
834
total_page_fail_bytes += bytes;
835
836
return 0;
837
}
838
839
if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
840
return -1;
841
842
callsite = find_callsite(evsel, sample);
843
844
/*
845
* This is to find the current page (with correct gfp flags and
846
* migrate type) at free event.
847
*/
848
this.page = page;
849
pstat = page_stat__findnew_page(&this);
850
if (pstat == NULL)
851
return -ENOMEM;
852
853
pstat->nr_alloc++;
854
pstat->alloc_bytes += bytes;
855
pstat->callsite = callsite;
856
857
if (!live_page) {
858
pstat = page_stat__findnew_alloc(&this);
859
if (pstat == NULL)
860
return -ENOMEM;
861
862
pstat->nr_alloc++;
863
pstat->alloc_bytes += bytes;
864
pstat->callsite = callsite;
865
}
866
867
this.callsite = callsite;
868
pstat = page_stat__findnew_caller(&this);
869
if (pstat == NULL)
870
return -ENOMEM;
871
872
pstat->nr_alloc++;
873
pstat->alloc_bytes += bytes;
874
875
order_stats[order][migrate_type]++;
876
877
return 0;
878
}
879
880
static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample)
881
{
882
u64 page;
883
unsigned int order = evsel__intval(evsel, sample, "order");
884
u64 bytes = kmem_page_size << order;
885
struct page_stat *pstat;
886
struct page_stat this = {
887
.order = order,
888
};
889
890
if (use_pfn)
891
page = evsel__intval(evsel, sample, "pfn");
892
else
893
page = evsel__intval(evsel, sample, "page");
894
895
nr_page_frees++;
896
total_page_free_bytes += bytes;
897
898
this.page = page;
899
pstat = page_stat__find_page(&this);
900
if (pstat == NULL) {
901
pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
902
page, order);
903
904
nr_page_nomatch++;
905
total_page_nomatch_bytes += bytes;
906
907
return 0;
908
}
909
910
this.gfp_flags = pstat->gfp_flags;
911
this.migrate_type = pstat->migrate_type;
912
this.callsite = pstat->callsite;
913
914
rb_erase(&pstat->node, &page_live_tree);
915
free(pstat);
916
917
if (live_page) {
918
order_stats[this.order][this.migrate_type]--;
919
} else {
920
pstat = page_stat__find_alloc(&this);
921
if (pstat == NULL)
922
return -ENOMEM;
923
924
pstat->nr_free++;
925
pstat->free_bytes += bytes;
926
}
927
928
pstat = page_stat__find_caller(&this);
929
if (pstat == NULL)
930
return -ENOENT;
931
932
pstat->nr_free++;
933
pstat->free_bytes += bytes;
934
935
if (live_page) {
936
pstat->nr_alloc--;
937
pstat->alloc_bytes -= bytes;
938
939
if (pstat->nr_alloc == 0) {
940
rb_erase(&pstat->node, &page_caller_tree);
941
free(pstat);
942
}
943
}
944
945
return 0;
946
}
947
948
static bool perf_kmem__skip_sample(struct perf_sample *sample)
949
{
950
/* skip sample based on time? */
951
if (perf_time__skip_sample(&ptime, sample->time))
952
return true;
953
954
return false;
955
}
956
957
typedef int (*tracepoint_handler)(struct evsel *evsel,
958
struct perf_sample *sample);
959
960
static int process_sample_event(const struct perf_tool *tool __maybe_unused,
961
union perf_event *event,
962
struct perf_sample *sample,
963
struct evsel *evsel,
964
struct machine *machine)
965
{
966
int err = 0;
967
struct thread *thread = machine__findnew_thread(machine, sample->pid,
968
sample->tid);
969
970
if (thread == NULL) {
971
pr_debug("problem processing %d event, skipping it.\n",
972
event->header.type);
973
return -1;
974
}
975
976
if (perf_kmem__skip_sample(sample))
977
return 0;
978
979
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread));
980
981
if (evsel->handler != NULL) {
982
tracepoint_handler f = evsel->handler;
983
err = f(evsel, sample);
984
}
985
986
thread__put(thread);
987
988
return err;
989
}
990
991
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
992
{
993
if (n_alloc == 0)
994
return 0.0;
995
else
996
return 100.0 - (100.0 * n_req / n_alloc);
997
}
998
999
static void __print_slab_result(struct rb_root *root,
1000
struct perf_session *session,
1001
int n_lines, int is_caller)
1002
{
1003
struct rb_node *next;
1004
struct machine *machine = &session->machines.host;
1005
1006
printf("%.105s\n", graph_dotted_line);
1007
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
1008
printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
1009
printf("%.105s\n", graph_dotted_line);
1010
1011
next = rb_first(root);
1012
1013
while (next && n_lines--) {
1014
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
1015
node);
1016
struct symbol *sym = NULL;
1017
struct map *map;
1018
char buf[BUFSIZ];
1019
u64 addr;
1020
1021
if (is_caller) {
1022
addr = data->call_site;
1023
if (!raw_ip)
1024
sym = machine__find_kernel_symbol(machine, addr, &map);
1025
} else
1026
addr = data->ptr;
1027
1028
if (sym != NULL)
1029
snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
1030
addr - map__unmap_ip(map, sym->start));
1031
else
1032
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
1033
printf(" %-34s |", buf);
1034
1035
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
1036
(unsigned long long)data->bytes_alloc,
1037
(unsigned long)data->bytes_alloc / data->hit,
1038
(unsigned long long)data->bytes_req,
1039
(unsigned long)data->bytes_req / data->hit,
1040
(unsigned long)data->hit,
1041
(unsigned long)data->pingpong,
1042
fragmentation(data->bytes_req, data->bytes_alloc));
1043
1044
next = rb_next(next);
1045
}
1046
1047
if (n_lines == -1)
1048
printf(" ... | ... | ... | ... | ... | ... \n");
1049
1050
printf("%.105s\n", graph_dotted_line);
1051
}
1052
1053
static const char * const migrate_type_str[] = {
1054
"UNMOVABL",
1055
"RECLAIM",
1056
"MOVABLE",
1057
"RESERVED",
1058
"CMA/ISLT",
1059
"UNKNOWN",
1060
};
1061
1062
static void __print_page_alloc_result(struct perf_session *session, int n_lines)
1063
{
1064
struct rb_node *next = rb_first(&page_alloc_sorted);
1065
struct machine *machine = &session->machines.host;
1066
const char *format;
1067
int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1068
1069
printf("\n%.105s\n", graph_dotted_line);
1070
printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
1071
use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
1072
gfp_len, "GFP flags");
1073
printf("%.105s\n", graph_dotted_line);
1074
1075
if (use_pfn)
1076
format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
1077
else
1078
format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
1079
1080
while (next && n_lines--) {
1081
struct page_stat *data;
1082
struct symbol *sym;
1083
struct map *map;
1084
char buf[32];
1085
char *caller = buf;
1086
1087
data = rb_entry(next, struct page_stat, node);
1088
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1089
if (sym)
1090
caller = sym->name;
1091
else
1092
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1093
1094
printf(format, (unsigned long long)data->page,
1095
(unsigned long long)data->alloc_bytes / 1024,
1096
data->nr_alloc, data->order,
1097
migrate_type_str[data->migrate_type],
1098
gfp_len, compact_gfp_string(data->gfp_flags), caller);
1099
1100
next = rb_next(next);
1101
}
1102
1103
if (n_lines == -1) {
1104
printf(" ... | ... | ... | ... | ... | %-*s | ...\n",
1105
gfp_len, "...");
1106
}
1107
1108
printf("%.105s\n", graph_dotted_line);
1109
}
1110
1111
static void __print_page_caller_result(struct perf_session *session, int n_lines)
1112
{
1113
struct rb_node *next = rb_first(&page_caller_sorted);
1114
struct machine *machine = &session->machines.host;
1115
int gfp_len = max(strlen("GFP flags"), max_gfp_len);
1116
1117
printf("\n%.105s\n", graph_dotted_line);
1118
printf(" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n",
1119
live_page ? "Live" : "Total", gfp_len, "GFP flags");
1120
printf("%.105s\n", graph_dotted_line);
1121
1122
while (next && n_lines--) {
1123
struct page_stat *data;
1124
struct symbol *sym;
1125
struct map *map;
1126
char buf[32];
1127
char *caller = buf;
1128
1129
data = rb_entry(next, struct page_stat, node);
1130
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
1131
if (sym)
1132
caller = sym->name;
1133
else
1134
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
1135
1136
printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
1137
(unsigned long long)data->alloc_bytes / 1024,
1138
data->nr_alloc, data->order,
1139
migrate_type_str[data->migrate_type],
1140
gfp_len, compact_gfp_string(data->gfp_flags), caller);
1141
1142
next = rb_next(next);
1143
}
1144
1145
if (n_lines == -1) {
1146
printf(" ... | ... | ... | ... | %-*s | ...\n",
1147
gfp_len, "...");
1148
}
1149
1150
printf("%.105s\n", graph_dotted_line);
1151
}
1152
1153
static void print_gfp_flags(void)
1154
{
1155
int i;
1156
1157
printf("#\n");
1158
printf("# GFP flags\n");
1159
printf("# ---------\n");
1160
for (i = 0; i < nr_gfps; i++) {
1161
printf("# %08x: %*s: %s\n", gfps[i].flags,
1162
(int) max_gfp_len, gfps[i].compact_str,
1163
gfps[i].human_readable);
1164
}
1165
}
1166
1167
static void print_slab_summary(void)
1168
{
1169
printf("\nSUMMARY (SLAB allocator)");
1170
printf("\n========================\n");
1171
printf("Total bytes requested: %'lu\n", total_requested);
1172
printf("Total bytes allocated: %'lu\n", total_allocated);
1173
printf("Total bytes freed: %'lu\n", total_freed);
1174
if (total_allocated > total_freed) {
1175
printf("Net total bytes allocated: %'lu\n",
1176
total_allocated - total_freed);
1177
}
1178
printf("Total bytes wasted on internal fragmentation: %'lu\n",
1179
total_allocated - total_requested);
1180
printf("Internal fragmentation: %f%%\n",
1181
fragmentation(total_requested, total_allocated));
1182
printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
1183
}
1184
1185
static void print_page_summary(void)
1186
{
1187
int o, m;
1188
u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
1189
u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
1190
1191
printf("\nSUMMARY (page allocator)");
1192
printf("\n========================\n");
1193
printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation requests",
1194
nr_page_allocs, total_page_alloc_bytes / 1024);
1195
printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free requests",
1196
nr_page_frees, total_page_free_bytes / 1024);
1197
printf("\n");
1198
1199
printf("%-30s: %'16"PRIu64" [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
1200
nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
1201
printf("%-30s: %'16"PRIu64" [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
1202
nr_page_allocs - nr_alloc_freed,
1203
(total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
1204
printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free-only requests",
1205
nr_page_nomatch, total_page_nomatch_bytes / 1024);
1206
printf("\n");
1207
1208
printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation failures",
1209
nr_page_fails, total_page_fail_bytes / 1024);
1210
printf("\n");
1211
1212
printf("%5s %12s %12s %12s %12s %12s\n", "Order", "Unmovable",
1213
"Reclaimable", "Movable", "Reserved", "CMA/Isolated");
1214
printf("%.5s %.12s %.12s %.12s %.12s %.12s\n", graph_dotted_line,
1215
graph_dotted_line, graph_dotted_line, graph_dotted_line,
1216
graph_dotted_line, graph_dotted_line);
1217
1218
for (o = 0; o < MAX_PAGE_ORDER; o++) {
1219
printf("%5d", o);
1220
for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
1221
if (order_stats[o][m])
1222
printf(" %'12d", order_stats[o][m]);
1223
else
1224
printf(" %12c", '.');
1225
}
1226
printf("\n");
1227
}
1228
}
1229
1230
static void print_slab_result(struct perf_session *session)
1231
{
1232
if (caller_flag)
1233
__print_slab_result(&root_caller_sorted, session, caller_lines, 1);
1234
if (alloc_flag)
1235
__print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
1236
print_slab_summary();
1237
}
1238
1239
static void print_page_result(struct perf_session *session)
1240
{
1241
if (caller_flag || alloc_flag)
1242
print_gfp_flags();
1243
if (caller_flag)
1244
__print_page_caller_result(session, caller_lines);
1245
if (alloc_flag)
1246
__print_page_alloc_result(session, alloc_lines);
1247
print_page_summary();
1248
}
1249
1250
static void print_result(struct perf_session *session)
1251
{
1252
if (kmem_slab)
1253
print_slab_result(session);
1254
if (kmem_page)
1255
print_page_result(session);
1256
}
1257
1258
static LIST_HEAD(slab_caller_sort);
1259
static LIST_HEAD(slab_alloc_sort);
1260
static LIST_HEAD(page_caller_sort);
1261
static LIST_HEAD(page_alloc_sort);
1262
1263
static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
1264
struct list_head *sort_list)
1265
{
1266
struct rb_node **new = &(root->rb_node);
1267
struct rb_node *parent = NULL;
1268
struct sort_dimension *sort;
1269
1270
while (*new) {
1271
struct alloc_stat *this;
1272
int cmp = 0;
1273
1274
this = rb_entry(*new, struct alloc_stat, node);
1275
parent = *new;
1276
1277
list_for_each_entry(sort, sort_list, list) {
1278
cmp = sort->cmp(data, this);
1279
if (cmp)
1280
break;
1281
}
1282
1283
if (cmp > 0)
1284
new = &((*new)->rb_left);
1285
else
1286
new = &((*new)->rb_right);
1287
}
1288
1289
rb_link_node(&data->node, parent, new);
1290
rb_insert_color(&data->node, root);
1291
}
1292
1293
static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
1294
struct list_head *sort_list)
1295
{
1296
struct rb_node *node;
1297
struct alloc_stat *data;
1298
1299
for (;;) {
1300
node = rb_first(root);
1301
if (!node)
1302
break;
1303
1304
rb_erase(node, root);
1305
data = rb_entry(node, struct alloc_stat, node);
1306
sort_slab_insert(root_sorted, data, sort_list);
1307
}
1308
}
1309
1310
static void sort_page_insert(struct rb_root *root, struct page_stat *data,
1311
struct list_head *sort_list)
1312
{
1313
struct rb_node **new = &root->rb_node;
1314
struct rb_node *parent = NULL;
1315
struct sort_dimension *sort;
1316
1317
while (*new) {
1318
struct page_stat *this;
1319
int cmp = 0;
1320
1321
this = rb_entry(*new, struct page_stat, node);
1322
parent = *new;
1323
1324
list_for_each_entry(sort, sort_list, list) {
1325
cmp = sort->cmp(data, this);
1326
if (cmp)
1327
break;
1328
}
1329
1330
if (cmp > 0)
1331
new = &parent->rb_left;
1332
else
1333
new = &parent->rb_right;
1334
}
1335
1336
rb_link_node(&data->node, parent, new);
1337
rb_insert_color(&data->node, root);
1338
}
1339
1340
static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
1341
struct list_head *sort_list)
1342
{
1343
struct rb_node *node;
1344
struct page_stat *data;
1345
1346
for (;;) {
1347
node = rb_first(root);
1348
if (!node)
1349
break;
1350
1351
rb_erase(node, root);
1352
data = rb_entry(node, struct page_stat, node);
1353
sort_page_insert(root_sorted, data, sort_list);
1354
}
1355
}
1356
1357
static void sort_result(void)
1358
{
1359
if (kmem_slab) {
1360
__sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
1361
&slab_alloc_sort);
1362
__sort_slab_result(&root_caller_stat, &root_caller_sorted,
1363
&slab_caller_sort);
1364
}
1365
if (kmem_page) {
1366
if (live_page)
1367
__sort_page_result(&page_live_tree, &page_alloc_sorted,
1368
&page_alloc_sort);
1369
else
1370
__sort_page_result(&page_alloc_tree, &page_alloc_sorted,
1371
&page_alloc_sort);
1372
1373
__sort_page_result(&page_caller_tree, &page_caller_sorted,
1374
&page_caller_sort);
1375
}
1376
}
1377
1378
static int __cmd_kmem(struct perf_session *session)
1379
{
1380
int err = -EINVAL;
1381
struct evsel *evsel;
1382
const struct evsel_str_handler kmem_tracepoints[] = {
1383
/* slab allocator */
1384
{ "kmem:kmalloc", evsel__process_alloc_event, },
1385
{ "kmem:kmem_cache_alloc", evsel__process_alloc_event, },
1386
{ "kmem:kmalloc_node", evsel__process_alloc_event, },
1387
{ "kmem:kmem_cache_alloc_node", evsel__process_alloc_event, },
1388
{ "kmem:kfree", evsel__process_free_event, },
1389
{ "kmem:kmem_cache_free", evsel__process_free_event, },
1390
/* page allocator */
1391
{ "kmem:mm_page_alloc", evsel__process_page_alloc_event, },
1392
{ "kmem:mm_page_free", evsel__process_page_free_event, },
1393
};
1394
1395
if (!perf_session__has_traces(session, "kmem record"))
1396
goto out;
1397
1398
if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
1399
pr_err("Initializing perf session tracepoint handlers failed\n");
1400
goto out;
1401
}
1402
1403
evlist__for_each_entry(session->evlist, evsel) {
1404
if (evsel__name_is(evsel, "kmem:mm_page_alloc") &&
1405
evsel__field(evsel, "pfn")) {
1406
use_pfn = true;
1407
break;
1408
}
1409
}
1410
1411
setup_pager();
1412
err = perf_session__process_events(session);
1413
if (err != 0) {
1414
pr_err("error during process events: %d\n", err);
1415
goto out;
1416
}
1417
sort_result();
1418
print_result(session);
1419
out:
1420
return err;
1421
}
1422
1423
/* slab sort keys */
1424
static int ptr_cmp(void *a, void *b)
1425
{
1426
struct alloc_stat *l = a;
1427
struct alloc_stat *r = b;
1428
1429
if (l->ptr < r->ptr)
1430
return -1;
1431
else if (l->ptr > r->ptr)
1432
return 1;
1433
return 0;
1434
}
1435
1436
static struct sort_dimension ptr_sort_dimension = {
1437
.name = "ptr",
1438
.cmp = ptr_cmp,
1439
};
1440
1441
static int slab_callsite_cmp(void *a, void *b)
1442
{
1443
struct alloc_stat *l = a;
1444
struct alloc_stat *r = b;
1445
1446
if (l->call_site < r->call_site)
1447
return -1;
1448
else if (l->call_site > r->call_site)
1449
return 1;
1450
return 0;
1451
}
1452
1453
static struct sort_dimension callsite_sort_dimension = {
1454
.name = "callsite",
1455
.cmp = slab_callsite_cmp,
1456
};
1457
1458
static int hit_cmp(void *a, void *b)
1459
{
1460
struct alloc_stat *l = a;
1461
struct alloc_stat *r = b;
1462
1463
if (l->hit < r->hit)
1464
return -1;
1465
else if (l->hit > r->hit)
1466
return 1;
1467
return 0;
1468
}
1469
1470
static struct sort_dimension hit_sort_dimension = {
1471
.name = "hit",
1472
.cmp = hit_cmp,
1473
};
1474
1475
static int bytes_cmp(void *a, void *b)
1476
{
1477
struct alloc_stat *l = a;
1478
struct alloc_stat *r = b;
1479
1480
if (l->bytes_alloc < r->bytes_alloc)
1481
return -1;
1482
else if (l->bytes_alloc > r->bytes_alloc)
1483
return 1;
1484
return 0;
1485
}
1486
1487
static struct sort_dimension bytes_sort_dimension = {
1488
.name = "bytes",
1489
.cmp = bytes_cmp,
1490
};
1491
1492
static int frag_cmp(void *a, void *b)
1493
{
1494
double x, y;
1495
struct alloc_stat *l = a;
1496
struct alloc_stat *r = b;
1497
1498
x = fragmentation(l->bytes_req, l->bytes_alloc);
1499
y = fragmentation(r->bytes_req, r->bytes_alloc);
1500
1501
if (x < y)
1502
return -1;
1503
else if (x > y)
1504
return 1;
1505
return 0;
1506
}
1507
1508
static struct sort_dimension frag_sort_dimension = {
1509
.name = "frag",
1510
.cmp = frag_cmp,
1511
};
1512
1513
static int pingpong_cmp(void *a, void *b)
1514
{
1515
struct alloc_stat *l = a;
1516
struct alloc_stat *r = b;
1517
1518
if (l->pingpong < r->pingpong)
1519
return -1;
1520
else if (l->pingpong > r->pingpong)
1521
return 1;
1522
return 0;
1523
}
1524
1525
static struct sort_dimension pingpong_sort_dimension = {
1526
.name = "pingpong",
1527
.cmp = pingpong_cmp,
1528
};
1529
1530
/* page sort keys */
1531
static int page_cmp(void *a, void *b)
1532
{
1533
struct page_stat *l = a;
1534
struct page_stat *r = b;
1535
1536
if (l->page < r->page)
1537
return -1;
1538
else if (l->page > r->page)
1539
return 1;
1540
return 0;
1541
}
1542
1543
static struct sort_dimension page_sort_dimension = {
1544
.name = "page",
1545
.cmp = page_cmp,
1546
};
1547
1548
static int page_callsite_cmp(void *a, void *b)
1549
{
1550
struct page_stat *l = a;
1551
struct page_stat *r = b;
1552
1553
if (l->callsite < r->callsite)
1554
return -1;
1555
else if (l->callsite > r->callsite)
1556
return 1;
1557
return 0;
1558
}
1559
1560
static struct sort_dimension page_callsite_sort_dimension = {
1561
.name = "callsite",
1562
.cmp = page_callsite_cmp,
1563
};
1564
1565
static int page_hit_cmp(void *a, void *b)
1566
{
1567
struct page_stat *l = a;
1568
struct page_stat *r = b;
1569
1570
if (l->nr_alloc < r->nr_alloc)
1571
return -1;
1572
else if (l->nr_alloc > r->nr_alloc)
1573
return 1;
1574
return 0;
1575
}
1576
1577
static struct sort_dimension page_hit_sort_dimension = {
1578
.name = "hit",
1579
.cmp = page_hit_cmp,
1580
};
1581
1582
static int page_bytes_cmp(void *a, void *b)
1583
{
1584
struct page_stat *l = a;
1585
struct page_stat *r = b;
1586
1587
if (l->alloc_bytes < r->alloc_bytes)
1588
return -1;
1589
else if (l->alloc_bytes > r->alloc_bytes)
1590
return 1;
1591
return 0;
1592
}
1593
1594
static struct sort_dimension page_bytes_sort_dimension = {
1595
.name = "bytes",
1596
.cmp = page_bytes_cmp,
1597
};
1598
1599
static int page_order_cmp(void *a, void *b)
1600
{
1601
struct page_stat *l = a;
1602
struct page_stat *r = b;
1603
1604
if (l->order < r->order)
1605
return -1;
1606
else if (l->order > r->order)
1607
return 1;
1608
return 0;
1609
}
1610
1611
static struct sort_dimension page_order_sort_dimension = {
1612
.name = "order",
1613
.cmp = page_order_cmp,
1614
};
1615
1616
static int migrate_type_cmp(void *a, void *b)
1617
{
1618
struct page_stat *l = a;
1619
struct page_stat *r = b;
1620
1621
/* for internal use to find free'd page */
1622
if (l->migrate_type == -1U)
1623
return 0;
1624
1625
if (l->migrate_type < r->migrate_type)
1626
return -1;
1627
else if (l->migrate_type > r->migrate_type)
1628
return 1;
1629
return 0;
1630
}
1631
1632
static struct sort_dimension migrate_type_sort_dimension = {
1633
.name = "migtype",
1634
.cmp = migrate_type_cmp,
1635
};
1636
1637
static int gfp_flags_cmp(void *a, void *b)
1638
{
1639
struct page_stat *l = a;
1640
struct page_stat *r = b;
1641
1642
/* for internal use to find free'd page */
1643
if (l->gfp_flags == -1U)
1644
return 0;
1645
1646
if (l->gfp_flags < r->gfp_flags)
1647
return -1;
1648
else if (l->gfp_flags > r->gfp_flags)
1649
return 1;
1650
return 0;
1651
}
1652
1653
static struct sort_dimension gfp_flags_sort_dimension = {
1654
.name = "gfp",
1655
.cmp = gfp_flags_cmp,
1656
};
1657
1658
static struct sort_dimension *slab_sorts[] = {
1659
&ptr_sort_dimension,
1660
&callsite_sort_dimension,
1661
&hit_sort_dimension,
1662
&bytes_sort_dimension,
1663
&frag_sort_dimension,
1664
&pingpong_sort_dimension,
1665
};
1666
1667
static struct sort_dimension *page_sorts[] = {
1668
&page_sort_dimension,
1669
&page_callsite_sort_dimension,
1670
&page_hit_sort_dimension,
1671
&page_bytes_sort_dimension,
1672
&page_order_sort_dimension,
1673
&migrate_type_sort_dimension,
1674
&gfp_flags_sort_dimension,
1675
};
1676
1677
static int slab_sort_dimension__add(const char *tok, struct list_head *list)
1678
{
1679
struct sort_dimension *sort;
1680
int i;
1681
1682
for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
1683
if (!strcmp(slab_sorts[i]->name, tok)) {
1684
sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
1685
if (!sort) {
1686
pr_err("%s: memdup failed\n", __func__);
1687
return -1;
1688
}
1689
list_add_tail(&sort->list, list);
1690
return 0;
1691
}
1692
}
1693
1694
return -1;
1695
}
1696
1697
static int page_sort_dimension__add(const char *tok, struct list_head *list)
1698
{
1699
struct sort_dimension *sort;
1700
int i;
1701
1702
for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
1703
if (!strcmp(page_sorts[i]->name, tok)) {
1704
sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
1705
if (!sort) {
1706
pr_err("%s: memdup failed\n", __func__);
1707
return -1;
1708
}
1709
list_add_tail(&sort->list, list);
1710
return 0;
1711
}
1712
}
1713
1714
return -1;
1715
}
1716
1717
static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
1718
{
1719
char *tok;
1720
char *str = strdup(arg);
1721
char *pos = str;
1722
1723
if (!str) {
1724
pr_err("%s: strdup failed\n", __func__);
1725
return -1;
1726
}
1727
1728
while (true) {
1729
tok = strsep(&pos, ",");
1730
if (!tok)
1731
break;
1732
if (slab_sort_dimension__add(tok, sort_list) < 0) {
1733
pr_err("Unknown slab --sort key: '%s'", tok);
1734
free(str);
1735
return -1;
1736
}
1737
}
1738
1739
free(str);
1740
return 0;
1741
}
1742
1743
static int setup_page_sorting(struct list_head *sort_list, const char *arg)
1744
{
1745
char *tok;
1746
char *str = strdup(arg);
1747
char *pos = str;
1748
1749
if (!str) {
1750
pr_err("%s: strdup failed\n", __func__);
1751
return -1;
1752
}
1753
1754
while (true) {
1755
tok = strsep(&pos, ",");
1756
if (!tok)
1757
break;
1758
if (page_sort_dimension__add(tok, sort_list) < 0) {
1759
pr_err("Unknown page --sort key: '%s'", tok);
1760
free(str);
1761
return -1;
1762
}
1763
}
1764
1765
free(str);
1766
return 0;
1767
}
1768
1769
static int parse_sort_opt(const struct option *opt __maybe_unused,
1770
const char *arg, int unset __maybe_unused)
1771
{
1772
if (!arg)
1773
return -1;
1774
1775
if (kmem_page > kmem_slab ||
1776
(kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) {
1777
if (caller_flag > alloc_flag)
1778
return setup_page_sorting(&page_caller_sort, arg);
1779
else
1780
return setup_page_sorting(&page_alloc_sort, arg);
1781
} else {
1782
if (caller_flag > alloc_flag)
1783
return setup_slab_sorting(&slab_caller_sort, arg);
1784
else
1785
return setup_slab_sorting(&slab_alloc_sort, arg);
1786
}
1787
1788
return 0;
1789
}
1790
1791
static int parse_caller_opt(const struct option *opt __maybe_unused,
1792
const char *arg __maybe_unused,
1793
int unset __maybe_unused)
1794
{
1795
caller_flag = (alloc_flag + 1);
1796
return 0;
1797
}
1798
1799
static int parse_alloc_opt(const struct option *opt __maybe_unused,
1800
const char *arg __maybe_unused,
1801
int unset __maybe_unused)
1802
{
1803
alloc_flag = (caller_flag + 1);
1804
return 0;
1805
}
1806
1807
static int parse_slab_opt(const struct option *opt __maybe_unused,
1808
const char *arg __maybe_unused,
1809
int unset __maybe_unused)
1810
{
1811
kmem_slab = (kmem_page + 1);
1812
return 0;
1813
}
1814
1815
static int parse_page_opt(const struct option *opt __maybe_unused,
1816
const char *arg __maybe_unused,
1817
int unset __maybe_unused)
1818
{
1819
kmem_page = (kmem_slab + 1);
1820
return 0;
1821
}
1822
1823
static int parse_line_opt(const struct option *opt __maybe_unused,
1824
const char *arg, int unset __maybe_unused)
1825
{
1826
int lines;
1827
1828
if (!arg)
1829
return -1;
1830
1831
lines = strtoul(arg, NULL, 10);
1832
1833
if (caller_flag > alloc_flag)
1834
caller_lines = lines;
1835
else
1836
alloc_lines = lines;
1837
1838
return 0;
1839
}
1840
1841
static bool slab_legacy_tp_is_exposed(void)
1842
{
1843
/*
1844
* The tracepoints "kmem:kmalloc_node" and
1845
* "kmem:kmem_cache_alloc_node" have been removed on the latest
1846
* kernel, if the tracepoint "kmem:kmalloc_node" is existed it
1847
* means the tool is running on an old kernel, we need to
1848
* rollback to support these legacy tracepoints.
1849
*/
1850
return IS_ERR(trace_event__tp_format("kmem", "kmalloc_node")) ?
1851
false : true;
1852
}
1853
1854
static int __cmd_record(int argc, const char **argv)
1855
{
1856
const char * const record_args[] = {
1857
"record", "-a", "-R", "-c", "1",
1858
};
1859
const char * const slab_events[] = {
1860
"-e", "kmem:kmalloc",
1861
"-e", "kmem:kfree",
1862
"-e", "kmem:kmem_cache_alloc",
1863
"-e", "kmem:kmem_cache_free",
1864
};
1865
const char * const slab_legacy_events[] = {
1866
"-e", "kmem:kmalloc_node",
1867
"-e", "kmem:kmem_cache_alloc_node",
1868
};
1869
const char * const page_events[] = {
1870
"-e", "kmem:mm_page_alloc",
1871
"-e", "kmem:mm_page_free",
1872
};
1873
unsigned int rec_argc, i, j;
1874
const char **rec_argv;
1875
unsigned int slab_legacy_tp_exposed = slab_legacy_tp_is_exposed();
1876
1877
rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1878
if (kmem_slab) {
1879
rec_argc += ARRAY_SIZE(slab_events);
1880
if (slab_legacy_tp_exposed)
1881
rec_argc += ARRAY_SIZE(slab_legacy_events);
1882
}
1883
if (kmem_page)
1884
rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
1885
1886
rec_argv = calloc(rec_argc + 1, sizeof(char *));
1887
1888
if (rec_argv == NULL)
1889
return -ENOMEM;
1890
1891
for (i = 0; i < ARRAY_SIZE(record_args); i++)
1892
rec_argv[i] = strdup(record_args[i]);
1893
1894
if (kmem_slab) {
1895
for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
1896
rec_argv[i] = strdup(slab_events[j]);
1897
if (slab_legacy_tp_exposed) {
1898
for (j = 0; j < ARRAY_SIZE(slab_legacy_events); j++, i++)
1899
rec_argv[i] = strdup(slab_legacy_events[j]);
1900
}
1901
}
1902
if (kmem_page) {
1903
rec_argv[i++] = strdup("-g");
1904
1905
for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
1906
rec_argv[i] = strdup(page_events[j]);
1907
}
1908
1909
for (j = 1; j < (unsigned int)argc; j++, i++)
1910
rec_argv[i] = argv[j];
1911
1912
return cmd_record(i, rec_argv);
1913
}
1914
1915
static int kmem_config(const char *var, const char *value, void *cb __maybe_unused)
1916
{
1917
if (!strcmp(var, "kmem.default")) {
1918
if (!strcmp(value, "slab"))
1919
kmem_default = KMEM_SLAB;
1920
else if (!strcmp(value, "page"))
1921
kmem_default = KMEM_PAGE;
1922
else
1923
pr_err("invalid default value ('slab' or 'page' required): %s\n",
1924
value);
1925
return 0;
1926
}
1927
1928
return 0;
1929
}
1930
1931
int cmd_kmem(int argc, const char **argv)
1932
{
1933
const char * const default_slab_sort = "frag,hit,bytes";
1934
const char * const default_page_sort = "bytes,hit";
1935
struct perf_data data = {
1936
.mode = PERF_DATA_MODE_READ,
1937
};
1938
const struct option kmem_options[] = {
1939
OPT_STRING('i', "input", &input_name, "file", "input file name"),
1940
OPT_INCR('v', "verbose", &verbose,
1941
"be more verbose (show symbol address, etc)"),
1942
OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
1943
"show per-callsite statistics", parse_caller_opt),
1944
OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
1945
"show per-allocation statistics", parse_alloc_opt),
1946
OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
1947
"sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
1948
"page, order, migtype, gfp", parse_sort_opt),
1949
OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
1950
OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
1951
OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"),
1952
OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
1953
parse_slab_opt),
1954
OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
1955
parse_page_opt),
1956
OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
1957
OPT_STRING(0, "time", &time_str, "str",
1958
"Time span of interest (start,stop)"),
1959
OPT_END()
1960
};
1961
const char *const kmem_subcommands[] = { "record", "stat", NULL };
1962
const char *kmem_usage[] = {
1963
NULL,
1964
NULL
1965
};
1966
struct perf_session *session;
1967
struct perf_tool perf_kmem;
1968
static const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n";
1969
int ret = perf_config(kmem_config, NULL);
1970
1971
if (ret)
1972
return ret;
1973
1974
argc = parse_options_subcommand(argc, argv, kmem_options,
1975
kmem_subcommands, kmem_usage,
1976
PARSE_OPT_STOP_AT_NON_OPTION);
1977
1978
if (!argc)
1979
usage_with_options(kmem_usage, kmem_options);
1980
1981
if (kmem_slab == 0 && kmem_page == 0) {
1982
if (kmem_default == KMEM_SLAB)
1983
kmem_slab = 1;
1984
else
1985
kmem_page = 1;
1986
}
1987
1988
if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
1989
symbol__init(NULL);
1990
return __cmd_record(argc, argv);
1991
}
1992
1993
data.path = input_name;
1994
1995
perf_tool__init(&perf_kmem, /*ordered_events=*/true);
1996
perf_kmem.sample = process_sample_event;
1997
perf_kmem.comm = perf_event__process_comm;
1998
perf_kmem.mmap = perf_event__process_mmap;
1999
perf_kmem.mmap2 = perf_event__process_mmap2;
2000
perf_kmem.namespaces = perf_event__process_namespaces;
2001
2002
kmem_session = session = perf_session__new(&data, &perf_kmem);
2003
if (IS_ERR(session))
2004
return PTR_ERR(session);
2005
2006
ret = -1;
2007
2008
if (kmem_slab) {
2009
if (!evlist__find_tracepoint_by_name(session->evlist, "kmem:kmalloc")) {
2010
pr_err(errmsg, "slab", "slab");
2011
goto out_delete;
2012
}
2013
}
2014
2015
if (kmem_page) {
2016
struct evsel *evsel = evlist__find_tracepoint_by_name(session->evlist, "kmem:mm_page_alloc");
2017
const struct tep_event *tp_format = evsel ? evsel__tp_format(evsel) : NULL;
2018
2019
if (tp_format == NULL) {
2020
pr_err(errmsg, "page", "page");
2021
goto out_delete;
2022
}
2023
kmem_page_size = tep_get_page_size(tp_format->tep);
2024
symbol_conf.use_callchain = true;
2025
}
2026
2027
symbol__init(perf_session__env(session));
2028
2029
if (perf_time__parse_str(&ptime, time_str) != 0) {
2030
pr_err("Invalid time string\n");
2031
ret = -EINVAL;
2032
goto out_delete;
2033
}
2034
2035
if (!strcmp(argv[0], "stat")) {
2036
setlocale(LC_ALL, "");
2037
2038
if (cpu__setup_cpunode_map())
2039
goto out_delete;
2040
2041
if (list_empty(&slab_caller_sort))
2042
setup_slab_sorting(&slab_caller_sort, default_slab_sort);
2043
if (list_empty(&slab_alloc_sort))
2044
setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
2045
if (list_empty(&page_caller_sort))
2046
setup_page_sorting(&page_caller_sort, default_page_sort);
2047
if (list_empty(&page_alloc_sort))
2048
setup_page_sorting(&page_alloc_sort, default_page_sort);
2049
2050
if (kmem_page) {
2051
setup_page_sorting(&page_alloc_sort_input,
2052
"page,order,migtype,gfp");
2053
setup_page_sorting(&page_caller_sort_input,
2054
"callsite,order,migtype,gfp");
2055
}
2056
ret = __cmd_kmem(session);
2057
} else
2058
usage_with_options(kmem_usage, kmem_options);
2059
2060
out_delete:
2061
perf_session__delete(session);
2062
/* free usage string allocated by parse_options_subcommand */
2063
free((void *)kmem_usage[0]);
2064
2065
return ret;
2066
}
2067
2068