Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf kmem: Analyze page allocator events also

The perf kmem command records and analyze kernel memory allocation only
for SLAB objects. This patch implement a simple page allocator analyzer
using kmem:mm_page_alloc and kmem:mm_page_free events.

It adds two new options of --slab and --page. The --slab option is for
analyzing SLAB allocator and that's what perf kmem currently does.

The new --page option enables page allocator events and analyze kernel
memory usage in page unit. Currently, 'stat --alloc' subcommand is
implemented only.

If none of these --slab nor --page is specified, --slab is implied.

First run 'perf kmem record' to generate a suitable perf.data file:

# perf kmem record --page sleep 5

Then run 'perf kmem stat' to postprocess the perf.data file:

# perf kmem stat --page --alloc --line 10

-------------------------------------------------------------------------------
PFN | Total alloc (KB) | Hits | Order | Mig.type | GFP flags
-------------------------------------------------------------------------------
4045014 | 16 | 1 | 2 | RECLAIM | 00285250
4143980 | 16 | 1 | 2 | RECLAIM | 00285250
3938658 | 16 | 1 | 2 | RECLAIM | 00285250
4045400 | 16 | 1 | 2 | RECLAIM | 00285250
3568708 | 16 | 1 | 2 | RECLAIM | 00285250
3729824 | 16 | 1 | 2 | RECLAIM | 00285250
3657210 | 16 | 1 | 2 | RECLAIM | 00285250
4120750 | 16 | 1 | 2 | RECLAIM | 00285250
3678850 | 16 | 1 | 2 | RECLAIM | 00285250
3693874 | 16 | 1 | 2 | RECLAIM | 00285250
... | ... | ... | ... | ... | ...
-------------------------------------------------------------------------------

SUMMARY (page allocator)
========================
Total allocation requests : 44,260 [ 177,256 KB ]
Total free requests : 117 [ 468 KB ]

Total alloc+freed requests : 49 [ 196 KB ]
Total alloc-only requests : 44,211 [ 177,060 KB ]
Total free-only requests : 68 [ 272 KB ]

Total allocation failures : 0 [ 0 KB ]

Order Unmovable Reclaimable Movable Reserved CMA/Isolated
----- ------------ ------------ ------------ ------------ ------------
0 32 . 44,210 . .
1 . . . . .
2 . 18 . . .
3 . . . . .
4 . . . . .
5 . . . . .
6 . . . . .
7 . . . . .
8 . . . . .
9 . . . . .
10 . . . . .

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joonsoo Kim <js1304@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-mm@kvack.org
Link: http://lkml.kernel.org/r/1428298576-9785-4-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
0d68bc92 9fdd8a87

