linux/tools/perf/builtin-kmem.c
<<
>>
Prefs
   1#include "builtin.h"
   2#include "perf.h"
   3
   4#include "util/evlist.h"
   5#include "util/evsel.h"
   6#include "util/util.h"
   7#include "util/cache.h"
   8#include "util/symbol.h"
   9#include "util/thread.h"
  10#include "util/header.h"
  11#include "util/session.h"
  12#include "util/tool.h"
  13
  14#include "util/parse-options.h"
  15#include "util/trace-event.h"
  16
  17#include "util/debug.h"
  18
  19#include <linux/rbtree.h>
  20#include <linux/string.h>
  21
  22struct alloc_stat;
  23typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
  24
  25static int                      alloc_flag;
  26static int                      caller_flag;
  27
  28static int                      alloc_lines = -1;
  29static int                      caller_lines = -1;
  30
  31static bool                     raw_ip;
  32
  33static int                      *cpunode_map;
  34static int                      max_cpu_num;
  35
  36struct alloc_stat {
  37        u64     call_site;
  38        u64     ptr;
  39        u64     bytes_req;
  40        u64     bytes_alloc;
  41        u32     hit;
  42        u32     pingpong;
  43
  44        short   alloc_cpu;
  45
  46        struct rb_node node;
  47};
  48
  49static struct rb_root root_alloc_stat;
  50static struct rb_root root_alloc_sorted;
  51static struct rb_root root_caller_stat;
  52static struct rb_root root_caller_sorted;
  53
  54static unsigned long total_requested, total_allocated;
  55static unsigned long nr_allocs, nr_cross_allocs;
  56
  57#define PATH_SYS_NODE   "/sys/devices/system/node"
  58
  59static int init_cpunode_map(void)
  60{
  61        FILE *fp;
  62        int i, err = -1;
  63
  64        fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
  65        if (!fp) {
  66                max_cpu_num = 4096;
  67                return 0;
  68        }
  69
  70        if (fscanf(fp, "%d", &max_cpu_num) < 1) {
  71                pr_err("Failed to read 'kernel_max' from sysfs");
  72                goto out_close;
  73        }
  74
  75        max_cpu_num++;
  76
  77        cpunode_map = calloc(max_cpu_num, sizeof(int));
  78        if (!cpunode_map) {
  79                pr_err("%s: calloc failed\n", __func__);
  80                goto out_close;
  81        }
  82
  83        for (i = 0; i < max_cpu_num; i++)
  84                cpunode_map[i] = -1;
  85
  86        err = 0;
  87out_close:
  88        fclose(fp);
  89        return err;
  90}
  91
  92static int setup_cpunode_map(void)
  93{
  94        struct dirent *dent1, *dent2;
  95        DIR *dir1, *dir2;
  96        unsigned int cpu, mem;
  97        char buf[PATH_MAX];
  98
  99        if (init_cpunode_map())
 100                return -1;
 101
 102        dir1 = opendir(PATH_SYS_NODE);
 103        if (!dir1)
 104                return -1;
 105
 106        while ((dent1 = readdir(dir1)) != NULL) {
 107                if (dent1->d_type != DT_DIR ||
 108                    sscanf(dent1->d_name, "node%u", &mem) < 1)
 109                        continue;
 110
 111                snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
 112                dir2 = opendir(buf);
 113                if (!dir2)
 114                        continue;
 115                while ((dent2 = readdir(dir2)) != NULL) {
 116                        if (dent2->d_type != DT_LNK ||
 117                            sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
 118                                continue;
 119                        cpunode_map[cpu] = mem;
 120                }
 121                closedir(dir2);
 122        }
 123        closedir(dir1);
 124        return 0;
 125}
 126
 127static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
 128                             int bytes_req, int bytes_alloc, int cpu)
 129{
 130        struct rb_node **node = &root_alloc_stat.rb_node;
 131        struct rb_node *parent = NULL;
 132        struct alloc_stat *data = NULL;
 133
 134        while (*node) {
 135                parent = *node;
 136                data = rb_entry(*node, struct alloc_stat, node);
 137
 138                if (ptr > data->ptr)
 139                        node = &(*node)->rb_right;
 140                else if (ptr < data->ptr)
 141                        node = &(*node)->rb_left;
 142                else
 143                        break;
 144        }
 145
 146        if (data && data->ptr == ptr) {
 147                data->hit++;
 148                data->bytes_req += bytes_req;
 149                data->bytes_alloc += bytes_alloc;
 150        } else {
 151                data = malloc(sizeof(*data));
 152                if (!data) {
 153                        pr_err("%s: malloc failed\n", __func__);
 154                        return -1;
 155                }
 156                data->ptr = ptr;
 157                data->pingpong = 0;
 158                data->hit = 1;
 159                data->bytes_req = bytes_req;
 160                data->bytes_alloc = bytes_alloc;
 161
 162                rb_link_node(&data->node, parent, node);
 163                rb_insert_color(&data->node, &root_alloc_stat);
 164        }
 165        data->call_site = call_site;
 166        data->alloc_cpu = cpu;
 167        return 0;
 168}
 169
 170static int insert_caller_stat(unsigned long call_site,
 171                              int bytes_req, int bytes_alloc)
 172{
 173        struct rb_node **node = &root_caller_stat.rb_node;
 174        struct rb_node *parent = NULL;
 175        struct alloc_stat *data = NULL;
 176
 177        while (*node) {
 178                parent = *node;
 179                data = rb_entry(*node, struct alloc_stat, node);
 180
 181                if (call_site > data->call_site)
 182                        node = &(*node)->rb_right;
 183                else if (call_site < data->call_site)
 184                        node = &(*node)->rb_left;
 185                else
 186                        break;
 187        }
 188
 189        if (data && data->call_site == call_site) {
 190                data->hit++;
 191                data->bytes_req += bytes_req;
 192                data->bytes_alloc += bytes_alloc;
 193        } else {
 194                data = malloc(sizeof(*data));
 195                if (!data) {
 196                        pr_err("%s: malloc failed\n", __func__);
 197                        return -1;
 198                }
 199                data->call_site = call_site;
 200                data->pingpong = 0;
 201                data->hit = 1;
 202                data->bytes_req = bytes_req;
 203                data->bytes_alloc = bytes_alloc;
 204
 205                rb_link_node(&data->node, parent, node);
 206                rb_insert_color(&data->node, &root_caller_stat);
 207        }
 208
 209        return 0;
 210}
 211
 212static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
 213                                           struct perf_sample *sample)
 214{
 215        unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
 216                      call_site = perf_evsel__intval(evsel, sample, "call_site");
 217        int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
 218            bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
 219
 220        if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
 221            insert_caller_stat(call_site, bytes_req, bytes_alloc))
 222                return -1;
 223
 224        total_requested += bytes_req;
 225        total_allocated += bytes_alloc;
 226
 227        nr_allocs++;
 228        return 0;
 229}
 230
 231static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
 232                                                struct perf_sample *sample)
 233{
 234        int ret = perf_evsel__process_alloc_event(evsel, sample);
 235
 236        if (!ret) {
 237                int node1 = cpunode_map[sample->cpu],
 238                    node2 = perf_evsel__intval(evsel, sample, "node");
 239
 240                if (node1 != node2)
 241                        nr_cross_allocs++;
 242        }
 243
 244        return ret;
 245}
 246
 247static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
 248static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
 249
 250static struct alloc_stat *search_alloc_stat(unsigned long ptr,
 251                                            unsigned long call_site,
 252                                            struct rb_root *root,
 253                                            sort_fn_t sort_fn)
 254{
 255        struct rb_node *node = root->rb_node;
 256        struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
 257
 258        while (node) {
 259                struct alloc_stat *data;
 260                int cmp;
 261
 262                data = rb_entry(node, struct alloc_stat, node);
 263
 264                cmp = sort_fn(&key, data);
 265                if (cmp < 0)
 266                        node = node->rb_left;
 267                else if (cmp > 0)
 268                        node = node->rb_right;
 269                else
 270                        return data;
 271        }
 272        return NULL;
 273}
 274
 275static int perf_evsel__process_free_event(struct perf_evsel *evsel,
 276                                          struct perf_sample *sample)
 277{
 278        unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
 279        struct alloc_stat *s_alloc, *s_caller;
 280
 281        s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
 282        if (!s_alloc)
 283                return 0;
 284
 285        if ((short)sample->cpu != s_alloc->alloc_cpu) {
 286                s_alloc->pingpong++;
 287
 288                s_caller = search_alloc_stat(0, s_alloc->call_site,
 289                                             &root_caller_stat, callsite_cmp);
 290                if (!s_caller)
 291                        return -1;
 292                s_caller->pingpong++;
 293        }
 294        s_alloc->alloc_cpu = -1;
 295
 296        return 0;
 297}
 298
 299typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
 300                                  struct perf_sample *sample);
 301
 302static int process_sample_event(struct perf_tool *tool __maybe_unused,
 303                                union perf_event *event,
 304                                struct perf_sample *sample,
 305                                struct perf_evsel *evsel,
 306                                struct machine *machine)
 307{
 308        struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
 309
 310        if (thread == NULL) {
 311                pr_debug("problem processing %d event, skipping it.\n",
 312                         event->header.type);
 313                return -1;
 314        }
 315
 316        dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
 317
 318        if (evsel->handler.func != NULL) {
 319                tracepoint_handler f = evsel->handler.func;
 320                return f(evsel, sample);
 321        }
 322
 323        return 0;
 324}
 325
 326static struct perf_tool perf_kmem = {
 327        .sample          = process_sample_event,
 328        .comm            = perf_event__process_comm,
 329        .ordered_samples = true,
 330};
 331
 332static double fragmentation(unsigned long n_req, unsigned long n_alloc)
 333{
 334        if (n_alloc == 0)
 335                return 0.0;
 336        else
 337                return 100.0 - (100.0 * n_req / n_alloc);
 338}
 339
 340static void __print_result(struct rb_root *root, struct perf_session *session,
 341                           int n_lines, int is_caller)
 342{
 343        struct rb_node *next;
 344        struct machine *machine = &session->machines.host;
 345
 346        printf("%.102s\n", graph_dotted_line);
 347        printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
 348        printf(" Total_alloc/Per | Total_req/Per   | Hit      | Ping-pong | Frag\n");
 349        printf("%.102s\n", graph_dotted_line);
 350
 351        next = rb_first(root);
 352
 353        while (next && n_lines--) {
 354                struct alloc_stat *data = rb_entry(next, struct alloc_stat,
 355                                                   node);
 356                struct symbol *sym = NULL;
 357                struct map *map;
 358                char buf[BUFSIZ];
 359                u64 addr;
 360
 361                if (is_caller) {
 362                        addr = data->call_site;
 363                        if (!raw_ip)
 364                                sym = machine__find_kernel_function(machine, addr, &map, NULL);
 365                } else
 366                        addr = data->ptr;
 367
 368                if (sym != NULL)
 369                        snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
 370                                 addr - map->unmap_ip(map, sym->start));
 371                else
 372                        snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
 373                printf(" %-34s |", buf);
 374
 375                printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
 376                       (unsigned long long)data->bytes_alloc,
 377                       (unsigned long)data->bytes_alloc / data->hit,
 378                       (unsigned long long)data->bytes_req,
 379                       (unsigned long)data->bytes_req / data->hit,
 380                       (unsigned long)data->hit,
 381                       (unsigned long)data->pingpong,
 382                       fragmentation(data->bytes_req, data->bytes_alloc));
 383
 384                next = rb_next(next);
 385        }
 386
 387        if (n_lines == -1)
 388                printf(" ...                                | ...             | ...             | ...    | ...      | ...   \n");
 389
 390        printf("%.102s\n", graph_dotted_line);
 391}
 392
 393static void print_summary(void)
 394{
 395        printf("\nSUMMARY\n=======\n");
 396        printf("Total bytes requested: %lu\n", total_requested);
 397        printf("Total bytes allocated: %lu\n", total_allocated);
 398        printf("Total bytes wasted on internal fragmentation: %lu\n",
 399               total_allocated - total_requested);
 400        printf("Internal fragmentation: %f%%\n",
 401               fragmentation(total_requested, total_allocated));
 402        printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
 403}
 404
 405static void print_result(struct perf_session *session)
 406{
 407        if (caller_flag)
 408                __print_result(&root_caller_sorted, session, caller_lines, 1);
 409        if (alloc_flag)
 410                __print_result(&root_alloc_sorted, session, alloc_lines, 0);
 411        print_summary();
 412}
 413
 414struct sort_dimension {
 415        const char              name[20];
 416        sort_fn_t               cmp;
 417        struct list_head        list;
 418};
 419
 420static LIST_HEAD(caller_sort);
 421static LIST_HEAD(alloc_sort);
 422
 423static void sort_insert(struct rb_root *root, struct alloc_stat *data,
 424                        struct list_head *sort_list)
 425{
 426        struct rb_node **new = &(root->rb_node);
 427        struct rb_node *parent = NULL;
 428        struct sort_dimension *sort;
 429
 430        while (*new) {
 431                struct alloc_stat *this;
 432                int cmp = 0;
 433
 434                this = rb_entry(*new, struct alloc_stat, node);
 435                parent = *new;
 436
 437                list_for_each_entry(sort, sort_list, list) {
 438                        cmp = sort->cmp(data, this);
 439                        if (cmp)
 440                                break;
 441                }
 442
 443                if (cmp > 0)
 444                        new = &((*new)->rb_left);
 445                else
 446                        new = &((*new)->rb_right);
 447        }
 448
 449        rb_link_node(&data->node, parent, new);
 450        rb_insert_color(&data->node, root);
 451}
 452
 453static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
 454                          struct list_head *sort_list)
 455{
 456        struct rb_node *node;
 457        struct alloc_stat *data;
 458
 459        for (;;) {
 460                node = rb_first(root);
 461                if (!node)
 462                        break;
 463
 464                rb_erase(node, root);
 465                data = rb_entry(node, struct alloc_stat, node);
 466                sort_insert(root_sorted, data, sort_list);
 467        }
 468}
 469
 470static void sort_result(void)
 471{
 472        __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
 473        __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
 474}
 475
 476static int __cmd_kmem(void)
 477{
 478        int err = -EINVAL;
 479        struct perf_session *session;
 480        const struct perf_evsel_str_handler kmem_tracepoints[] = {
 481                { "kmem:kmalloc",               perf_evsel__process_alloc_event, },
 482                { "kmem:kmem_cache_alloc",      perf_evsel__process_alloc_event, },
 483                { "kmem:kmalloc_node",          perf_evsel__process_alloc_node_event, },
 484                { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
 485                { "kmem:kfree",                 perf_evsel__process_free_event, },
 486                { "kmem:kmem_cache_free",       perf_evsel__process_free_event, },
 487        };
 488
 489        session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem);
 490        if (session == NULL)
 491                return -ENOMEM;
 492
 493        if (perf_session__create_kernel_maps(session) < 0)
 494                goto out_delete;
 495
 496        if (!perf_session__has_traces(session, "kmem record"))
 497                goto out_delete;
 498
 499        if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
 500                pr_err("Initializing perf session tracepoint handlers failed\n");
 501                return -1;
 502        }
 503
 504        setup_pager();
 505        err = perf_session__process_events(session, &perf_kmem);
 506        if (err != 0)
 507                goto out_delete;
 508        sort_result();
 509        print_result(session);
 510out_delete:
 511        perf_session__delete(session);
 512        return err;
 513}
 514
 515static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
 516{
 517        if (l->ptr < r->ptr)
 518                return -1;
 519        else if (l->ptr > r->ptr)
 520                return 1;
 521        return 0;
 522}
 523
 524static struct sort_dimension ptr_sort_dimension = {
 525        .name   = "ptr",
 526        .cmp    = ptr_cmp,
 527};
 528
 529static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
 530{
 531        if (l->call_site < r->call_site)
 532                return -1;
 533        else if (l->call_site > r->call_site)
 534                return 1;
 535        return 0;
 536}
 537
 538static struct sort_dimension callsite_sort_dimension = {
 539        .name   = "callsite",
 540        .cmp    = callsite_cmp,
 541};
 542
 543static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
 544{
 545        if (l->hit < r->hit)
 546                return -1;
 547        else if (l->hit > r->hit)
 548                return 1;
 549        return 0;
 550}
 551
 552static struct sort_dimension hit_sort_dimension = {
 553        .name   = "hit",
 554        .cmp    = hit_cmp,
 555};
 556
 557static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
 558{
 559        if (l->bytes_alloc < r->bytes_alloc)
 560                return -1;
 561        else if (l->bytes_alloc > r->bytes_alloc)
 562                return 1;
 563        return 0;
 564}
 565
 566static struct sort_dimension bytes_sort_dimension = {
 567        .name   = "bytes",
 568        .cmp    = bytes_cmp,
 569};
 570
 571static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
 572{
 573        double x, y;
 574
 575        x = fragmentation(l->bytes_req, l->bytes_alloc);
 576        y = fragmentation(r->bytes_req, r->bytes_alloc);
 577
 578        if (x < y)
 579                return -1;
 580        else if (x > y)
 581                return 1;
 582        return 0;
 583}
 584
 585static struct sort_dimension frag_sort_dimension = {
 586        .name   = "frag",
 587        .cmp    = frag_cmp,
 588};
 589
 590static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
 591{
 592        if (l->pingpong < r->pingpong)
 593                return -1;
 594        else if (l->pingpong > r->pingpong)
 595                return 1;
 596        return 0;
 597}
 598
 599static struct sort_dimension pingpong_sort_dimension = {
 600        .name   = "pingpong",
 601        .cmp    = pingpong_cmp,
 602};
 603
 604static struct sort_dimension *avail_sorts[] = {
 605        &ptr_sort_dimension,
 606        &callsite_sort_dimension,
 607        &hit_sort_dimension,
 608        &bytes_sort_dimension,
 609        &frag_sort_dimension,
 610        &pingpong_sort_dimension,
 611};
 612
 613#define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts))
 614
 615static int sort_dimension__add(const char *tok, struct list_head *list)
 616{
 617        struct sort_dimension *sort;
 618        int i;
 619
 620        for (i = 0; i < NUM_AVAIL_SORTS; i++) {
 621                if (!strcmp(avail_sorts[i]->name, tok)) {
 622                        sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
 623                        if (!sort) {
 624                                pr_err("%s: memdup failed\n", __func__);
 625                                return -1;
 626                        }
 627                        list_add_tail(&sort->list, list);
 628                        return 0;
 629                }
 630        }
 631
 632        return -1;
 633}
 634
 635static int setup_sorting(struct list_head *sort_list, const char *arg)
 636{
 637        char *tok;
 638        char *str = strdup(arg);
 639
 640        if (!str) {
 641                pr_err("%s: strdup failed\n", __func__);
 642                return -1;
 643        }
 644
 645        while (true) {
 646                tok = strsep(&str, ",");
 647                if (!tok)
 648                        break;
 649                if (sort_dimension__add(tok, sort_list) < 0) {
 650                        error("Unknown --sort key: '%s'", tok);
 651                        free(str);
 652                        return -1;
 653                }
 654        }
 655
 656        free(str);
 657        return 0;
 658}
 659
 660static int parse_sort_opt(const struct option *opt __maybe_unused,
 661                          const char *arg, int unset __maybe_unused)
 662{
 663        if (!arg)
 664                return -1;
 665
 666        if (caller_flag > alloc_flag)
 667                return setup_sorting(&caller_sort, arg);
 668        else
 669                return setup_sorting(&alloc_sort, arg);
 670
 671        return 0;
 672}
 673
 674static int parse_caller_opt(const struct option *opt __maybe_unused,
 675                            const char *arg __maybe_unused,
 676                            int unset __maybe_unused)
 677{
 678        caller_flag = (alloc_flag + 1);
 679        return 0;
 680}
 681
 682static int parse_alloc_opt(const struct option *opt __maybe_unused,
 683                           const char *arg __maybe_unused,
 684                           int unset __maybe_unused)
 685{
 686        alloc_flag = (caller_flag + 1);
 687        return 0;
 688}
 689
 690static int parse_line_opt(const struct option *opt __maybe_unused,
 691                          const char *arg, int unset __maybe_unused)
 692{
 693        int lines;
 694
 695        if (!arg)
 696                return -1;
 697
 698        lines = strtoul(arg, NULL, 10);
 699
 700        if (caller_flag > alloc_flag)
 701                caller_lines = lines;
 702        else
 703                alloc_lines = lines;
 704
 705        return 0;
 706}
 707
 708static int __cmd_record(int argc, const char **argv)
 709{
 710        const char * const record_args[] = {
 711        "record", "-a", "-R", "-c", "1",
 712        "-e", "kmem:kmalloc",
 713        "-e", "kmem:kmalloc_node",
 714        "-e", "kmem:kfree",
 715        "-e", "kmem:kmem_cache_alloc",
 716        "-e", "kmem:kmem_cache_alloc_node",
 717        "-e", "kmem:kmem_cache_free",
 718        };
 719        unsigned int rec_argc, i, j;
 720        const char **rec_argv;
 721
 722        rec_argc = ARRAY_SIZE(record_args) + argc - 1;
 723        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 724
 725        if (rec_argv == NULL)
 726                return -ENOMEM;
 727
 728        for (i = 0; i < ARRAY_SIZE(record_args); i++)
 729                rec_argv[i] = strdup(record_args[i]);
 730
 731        for (j = 1; j < (unsigned int)argc; j++, i++)
 732                rec_argv[i] = argv[j];
 733
 734        return cmd_record(i, rec_argv, NULL);
 735}
 736
 737int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 738{
 739        const char * const default_sort_order = "frag,hit,bytes";
 740        const struct option kmem_options[] = {
 741        OPT_STRING('i', "input", &input_name, "file", "input file name"),
 742        OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
 743                           "show per-callsite statistics", parse_caller_opt),
 744        OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
 745                           "show per-allocation statistics", parse_alloc_opt),
 746        OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
 747                     "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
 748                     parse_sort_opt),
 749        OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
 750        OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
 751        OPT_END()
 752        };
 753        const char * const kmem_usage[] = {
 754                "perf kmem [<options>] {record|stat}",
 755                NULL
 756        };
 757        argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
 758
 759        if (!argc)
 760                usage_with_options(kmem_usage, kmem_options);
 761
 762        symbol__init();
 763
 764        if (!strncmp(argv[0], "rec", 3)) {
 765                return __cmd_record(argc, argv);
 766        } else if (!strcmp(argv[0], "stat")) {
 767                if (setup_cpunode_map())
 768                        return -1;
 769
 770                if (list_empty(&caller_sort))
 771                        setup_sorting(&caller_sort, default_sort_order);
 772                if (list_empty(&alloc_sort))
 773                        setup_sorting(&alloc_sort, default_sort_order);
 774
 775                return __cmd_kmem();
 776        } else
 777                usage_with_options(kmem_usage, kmem_options);
 778
 779        return 0;
 780}
 781
 782