linux/tools/perf/builtin-report.c
<<
>>
Prefs
   1/*
   2 * builtin-report.c
   3 *
   4 * Builtin report command: Analyze the perf.data input file,
   5 * look up and read DSOs and symbol information and display
   6 * a histogram of results, along various sorting keys.
   7 */
   8#include "builtin.h"
   9
  10#include "util/util.h"
  11
  12#include "util/color.h"
  13#include <linux/list.h>
  14#include "util/cache.h"
  15#include <linux/rbtree.h>
  16#include "util/symbol.h"
  17#include "util/string.h"
  18#include "util/callchain.h"
  19#include "util/strlist.h"
  20#include "util/values.h"
  21
  22#include "perf.h"
  23#include "util/debug.h"
  24#include "util/header.h"
  25
  26#include "util/parse-options.h"
  27#include "util/parse-events.h"
  28
  29#include "util/thread.h"
  30
  31static char             const *input_name = "perf.data";
  32
  33static char             default_sort_order[] = "comm,dso,symbol";
  34static char             *sort_order = default_sort_order;
  35static char             *dso_list_str, *comm_list_str, *sym_list_str,
  36                        *col_width_list_str;
  37static struct strlist   *dso_list, *comm_list, *sym_list;
  38static char             *field_sep;
  39
  40static int              force;
  41static int              input;
  42static int              show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
  43
  44static int              full_paths;
  45static int              show_nr_samples;
  46
  47static int              show_threads;
  48static struct perf_read_values  show_threads_values;
  49
  50static char             default_pretty_printing_style[] = "normal";
  51static char             *pretty_printing_style = default_pretty_printing_style;
  52
  53static unsigned long    page_size;
  54static unsigned long    mmap_window = 32;
  55
  56static char             default_parent_pattern[] = "^sys_|^do_page_fault";
  57static char             *parent_pattern = default_parent_pattern;
  58static regex_t          parent_regex;
  59
  60static int              exclude_other = 1;
  61
  62static char             callchain_default_opt[] = "fractal,0.5";
  63
  64static int              callchain;
  65
  66static char             __cwd[PATH_MAX];
  67static char             *cwd = __cwd;
  68static int              cwdlen;
  69
  70static struct rb_root   threads;
  71static struct thread    *last_match;
  72
  73static struct perf_header *header;
  74
  75static
  76struct callchain_param  callchain_param = {
  77        .mode   = CHAIN_GRAPH_REL,
  78        .min_percent = 0.5
  79};
  80
  81static u64              sample_type;
  82
  83static int repsep_fprintf(FILE *fp, const char *fmt, ...)
  84{
  85        int n;
  86        va_list ap;
  87
  88        va_start(ap, fmt);
  89        if (!field_sep)
  90                n = vfprintf(fp, fmt, ap);
  91        else {
  92                char *bf = NULL;
  93                n = vasprintf(&bf, fmt, ap);
  94                if (n > 0) {
  95                        char *sep = bf;
  96
  97                        while (1) {
  98                                sep = strchr(sep, *field_sep);
  99                                if (sep == NULL)
 100                                        break;
 101                                *sep = '.';
 102                        }
 103                }
 104                fputs(bf, fp);
 105                free(bf);
 106        }
 107        va_end(ap);
 108        return n;
 109}
 110
 111static unsigned int dsos__col_width,
 112                    comms__col_width,
 113                    threads__col_width;
 114
 115/*
 116 * histogram, sorted on item, collects counts
 117 */
 118
 119static struct rb_root hist;
 120
 121struct hist_entry {
 122        struct rb_node          rb_node;
 123
 124        struct thread           *thread;
 125        struct map              *map;
 126        struct dso              *dso;
 127        struct symbol           *sym;
 128        struct symbol           *parent;
 129        u64                     ip;
 130        char                    level;
 131        struct callchain_node   callchain;
 132        struct rb_root          sorted_chain;
 133
 134        u64                     count;
 135};
 136
 137/*
 138 * configurable sorting bits
 139 */
 140
 141struct sort_entry {
 142        struct list_head list;
 143
 144        const char *header;
 145
 146        int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
 147        int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
 148        size_t  (*print)(FILE *fp, struct hist_entry *, unsigned int width);
 149        unsigned int *width;
 150        bool    elide;
 151};
 152
 153static int64_t cmp_null(void *l, void *r)
 154{
 155        if (!l && !r)
 156                return 0;
 157        else if (!l)
 158                return -1;
 159        else
 160                return 1;
 161}
 162
 163/* --sort pid */
 164
 165static int64_t
 166sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
 167{
 168        return right->thread->pid - left->thread->pid;
 169}
 170
 171static size_t
 172sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)
 173{
 174        return repsep_fprintf(fp, "%*s:%5d", width - 6,
 175                              self->thread->comm ?: "", self->thread->pid);
 176}
 177
 178static struct sort_entry sort_thread = {
 179        .header = "Command:  Pid",
 180        .cmp    = sort__thread_cmp,
 181        .print  = sort__thread_print,
 182        .width  = &threads__col_width,
 183};
 184
 185/* --sort comm */
 186
 187static int64_t
 188sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
 189{
 190        return right->thread->pid - left->thread->pid;
 191}
 192
 193static int64_t
 194sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
 195{
 196        char *comm_l = left->thread->comm;
 197        char *comm_r = right->thread->comm;
 198
 199        if (!comm_l || !comm_r)
 200                return cmp_null(comm_l, comm_r);
 201
 202        return strcmp(comm_l, comm_r);
 203}
 204
 205static size_t
 206sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
 207{
 208        return repsep_fprintf(fp, "%*s", width, self->thread->comm);
 209}
 210
 211static struct sort_entry sort_comm = {
 212        .header         = "Command",
 213        .cmp            = sort__comm_cmp,
 214        .collapse       = sort__comm_collapse,
 215        .print          = sort__comm_print,
 216        .width          = &comms__col_width,
 217};
 218
 219/* --sort dso */
 220
 221static int64_t
 222sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
 223{
 224        struct dso *dso_l = left->dso;
 225        struct dso *dso_r = right->dso;
 226
 227        if (!dso_l || !dso_r)
 228                return cmp_null(dso_l, dso_r);
 229
 230        return strcmp(dso_l->name, dso_r->name);
 231}
 232
 233static size_t
 234sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
 235{
 236        if (self->dso)
 237                return repsep_fprintf(fp, "%-*s", width, self->dso->name);
 238
 239        return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
 240}
 241
 242static struct sort_entry sort_dso = {
 243        .header = "Shared Object",
 244        .cmp    = sort__dso_cmp,
 245        .print  = sort__dso_print,
 246        .width  = &dsos__col_width,
 247};
 248
 249/* --sort symbol */
 250
 251static int64_t
 252sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 253{
 254        u64 ip_l, ip_r;
 255
 256        if (left->sym == right->sym)
 257                return 0;
 258
 259        ip_l = left->sym ? left->sym->start : left->ip;
 260        ip_r = right->sym ? right->sym->start : right->ip;
 261
 262        return (int64_t)(ip_r - ip_l);
 263}
 264
 265static size_t
 266sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
 267{
 268        size_t ret = 0;
 269
 270        if (verbose)
 271                ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip,
 272                                      dso__symtab_origin(self->dso));
 273
 274        ret += repsep_fprintf(fp, "[%c] ", self->level);
 275        if (self->sym) {
 276                ret += repsep_fprintf(fp, "%s", self->sym->name);
 277
 278                if (self->sym->module)
 279                        ret += repsep_fprintf(fp, "\t[%s]",
 280                                             self->sym->module->name);
 281        } else {
 282                ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
 283        }
 284
 285        return ret;
 286}
 287
 288static struct sort_entry sort_sym = {
 289        .header = "Symbol",
 290        .cmp    = sort__sym_cmp,
 291        .print  = sort__sym_print,
 292};
 293
 294/* --sort parent */
 295
 296static int64_t
 297sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
 298{
 299        struct symbol *sym_l = left->parent;
 300        struct symbol *sym_r = right->parent;
 301
 302        if (!sym_l || !sym_r)
 303                return cmp_null(sym_l, sym_r);
 304
 305        return strcmp(sym_l->name, sym_r->name);
 306}
 307
 308static size_t
 309sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)
 310{
 311        return repsep_fprintf(fp, "%-*s", width,
 312                              self->parent ? self->parent->name : "[other]");
 313}
 314
 315static unsigned int parent_symbol__col_width;
 316
 317static struct sort_entry sort_parent = {
 318        .header = "Parent symbol",
 319        .cmp    = sort__parent_cmp,
 320        .print  = sort__parent_print,
 321        .width  = &parent_symbol__col_width,
 322};
 323
 324static int sort__need_collapse = 0;
 325static int sort__has_parent = 0;
 326
 327struct sort_dimension {
 328        const char              *name;
 329        struct sort_entry       *entry;
 330        int                     taken;
 331};
 332
 333static struct sort_dimension sort_dimensions[] = {
 334        { .name = "pid",        .entry = &sort_thread,  },
 335        { .name = "comm",       .entry = &sort_comm,    },
 336        { .name = "dso",        .entry = &sort_dso,     },
 337        { .name = "symbol",     .entry = &sort_sym,     },
 338        { .name = "parent",     .entry = &sort_parent,  },
 339};
 340
 341static LIST_HEAD(hist_entry__sort_list);
 342
 343static int sort_dimension__add(const char *tok)
 344{
 345        unsigned int i;
 346
 347        for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
 348                struct sort_dimension *sd = &sort_dimensions[i];
 349
 350                if (sd->taken)
 351                        continue;
 352
 353                if (strncasecmp(tok, sd->name, strlen(tok)))
 354                        continue;
 355
 356                if (sd->entry->collapse)
 357                        sort__need_collapse = 1;
 358
 359                if (sd->entry == &sort_parent) {
 360                        int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
 361                        if (ret) {
 362                                char err[BUFSIZ];
 363
 364                                regerror(ret, &parent_regex, err, sizeof(err));
 365                                fprintf(stderr, "Invalid regex: %s\n%s",
 366                                        parent_pattern, err);
 367                                exit(-1);
 368                        }
 369                        sort__has_parent = 1;
 370                }
 371
 372                list_add_tail(&sd->entry->list, &hist_entry__sort_list);
 373                sd->taken = 1;
 374
 375                return 0;
 376        }
 377
 378        return -ESRCH;
 379}
 380
 381static int64_t
 382hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
 383{
 384        struct sort_entry *se;
 385        int64_t cmp = 0;
 386
 387        list_for_each_entry(se, &hist_entry__sort_list, list) {
 388                cmp = se->cmp(left, right);
 389                if (cmp)
 390                        break;
 391        }
 392
 393        return cmp;
 394}
 395
 396static int64_t
 397hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
 398{
 399        struct sort_entry *se;
 400        int64_t cmp = 0;
 401
 402        list_for_each_entry(se, &hist_entry__sort_list, list) {
 403                int64_t (*f)(struct hist_entry *, struct hist_entry *);
 404
 405                f = se->collapse ?: se->cmp;
 406
 407                cmp = f(left, right);
 408                if (cmp)
 409                        break;
 410        }
 411
 412        return cmp;
 413}
 414
 415static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask)
 416{
 417        int i;
 418        size_t ret = 0;
 419
 420        ret += fprintf(fp, "%s", "                ");
 421
 422        for (i = 0; i < depth; i++)
 423                if (depth_mask & (1 << i))
 424                        ret += fprintf(fp, "|          ");
 425                else
 426                        ret += fprintf(fp, "           ");
 427
 428        ret += fprintf(fp, "\n");
 429
 430        return ret;
 431}
 432static size_t
 433ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
 434                       int depth_mask, int count, u64 total_samples,
 435                       int hits)
 436{
 437        int i;
 438        size_t ret = 0;
 439
 440        ret += fprintf(fp, "%s", "                ");
 441        for (i = 0; i < depth; i++) {
 442                if (depth_mask & (1 << i))
 443                        ret += fprintf(fp, "|");
 444                else
 445                        ret += fprintf(fp, " ");
 446                if (!count && i == depth - 1) {
 447                        double percent;
 448
 449                        percent = hits * 100.0 / total_samples;
 450                        ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
 451                } else
 452                        ret += fprintf(fp, "%s", "          ");
 453        }
 454        if (chain->sym)
 455                ret += fprintf(fp, "%s\n", chain->sym->name);
 456        else
 457                ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
 458
 459        return ret;
 460}
 461
 462static struct symbol *rem_sq_bracket;
 463static struct callchain_list rem_hits;
 464
 465static void init_rem_hits(void)
 466{
 467        rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
 468        if (!rem_sq_bracket) {
 469                fprintf(stderr, "Not enough memory to display remaining hits\n");
 470                return;
 471        }
 472
 473        strcpy(rem_sq_bracket->name, "[...]");
 474        rem_hits.sym = rem_sq_bracket;
 475}
 476
 477static size_t
 478callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
 479                        u64 total_samples, int depth, int depth_mask)
 480{
 481        struct rb_node *node, *next;
 482        struct callchain_node *child;
 483        struct callchain_list *chain;
 484        int new_depth_mask = depth_mask;
 485        u64 new_total;
 486        u64 remaining;
 487        size_t ret = 0;
 488        int i;
 489
 490        if (callchain_param.mode == CHAIN_GRAPH_REL)
 491                new_total = self->children_hit;
 492        else
 493                new_total = total_samples;
 494
 495        remaining = new_total;
 496
 497        node = rb_first(&self->rb_root);
 498        while (node) {
 499                u64 cumul;
 500
 501                child = rb_entry(node, struct callchain_node, rb_node);
 502                cumul = cumul_hits(child);
 503                remaining -= cumul;
 504
 505                /*
 506                 * The depth mask manages the output of pipes that show
 507                 * the depth. We don't want to keep the pipes of the current
 508                 * level for the last child of this depth.
 509                 * Except if we have remaining filtered hits. They will
 510                 * supersede the last child
 511                 */
 512                next = rb_next(node);
 513                if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
 514                        new_depth_mask &= ~(1 << (depth - 1));
 515
 516                /*
 517                 * But we keep the older depth mask for the line seperator
 518                 * to keep the level link until we reach the last child
 519                 */
 520                ret += ipchain__fprintf_graph_line(fp, depth, depth_mask);
 521                i = 0;
 522                list_for_each_entry(chain, &child->val, list) {
 523                        if (chain->ip >= PERF_CONTEXT_MAX)
 524                                continue;
 525                        ret += ipchain__fprintf_graph(fp, chain, depth,
 526                                                      new_depth_mask, i++,
 527                                                      new_total,
 528                                                      cumul);
 529                }
 530                ret += callchain__fprintf_graph(fp, child, new_total,
 531                                                depth + 1,
 532                                                new_depth_mask | (1 << depth));
 533                node = next;
 534        }
 535
 536        if (callchain_param.mode == CHAIN_GRAPH_REL &&
 537                remaining && remaining != new_total) {
 538
 539                if (!rem_sq_bracket)
 540                        return ret;
 541
 542                new_depth_mask &= ~(1 << (depth - 1));
 543
 544                ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
 545                                              new_depth_mask, 0, new_total,
 546                                              remaining);
 547        }
 548
 549        return ret;
 550}
 551
 552static size_t
 553callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
 554                        u64 total_samples)
 555{
 556        struct callchain_list *chain;
 557        size_t ret = 0;
 558
 559        if (!self)
 560                return 0;
 561
 562        ret += callchain__fprintf_flat(fp, self->parent, total_samples);
 563
 564
 565        list_for_each_entry(chain, &self->val, list) {
 566                if (chain->ip >= PERF_CONTEXT_MAX)
 567                        continue;
 568                if (chain->sym)
 569                        ret += fprintf(fp, "                %s\n", chain->sym->name);
 570                else
 571                        ret += fprintf(fp, "                %p\n",
 572                                        (void *)(long)chain->ip);
 573        }
 574
 575        return ret;
 576}
 577
 578static size_t
 579hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
 580                              u64 total_samples)
 581{
 582        struct rb_node *rb_node;
 583        struct callchain_node *chain;
 584        size_t ret = 0;
 585
 586        rb_node = rb_first(&self->sorted_chain);
 587        while (rb_node) {
 588                double percent;
 589
 590                chain = rb_entry(rb_node, struct callchain_node, rb_node);
 591                percent = chain->hit * 100.0 / total_samples;
 592                switch (callchain_param.mode) {
 593                case CHAIN_FLAT:
 594                        ret += percent_color_fprintf(fp, "           %6.2f%%\n",
 595                                                     percent);
 596                        ret += callchain__fprintf_flat(fp, chain, total_samples);
 597                        break;
 598                case CHAIN_GRAPH_ABS: /* Falldown */
 599                case CHAIN_GRAPH_REL:
 600                        ret += callchain__fprintf_graph(fp, chain,
 601                                                        total_samples, 1, 1);
 602                case CHAIN_NONE:
 603                default:
 604                        break;
 605                }
 606                ret += fprintf(fp, "\n");
 607                rb_node = rb_next(rb_node);
 608        }
 609
 610        return ret;
 611}
 612
 613
 614static size_t
 615hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
 616{
 617        struct sort_entry *se;
 618        size_t ret;
 619
 620        if (exclude_other && !self->parent)
 621                return 0;
 622
 623        if (total_samples)
 624                ret = percent_color_fprintf(fp,
 625                                            field_sep ? "%.2f" : "   %6.2f%%",
 626                                        (self->count * 100.0) / total_samples);
 627        else
 628                ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
 629
 630        if (show_nr_samples) {
 631                if (field_sep)
 632                        fprintf(fp, "%c%lld", *field_sep, self->count);
 633                else
 634                        fprintf(fp, "%11lld", self->count);
 635        }
 636
 637        list_for_each_entry(se, &hist_entry__sort_list, list) {
 638                if (se->elide)
 639                        continue;
 640
 641                fprintf(fp, "%s", field_sep ?: "  ");
 642                ret += se->print(fp, self, se->width ? *se->width : 0);
 643        }
 644
 645        ret += fprintf(fp, "\n");
 646
 647        if (callchain)
 648                hist_entry_callchain__fprintf(fp, self, total_samples);
 649
 650        return ret;
 651}
 652
 653/*
 654 *
 655 */
 656
 657static void dso__calc_col_width(struct dso *self)
 658{
 659        if (!col_width_list_str && !field_sep &&
 660            (!dso_list || strlist__has_entry(dso_list, self->name))) {
 661                unsigned int slen = strlen(self->name);
 662                if (slen > dsos__col_width)
 663                        dsos__col_width = slen;
 664        }
 665
 666        self->slen_calculated = 1;
 667}
 668
 669static void thread__comm_adjust(struct thread *self)
 670{
 671        char *comm = self->comm;
 672
 673        if (!col_width_list_str && !field_sep &&
 674            (!comm_list || strlist__has_entry(comm_list, comm))) {
 675                unsigned int slen = strlen(comm);
 676
 677                if (slen > comms__col_width) {
 678                        comms__col_width = slen;
 679                        threads__col_width = slen + 6;
 680                }
 681        }
 682}
 683
 684static int thread__set_comm_adjust(struct thread *self, const char *comm)
 685{
 686        int ret = thread__set_comm(self, comm);
 687
 688        if (ret)
 689                return ret;
 690
 691        thread__comm_adjust(self);
 692
 693        return 0;
 694}
 695
 696
 697static struct symbol *
 698resolve_symbol(struct thread *thread, struct map **mapp,
 699               struct dso **dsop, u64 *ipp)
 700{
 701        struct dso *dso = dsop ? *dsop : NULL;
 702        struct map *map = mapp ? *mapp : NULL;
 703        u64 ip = *ipp;
 704
 705        if (!thread)
 706                return NULL;
 707
 708        if (dso)
 709                goto got_dso;
 710
 711        if (map)
 712                goto got_map;
 713
 714        map = thread__find_map(thread, ip);
 715        if (map != NULL) {
 716                /*
 717                 * We have to do this here as we may have a dso
 718                 * with no symbol hit that has a name longer than
 719                 * the ones with symbols sampled.
 720                 */
 721                if (!sort_dso.elide && !map->dso->slen_calculated)
 722                        dso__calc_col_width(map->dso);
 723
 724                if (mapp)
 725                        *mapp = map;
 726got_map:
 727                ip = map->map_ip(map, ip);
 728
 729                dso = map->dso;
 730        } else {
 731                /*
 732                 * If this is outside of all known maps,
 733                 * and is a negative address, try to look it
 734                 * up in the kernel dso, as it might be a
 735                 * vsyscall (which executes in user-mode):
 736                 */
 737                if ((long long)ip < 0)
 738                dso = kernel_dso;
 739        }
 740        dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
 741        dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
 742        *ipp  = ip;
 743
 744        if (dsop)
 745                *dsop = dso;
 746
 747        if (!dso)
 748                return NULL;
 749got_dso:
 750        return dso->find_symbol(dso, ip);
 751}
 752
 753static int call__match(struct symbol *sym)
 754{
 755        if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
 756                return 1;
 757
 758        return 0;
 759}
 760
 761static struct symbol **
 762resolve_callchain(struct thread *thread, struct map *map __used,
 763                    struct ip_callchain *chain, struct hist_entry *entry)
 764{
 765        u64 context = PERF_CONTEXT_MAX;
 766        struct symbol **syms = NULL;
 767        unsigned int i;
 768
 769        if (callchain) {
 770                syms = calloc(chain->nr, sizeof(*syms));
 771                if (!syms) {
 772                        fprintf(stderr, "Can't allocate memory for symbols\n");
 773                        exit(-1);
 774                }
 775        }
 776
 777        for (i = 0; i < chain->nr; i++) {
 778                u64 ip = chain->ips[i];
 779                struct dso *dso = NULL;
 780                struct symbol *sym;
 781
 782                if (ip >= PERF_CONTEXT_MAX) {
 783                        context = ip;
 784                        continue;
 785                }
 786
 787                switch (context) {
 788                case PERF_CONTEXT_HV:
 789                        dso = hypervisor_dso;
 790                        break;
 791                case PERF_CONTEXT_KERNEL:
 792                        dso = kernel_dso;
 793                        break;
 794                default:
 795                        break;
 796                }
 797
 798                sym = resolve_symbol(thread, NULL, &dso, &ip);
 799
 800                if (sym) {
 801                        if (sort__has_parent && call__match(sym) &&
 802                            !entry->parent)
 803                                entry->parent = sym;
 804                        if (!callchain)
 805                                break;
 806                        syms[i] = sym;
 807                }
 808        }
 809
 810        return syms;
 811}
 812
 813/*
 814 * collect histogram counts
 815 */
 816
 817static int
 818hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
 819                struct symbol *sym, u64 ip, struct ip_callchain *chain,
 820                char level, u64 count)
 821{
 822        struct rb_node **p = &hist.rb_node;
 823        struct rb_node *parent = NULL;
 824        struct hist_entry *he;
 825        struct symbol **syms = NULL;
 826        struct hist_entry entry = {
 827                .thread = thread,
 828                .map    = map,
 829                .dso    = dso,
 830                .sym    = sym,
 831                .ip     = ip,
 832                .level  = level,
 833                .count  = count,
 834                .parent = NULL,
 835                .sorted_chain = RB_ROOT
 836        };
 837        int cmp;
 838
 839        if ((sort__has_parent || callchain) && chain)
 840                syms = resolve_callchain(thread, map, chain, &entry);
 841
 842        while (*p != NULL) {
 843                parent = *p;
 844                he = rb_entry(parent, struct hist_entry, rb_node);
 845
 846                cmp = hist_entry__cmp(&entry, he);
 847
 848                if (!cmp) {
 849                        he->count += count;
 850                        if (callchain) {
 851                                append_chain(&he->callchain, chain, syms);
 852                                free(syms);
 853                        }
 854                        return 0;
 855                }
 856
 857                if (cmp < 0)
 858                        p = &(*p)->rb_left;
 859                else
 860                        p = &(*p)->rb_right;
 861        }
 862
 863        he = malloc(sizeof(*he));
 864        if (!he)
 865                return -ENOMEM;
 866        *he = entry;
 867        if (callchain) {
 868                callchain_init(&he->callchain);
 869                append_chain(&he->callchain, chain, syms);
 870                free(syms);
 871        }
 872        rb_link_node(&he->rb_node, parent, p);
 873        rb_insert_color(&he->rb_node, &hist);
 874
 875        return 0;
 876}
 877
 878static void hist_entry__free(struct hist_entry *he)
 879{
 880        free(he);
 881}
 882
 883/*
 884 * collapse the histogram
 885 */
 886
 887static struct rb_root collapse_hists;
 888
 889static void collapse__insert_entry(struct hist_entry *he)
 890{
 891        struct rb_node **p = &collapse_hists.rb_node;
 892        struct rb_node *parent = NULL;
 893        struct hist_entry *iter;
 894        int64_t cmp;
 895
 896        while (*p != NULL) {
 897                parent = *p;
 898                iter = rb_entry(parent, struct hist_entry, rb_node);
 899
 900                cmp = hist_entry__collapse(iter, he);
 901
 902                if (!cmp) {
 903                        iter->count += he->count;
 904                        hist_entry__free(he);
 905                        return;
 906                }
 907
 908                if (cmp < 0)
 909                        p = &(*p)->rb_left;
 910                else
 911                        p = &(*p)->rb_right;
 912        }
 913
 914        rb_link_node(&he->rb_node, parent, p);
 915        rb_insert_color(&he->rb_node, &collapse_hists);
 916}
 917
 918static void collapse__resort(void)
 919{
 920        struct rb_node *next;
 921        struct hist_entry *n;
 922
 923        if (!sort__need_collapse)
 924                return;
 925
 926        next = rb_first(&hist);
 927        while (next) {
 928                n = rb_entry(next, struct hist_entry, rb_node);
 929                next = rb_next(&n->rb_node);
 930
 931                rb_erase(&n->rb_node, &hist);
 932                collapse__insert_entry(n);
 933        }
 934}
 935
 936/*
 937 * reverse the map, sort on count.
 938 */
 939
 940static struct rb_root output_hists;
 941
 942static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
 943{
 944        struct rb_node **p = &output_hists.rb_node;
 945        struct rb_node *parent = NULL;
 946        struct hist_entry *iter;
 947
 948        if (callchain)
 949                callchain_param.sort(&he->sorted_chain, &he->callchain,
 950                                      min_callchain_hits, &callchain_param);
 951
 952        while (*p != NULL) {
 953                parent = *p;
 954                iter = rb_entry(parent, struct hist_entry, rb_node);
 955
 956                if (he->count > iter->count)
 957                        p = &(*p)->rb_left;
 958                else
 959                        p = &(*p)->rb_right;
 960        }
 961
 962        rb_link_node(&he->rb_node, parent, p);
 963        rb_insert_color(&he->rb_node, &output_hists);
 964}
 965
 966static void output__resort(u64 total_samples)
 967{
 968        struct rb_node *next;
 969        struct hist_entry *n;
 970        struct rb_root *tree = &hist;
 971        u64 min_callchain_hits;
 972
 973        min_callchain_hits = total_samples * (callchain_param.min_percent / 100);
 974
 975        if (sort__need_collapse)
 976                tree = &collapse_hists;
 977
 978        next = rb_first(tree);
 979
 980        while (next) {
 981                n = rb_entry(next, struct hist_entry, rb_node);
 982                next = rb_next(&n->rb_node);
 983
 984                rb_erase(&n->rb_node, tree);
 985                output__insert_entry(n, min_callchain_hits);
 986        }
 987}
 988
 989static size_t output__fprintf(FILE *fp, u64 total_samples)
 990{
 991        struct hist_entry *pos;
 992        struct sort_entry *se;
 993        struct rb_node *nd;
 994        size_t ret = 0;
 995        unsigned int width;
 996        char *col_width = col_width_list_str;
 997        int raw_printing_style;
 998
 999        raw_printing_style = !strcmp(pretty_printing_style, "raw");
1000
1001        init_rem_hits();
1002
1003        fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
1004        fprintf(fp, "#\n");
1005
1006        fprintf(fp, "# Overhead");
1007        if (show_nr_samples) {
1008                if (field_sep)
1009                        fprintf(fp, "%cSamples", *field_sep);
1010                else
1011                        fputs("  Samples  ", fp);
1012        }
1013        list_for_each_entry(se, &hist_entry__sort_list, list) {
1014                if (se->elide)
1015                        continue;
1016                if (field_sep) {
1017                        fprintf(fp, "%c%s", *field_sep, se->header);
1018                        continue;
1019                }
1020                width = strlen(se->header);
1021                if (se->width) {
1022                        if (col_width_list_str) {
1023                                if (col_width) {
1024                                        *se->width = atoi(col_width);
1025                                        col_width = strchr(col_width, ',');
1026                                        if (col_width)
1027                                                ++col_width;
1028                                }
1029                        }
1030                        width = *se->width = max(*se->width, width);
1031                }
1032                fprintf(fp, "  %*s", width, se->header);
1033        }
1034        fprintf(fp, "\n");
1035
1036        if (field_sep)
1037                goto print_entries;
1038
1039        fprintf(fp, "# ........");
1040        if (show_nr_samples)
1041                fprintf(fp, " ..........");
1042        list_for_each_entry(se, &hist_entry__sort_list, list) {
1043                unsigned int i;
1044
1045                if (se->elide)
1046                        continue;
1047
1048                fprintf(fp, "  ");
1049                if (se->width)
1050                        width = *se->width;
1051                else
1052                        width = strlen(se->header);
1053                for (i = 0; i < width; i++)
1054                        fprintf(fp, ".");
1055        }
1056        fprintf(fp, "\n");
1057
1058        fprintf(fp, "#\n");
1059
1060print_entries:
1061        for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
1062                pos = rb_entry(nd, struct hist_entry, rb_node);
1063                ret += hist_entry__fprintf(fp, pos, total_samples);
1064        }
1065
1066        if (sort_order == default_sort_order &&
1067                        parent_pattern == default_parent_pattern) {
1068                fprintf(fp, "#\n");
1069                fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
1070                fprintf(fp, "#\n");
1071        }
1072        fprintf(fp, "\n");
1073
1074        free(rem_sq_bracket);
1075
1076        if (show_threads)
1077                perf_read_values_display(fp, &show_threads_values,
1078                                         raw_printing_style);
1079
1080        return ret;
1081}
1082
1083static unsigned long total = 0,
1084                     total_mmap = 0,
1085                     total_comm = 0,
1086                     total_fork = 0,
1087                     total_unknown = 0,
1088                     total_lost = 0;
1089
1090static int validate_chain(struct ip_callchain *chain, event_t *event)
1091{
1092        unsigned int chain_size;
1093
1094        chain_size = event->header.size;
1095        chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
1096
1097        if (chain->nr*sizeof(u64) > chain_size)
1098                return -1;
1099
1100        return 0;
1101}
1102
1103static int
1104process_sample_event(event_t *event, unsigned long offset, unsigned long head)
1105{
1106        char level;
1107        int show = 0;
1108        struct dso *dso = NULL;
1109        struct thread *thread;
1110        u64 ip = event->ip.ip;
1111        u64 period = 1;
1112        struct map *map = NULL;
1113        void *more_data = event->ip.__more_data;
1114        struct ip_callchain *chain = NULL;
1115        int cpumode;
1116
1117        thread = threads__findnew(event->ip.pid, &threads, &last_match);
1118
1119        if (sample_type & PERF_SAMPLE_PERIOD) {
1120                period = *(u64 *)more_data;
1121                more_data += sizeof(u64);
1122        }
1123
1124        dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
1125                (void *)(offset + head),
1126                (void *)(long)(event->header.size),
1127                event->header.misc,
1128                event->ip.pid, event->ip.tid,
1129                (void *)(long)ip,
1130                (long long)period);
1131
1132        if (sample_type & PERF_SAMPLE_CALLCHAIN) {
1133                unsigned int i;
1134
1135                chain = (void *)more_data;
1136
1137                dump_printf("... chain: nr:%Lu\n", chain->nr);
1138
1139                if (validate_chain(chain, event) < 0) {
1140                        eprintf("call-chain problem with event, skipping it.\n");
1141                        return 0;
1142                }
1143
1144                if (dump_trace) {
1145                        for (i = 0; i < chain->nr; i++)
1146                                dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]);
1147                }
1148        }
1149
1150        dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
1151
1152        if (thread == NULL) {
1153                eprintf("problem processing %d event, skipping it.\n",
1154                        event->header.type);
1155                return -1;
1156        }
1157
1158        if (comm_list && !strlist__has_entry(comm_list, thread->comm))
1159                return 0;
1160
1161        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
1162
1163        if (cpumode == PERF_RECORD_MISC_KERNEL) {
1164                show = SHOW_KERNEL;
1165                level = 'k';
1166
1167                dso = kernel_dso;
1168
1169                dump_printf(" ...... dso: %s\n", dso->name);
1170
1171        } else if (cpumode == PERF_RECORD_MISC_USER) {
1172
1173                show = SHOW_USER;
1174                level = '.';
1175
1176        } else {
1177                show = SHOW_HV;
1178                level = 'H';
1179
1180                dso = hypervisor_dso;
1181
1182                dump_printf(" ...... dso: [hypervisor]\n");
1183        }
1184
1185        if (show & show_mask) {
1186                struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
1187
1188                if (dso_list && (!dso || !dso->name ||
1189                                 !strlist__has_entry(dso_list, dso->name)))
1190                        return 0;
1191
1192                if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
1193                        return 0;
1194
1195                if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
1196                        eprintf("problem incrementing symbol count, skipping event\n");
1197                        return -1;
1198                }
1199        }
1200        total += period;
1201
1202        return 0;
1203}
1204
1205static int
1206process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
1207{
1208        struct thread *thread;
1209        struct map *map = map__new(&event->mmap, cwd, cwdlen);
1210
1211        thread = threads__findnew(event->mmap.pid, &threads, &last_match);
1212
1213        dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
1214                (void *)(offset + head),
1215                (void *)(long)(event->header.size),
1216                event->mmap.pid,
1217                event->mmap.tid,
1218                (void *)(long)event->mmap.start,
1219                (void *)(long)event->mmap.len,
1220                (void *)(long)event->mmap.pgoff,
1221                event->mmap.filename);
1222
1223        if (thread == NULL || map == NULL) {
1224                dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
1225                return 0;
1226        }
1227
1228        thread__insert_map(thread, map);
1229        total_mmap++;
1230
1231        return 0;
1232}
1233
1234static int
1235process_comm_event(event_t *event, unsigned long offset, unsigned long head)
1236{
1237        struct thread *thread;
1238
1239        thread = threads__findnew(event->comm.pid, &threads, &last_match);
1240
1241        dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
1242                (void *)(offset + head),
1243                (void *)(long)(event->header.size),
1244                event->comm.comm, event->comm.pid);
1245
1246        if (thread == NULL ||
1247            thread__set_comm_adjust(thread, event->comm.comm)) {
1248                dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
1249                return -1;
1250        }
1251        total_comm++;
1252
1253        return 0;
1254}
1255
1256static int
1257process_task_event(event_t *event, unsigned long offset, unsigned long head)
1258{
1259        struct thread *thread;
1260        struct thread *parent;
1261
1262        thread = threads__findnew(event->fork.pid, &threads, &last_match);
1263        parent = threads__findnew(event->fork.ppid, &threads, &last_match);
1264
1265        dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
1266                (void *)(offset + head),
1267                (void *)(long)(event->header.size),
1268                event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT",
1269                event->fork.pid, event->fork.tid,
1270                event->fork.ppid, event->fork.ptid);
1271
1272        /*
1273         * A thread clone will have the same PID for both
1274         * parent and child.
1275         */
1276        if (thread == parent)
1277                return 0;
1278
1279        if (event->header.type == PERF_RECORD_EXIT)
1280                return 0;
1281
1282        if (!thread || !parent || thread__fork(thread, parent)) {
1283                dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
1284                return -1;
1285        }
1286        total_fork++;
1287
1288        return 0;
1289}
1290
1291static int
1292process_lost_event(event_t *event, unsigned long offset, unsigned long head)
1293{
1294        dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n",
1295                (void *)(offset + head),
1296                (void *)(long)(event->header.size),
1297                event->lost.id,
1298                event->lost.lost);
1299
1300        total_lost += event->lost.lost;
1301
1302        return 0;
1303}
1304
1305static int
1306process_read_event(event_t *event, unsigned long offset, unsigned long head)
1307{
1308        struct perf_event_attr *attr;
1309
1310        attr = perf_header__find_attr(event->read.id, header);
1311
1312        if (show_threads) {
1313                const char *name = attr ? __event_name(attr->type, attr->config)
1314                                   : "unknown";
1315                perf_read_values_add_value(&show_threads_values,
1316                                           event->read.pid, event->read.tid,
1317                                           event->read.id,
1318                                           name,
1319                                           event->read.value);
1320        }
1321
1322        dump_printf("%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n",
1323                        (void *)(offset + head),
1324                        (void *)(long)(event->header.size),
1325                        event->read.pid,
1326                        event->read.tid,
1327                        attr ? __event_name(attr->type, attr->config)
1328                             : "FAIL",
1329                        event->read.value);
1330
1331        return 0;
1332}
1333
1334static int
1335process_event(event_t *event, unsigned long offset, unsigned long head)
1336{
1337        trace_event(event);
1338
1339        switch (event->header.type) {
1340        case PERF_RECORD_SAMPLE:
1341                return process_sample_event(event, offset, head);
1342
1343        case PERF_RECORD_MMAP:
1344                return process_mmap_event(event, offset, head);
1345
1346        case PERF_RECORD_COMM:
1347                return process_comm_event(event, offset, head);
1348
1349        case PERF_RECORD_FORK:
1350        case PERF_RECORD_EXIT:
1351                return process_task_event(event, offset, head);
1352
1353        case PERF_RECORD_LOST:
1354                return process_lost_event(event, offset, head);
1355
1356        case PERF_RECORD_READ:
1357                return process_read_event(event, offset, head);
1358
1359        /*
1360         * We dont process them right now but they are fine:
1361         */
1362
1363        case PERF_RECORD_THROTTLE:
1364        case PERF_RECORD_UNTHROTTLE:
1365                return 0;
1366
1367        default:
1368                return -1;
1369        }
1370
1371        return 0;
1372}
1373
1374static int __cmd_report(void)
1375{
1376        int ret, rc = EXIT_FAILURE;
1377        unsigned long offset = 0;
1378        unsigned long head, shift;
1379        struct stat input_stat;
1380        struct thread *idle;
1381        event_t *event;
1382        uint32_t size;
1383        char *buf;
1384
1385        idle = register_idle_thread(&threads, &last_match);
1386        thread__comm_adjust(idle);
1387
1388        if (show_threads)
1389                perf_read_values_init(&show_threads_values);
1390
1391        input = open(input_name, O_RDONLY);
1392        if (input < 0) {
1393                fprintf(stderr, " failed to open file: %s", input_name);
1394                if (!strcmp(input_name, "perf.data"))
1395                        fprintf(stderr, "  (try 'perf record' first)");
1396                fprintf(stderr, "\n");
1397                exit(-1);
1398        }
1399
1400        ret = fstat(input, &input_stat);
1401        if (ret < 0) {
1402                perror("failed to stat file");
1403                exit(-1);
1404        }
1405
1406        if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
1407                fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
1408                exit(-1);
1409        }
1410
1411        if (!input_stat.st_size) {
1412                fprintf(stderr, "zero-sized file, nothing to do!\n");
1413                exit(0);
1414        }
1415
1416        header = perf_header__read(input);
1417        head = header->data_offset;
1418
1419        sample_type = perf_header__sample_type(header);
1420
1421        if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
1422                if (sort__has_parent) {
1423                        fprintf(stderr, "selected --sort parent, but no"
1424                                        " callchain data. Did you call"
1425                                        " perf record without -g?\n");
1426                        exit(-1);
1427                }
1428                if (callchain) {
1429                        fprintf(stderr, "selected -g but no callchain data."
1430                                        " Did you call perf record without"
1431                                        " -g?\n");
1432                        exit(-1);
1433                }
1434        } else if (callchain_param.mode != CHAIN_NONE && !callchain) {
1435                        callchain = 1;
1436                        if (register_callchain_param(&callchain_param) < 0) {
1437                                fprintf(stderr, "Can't register callchain"
1438                                                " params\n");
1439                                exit(-1);
1440                        }
1441        }
1442
1443        if (load_kernel() < 0) {
1444                perror("failed to load kernel symbols");
1445                return EXIT_FAILURE;
1446        }
1447
1448        if (!full_paths) {
1449                if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
1450                        perror("failed to get the current directory");
1451                        return EXIT_FAILURE;
1452                }
1453                cwdlen = strlen(cwd);
1454        } else {
1455                cwd = NULL;
1456                cwdlen = 0;
1457        }
1458
1459        shift = page_size * (head / page_size);
1460        offset += shift;
1461        head -= shift;
1462
1463remap:
1464        buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1465                           MAP_SHARED, input, offset);
1466        if (buf == MAP_FAILED) {
1467                perror("failed to mmap file");
1468                exit(-1);
1469        }
1470
1471more:
1472        event = (event_t *)(buf + head);
1473
1474        size = event->header.size;
1475        if (!size)
1476                size = 8;
1477
1478        if (head + event->header.size >= page_size * mmap_window) {
1479                int munmap_ret;
1480
1481                shift = page_size * (head / page_size);
1482
1483                munmap_ret = munmap(buf, page_size * mmap_window);
1484                assert(munmap_ret == 0);
1485
1486                offset += shift;
1487                head -= shift;
1488                goto remap;
1489        }
1490
1491        size = event->header.size;
1492
1493        dump_printf("\n%p [%p]: event: %d\n",
1494                        (void *)(offset + head),
1495                        (void *)(long)event->header.size,
1496                        event->header.type);
1497
1498        if (!size || process_event(event, offset, head) < 0) {
1499
1500                dump_printf("%p [%p]: skipping unknown header type: %d\n",
1501                        (void *)(offset + head),
1502                        (void *)(long)(event->header.size),
1503                        event->header.type);
1504
1505                total_unknown++;
1506
1507                /*
1508                 * assume we lost track of the stream, check alignment, and
1509                 * increment a single u64 in the hope to catch on again 'soon'.
1510                 */
1511
1512                if (unlikely(head & 7))
1513                        head &= ~7ULL;
1514
1515                size = 8;
1516        }
1517
1518        head += size;
1519
1520        if (offset + head >= header->data_offset + header->data_size)
1521                goto done;
1522
1523        if (offset + head < (unsigned long)input_stat.st_size)
1524                goto more;
1525
1526done:
1527        rc = EXIT_SUCCESS;
1528        close(input);
1529
1530        dump_printf("      IP events: %10ld\n", total);
1531        dump_printf("    mmap events: %10ld\n", total_mmap);
1532        dump_printf("    comm events: %10ld\n", total_comm);
1533        dump_printf("    fork events: %10ld\n", total_fork);
1534        dump_printf("    lost events: %10ld\n", total_lost);
1535        dump_printf(" unknown events: %10ld\n", total_unknown);
1536
1537        if (dump_trace)
1538                return 0;
1539
1540        if (verbose >= 3)
1541                threads__fprintf(stdout, &threads);
1542
1543        if (verbose >= 2)
1544                dsos__fprintf(stdout);
1545
1546        collapse__resort();
1547        output__resort(total);
1548        output__fprintf(stdout, total);
1549
1550        if (show_threads)
1551                perf_read_values_destroy(&show_threads_values);
1552
1553        return rc;
1554}
1555
1556static int
1557parse_callchain_opt(const struct option *opt __used, const char *arg,
1558                    int unset __used)
1559{
1560        char *tok;
1561        char *endptr;
1562
1563        callchain = 1;
1564
1565        if (!arg)
1566                return 0;
1567
1568        tok = strtok((char *)arg, ",");
1569        if (!tok)
1570                return -1;
1571
1572        /* get the output mode */
1573        if (!strncmp(tok, "graph", strlen(arg)))
1574                callchain_param.mode = CHAIN_GRAPH_ABS;
1575
1576        else if (!strncmp(tok, "flat", strlen(arg)))
1577                callchain_param.mode = CHAIN_FLAT;
1578
1579        else if (!strncmp(tok, "fractal", strlen(arg)))
1580                callchain_param.mode = CHAIN_GRAPH_REL;
1581
1582        else if (!strncmp(tok, "none", strlen(arg))) {
1583                callchain_param.mode = CHAIN_NONE;
1584                callchain = 0;
1585
1586                return 0;
1587        }
1588
1589        else
1590                return -1;
1591
1592        /* get the min percentage */
1593        tok = strtok(NULL, ",");
1594        if (!tok)
1595                goto setup;
1596
1597        callchain_param.min_percent = strtod(tok, &endptr);
1598        if (tok == endptr)
1599                return -1;
1600
1601setup:
1602        if (register_callchain_param(&callchain_param) < 0) {
1603                fprintf(stderr, "Can't register callchain params\n");
1604                return -1;
1605        }
1606        return 0;
1607}
1608
1609static const char * const report_usage[] = {
1610        "perf report [<options>] <command>",
1611        NULL
1612};
1613
1614static const struct option options[] = {
1615        OPT_STRING('i', "input", &input_name, "file",
1616                    "input file name"),
1617        OPT_BOOLEAN('v', "verbose", &verbose,
1618                    "be more verbose (show symbol address, etc)"),
1619        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1620                    "dump raw trace in ASCII"),
1621        OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
1622        OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
1623        OPT_BOOLEAN('m', "modules", &modules,
1624                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
1625        OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
1626                    "Show a column with the number of samples"),
1627        OPT_BOOLEAN('T', "threads", &show_threads,
1628                    "Show per-thread event counters"),
1629        OPT_STRING(0, "pretty", &pretty_printing_style, "key",
1630                   "pretty printing style key: normal raw"),
1631        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1632                   "sort by key(s): pid, comm, dso, symbol, parent"),
1633        OPT_BOOLEAN('P', "full-paths", &full_paths,
1634                    "Don't shorten the pathnames taking into account the cwd"),
1635        OPT_STRING('p', "parent", &parent_pattern, "regex",
1636                   "regex filter to identify parent, see: '--sort parent'"),
1637        OPT_BOOLEAN('x', "exclude-other", &exclude_other,
1638                    "Only display entries with parent-match"),
1639        OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
1640                     "Display callchains using output_type and min percent threshold. "
1641                     "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
1642        OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
1643                   "only consider symbols in these dsos"),
1644        OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
1645                   "only consider symbols in these comms"),
1646        OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
1647                   "only consider these symbols"),
1648        OPT_STRING('w', "column-widths", &col_width_list_str,
1649                   "width[,width...]",
1650                   "don't try to adjust column width, use these fixed values"),
1651        OPT_STRING('t', "field-separator", &field_sep, "separator",
1652                   "separator for columns, no spaces will be added between "
1653                   "columns '.' is reserved."),
1654        OPT_END()
1655};
1656
1657static void setup_sorting(void)
1658{
1659        char *tmp, *tok, *str = strdup(sort_order);
1660
1661        for (tok = strtok_r(str, ", ", &tmp);
1662                        tok; tok = strtok_r(NULL, ", ", &tmp)) {
1663                if (sort_dimension__add(tok) < 0) {
1664                        error("Unknown --sort key: `%s'", tok);
1665                        usage_with_options(report_usage, options);
1666                }
1667        }
1668
1669        free(str);
1670}
1671
1672static void setup_list(struct strlist **list, const char *list_str,
1673                       struct sort_entry *se, const char *list_name,
1674                       FILE *fp)
1675{
1676        if (list_str) {
1677                *list = strlist__new(true, list_str);
1678                if (!*list) {
1679                        fprintf(stderr, "problems parsing %s list\n",
1680                                list_name);
1681                        exit(129);
1682                }
1683                if (strlist__nr_entries(*list) == 1) {
1684                        fprintf(fp, "# %s: %s\n", list_name,
1685                                strlist__entry(*list, 0)->s);
1686                        se->elide = true;
1687                }
1688        }
1689}
1690
1691int cmd_report(int argc, const char **argv, const char *prefix __used)
1692{
1693        symbol__init();
1694
1695        page_size = getpagesize();
1696
1697        argc = parse_options(argc, argv, options, report_usage, 0);
1698
1699        setup_sorting();
1700
1701        if (parent_pattern != default_parent_pattern) {
1702                sort_dimension__add("parent");
1703                sort_parent.elide = 1;
1704        } else
1705                exclude_other = 0;
1706
1707        /*
1708         * Any (unrecognized) arguments left?
1709         */
1710        if (argc)
1711                usage_with_options(report_usage, options);
1712
1713        setup_pager();
1714
1715        setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
1716        setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
1717        setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
1718
1719        if (field_sep && *field_sep == '.') {
1720                fputs("'.' is the only non valid --field-separator argument\n",
1721                      stderr);
1722                exit(129);
1723        }
1724
1725        return __cmd_report();
1726}
1727