+491 -17
+7 -1
tools/perf/Documentation/perf-kmem.txt
··· 3 3 4 4 NAME 5 5 ---- 6 - perf-kmem - Tool to trace/measure kernel memory(slab) properties 6 + perf-kmem - Tool to trace/measure kernel memory properties 7 7 8 8 SYNOPSIS 9 9 -------- ··· 45 45 46 46 --raw-ip:: 47 47 Print raw ip instead of symbol 48 + 49 + --slab:: 50 + Analyze SLAB allocator events. 51 + 52 + --page:: 53 + Analyze page allocator events 48 54 49 55 SEE ALSO 50 56 --------
+484 -16
tools/perf/builtin-kmem.c
··· 22 22 #include <linux/string.h> 23 23 #include <locale.h> 24 24 25 + static int kmem_slab; 26 + static int kmem_page; 27 + 28 + static long kmem_page_size; 29 + 25 30 struct alloc_stat; 26 31 typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); 27 32 ··· 231 226 return 0; 232 227 } 233 228 229 + static u64 total_page_alloc_bytes; 230 + static u64 total_page_free_bytes; 231 + static u64 total_page_nomatch_bytes; 232 + static u64 total_page_fail_bytes; 233 + static unsigned long nr_page_allocs; 234 + static unsigned long nr_page_frees; 235 + static unsigned long nr_page_fails; 236 + static unsigned long nr_page_nomatch; 237 + 238 + static bool use_pfn; 239 + 240 + #define MAX_MIGRATE_TYPES 6 241 + #define MAX_PAGE_ORDER 11 242 + 243 + static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES]; 244 + 245 + struct page_stat { 246 + struct rb_node node; 247 + u64 page; 248 + int order; 249 + unsigned gfp_flags; 250 + unsigned migrate_type; 251 + u64 alloc_bytes; 252 + u64 free_bytes; 253 + int nr_alloc; 254 + int nr_free; 255 + }; 256 + 257 + static struct rb_root page_tree; 258 + static struct rb_root page_alloc_tree; 259 + static struct rb_root page_alloc_sorted; 260 + 261 + static struct page_stat *search_page(unsigned long page, bool create) 262 + { 263 + struct rb_node **node = &page_tree.rb_node; 264 + struct rb_node *parent = NULL; 265 + struct page_stat *data; 266 + 267 + while (*node) { 268 + s64 cmp; 269 + 270 + parent = *node; 271 + data = rb_entry(*node, struct page_stat, node); 272 + 273 + cmp = data->page - page; 274 + if (cmp < 0) 275 + node = &parent->rb_left; 276 + else if (cmp > 0) 277 + node = &parent->rb_right; 278 + else 279 + return data; 280 + } 281 + 282 + if (!create) 283 + return NULL; 284 + 285 + data = zalloc(sizeof(*data)); 286 + if (data != NULL) { 287 + data->page = page; 288 + 289 + rb_link_node(&data->node, parent, node); 290 + rb_insert_color(&data->node, &page_tree); 291 + } 292 + 293 + return data; 294 + } 295 + 296 + static int page_stat_cmp(struct page_stat *a, struct page_stat *b) 297 + { 298 + if (a->page > b->page) 299 + return -1; 300 + if (a->page < b->page) 301 + return 1; 302 + if (a->order > b->order) 303 + return -1; 304 + if (a->order < b->order) 305 + return 1; 306 + if (a->migrate_type > b->migrate_type) 307 + return -1; 308 + if (a->migrate_type < b->migrate_type) 309 + return 1; 310 + if (a->gfp_flags > b->gfp_flags) 311 + return -1; 312 + if (a->gfp_flags < b->gfp_flags) 313 + return 1; 314 + return 0; 315 + } 316 + 317 + static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool create) 318 + { 319 + struct rb_node **node = &page_alloc_tree.rb_node; 320 + struct rb_node *parent = NULL; 321 + struct page_stat *data; 322 + 323 + while (*node) { 324 + s64 cmp; 325 + 326 + parent = *node; 327 + data = rb_entry(*node, struct page_stat, node); 328 + 329 + cmp = page_stat_cmp(data, stat); 330 + if (cmp < 0) 331 + node = &parent->rb_left; 332 + else if (cmp > 0) 333 + node = &parent->rb_right; 334 + else 335 + return data; 336 + } 337 + 338 + if (!create) 339 + return NULL; 340 + 341 + data = zalloc(sizeof(*data)); 342 + if (data != NULL) { 343 + data->page = stat->page; 344 + data->order = stat->order; 345 + data->gfp_flags = stat->gfp_flags; 346 + data->migrate_type = stat->migrate_type; 347 + 348 + rb_link_node(&data->node, parent, node); 349 + rb_insert_color(&data->node, &page_alloc_tree); 350 + } 351 + 352 + return data; 353 + } 354 + 355 + static bool valid_page(u64 pfn_or_page) 356 + { 357 + if (use_pfn && pfn_or_page == -1UL) 358 + return false; 359 + if (!use_pfn && pfn_or_page == 0) 360 + return false; 361 + return true; 362 + } 363 + 364 + static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, 365 + struct perf_sample *sample) 366 + { 367 + u64 page; 368 + unsigned int order = perf_evsel__intval(evsel, sample, "order"); 369 + unsigned int gfp_flags = perf_evsel__intval(evsel, sample, "gfp_flags"); 370 + unsigned int migrate_type = perf_evsel__intval(evsel, sample, 371 + "migratetype"); 372 + u64 bytes = kmem_page_size << order; 373 + struct page_stat *stat; 374 + struct page_stat this = { 375 + .order = order, 376 + .gfp_flags = gfp_flags, 377 + .migrate_type = migrate_type, 378 + }; 379 + 380 + if (use_pfn) 381 + page = perf_evsel__intval(evsel, sample, "pfn"); 382 + else 383 + page = perf_evsel__intval(evsel, sample, "page"); 384 + 385 + nr_page_allocs++; 386 + total_page_alloc_bytes += bytes; 387 + 388 + if (!valid_page(page)) { 389 + nr_page_fails++; 390 + total_page_fail_bytes += bytes; 391 + 392 + return 0; 393 + } 394 + 395 + /* 396 + * This is to find the current page (with correct gfp flags and 397 + * migrate type) at free event. 398 + */ 399 + stat = search_page(page, true); 400 + if (stat == NULL) 401 + return -ENOMEM; 402 + 403 + stat->order = order; 404 + stat->gfp_flags = gfp_flags; 405 + stat->migrate_type = migrate_type; 406 + 407 + this.page = page; 408 + stat = search_page_alloc_stat(&this, true); 409 + if (stat == NULL) 410 + return -ENOMEM; 411 + 412 + stat->nr_alloc++; 413 + stat->alloc_bytes += bytes; 414 + 415 + order_stats[order][migrate_type]++; 416 + 417 + return 0; 418 + } 419 + 420 + static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, 421 + struct perf_sample *sample) 422 + { 423 + u64 page; 424 + unsigned int order = perf_evsel__intval(evsel, sample, "order"); 425 + u64 bytes = kmem_page_size << order; 426 + struct page_stat *stat; 427 + struct page_stat this = { 428 + .order = order, 429 + }; 430 + 431 + if (use_pfn) 432 + page = perf_evsel__intval(evsel, sample, "pfn"); 433 + else 434 + page = perf_evsel__intval(evsel, sample, "page"); 435 + 436 + nr_page_frees++; 437 + total_page_free_bytes += bytes; 438 + 439 + stat = search_page(page, false); 440 + if (stat == NULL) { 441 + pr_debug2("missing free at page %"PRIx64" (order: %d)\n", 442 + page, order); 443 + 444 + nr_page_nomatch++; 445 + total_page_nomatch_bytes += bytes; 446 + 447 + return 0; 448 + } 449 + 450 + this.page = page; 451 + this.gfp_flags = stat->gfp_flags; 452 + this.migrate_type = stat->migrate_type; 453 + 454 + rb_erase(&stat->node, &page_tree); 455 + free(stat); 456 + 457 + stat = search_page_alloc_stat(&this, false); 458 + if (stat == NULL) 459 + return -ENOENT; 460 + 461 + stat->nr_free++; 462 + stat->free_bytes += bytes; 463 + 464 + return 0; 465 + } 466 + 234 467 typedef int (*tracepoint_handler)(struct perf_evsel *evsel, 235 468 struct perf_sample *sample); 236 469 ··· 513 270 return 100.0 - (100.0 * n_req / n_alloc); 514 271 } 515 272 516 - static void __print_result(struct rb_root *root, struct perf_session *session, 517 - int n_lines, int is_caller) 273 + static void __print_slab_result(struct rb_root *root, 274 + struct perf_session *session, 275 + int n_lines, int is_caller) 518 276 { 519 277 struct rb_node *next; 520 278 struct machine *machine = &session->machines.host; ··· 567 323 printf("%.105s\n", graph_dotted_line); 568 324 } 569 325 570 - static void print_summary(void) 326 + static const char * const migrate_type_str[] = { 327 + "UNMOVABL", 328 + "RECLAIM", 329 + "MOVABLE", 330 + "RESERVED", 331 + "CMA/ISLT", 332 + "UNKNOWN", 333 + }; 334 + 335 + static void __print_page_result(struct rb_root *root, 336 + struct perf_session *session __maybe_unused, 337 + int n_lines) 571 338 { 572 - printf("\nSUMMARY\n=======\n"); 339 + struct rb_node *next = rb_first(root); 340 + const char *format; 341 + 342 + printf("\n%.80s\n", graph_dotted_line); 343 + printf(" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags\n", 344 + use_pfn ? "PFN" : "Page"); 345 + printf("%.80s\n", graph_dotted_line); 346 + 347 + if (use_pfn) 348 + format = " %16llu | %'16llu | %'9d | %5d | %8s | %08lx\n"; 349 + else 350 + format = " %016llx | %'16llu | %'9d | %5d | %8s | %08lx\n"; 351 + 352 + while (next && n_lines--) { 353 + struct page_stat *data; 354 + 355 + data = rb_entry(next, struct page_stat, node); 356 + 357 + printf(format, (unsigned long long)data->page, 358 + (unsigned long long)data->alloc_bytes / 1024, 359 + data->nr_alloc, data->order, 360 + migrate_type_str[data->migrate_type], 361 + (unsigned long)data->gfp_flags); 362 + 363 + next = rb_next(next); 364 + } 365 + 366 + if (n_lines == -1) 367 + printf(" ... | ... | ... | ... | ... | ... \n"); 368 + 369 + printf("%.80s\n", graph_dotted_line); 370 + } 371 + 372 + static void print_slab_summary(void) 373 + { 374 + printf("\nSUMMARY (SLAB allocator)"); 375 + printf("\n========================\n"); 573 376 printf("Total bytes requested: %'lu\n", total_requested); 574 377 printf("Total bytes allocated: %'lu\n", total_allocated); 575 378 printf("Total bytes wasted on internal fragmentation: %'lu\n", ··· 626 335 printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs); 627 336 } 628 337 629 - static void print_result(struct perf_session *session) 338 + static void print_page_summary(void) 339 + { 340 + int o, m; 341 + u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch; 342 + u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes; 343 + 344 + printf("\nSUMMARY (page allocator)"); 345 + printf("\n========================\n"); 346 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation requests", 347 + nr_page_allocs, total_page_alloc_bytes / 1024); 348 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free requests", 349 + nr_page_frees, total_page_free_bytes / 1024); 350 + printf("\n"); 351 + 352 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests", 353 + nr_alloc_freed, (total_alloc_freed_bytes) / 1024); 354 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total alloc-only requests", 355 + nr_page_allocs - nr_alloc_freed, 356 + (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024); 357 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total free-only requests", 358 + nr_page_nomatch, total_page_nomatch_bytes / 1024); 359 + printf("\n"); 360 + 361 + printf("%-30s: %'16lu [ %'16"PRIu64" KB ]\n", "Total allocation failures", 362 + nr_page_fails, total_page_fail_bytes / 1024); 363 + printf("\n"); 364 + 365 + printf("%5s %12s %12s %12s %12s %12s\n", "Order", "Unmovable", 366 + "Reclaimable", "Movable", "Reserved", "CMA/Isolated"); 367 + printf("%.5s %.12s %.12s %.12s %.12s %.12s\n", graph_dotted_line, 368 + graph_dotted_line, graph_dotted_line, graph_dotted_line, 369 + graph_dotted_line, graph_dotted_line); 370 + 371 + for (o = 0; o < MAX_PAGE_ORDER; o++) { 372 + printf("%5d", o); 373 + for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) { 374 + if (order_stats[o][m]) 375 + printf(" %'12d", order_stats[o][m]); 376 + else 377 + printf(" %12c", '.'); 378 + } 379 + printf("\n"); 380 + } 381 + } 382 + 383 + static void print_slab_result(struct perf_session *session) 630 384 { 631 385 if (caller_flag) 632 - __print_result(&root_caller_sorted, session, caller_lines, 1); 386 + __print_slab_result(&root_caller_sorted, session, caller_lines, 1); 633 387 if (alloc_flag) 634 - __print_result(&root_alloc_sorted, session, alloc_lines, 0); 635 - print_summary(); 388 + __print_slab_result(&root_alloc_sorted, session, alloc_lines, 0); 389 + print_slab_summary(); 390 + } 391 + 392 + static void print_page_result(struct perf_session *session) 393 + { 394 + if (alloc_flag) 395 + __print_page_result(&page_alloc_sorted, session, alloc_lines); 396 + print_page_summary(); 397 + } 398 + 399 + static void print_result(struct perf_session *session) 400 + { 401 + if (kmem_slab) 402 + print_slab_result(session); 403 + if (kmem_page) 404 + print_page_result(session); 636 405 } 637 406 638 407 struct sort_dimension { ··· 704 353 static LIST_HEAD(caller_sort); 705 354 static LIST_HEAD(alloc_sort); 706 355 707 - static void sort_insert(struct rb_root *root, struct alloc_stat *data, 708 - struct list_head *sort_list) 356 + static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data, 357 + struct list_head *sort_list) 709 358 { 710 359 struct rb_node **new = &(root->rb_node); 711 360 struct rb_node *parent = NULL; ··· 734 383 rb_insert_color(&data->node, root); 735 384 } 736 385 737 - static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, 738 - struct list_head *sort_list) 386 + static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted, 387 + struct list_head *sort_list) 739 388 { 740 389 struct rb_node *node; 741 390 struct alloc_stat *data; ··· 747 396 748 397 rb_erase(node, root); 749 398 data = rb_entry(node, struct alloc_stat, node); 750 - sort_insert(root_sorted, data, sort_list); 399 + sort_slab_insert(root_sorted, data, sort_list); 400 + } 401 + } 402 + 403 + static void sort_page_insert(struct rb_root *root, struct page_stat *data) 404 + { 405 + struct rb_node **new = &root->rb_node; 406 + struct rb_node *parent = NULL; 407 + 408 + while (*new) { 409 + struct page_stat *this; 410 + int cmp = 0; 411 + 412 + this = rb_entry(*new, struct page_stat, node); 413 + parent = *new; 414 + 415 + /* TODO: support more sort key */ 416 + cmp = data->alloc_bytes - this->alloc_bytes; 417 + 418 + if (cmp > 0) 419 + new = &parent->rb_left; 420 + else 421 + new = &parent->rb_right; 422 + } 423 + 424 + rb_link_node(&data->node, parent, new); 425 + rb_insert_color(&data->node, root); 426 + } 427 + 428 + static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted) 429 + { 430 + struct rb_node *node; 431 + struct page_stat *data; 432 + 433 + for (;;) { 434 + node = rb_first(root); 435 + if (!node) 436 + break; 437 + 438 + rb_erase(node, root); 439 + data = rb_entry(node, struct page_stat, node); 440 + sort_page_insert(root_sorted, data); 751 441 } 752 442 } 753 443 754 444 static void sort_result(void) 755 445 { 756 - __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort); 757 - __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); 446 + if (kmem_slab) { 447 + __sort_slab_result(&root_alloc_stat, &root_alloc_sorted, 448 + &alloc_sort); 449 + __sort_slab_result(&root_caller_stat, &root_caller_sorted, 450 + &caller_sort); 451 + } 452 + if (kmem_page) { 453 + __sort_page_result(&page_alloc_tree, &page_alloc_sorted); 454 + } 758 455 } 759 456 760 457 static int __cmd_kmem(struct perf_session *session) 761 458 { 762 459 int err = -EINVAL; 460 + struct perf_evsel *evsel; 763 461 const struct perf_evsel_str_handler kmem_tracepoints[] = { 462 + /* slab allocator */ 764 463 { "kmem:kmalloc", perf_evsel__process_alloc_event, }, 765 464 { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, 766 465 { "kmem:kmalloc_node", perf_evsel__process_alloc_node_event, }, 767 466 { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, }, 768 467 { "kmem:kfree", perf_evsel__process_free_event, }, 769 468 { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, 469 + /* page allocator */ 470 + { "kmem:mm_page_alloc", perf_evsel__process_page_alloc_event, }, 471 + { "kmem:mm_page_free", perf_evsel__process_page_free_event, }, 770 472 }; 771 473 772 474 if (!perf_session__has_traces(session, "kmem record")) ··· 830 426 goto out; 831 427 } 832 428 429 + evlist__for_each(session->evlist, evsel) { 430 + if (!strcmp(perf_evsel__name(evsel), "kmem:mm_page_alloc") && 431 + perf_evsel__field(evsel, "pfn")) { 432 + use_pfn = true; 433 + break; 434 + } 435 + } 436 + 833 437 setup_pager(); 834 438 err = perf_session__process_events(session); 835 - if (err != 0) 439 + if (err != 0) { 440 + pr_err("error during process events: %d\n", err); 836 441 goto out; 442 + } 837 443 sort_result(); 838 444 print_result(session); 839 445 out: ··· 1026 612 return 0; 1027 613 } 1028 614 615 + static int parse_slab_opt(const struct option *opt __maybe_unused, 616 + const char *arg __maybe_unused, 617 + int unset __maybe_unused) 618 + { 619 + kmem_slab = (kmem_page + 1); 620 + return 0; 621 + } 622 + 623 + static int parse_page_opt(const struct option *opt __maybe_unused, 624 + const char *arg __maybe_unused, 625 + int unset __maybe_unused) 626 + { 627 + kmem_page = (kmem_slab + 1); 628 + return 0; 629 + } 630 + 1029 631 static int parse_line_opt(const struct option *opt __maybe_unused, 1030 632 const char *arg, int unset __maybe_unused) 1031 633 { ··· 1064 634 { 1065 635 const char * const record_args[] = { 1066 636 "record", "-a", "-R", "-c", "1", 637 + }; 638 + const char * const slab_events[] = { 1067 639 "-e", "kmem:kmalloc", 1068 640 "-e", "kmem:kmalloc_node", 1069 641 "-e", "kmem:kfree", ··· 1073 641 "-e", "kmem:kmem_cache_alloc_node", 1074 642 "-e", "kmem:kmem_cache_free", 1075 643 }; 644 + const char * const page_events[] = { 645 + "-e", "kmem:mm_page_alloc", 646 + "-e", "kmem:mm_page_free", 647 + }; 1076 648 unsigned int rec_argc, i, j; 1077 649 const char **rec_argv; 1078 650 1079 651 rec_argc = ARRAY_SIZE(record_args) + argc - 1; 652 + if (kmem_slab) 653 + rec_argc += ARRAY_SIZE(slab_events); 654 + if (kmem_page) 655 + rec_argc += ARRAY_SIZE(page_events); 656 + 1080 657 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 1081 658 1082 659 if (rec_argv == NULL) ··· 1093 652 1094 653 for (i = 0; i < ARRAY_SIZE(record_args); i++) 1095 654 rec_argv[i] = strdup(record_args[i]); 655 + 656 + if (kmem_slab) { 657 + for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++) 658 + rec_argv[i] = strdup(slab_events[j]); 659 + } 660 + if (kmem_page) { 661 + for (j = 0; j < ARRAY_SIZE(page_events); j++, i++) 662 + rec_argv[i] = strdup(page_events[j]); 663 + } 1096 664 1097 665 for (j = 1; j < (unsigned int)argc; j++, i++) 1098 666 rec_argv[i] = argv[j]; ··· 1129 679 OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), 1130 680 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), 1131 681 OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), 682 + OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator", 683 + parse_slab_opt), 684 + OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", 685 + parse_page_opt), 1132 686 OPT_END() 1133 687 }; 1134 688 const char *const kmem_subcommands[] = { "record", "stat", NULL }; ··· 1149 695 if (!argc) 1150 696 usage_with_options(kmem_usage, kmem_options); 1151 697 698 + if (kmem_slab == 0 && kmem_page == 0) 699 + kmem_slab = 1; /* for backward compatibility */ 700 + 1152 701 if (!strncmp(argv[0], "rec", 3)) { 1153 702 symbol__init(NULL); 1154 703 return __cmd_record(argc, argv); ··· 1162 705 session = perf_session__new(&file, false, &perf_kmem); 1163 706 if (session == NULL) 1164 707 return -1; 708 + 709 + if (kmem_page) { 710 + struct perf_evsel *evsel = perf_evlist__first(session->evlist); 711 + 712 + if (evsel == NULL || evsel->tp_format == NULL) { 713 + pr_err("invalid event found.. aborting\n"); 714 + return -1; 715 + } 716 + 717 + kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent); 718 + } 1165 719 1166 720 symbol__init(&session->header.env); 1167 721