linux/tools/perf/ui/browsers/hists.c
<<
>>
Prefs
   1#include <dirent.h>
   2#include <errno.h>
   3#include <inttypes.h>
   4#include <stdio.h>
   5#include <stdlib.h>
   6#include <string.h>
   7#include <linux/rbtree.h>
   8#include <sys/ttydefaults.h>
   9
  10#include "../../util/evsel.h"
  11#include "../../util/evlist.h"
  12#include "../../util/hist.h"
  13#include "../../util/pstack.h"
  14#include "../../util/sort.h"
  15#include "../../util/util.h"
  16#include "../../util/top.h"
  17#include "../../util/thread.h"
  18#include "../../arch/common.h"
  19
  20#include "../browsers/hists.h"
  21#include "../helpline.h"
  22#include "../util.h"
  23#include "../ui.h"
  24#include "map.h"
  25#include "annotate.h"
  26#include "srcline.h"
  27#include "string2.h"
  28#include "units.h"
  29
  30#include "sane_ctype.h"
  31
  32extern void hist_browser__init_hpp(void);
  33
  34static int perf_evsel_browser_title(struct hist_browser *browser,
  35                                    char *bf, size_t size);
  36static void hist_browser__update_nr_entries(struct hist_browser *hb);
  37
  38static struct rb_node *hists__filter_entries(struct rb_node *nd,
  39                                             float min_pcnt);
  40
  41static bool hist_browser__has_filter(struct hist_browser *hb)
  42{
  43        return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
  44}
  45
  46static int hist_browser__get_folding(struct hist_browser *browser)
  47{
  48        struct rb_node *nd;
  49        struct hists *hists = browser->hists;
  50        int unfolded_rows = 0;
  51
  52        for (nd = rb_first(&hists->entries);
  53             (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
  54             nd = rb_hierarchy_next(nd)) {
  55                struct hist_entry *he =
  56                        rb_entry(nd, struct hist_entry, rb_node);
  57
  58                if (he->leaf && he->unfolded)
  59                        unfolded_rows += he->nr_rows;
  60        }
  61        return unfolded_rows;
  62}
  63
  64static u32 hist_browser__nr_entries(struct hist_browser *hb)
  65{
  66        u32 nr_entries;
  67
  68        if (symbol_conf.report_hierarchy)
  69                nr_entries = hb->nr_hierarchy_entries;
  70        else if (hist_browser__has_filter(hb))
  71                nr_entries = hb->nr_non_filtered_entries;
  72        else
  73                nr_entries = hb->hists->nr_entries;
  74
  75        hb->nr_callchain_rows = hist_browser__get_folding(hb);
  76        return nr_entries + hb->nr_callchain_rows;
  77}
  78
  79static void hist_browser__update_rows(struct hist_browser *hb)
  80{
  81        struct ui_browser *browser = &hb->b;
  82        struct hists *hists = hb->hists;
  83        struct perf_hpp_list *hpp_list = hists->hpp_list;
  84        u16 header_offset, index_row;
  85
  86        header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
  87        browser->rows = browser->height - header_offset;
  88        /*
  89         * Verify if we were at the last line and that line isn't
  90         * visibe because we now show the header line(s).
  91         */
  92        index_row = browser->index - browser->top_idx;
  93        if (index_row >= browser->rows)
  94                browser->index -= index_row - browser->rows + 1;
  95}
  96
  97static void hist_browser__refresh_dimensions(struct ui_browser *browser)
  98{
  99        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 100
 101        /* 3 == +/- toggle symbol before actual hist_entry rendering */
 102        browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
 103        /*
 104         * FIXME: Just keeping existing behaviour, but this really should be
 105         *        before updating browser->width, as it will invalidate the
 106         *        calculation above. Fix this and the fallout in another
 107         *        changeset.
 108         */
 109        ui_browser__refresh_dimensions(browser);
 110        hist_browser__update_rows(hb);
 111}
 112
 113static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
 114{
 115        struct hists *hists = browser->hists;
 116        struct perf_hpp_list *hpp_list = hists->hpp_list;
 117        u16 header_offset;
 118
 119        header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
 120        ui_browser__gotorc(&browser->b, row + header_offset, column);
 121}
 122
 123static void hist_browser__reset(struct hist_browser *browser)
 124{
 125        /*
 126         * The hists__remove_entry_filter() already folds non-filtered
 127         * entries so we can assume it has 0 callchain rows.
 128         */
 129        browser->nr_callchain_rows = 0;
 130
 131        hist_browser__update_nr_entries(browser);
 132        browser->b.nr_entries = hist_browser__nr_entries(browser);
 133        hist_browser__refresh_dimensions(&browser->b);
 134        ui_browser__reset_index(&browser->b);
 135}
 136
 137static char tree__folded_sign(bool unfolded)
 138{
 139        return unfolded ? '-' : '+';
 140}
 141
 142static char hist_entry__folded(const struct hist_entry *he)
 143{
 144        return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
 145}
 146
 147static char callchain_list__folded(const struct callchain_list *cl)
 148{
 149        return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
 150}
 151
 152static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
 153{
 154        cl->unfolded = unfold ? cl->has_children : false;
 155}
 156
 157static struct inline_node *inline_node__create(struct map *map, u64 ip)
 158{
 159        struct dso *dso;
 160        struct inline_node *node;
 161
 162        if (map == NULL)
 163                return NULL;
 164
 165        dso = map->dso;
 166        if (dso == NULL)
 167                return NULL;
 168
 169        if (dso->kernel != DSO_TYPE_USER)
 170                return NULL;
 171
 172        node = dso__parse_addr_inlines(dso,
 173                                       map__rip_2objdump(map, ip));
 174
 175        return node;
 176}
 177
 178static int inline__count_rows(struct inline_node *node)
 179{
 180        struct inline_list *ilist;
 181        int i = 0;
 182
 183        if (node == NULL)
 184                return 0;
 185
 186        list_for_each_entry(ilist, &node->val, list) {
 187                if ((ilist->filename != NULL) || (ilist->funcname != NULL))
 188                        i++;
 189        }
 190
 191        return i;
 192}
 193
 194static int callchain_list__inline_rows(struct callchain_list *chain)
 195{
 196        struct inline_node *node;
 197        int rows;
 198
 199        node = inline_node__create(chain->ms.map, chain->ip);
 200        if (node == NULL)
 201                return 0;
 202
 203        rows = inline__count_rows(node);
 204        inline_node__delete(node);
 205        return rows;
 206}
 207
 208static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
 209{
 210        int n = 0, inline_rows;
 211        struct rb_node *nd;
 212
 213        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 214                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 215                struct callchain_list *chain;
 216                char folded_sign = ' '; /* No children */
 217
 218                list_for_each_entry(chain, &child->val, list) {
 219                        ++n;
 220
 221                        if (symbol_conf.inline_name) {
 222                                inline_rows =
 223                                        callchain_list__inline_rows(chain);
 224                                n += inline_rows;
 225                        }
 226
 227                        /* We need this because we may not have children */
 228                        folded_sign = callchain_list__folded(chain);
 229                        if (folded_sign == '+')
 230                                break;
 231                }
 232
 233                if (folded_sign == '-') /* Have children and they're unfolded */
 234                        n += callchain_node__count_rows_rb_tree(child);
 235        }
 236
 237        return n;
 238}
 239
 240static int callchain_node__count_flat_rows(struct callchain_node *node)
 241{
 242        struct callchain_list *chain;
 243        char folded_sign = 0;
 244        int n = 0;
 245
 246        list_for_each_entry(chain, &node->parent_val, list) {
 247                if (!folded_sign) {
 248                        /* only check first chain list entry */
 249                        folded_sign = callchain_list__folded(chain);
 250                        if (folded_sign == '+')
 251                                return 1;
 252                }
 253                n++;
 254        }
 255
 256        list_for_each_entry(chain, &node->val, list) {
 257                if (!folded_sign) {
 258                        /* node->parent_val list might be empty */
 259                        folded_sign = callchain_list__folded(chain);
 260                        if (folded_sign == '+')
 261                                return 1;
 262                }
 263                n++;
 264        }
 265
 266        return n;
 267}
 268
 269static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
 270{
 271        return 1;
 272}
 273
 274static int callchain_node__count_rows(struct callchain_node *node)
 275{
 276        struct callchain_list *chain;
 277        bool unfolded = false;
 278        int n = 0, inline_rows;
 279
 280        if (callchain_param.mode == CHAIN_FLAT)
 281                return callchain_node__count_flat_rows(node);
 282        else if (callchain_param.mode == CHAIN_FOLDED)
 283                return callchain_node__count_folded_rows(node);
 284
 285        list_for_each_entry(chain, &node->val, list) {
 286                ++n;
 287                if (symbol_conf.inline_name) {
 288                        inline_rows = callchain_list__inline_rows(chain);
 289                        n += inline_rows;
 290                }
 291
 292                unfolded = chain->unfolded;
 293        }
 294
 295        if (unfolded)
 296                n += callchain_node__count_rows_rb_tree(node);
 297
 298        return n;
 299}
 300
 301static int callchain__count_rows(struct rb_root *chain)
 302{
 303        struct rb_node *nd;
 304        int n = 0;
 305
 306        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 307                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 308                n += callchain_node__count_rows(node);
 309        }
 310
 311        return n;
 312}
 313
 314static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
 315                                bool include_children)
 316{
 317        int count = 0;
 318        struct rb_node *node;
 319        struct hist_entry *child;
 320
 321        if (he->leaf)
 322                return callchain__count_rows(&he->sorted_chain);
 323
 324        if (he->has_no_entry)
 325                return 1;
 326
 327        node = rb_first(&he->hroot_out);
 328        while (node) {
 329                float percent;
 330
 331                child = rb_entry(node, struct hist_entry, rb_node);
 332                percent = hist_entry__get_percent_limit(child);
 333
 334                if (!child->filtered && percent >= hb->min_pcnt) {
 335                        count++;
 336
 337                        if (include_children && child->unfolded)
 338                                count += hierarchy_count_rows(hb, child, true);
 339                }
 340
 341                node = rb_next(node);
 342        }
 343        return count;
 344}
 345
 346static bool hist_entry__toggle_fold(struct hist_entry *he)
 347{
 348        if (!he)
 349                return false;
 350
 351        if (!he->has_children)
 352                return false;
 353
 354        he->unfolded = !he->unfolded;
 355        return true;
 356}
 357
 358static bool callchain_list__toggle_fold(struct callchain_list *cl)
 359{
 360        if (!cl)
 361                return false;
 362
 363        if (!cl->has_children)
 364                return false;
 365
 366        cl->unfolded = !cl->unfolded;
 367        return true;
 368}
 369
 370static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
 371{
 372        struct rb_node *nd = rb_first(&node->rb_root);
 373
 374        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 375                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 376                struct callchain_list *chain;
 377                bool first = true;
 378
 379                list_for_each_entry(chain, &child->val, list) {
 380                        if (first) {
 381                                first = false;
 382                                chain->has_children = chain->list.next != &child->val ||
 383                                                         !RB_EMPTY_ROOT(&child->rb_root);
 384                        } else
 385                                chain->has_children = chain->list.next == &child->val &&
 386                                                         !RB_EMPTY_ROOT(&child->rb_root);
 387                }
 388
 389                callchain_node__init_have_children_rb_tree(child);
 390        }
 391}
 392
 393static void callchain_node__init_have_children(struct callchain_node *node,
 394                                               bool has_sibling)
 395{
 396        struct callchain_list *chain;
 397
 398        chain = list_entry(node->val.next, struct callchain_list, list);
 399        chain->has_children = has_sibling;
 400
 401        if (!list_empty(&node->val)) {
 402                chain = list_entry(node->val.prev, struct callchain_list, list);
 403                chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
 404        }
 405
 406        callchain_node__init_have_children_rb_tree(node);
 407}
 408
 409static void callchain__init_have_children(struct rb_root *root)
 410{
 411        struct rb_node *nd = rb_first(root);
 412        bool has_sibling = nd && rb_next(nd);
 413
 414        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
 415                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 416                callchain_node__init_have_children(node, has_sibling);
 417                if (callchain_param.mode == CHAIN_FLAT ||
 418                    callchain_param.mode == CHAIN_FOLDED)
 419                        callchain_node__make_parent_list(node);
 420        }
 421}
 422
 423static void hist_entry__init_have_children(struct hist_entry *he)
 424{
 425        if (he->init_have_children)
 426                return;
 427
 428        if (he->leaf) {
 429                he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
 430                callchain__init_have_children(&he->sorted_chain);
 431        } else {
 432                he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
 433        }
 434
 435        he->init_have_children = true;
 436}
 437
 438static void hist_entry_init_inline_node(struct hist_entry *he)
 439{
 440        if (he->inline_node)
 441                return;
 442
 443        he->inline_node = inline_node__create(he->ms.map, he->ip);
 444
 445        if (he->inline_node == NULL)
 446                return;
 447
 448        he->has_children = true;
 449}
 450
 451static bool hist_browser__toggle_fold(struct hist_browser *browser)
 452{
 453        struct hist_entry *he = browser->he_selection;
 454        struct map_symbol *ms = browser->selection;
 455        struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
 456        bool has_children;
 457
 458        if (!he || !ms)
 459                return false;
 460
 461        if (ms == &he->ms)
 462                has_children = hist_entry__toggle_fold(he);
 463        else
 464                has_children = callchain_list__toggle_fold(cl);
 465
 466        if (has_children) {
 467                int child_rows = 0;
 468
 469                hist_entry__init_have_children(he);
 470                browser->b.nr_entries -= he->nr_rows;
 471
 472                if (he->leaf)
 473                        browser->nr_callchain_rows -= he->nr_rows;
 474                else
 475                        browser->nr_hierarchy_entries -= he->nr_rows;
 476
 477                if (symbol_conf.report_hierarchy)
 478                        child_rows = hierarchy_count_rows(browser, he, true);
 479
 480                if (he->unfolded) {
 481                        if (he->leaf)
 482                                if (he->inline_node)
 483                                        he->nr_rows = inline__count_rows(
 484                                                        he->inline_node);
 485                                else
 486                                        he->nr_rows = callchain__count_rows(
 487                                                        &he->sorted_chain);
 488                        else
 489                                he->nr_rows = hierarchy_count_rows(browser, he, false);
 490
 491                        /* account grand children */
 492                        if (symbol_conf.report_hierarchy)
 493                                browser->b.nr_entries += child_rows - he->nr_rows;
 494
 495                        if (!he->leaf && he->nr_rows == 0) {
 496                                he->has_no_entry = true;
 497                                he->nr_rows = 1;
 498                        }
 499                } else {
 500                        if (symbol_conf.report_hierarchy)
 501                                browser->b.nr_entries -= child_rows - he->nr_rows;
 502
 503                        if (he->has_no_entry)
 504                                he->has_no_entry = false;
 505
 506                        he->nr_rows = 0;
 507                }
 508
 509                browser->b.nr_entries += he->nr_rows;
 510
 511                if (he->leaf)
 512                        browser->nr_callchain_rows += he->nr_rows;
 513                else
 514                        browser->nr_hierarchy_entries += he->nr_rows;
 515
 516                return true;
 517        }
 518
 519        /* If it doesn't have children, no toggling performed */
 520        return false;
 521}
 522
 523static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
 524{
 525        int n = 0;
 526        struct rb_node *nd;
 527
 528        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 529                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 530                struct callchain_list *chain;
 531                bool has_children = false;
 532
 533                list_for_each_entry(chain, &child->val, list) {
 534                        ++n;
 535                        callchain_list__set_folding(chain, unfold);
 536                        has_children = chain->has_children;
 537                }
 538
 539                if (has_children)
 540                        n += callchain_node__set_folding_rb_tree(child, unfold);
 541        }
 542
 543        return n;
 544}
 545
 546static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
 547{
 548        struct callchain_list *chain;
 549        bool has_children = false;
 550        int n = 0;
 551
 552        list_for_each_entry(chain, &node->val, list) {
 553                ++n;
 554                callchain_list__set_folding(chain, unfold);
 555                has_children = chain->has_children;
 556        }
 557
 558        if (has_children)
 559                n += callchain_node__set_folding_rb_tree(node, unfold);
 560
 561        return n;
 562}
 563
 564static int callchain__set_folding(struct rb_root *chain, bool unfold)
 565{
 566        struct rb_node *nd;
 567        int n = 0;
 568
 569        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 570                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 571                n += callchain_node__set_folding(node, unfold);
 572        }
 573
 574        return n;
 575}
 576
 577static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
 578                                 bool unfold __maybe_unused)
 579{
 580        float percent;
 581        struct rb_node *nd;
 582        struct hist_entry *child;
 583        int n = 0;
 584
 585        for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
 586                child = rb_entry(nd, struct hist_entry, rb_node);
 587                percent = hist_entry__get_percent_limit(child);
 588                if (!child->filtered && percent >= hb->min_pcnt)
 589                        n++;
 590        }
 591
 592        return n;
 593}
 594
 595static void __hist_entry__set_folding(struct hist_entry *he,
 596                                      struct hist_browser *hb, bool unfold)
 597{
 598        hist_entry__init_have_children(he);
 599        he->unfolded = unfold ? he->has_children : false;
 600
 601        if (he->has_children) {
 602                int n;
 603
 604                if (he->leaf)
 605                        n = callchain__set_folding(&he->sorted_chain, unfold);
 606                else
 607                        n = hierarchy_set_folding(hb, he, unfold);
 608
 609                he->nr_rows = unfold ? n : 0;
 610        } else
 611                he->nr_rows = 0;
 612}
 613
 614static void hist_entry__set_folding(struct hist_entry *he,
 615                                    struct hist_browser *browser, bool unfold)
 616{
 617        double percent;
 618
 619        percent = hist_entry__get_percent_limit(he);
 620        if (he->filtered || percent < browser->min_pcnt)
 621                return;
 622
 623        __hist_entry__set_folding(he, browser, unfold);
 624
 625        if (!he->depth || unfold)
 626                browser->nr_hierarchy_entries++;
 627        if (he->leaf)
 628                browser->nr_callchain_rows += he->nr_rows;
 629        else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
 630                browser->nr_hierarchy_entries++;
 631                he->has_no_entry = true;
 632                he->nr_rows = 1;
 633        } else
 634                he->has_no_entry = false;
 635}
 636
 637static void
 638__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 639{
 640        struct rb_node *nd;
 641        struct hist_entry *he;
 642
 643        nd = rb_first(&browser->hists->entries);
 644        while (nd) {
 645                he = rb_entry(nd, struct hist_entry, rb_node);
 646
 647                /* set folding state even if it's currently folded */
 648                nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
 649
 650                hist_entry__set_folding(he, browser, unfold);
 651        }
 652}
 653
 654static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 655{
 656        browser->nr_hierarchy_entries = 0;
 657        browser->nr_callchain_rows = 0;
 658        __hist_browser__set_folding(browser, unfold);
 659
 660        browser->b.nr_entries = hist_browser__nr_entries(browser);
 661        /* Go to the start, we may be way after valid entries after a collapse */
 662        ui_browser__reset_index(&browser->b);
 663}
 664
 665static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
 666{
 667        if (!browser->he_selection)
 668                return;
 669
 670        hist_entry__set_folding(browser->he_selection, browser, unfold);
 671        browser->b.nr_entries = hist_browser__nr_entries(browser);
 672}
 673
 674static void ui_browser__warn_lost_events(struct ui_browser *browser)
 675{
 676        ui_browser__warning(browser, 4,
 677                "Events are being lost, check IO/CPU overload!\n\n"
 678                "You may want to run 'perf' using a RT scheduler policy:\n\n"
 679                " perf top -r 80\n\n"
 680                "Or reduce the sampling frequency.");
 681}
 682
 683static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
 684{
 685        return browser->title ? browser->title(browser, bf, size) : 0;
 686}
 687
 688int hist_browser__run(struct hist_browser *browser, const char *help)
 689{
 690        int key;
 691        char title[160];
 692        struct hist_browser_timer *hbt = browser->hbt;
 693        int delay_secs = hbt ? hbt->refresh : 0;
 694
 695        browser->b.entries = &browser->hists->entries;
 696        browser->b.nr_entries = hist_browser__nr_entries(browser);
 697
 698        hist_browser__title(browser, title, sizeof(title));
 699
 700        if (ui_browser__show(&browser->b, title, "%s", help) < 0)
 701                return -1;
 702
 703        while (1) {
 704                key = ui_browser__run(&browser->b, delay_secs);
 705
 706                switch (key) {
 707                case K_TIMER: {
 708                        u64 nr_entries;
 709                        hbt->timer(hbt->arg);
 710
 711                        if (hist_browser__has_filter(browser) ||
 712                            symbol_conf.report_hierarchy)
 713                                hist_browser__update_nr_entries(browser);
 714
 715                        nr_entries = hist_browser__nr_entries(browser);
 716                        ui_browser__update_nr_entries(&browser->b, nr_entries);
 717
 718                        if (browser->hists->stats.nr_lost_warned !=
 719                            browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
 720                                browser->hists->stats.nr_lost_warned =
 721                                        browser->hists->stats.nr_events[PERF_RECORD_LOST];
 722                                ui_browser__warn_lost_events(&browser->b);
 723                        }
 724
 725                        hist_browser__title(browser, title, sizeof(title));
 726                        ui_browser__show_title(&browser->b, title);
 727                        continue;
 728                }
 729                case 'D': { /* Debug */
 730                        static int seq;
 731                        struct hist_entry *h = rb_entry(browser->b.top,
 732                                                        struct hist_entry, rb_node);
 733                        ui_helpline__pop();
 734                        ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
 735                                           seq++, browser->b.nr_entries,
 736                                           browser->hists->nr_entries,
 737                                           browser->b.rows,
 738                                           browser->b.index,
 739                                           browser->b.top_idx,
 740                                           h->row_offset, h->nr_rows);
 741                }
 742                        break;
 743                case 'C':
 744                        /* Collapse the whole world. */
 745                        hist_browser__set_folding(browser, false);
 746                        break;
 747                case 'c':
 748                        /* Collapse the selected entry. */
 749                        hist_browser__set_folding_selected(browser, false);
 750                        break;
 751                case 'E':
 752                        /* Expand the whole world. */
 753                        hist_browser__set_folding(browser, true);
 754                        break;
 755                case 'e':
 756                        /* Expand the selected entry. */
 757                        hist_browser__set_folding_selected(browser, true);
 758                        break;
 759                case 'H':
 760                        browser->show_headers = !browser->show_headers;
 761                        hist_browser__update_rows(browser);
 762                        break;
 763                case K_ENTER:
 764                        if (hist_browser__toggle_fold(browser))
 765                                break;
 766                        /* fall thru */
 767                default:
 768                        goto out;
 769                }
 770        }
 771out:
 772        ui_browser__hide(&browser->b);
 773        return key;
 774}
 775
 776struct callchain_print_arg {
 777        /* for hists browser */
 778        off_t   row_offset;
 779        bool    is_current_entry;
 780
 781        /* for file dump */
 782        FILE    *fp;
 783        int     printed;
 784};
 785
 786typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
 787                                         struct callchain_list *chain,
 788                                         const char *str, int offset,
 789                                         unsigned short row,
 790                                         struct callchain_print_arg *arg);
 791
 792static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 793                                               struct callchain_list *chain,
 794                                               const char *str, int offset,
 795                                               unsigned short row,
 796                                               struct callchain_print_arg *arg)
 797{
 798        int color, width;
 799        char folded_sign = callchain_list__folded(chain);
 800        bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
 801
 802        color = HE_COLORSET_NORMAL;
 803        width = browser->b.width - (offset + 2);
 804        if (ui_browser__is_current_entry(&browser->b, row)) {
 805                browser->selection = &chain->ms;
 806                color = HE_COLORSET_SELECTED;
 807                arg->is_current_entry = true;
 808        }
 809
 810        ui_browser__set_color(&browser->b, color);
 811        hist_browser__gotorc(browser, row, 0);
 812        ui_browser__write_nstring(&browser->b, " ", offset);
 813        ui_browser__printf(&browser->b, "%c", folded_sign);
 814        ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
 815        ui_browser__write_nstring(&browser->b, str, width);
 816}
 817
 818static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
 819                                                  struct callchain_list *chain,
 820                                                  const char *str, int offset,
 821                                                  unsigned short row __maybe_unused,
 822                                                  struct callchain_print_arg *arg)
 823{
 824        char folded_sign = callchain_list__folded(chain);
 825
 826        arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
 827                                folded_sign, str);
 828}
 829
 830typedef bool (*check_output_full_fn)(struct hist_browser *browser,
 831                                     unsigned short row);
 832
 833static bool hist_browser__check_output_full(struct hist_browser *browser,
 834                                            unsigned short row)
 835{
 836        return browser->b.rows == row;
 837}
 838
 839static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
 840                                          unsigned short row __maybe_unused)
 841{
 842        return false;
 843}
 844
 845#define LEVEL_OFFSET_STEP 3
 846
 847static int hist_browser__show_inline(struct hist_browser *browser,
 848                                     struct inline_node *node,
 849                                     unsigned short row,
 850                                     int offset)
 851{
 852        struct inline_list *ilist;
 853        char buf[1024];
 854        int color, width, first_row;
 855
 856        first_row = row;
 857        width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
 858        list_for_each_entry(ilist, &node->val, list) {
 859                if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
 860                        color = HE_COLORSET_NORMAL;
 861                        if (ui_browser__is_current_entry(&browser->b, row))
 862                                color = HE_COLORSET_SELECTED;
 863
 864                        if (callchain_param.key == CCKEY_ADDRESS ||
 865                            callchain_param.key == CCKEY_SRCLINE) {
 866                                if (ilist->filename != NULL)
 867                                        scnprintf(buf, sizeof(buf),
 868                                                  "%s:%d (inline)",
 869                                                  ilist->filename,
 870                                                  ilist->line_nr);
 871                                else
 872                                        scnprintf(buf, sizeof(buf), "??");
 873                        } else if (ilist->funcname != NULL)
 874                                scnprintf(buf, sizeof(buf), "%s (inline)",
 875                                          ilist->funcname);
 876                        else if (ilist->filename != NULL)
 877                                scnprintf(buf, sizeof(buf),
 878                                          "%s:%d (inline)",
 879                                          ilist->filename,
 880                                          ilist->line_nr);
 881                        else
 882                                scnprintf(buf, sizeof(buf), "??");
 883
 884                        ui_browser__set_color(&browser->b, color);
 885                        hist_browser__gotorc(browser, row, 0);
 886                        ui_browser__write_nstring(&browser->b, " ",
 887                                LEVEL_OFFSET_STEP + offset);
 888                        ui_browser__write_nstring(&browser->b, buf, width);
 889                        row++;
 890                }
 891        }
 892
 893        return row - first_row;
 894}
 895
 896static size_t show_inline_list(struct hist_browser *browser, struct map *map,
 897                               u64 ip, int row, int offset)
 898{
 899        struct inline_node *node;
 900        int ret;
 901
 902        node = inline_node__create(map, ip);
 903        if (node == NULL)
 904                return 0;
 905
 906        ret = hist_browser__show_inline(browser, node, row, offset);
 907
 908        inline_node__delete(node);
 909        return ret;
 910}
 911
 912static int hist_browser__show_callchain_list(struct hist_browser *browser,
 913                                             struct callchain_node *node,
 914                                             struct callchain_list *chain,
 915                                             unsigned short row, u64 total,
 916                                             bool need_percent, int offset,
 917                                             print_callchain_entry_fn print,
 918                                             struct callchain_print_arg *arg)
 919{
 920        char bf[1024], *alloc_str;
 921        char buf[64], *alloc_str2;
 922        const char *str;
 923        int inline_rows = 0, ret = 1;
 924
 925        if (arg->row_offset != 0) {
 926                arg->row_offset--;
 927                return 0;
 928        }
 929
 930        alloc_str = NULL;
 931        alloc_str2 = NULL;
 932
 933        str = callchain_list__sym_name(chain, bf, sizeof(bf),
 934                                       browser->show_dso);
 935
 936        if (symbol_conf.show_branchflag_count) {
 937                if (need_percent)
 938                        callchain_list_counts__printf_value(node, chain, NULL,
 939                                                            buf, sizeof(buf));
 940                else
 941                        callchain_list_counts__printf_value(NULL, chain, NULL,
 942                                                            buf, sizeof(buf));
 943
 944                if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
 945                        str = "Not enough memory!";
 946                else
 947                        str = alloc_str2;
 948        }
 949
 950        if (need_percent) {
 951                callchain_node__scnprintf_value(node, buf, sizeof(buf),
 952                                                total);
 953
 954                if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
 955                        str = "Not enough memory!";
 956                else
 957                        str = alloc_str;
 958        }
 959
 960        print(browser, chain, str, offset, row, arg);
 961        free(alloc_str);
 962        free(alloc_str2);
 963
 964        if (symbol_conf.inline_name) {
 965                inline_rows = show_inline_list(browser, chain->ms.map,
 966                                               chain->ip, row + 1, offset);
 967        }
 968
 969        return ret + inline_rows;
 970}
 971
 972static bool check_percent_display(struct rb_node *node, u64 parent_total)
 973{
 974        struct callchain_node *child;
 975
 976        if (node == NULL)
 977                return false;
 978
 979        if (rb_next(node))
 980                return true;
 981
 982        child = rb_entry(node, struct callchain_node, rb_node);
 983        return callchain_cumul_hits(child) != parent_total;
 984}
 985
 986static int hist_browser__show_callchain_flat(struct hist_browser *browser,
 987                                             struct rb_root *root,
 988                                             unsigned short row, u64 total,
 989                                             u64 parent_total,
 990                                             print_callchain_entry_fn print,
 991                                             struct callchain_print_arg *arg,
 992                                             check_output_full_fn is_output_full)
 993{
 994        struct rb_node *node;
 995        int first_row = row, offset = LEVEL_OFFSET_STEP;
 996        bool need_percent;
 997
 998        node = rb_first(root);
 999        need_percent = check_percent_display(node, parent_total);
1000
1001        while (node) {
1002                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1003                struct rb_node *next = rb_next(node);
1004                struct callchain_list *chain;
1005                char folded_sign = ' ';
1006                int first = true;
1007                int extra_offset = 0;
1008
1009                list_for_each_entry(chain, &child->parent_val, list) {
1010                        bool was_first = first;
1011
1012                        if (first)
1013                                first = false;
1014                        else if (need_percent)
1015                                extra_offset = LEVEL_OFFSET_STEP;
1016
1017                        folded_sign = callchain_list__folded(chain);
1018
1019                        row += hist_browser__show_callchain_list(browser, child,
1020                                                        chain, row, total,
1021                                                        was_first && need_percent,
1022                                                        offset + extra_offset,
1023                                                        print, arg);
1024
1025                        if (is_output_full(browser, row))
1026                                goto out;
1027
1028                        if (folded_sign == '+')
1029                                goto next;
1030                }
1031
1032                list_for_each_entry(chain, &child->val, list) {
1033                        bool was_first = first;
1034
1035                        if (first)
1036                                first = false;
1037                        else if (need_percent)
1038                                extra_offset = LEVEL_OFFSET_STEP;
1039
1040                        folded_sign = callchain_list__folded(chain);
1041
1042                        row += hist_browser__show_callchain_list(browser, child,
1043                                                        chain, row, total,
1044                                                        was_first && need_percent,
1045                                                        offset + extra_offset,
1046                                                        print, arg);
1047
1048                        if (is_output_full(browser, row))
1049                                goto out;
1050
1051                        if (folded_sign == '+')
1052                                break;
1053                }
1054
1055next:
1056                if (is_output_full(browser, row))
1057                        break;
1058                node = next;
1059        }
1060out:
1061        return row - first_row;
1062}
1063
1064static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1065                                                struct callchain_list *chain,
1066                                                char *value_str, char *old_str)
1067{
1068        char bf[1024];
1069        const char *str;
1070        char *new;
1071
1072        str = callchain_list__sym_name(chain, bf, sizeof(bf),
1073                                       browser->show_dso);
1074        if (old_str) {
1075                if (asprintf(&new, "%s%s%s", old_str,
1076                             symbol_conf.field_sep ?: ";", str) < 0)
1077                        new = NULL;
1078        } else {
1079                if (value_str) {
1080                        if (asprintf(&new, "%s %s", value_str, str) < 0)
1081                                new = NULL;
1082                } else {
1083                        if (asprintf(&new, "%s", str) < 0)
1084                                new = NULL;
1085                }
1086        }
1087        return new;
1088}
1089
1090static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1091                                               struct rb_root *root,
1092                                               unsigned short row, u64 total,
1093                                               u64 parent_total,
1094                                               print_callchain_entry_fn print,
1095                                               struct callchain_print_arg *arg,
1096                                               check_output_full_fn is_output_full)
1097{
1098        struct rb_node *node;
1099        int first_row = row, offset = LEVEL_OFFSET_STEP;
1100        bool need_percent;
1101
1102        node = rb_first(root);
1103        need_percent = check_percent_display(node, parent_total);
1104
1105        while (node) {
1106                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1107                struct rb_node *next = rb_next(node);
1108                struct callchain_list *chain, *first_chain = NULL;
1109                int first = true;
1110                char *value_str = NULL, *value_str_alloc = NULL;
1111                char *chain_str = NULL, *chain_str_alloc = NULL;
1112
1113                if (arg->row_offset != 0) {
1114                        arg->row_offset--;
1115                        goto next;
1116                }
1117
1118                if (need_percent) {
1119                        char buf[64];
1120
1121                        callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1122                        if (asprintf(&value_str, "%s", buf) < 0) {
1123                                value_str = (char *)"<...>";
1124                                goto do_print;
1125                        }
1126                        value_str_alloc = value_str;
1127                }
1128
1129                list_for_each_entry(chain, &child->parent_val, list) {
1130                        chain_str = hist_browser__folded_callchain_str(browser,
1131                                                chain, value_str, chain_str);
1132                        if (first) {
1133                                first = false;
1134                                first_chain = chain;
1135                        }
1136
1137                        if (chain_str == NULL) {
1138                                chain_str = (char *)"Not enough memory!";
1139                                goto do_print;
1140                        }
1141
1142                        chain_str_alloc = chain_str;
1143                }
1144
1145                list_for_each_entry(chain, &child->val, list) {
1146                        chain_str = hist_browser__folded_callchain_str(browser,
1147                                                chain, value_str, chain_str);
1148                        if (first) {
1149                                first = false;
1150                                first_chain = chain;
1151                        }
1152
1153                        if (chain_str == NULL) {
1154                                chain_str = (char *)"Not enough memory!";
1155                                goto do_print;
1156                        }
1157
1158                        chain_str_alloc = chain_str;
1159                }
1160
1161do_print:
1162                print(browser, first_chain, chain_str, offset, row++, arg);
1163                free(value_str_alloc);
1164                free(chain_str_alloc);
1165
1166next:
1167                if (is_output_full(browser, row))
1168                        break;
1169                node = next;
1170        }
1171
1172        return row - first_row;
1173}
1174
1175static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1176                                        struct rb_root *root, int level,
1177                                        unsigned short row, u64 total,
1178                                        u64 parent_total,
1179                                        print_callchain_entry_fn print,
1180                                        struct callchain_print_arg *arg,
1181                                        check_output_full_fn is_output_full)
1182{
1183        struct rb_node *node;
1184        int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1185        bool need_percent;
1186        u64 percent_total = total;
1187
1188        if (callchain_param.mode == CHAIN_GRAPH_REL)
1189                percent_total = parent_total;
1190
1191        node = rb_first(root);
1192        need_percent = check_percent_display(node, parent_total);
1193
1194        while (node) {
1195                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1196                struct rb_node *next = rb_next(node);
1197                struct callchain_list *chain;
1198                char folded_sign = ' ';
1199                int first = true;
1200                int extra_offset = 0;
1201
1202                list_for_each_entry(chain, &child->val, list) {
1203                        bool was_first = first;
1204
1205                        if (first)
1206                                first = false;
1207                        else if (need_percent)
1208                                extra_offset = LEVEL_OFFSET_STEP;
1209
1210                        folded_sign = callchain_list__folded(chain);
1211
1212                        row += hist_browser__show_callchain_list(browser, child,
1213                                                        chain, row, percent_total,
1214                                                        was_first && need_percent,
1215                                                        offset + extra_offset,
1216                                                        print, arg);
1217
1218                        if (is_output_full(browser, row))
1219                                goto out;
1220
1221                        if (folded_sign == '+')
1222                                break;
1223                }
1224
1225                if (folded_sign == '-') {
1226                        const int new_level = level + (extra_offset ? 2 : 1);
1227
1228                        row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1229                                                            new_level, row, total,
1230                                                            child->children_hit,
1231                                                            print, arg, is_output_full);
1232                }
1233                if (is_output_full(browser, row))
1234                        break;
1235                node = next;
1236        }
1237out:
1238        return row - first_row;
1239}
1240
1241static int hist_browser__show_callchain(struct hist_browser *browser,
1242                                        struct hist_entry *entry, int level,
1243                                        unsigned short row,
1244                                        print_callchain_entry_fn print,
1245                                        struct callchain_print_arg *arg,
1246                                        check_output_full_fn is_output_full)
1247{
1248        u64 total = hists__total_period(entry->hists);
1249        u64 parent_total;
1250        int printed;
1251
1252        if (symbol_conf.cumulate_callchain)
1253                parent_total = entry->stat_acc->period;
1254        else
1255                parent_total = entry->stat.period;
1256
1257        if (callchain_param.mode == CHAIN_FLAT) {
1258                printed = hist_browser__show_callchain_flat(browser,
1259                                                &entry->sorted_chain, row,
1260                                                total, parent_total, print, arg,
1261                                                is_output_full);
1262        } else if (callchain_param.mode == CHAIN_FOLDED) {
1263                printed = hist_browser__show_callchain_folded(browser,
1264                                                &entry->sorted_chain, row,
1265                                                total, parent_total, print, arg,
1266                                                is_output_full);
1267        } else {
1268                printed = hist_browser__show_callchain_graph(browser,
1269                                                &entry->sorted_chain, level, row,
1270                                                total, parent_total, print, arg,
1271                                                is_output_full);
1272        }
1273
1274        if (arg->is_current_entry)
1275                browser->he_selection = entry;
1276
1277        return printed;
1278}
1279
1280struct hpp_arg {
1281        struct ui_browser *b;
1282        char folded_sign;
1283        bool current_entry;
1284};
1285
1286int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1287{
1288        struct hpp_arg *arg = hpp->ptr;
1289        int ret, len;
1290        va_list args;
1291        double percent;
1292
1293        va_start(args, fmt);
1294        len = va_arg(args, int);
1295        percent = va_arg(args, double);
1296        va_end(args);
1297
1298        ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1299
1300        ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1301        ui_browser__printf(arg->b, "%s", hpp->buf);
1302
1303        return ret;
1304}
1305
1306#define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
1307static u64 __hpp_get_##_field(struct hist_entry *he)                    \
1308{                                                                       \
1309        return he->stat._field;                                         \
1310}                                                                       \
1311                                                                        \
1312static int                                                              \
1313hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1314                                struct perf_hpp *hpp,                   \
1315                                struct hist_entry *he)                  \
1316{                                                                       \
1317        return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
1318                        __hpp__slsmg_color_printf, true);               \
1319}
1320
1321#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
1322static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
1323{                                                                       \
1324        return he->stat_acc->_field;                                    \
1325}                                                                       \
1326                                                                        \
1327static int                                                              \
1328hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1329                                struct perf_hpp *hpp,                   \
1330                                struct hist_entry *he)                  \
1331{                                                                       \
1332        if (!symbol_conf.cumulate_callchain) {                          \
1333                struct hpp_arg *arg = hpp->ptr;                         \
1334                int len = fmt->user_len ?: fmt->len;                    \
1335                int ret = scnprintf(hpp->buf, hpp->size,                \
1336                                    "%*s", len, "N/A");                 \
1337                ui_browser__printf(arg->b, "%s", hpp->buf);             \
1338                                                                        \
1339                return ret;                                             \
1340        }                                                               \
1341        return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
1342                        " %*.2f%%", __hpp__slsmg_color_printf, true);   \
1343}
1344
1345__HPP_COLOR_PERCENT_FN(overhead, period)
1346__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1347__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1348__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1349__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1350__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1351
1352#undef __HPP_COLOR_PERCENT_FN
1353#undef __HPP_COLOR_ACC_PERCENT_FN
1354
1355void hist_browser__init_hpp(void)
1356{
1357        perf_hpp__format[PERF_HPP__OVERHEAD].color =
1358                                hist_browser__hpp_color_overhead;
1359        perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1360                                hist_browser__hpp_color_overhead_sys;
1361        perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1362                                hist_browser__hpp_color_overhead_us;
1363        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1364                                hist_browser__hpp_color_overhead_guest_sys;
1365        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1366                                hist_browser__hpp_color_overhead_guest_us;
1367        perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1368                                hist_browser__hpp_color_overhead_acc;
1369}
1370
1371static int hist_browser__show_entry(struct hist_browser *browser,
1372                                    struct hist_entry *entry,
1373                                    unsigned short row)
1374{
1375        int printed = 0;
1376        int width = browser->b.width;
1377        char folded_sign = ' ';
1378        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1379        off_t row_offset = entry->row_offset;
1380        bool first = true;
1381        struct perf_hpp_fmt *fmt;
1382
1383        if (current_entry) {
1384                browser->he_selection = entry;
1385                browser->selection = &entry->ms;
1386        }
1387
1388        if (symbol_conf.use_callchain) {
1389                hist_entry__init_have_children(entry);
1390                folded_sign = hist_entry__folded(entry);
1391        }
1392
1393        if (symbol_conf.inline_name &&
1394            (!entry->has_children)) {
1395                hist_entry_init_inline_node(entry);
1396                folded_sign = hist_entry__folded(entry);
1397        }
1398
1399        if (row_offset == 0) {
1400                struct hpp_arg arg = {
1401                        .b              = &browser->b,
1402                        .folded_sign    = folded_sign,
1403                        .current_entry  = current_entry,
1404                };
1405                int column = 0;
1406
1407                hist_browser__gotorc(browser, row, 0);
1408
1409                hists__for_each_format(browser->hists, fmt) {
1410                        char s[2048];
1411                        struct perf_hpp hpp = {
1412                                .buf    = s,
1413                                .size   = sizeof(s),
1414                                .ptr    = &arg,
1415                        };
1416
1417                        if (perf_hpp__should_skip(fmt, entry->hists) ||
1418                            column++ < browser->b.horiz_scroll)
1419                                continue;
1420
1421                        if (current_entry && browser->b.navkeypressed) {
1422                                ui_browser__set_color(&browser->b,
1423                                                      HE_COLORSET_SELECTED);
1424                        } else {
1425                                ui_browser__set_color(&browser->b,
1426                                                      HE_COLORSET_NORMAL);
1427                        }
1428
1429                        if (first) {
1430                                if (symbol_conf.use_callchain ||
1431                                        symbol_conf.inline_name) {
1432                                        ui_browser__printf(&browser->b, "%c ", folded_sign);
1433                                        width -= 2;
1434                                }
1435                                first = false;
1436                        } else {
1437                                ui_browser__printf(&browser->b, "  ");
1438                                width -= 2;
1439                        }
1440
1441                        if (fmt->color) {
1442                                int ret = fmt->color(fmt, &hpp, entry);
1443                                hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1444                                /*
1445                                 * fmt->color() already used ui_browser to
1446                                 * print the non alignment bits, skip it (+ret):
1447                                 */
1448                                ui_browser__printf(&browser->b, "%s", s + ret);
1449                        } else {
1450                                hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1451                                ui_browser__printf(&browser->b, "%s", s);
1452                        }
1453                        width -= hpp.buf - s;
1454                }
1455
1456                /* The scroll bar isn't being used */
1457                if (!browser->b.navkeypressed)
1458                        width += 1;
1459
1460                ui_browser__write_nstring(&browser->b, "", width);
1461
1462                ++row;
1463                ++printed;
1464        } else
1465                --row_offset;
1466
1467        if (folded_sign == '-' && row != browser->b.rows) {
1468                struct callchain_print_arg arg = {
1469                        .row_offset = row_offset,
1470                        .is_current_entry = current_entry,
1471                };
1472
1473                if (entry->inline_node)
1474                        printed += hist_browser__show_inline(browser,
1475                                        entry->inline_node, row, 0);
1476                else
1477                        printed += hist_browser__show_callchain(browser,
1478                                        entry, 1, row,
1479                                        hist_browser__show_callchain_entry,
1480                                        &arg,
1481                                        hist_browser__check_output_full);
1482        }
1483
1484        return printed;
1485}
1486
1487static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1488                                              struct hist_entry *entry,
1489                                              unsigned short row,
1490                                              int level)
1491{
1492        int printed = 0;
1493        int width = browser->b.width;
1494        char folded_sign = ' ';
1495        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1496        off_t row_offset = entry->row_offset;
1497        bool first = true;
1498        struct perf_hpp_fmt *fmt;
1499        struct perf_hpp_list_node *fmt_node;
1500        struct hpp_arg arg = {
1501                .b              = &browser->b,
1502                .current_entry  = current_entry,
1503        };
1504        int column = 0;
1505        int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1506
1507        if (current_entry) {
1508                browser->he_selection = entry;
1509                browser->selection = &entry->ms;
1510        }
1511
1512        hist_entry__init_have_children(entry);
1513        folded_sign = hist_entry__folded(entry);
1514        arg.folded_sign = folded_sign;
1515
1516        if (entry->leaf && row_offset) {
1517                row_offset--;
1518                goto show_callchain;
1519        }
1520
1521        hist_browser__gotorc(browser, row, 0);
1522
1523        if (current_entry && browser->b.navkeypressed)
1524                ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1525        else
1526                ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1527
1528        ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1529        width -= level * HIERARCHY_INDENT;
1530
1531        /* the first hpp_list_node is for overhead columns */
1532        fmt_node = list_first_entry(&entry->hists->hpp_formats,
1533                                    struct perf_hpp_list_node, list);
1534        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1535                char s[2048];
1536                struct perf_hpp hpp = {
1537                        .buf            = s,
1538                        .size           = sizeof(s),
1539                        .ptr            = &arg,
1540                };
1541
1542                if (perf_hpp__should_skip(fmt, entry->hists) ||
1543                    column++ < browser->b.horiz_scroll)
1544                        continue;
1545
1546                if (current_entry && browser->b.navkeypressed) {
1547                        ui_browser__set_color(&browser->b,
1548                                              HE_COLORSET_SELECTED);
1549                } else {
1550                        ui_browser__set_color(&browser->b,
1551                                              HE_COLORSET_NORMAL);
1552                }
1553
1554                if (first) {
1555                        ui_browser__printf(&browser->b, "%c ", folded_sign);
1556                        width -= 2;
1557                        first = false;
1558                } else {
1559                        ui_browser__printf(&browser->b, "  ");
1560                        width -= 2;
1561                }
1562
1563                if (fmt->color) {
1564                        int ret = fmt->color(fmt, &hpp, entry);
1565                        hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1566                        /*
1567                         * fmt->color() already used ui_browser to
1568                         * print the non alignment bits, skip it (+ret):
1569                         */
1570                        ui_browser__printf(&browser->b, "%s", s + ret);
1571                } else {
1572                        int ret = fmt->entry(fmt, &hpp, entry);
1573                        hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1574                        ui_browser__printf(&browser->b, "%s", s);
1575                }
1576                width -= hpp.buf - s;
1577        }
1578
1579        if (!first) {
1580                ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1581                width -= hierarchy_indent;
1582        }
1583
1584        if (column >= browser->b.horiz_scroll) {
1585                char s[2048];
1586                struct perf_hpp hpp = {
1587                        .buf            = s,
1588                        .size           = sizeof(s),
1589                        .ptr            = &arg,
1590                };
1591
1592                if (current_entry && browser->b.navkeypressed) {
1593                        ui_browser__set_color(&browser->b,
1594                                              HE_COLORSET_SELECTED);
1595                } else {
1596                        ui_browser__set_color(&browser->b,
1597                                              HE_COLORSET_NORMAL);
1598                }
1599
1600                perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1601                        if (first) {
1602                                ui_browser__printf(&browser->b, "%c ", folded_sign);
1603                                first = false;
1604                        } else {
1605                                ui_browser__write_nstring(&browser->b, "", 2);
1606                        }
1607
1608                        width -= 2;
1609
1610                        /*
1611                         * No need to call hist_entry__snprintf_alignment()
1612                         * since this fmt is always the last column in the
1613                         * hierarchy mode.
1614                         */
1615                        if (fmt->color) {
1616                                width -= fmt->color(fmt, &hpp, entry);
1617                        } else {
1618                                int i = 0;
1619
1620                                width -= fmt->entry(fmt, &hpp, entry);
1621                                ui_browser__printf(&browser->b, "%s", ltrim(s));
1622
1623                                while (isspace(s[i++]))
1624                                        width++;
1625                        }
1626                }
1627        }
1628
1629        /* The scroll bar isn't being used */
1630        if (!browser->b.navkeypressed)
1631                width += 1;
1632
1633        ui_browser__write_nstring(&browser->b, "", width);
1634
1635        ++row;
1636        ++printed;
1637
1638show_callchain:
1639        if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1640                struct callchain_print_arg carg = {
1641                        .row_offset = row_offset,
1642                };
1643
1644                printed += hist_browser__show_callchain(browser, entry,
1645                                        level + 1, row,
1646                                        hist_browser__show_callchain_entry, &carg,
1647                                        hist_browser__check_output_full);
1648        }
1649
1650        return printed;
1651}
1652
1653static int hist_browser__show_no_entry(struct hist_browser *browser,
1654                                       unsigned short row, int level)
1655{
1656        int width = browser->b.width;
1657        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1658        bool first = true;
1659        int column = 0;
1660        int ret;
1661        struct perf_hpp_fmt *fmt;
1662        struct perf_hpp_list_node *fmt_node;
1663        int indent = browser->hists->nr_hpp_node - 2;
1664
1665        if (current_entry) {
1666                browser->he_selection = NULL;
1667                browser->selection = NULL;
1668        }
1669
1670        hist_browser__gotorc(browser, row, 0);
1671
1672        if (current_entry && browser->b.navkeypressed)
1673                ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1674        else
1675                ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1676
1677        ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1678        width -= level * HIERARCHY_INDENT;
1679
1680        /* the first hpp_list_node is for overhead columns */
1681        fmt_node = list_first_entry(&browser->hists->hpp_formats,
1682                                    struct perf_hpp_list_node, list);
1683        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1684                if (perf_hpp__should_skip(fmt, browser->hists) ||
1685                    column++ < browser->b.horiz_scroll)
1686                        continue;
1687
1688                ret = fmt->width(fmt, NULL, browser->hists);
1689
1690                if (first) {
1691                        /* for folded sign */
1692                        first = false;
1693                        ret++;
1694                } else {
1695                        /* space between columns */
1696                        ret += 2;
1697                }
1698
1699                ui_browser__write_nstring(&browser->b, "", ret);
1700                width -= ret;
1701        }
1702
1703        ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1704        width -= indent * HIERARCHY_INDENT;
1705
1706        if (column >= browser->b.horiz_scroll) {
1707                char buf[32];
1708
1709                ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1710                ui_browser__printf(&browser->b, "  %s", buf);
1711                width -= ret + 2;
1712        }
1713
1714        /* The scroll bar isn't being used */
1715        if (!browser->b.navkeypressed)
1716                width += 1;
1717
1718        ui_browser__write_nstring(&browser->b, "", width);
1719        return 1;
1720}
1721
1722static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1723{
1724        advance_hpp(hpp, inc);
1725        return hpp->size <= 0;
1726}
1727
1728static int
1729hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1730                                 size_t size, int line)
1731{
1732        struct hists *hists = browser->hists;
1733        struct perf_hpp dummy_hpp = {
1734                .buf    = buf,
1735                .size   = size,
1736        };
1737        struct perf_hpp_fmt *fmt;
1738        size_t ret = 0;
1739        int column = 0;
1740        int span = 0;
1741
1742        if (symbol_conf.use_callchain) {
1743                ret = scnprintf(buf, size, "  ");
1744                if (advance_hpp_check(&dummy_hpp, ret))
1745                        return ret;
1746        }
1747
1748        hists__for_each_format(browser->hists, fmt) {
1749                if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1750                        continue;
1751
1752                ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1753                if (advance_hpp_check(&dummy_hpp, ret))
1754                        break;
1755
1756                if (span)
1757                        continue;
1758
1759                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1760                if (advance_hpp_check(&dummy_hpp, ret))
1761                        break;
1762        }
1763
1764        return ret;
1765}
1766
1767static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1768{
1769        struct hists *hists = browser->hists;
1770        struct perf_hpp dummy_hpp = {
1771                .buf    = buf,
1772                .size   = size,
1773        };
1774        struct perf_hpp_fmt *fmt;
1775        struct perf_hpp_list_node *fmt_node;
1776        size_t ret = 0;
1777        int column = 0;
1778        int indent = hists->nr_hpp_node - 2;
1779        bool first_node, first_col;
1780
1781        ret = scnprintf(buf, size, "  ");
1782        if (advance_hpp_check(&dummy_hpp, ret))
1783                return ret;
1784
1785        first_node = true;
1786        /* the first hpp_list_node is for overhead columns */
1787        fmt_node = list_first_entry(&hists->hpp_formats,
1788                                    struct perf_hpp_list_node, list);
1789        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1790                if (column++ < browser->b.horiz_scroll)
1791                        continue;
1792
1793                ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1794                if (advance_hpp_check(&dummy_hpp, ret))
1795                        break;
1796
1797                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1798                if (advance_hpp_check(&dummy_hpp, ret))
1799                        break;
1800
1801                first_node = false;
1802        }
1803
1804        if (!first_node) {
1805                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1806                                indent * HIERARCHY_INDENT, "");
1807                if (advance_hpp_check(&dummy_hpp, ret))
1808                        return ret;
1809        }
1810
1811        first_node = true;
1812        list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1813                if (!first_node) {
1814                        ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1815                        if (advance_hpp_check(&dummy_hpp, ret))
1816                                break;
1817                }
1818                first_node = false;
1819
1820                first_col = true;
1821                perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1822                        char *start;
1823
1824                        if (perf_hpp__should_skip(fmt, hists))
1825                                continue;
1826
1827                        if (!first_col) {
1828                                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1829                                if (advance_hpp_check(&dummy_hpp, ret))
1830                                        break;
1831                        }
1832                        first_col = false;
1833
1834                        ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1835                        dummy_hpp.buf[ret] = '\0';
1836
1837                        start = trim(dummy_hpp.buf);
1838                        ret = strlen(start);
1839
1840                        if (start != dummy_hpp.buf)
1841                                memmove(dummy_hpp.buf, start, ret + 1);
1842
1843                        if (advance_hpp_check(&dummy_hpp, ret))
1844                                break;
1845                }
1846        }
1847
1848        return ret;
1849}
1850
1851static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1852{
1853        char headers[1024];
1854
1855        hists_browser__scnprintf_hierarchy_headers(browser, headers,
1856                                                   sizeof(headers));
1857
1858        ui_browser__gotorc(&browser->b, 0, 0);
1859        ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1860        ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1861}
1862
1863static void hists_browser__headers(struct hist_browser *browser)
1864{
1865        struct hists *hists = browser->hists;
1866        struct perf_hpp_list *hpp_list = hists->hpp_list;
1867
1868        int line;
1869
1870        for (line = 0; line < hpp_list->nr_header_lines; line++) {
1871                char headers[1024];
1872
1873                hists_browser__scnprintf_headers(browser, headers,
1874                                                 sizeof(headers), line);
1875
1876                ui_browser__gotorc(&browser->b, line, 0);
1877                ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1878                ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1879        }
1880}
1881
1882static void hist_browser__show_headers(struct hist_browser *browser)
1883{
1884        if (symbol_conf.report_hierarchy)
1885                hists_browser__hierarchy_headers(browser);
1886        else
1887                hists_browser__headers(browser);
1888}
1889
1890static void ui_browser__hists_init_top(struct ui_browser *browser)
1891{
1892        if (browser->top == NULL) {
1893                struct hist_browser *hb;
1894
1895                hb = container_of(browser, struct hist_browser, b);
1896                browser->top = rb_first(&hb->hists->entries);
1897        }
1898}
1899
1900static unsigned int hist_browser__refresh(struct ui_browser *browser)
1901{
1902        unsigned row = 0;
1903        u16 header_offset = 0;
1904        struct rb_node *nd;
1905        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1906        struct hists *hists = hb->hists;
1907
1908        if (hb->show_headers) {
1909                struct perf_hpp_list *hpp_list = hists->hpp_list;
1910
1911                hist_browser__show_headers(hb);
1912                header_offset = hpp_list->nr_header_lines;
1913        }
1914
1915        ui_browser__hists_init_top(browser);
1916        hb->he_selection = NULL;
1917        hb->selection = NULL;
1918
1919        for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1920                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1921                float percent;
1922
1923                if (h->filtered) {
1924                        /* let it move to sibling */
1925                        h->unfolded = false;
1926                        continue;
1927                }
1928
1929                percent = hist_entry__get_percent_limit(h);
1930                if (percent < hb->min_pcnt)
1931                        continue;
1932
1933                if (symbol_conf.report_hierarchy) {
1934                        row += hist_browser__show_hierarchy_entry(hb, h, row,
1935                                                                  h->depth);
1936                        if (row == browser->rows)
1937                                break;
1938
1939                        if (h->has_no_entry) {
1940                                hist_browser__show_no_entry(hb, row, h->depth + 1);
1941                                row++;
1942                        }
1943                } else {
1944                        row += hist_browser__show_entry(hb, h, row);
1945                }
1946
1947                if (row == browser->rows)
1948                        break;
1949        }
1950
1951        return row + header_offset;
1952}
1953
1954static struct rb_node *hists__filter_entries(struct rb_node *nd,
1955                                             float min_pcnt)
1956{
1957        while (nd != NULL) {
1958                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1959                float percent = hist_entry__get_percent_limit(h);
1960
1961                if (!h->filtered && percent >= min_pcnt)
1962                        return nd;
1963
1964                /*
1965                 * If it's filtered, its all children also were filtered.
1966                 * So move to sibling node.
1967                 */
1968                if (rb_next(nd))
1969                        nd = rb_next(nd);
1970                else
1971                        nd = rb_hierarchy_next(nd);
1972        }
1973
1974        return NULL;
1975}
1976
1977static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1978                                                  float min_pcnt)
1979{
1980        while (nd != NULL) {
1981                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1982                float percent = hist_entry__get_percent_limit(h);
1983
1984                if (!h->filtered && percent >= min_pcnt)
1985                        return nd;
1986
1987                nd = rb_hierarchy_prev(nd);
1988        }
1989
1990        return NULL;
1991}
1992
1993static void ui_browser__hists_seek(struct ui_browser *browser,
1994                                   off_t offset, int whence)
1995{
1996        struct hist_entry *h;
1997        struct rb_node *nd;
1998        bool first = true;
1999        struct hist_browser *hb;
2000
2001        hb = container_of(browser, struct hist_browser, b);
2002
2003        if (browser->nr_entries == 0)
2004                return;
2005
2006        ui_browser__hists_init_top(browser);
2007
2008        switch (whence) {
2009        case SEEK_SET:
2010                nd = hists__filter_entries(rb_first(browser->entries),
2011                                           hb->min_pcnt);
2012                break;
2013        case SEEK_CUR:
2014                nd = browser->top;
2015                goto do_offset;
2016        case SEEK_END:
2017                nd = rb_hierarchy_last(rb_last(browser->entries));
2018                nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2019                first = false;
2020                break;
2021        default:
2022                return;
2023        }
2024
2025        /*
2026         * Moves not relative to the first visible entry invalidates its
2027         * row_offset:
2028         */
2029        h = rb_entry(browser->top, struct hist_entry, rb_node);
2030        h->row_offset = 0;
2031
2032        /*
2033         * Here we have to check if nd is expanded (+), if it is we can't go
2034         * the next top level hist_entry, instead we must compute an offset of
2035         * what _not_ to show and not change the first visible entry.
2036         *
2037         * This offset increments when we are going from top to bottom and
2038         * decreases when we're going from bottom to top.
2039         *
2040         * As we don't have backpointers to the top level in the callchains
2041         * structure, we need to always print the whole hist_entry callchain,
2042         * skipping the first ones that are before the first visible entry
2043         * and stop when we printed enough lines to fill the screen.
2044         */
2045do_offset:
2046        if (!nd)
2047                return;
2048
2049        if (offset > 0) {
2050                do {
2051                        h = rb_entry(nd, struct hist_entry, rb_node);
2052                        if (h->unfolded && h->leaf) {
2053                                u16 remaining = h->nr_rows - h->row_offset;
2054                                if (offset > remaining) {
2055                                        offset -= remaining;
2056                                        h->row_offset = 0;
2057                                } else {
2058                                        h->row_offset += offset;
2059                                        offset = 0;
2060                                        browser->top = nd;
2061                                        break;
2062                                }
2063                        }
2064                        nd = hists__filter_entries(rb_hierarchy_next(nd),
2065                                                   hb->min_pcnt);
2066                        if (nd == NULL)
2067                                break;
2068                        --offset;
2069                        browser->top = nd;
2070                } while (offset != 0);
2071        } else if (offset < 0) {
2072                while (1) {
2073                        h = rb_entry(nd, struct hist_entry, rb_node);
2074                        if (h->unfolded && h->leaf) {
2075                                if (first) {
2076                                        if (-offset > h->row_offset) {
2077                                                offset += h->row_offset;
2078                                                h->row_offset = 0;
2079                                        } else {
2080                                                h->row_offset += offset;
2081                                                offset = 0;
2082                                                browser->top = nd;
2083                                                break;
2084                                        }
2085                                } else {
2086                                        if (-offset > h->nr_rows) {
2087                                                offset += h->nr_rows;
2088                                                h->row_offset = 0;
2089                                        } else {
2090                                                h->row_offset = h->nr_rows + offset;
2091                                                offset = 0;
2092                                                browser->top = nd;
2093                                                break;
2094                                        }
2095                                }
2096                        }
2097
2098                        nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2099                                                        hb->min_pcnt);
2100                        if (nd == NULL)
2101                                break;
2102                        ++offset;
2103                        browser->top = nd;
2104                        if (offset == 0) {
2105                                /*
2106                                 * Last unfiltered hist_entry, check if it is
2107                                 * unfolded, if it is then we should have
2108                                 * row_offset at its last entry.
2109                                 */
2110                                h = rb_entry(nd, struct hist_entry, rb_node);
2111                                if (h->unfolded && h->leaf)
2112                                        h->row_offset = h->nr_rows;
2113                                break;
2114                        }
2115                        first = false;
2116                }
2117        } else {
2118                browser->top = nd;
2119                h = rb_entry(nd, struct hist_entry, rb_node);
2120                h->row_offset = 0;
2121        }
2122}
2123
2124static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2125                                           struct hist_entry *he, FILE *fp,
2126                                           int level)
2127{
2128        struct callchain_print_arg arg  = {
2129                .fp = fp,
2130        };
2131
2132        hist_browser__show_callchain(browser, he, level, 0,
2133                                     hist_browser__fprintf_callchain_entry, &arg,
2134                                     hist_browser__check_dump_full);
2135        return arg.printed;
2136}
2137
2138static int hist_browser__fprintf_entry(struct hist_browser *browser,
2139                                       struct hist_entry *he, FILE *fp)
2140{
2141        char s[8192];
2142        int printed = 0;
2143        char folded_sign = ' ';
2144        struct perf_hpp hpp = {
2145                .buf = s,
2146                .size = sizeof(s),
2147        };
2148        struct perf_hpp_fmt *fmt;
2149        bool first = true;
2150        int ret;
2151
2152        if (symbol_conf.use_callchain) {
2153                folded_sign = hist_entry__folded(he);
2154                printed += fprintf(fp, "%c ", folded_sign);
2155        }
2156
2157        hists__for_each_format(browser->hists, fmt) {
2158                if (perf_hpp__should_skip(fmt, he->hists))
2159                        continue;
2160
2161                if (!first) {
2162                        ret = scnprintf(hpp.buf, hpp.size, "  ");
2163                        advance_hpp(&hpp, ret);
2164                } else
2165                        first = false;
2166
2167                ret = fmt->entry(fmt, &hpp, he);
2168                ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2169                advance_hpp(&hpp, ret);
2170        }
2171        printed += fprintf(fp, "%s\n", s);
2172
2173        if (folded_sign == '-')
2174                printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2175
2176        return printed;
2177}
2178
2179
2180static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2181                                                 struct hist_entry *he,
2182                                                 FILE *fp, int level)
2183{
2184        char s[8192];
2185        int printed = 0;
2186        char folded_sign = ' ';
2187        struct perf_hpp hpp = {
2188                .buf = s,
2189                .size = sizeof(s),
2190        };
2191        struct perf_hpp_fmt *fmt;
2192        struct perf_hpp_list_node *fmt_node;
2193        bool first = true;
2194        int ret;
2195        int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2196
2197        printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2198
2199        folded_sign = hist_entry__folded(he);
2200        printed += fprintf(fp, "%c", folded_sign);
2201
2202        /* the first hpp_list_node is for overhead columns */
2203        fmt_node = list_first_entry(&he->hists->hpp_formats,
2204                                    struct perf_hpp_list_node, list);
2205        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2206                if (!first) {
2207                        ret = scnprintf(hpp.buf, hpp.size, "  ");
2208                        advance_hpp(&hpp, ret);
2209                } else
2210                        first = false;
2211
2212                ret = fmt->entry(fmt, &hpp, he);
2213                advance_hpp(&hpp, ret);
2214        }
2215
2216        ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2217        advance_hpp(&hpp, ret);
2218
2219        perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2220                ret = scnprintf(hpp.buf, hpp.size, "  ");
2221                advance_hpp(&hpp, ret);
2222
2223                ret = fmt->entry(fmt, &hpp, he);
2224                advance_hpp(&hpp, ret);
2225        }
2226
2227        printed += fprintf(fp, "%s\n", rtrim(s));
2228
2229        if (he->leaf && folded_sign == '-') {
2230                printed += hist_browser__fprintf_callchain(browser, he, fp,
2231                                                           he->depth + 1);
2232        }
2233
2234        return printed;
2235}
2236
2237static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2238{
2239        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2240                                                   browser->min_pcnt);
2241        int printed = 0;
2242
2243        while (nd) {
2244                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2245
2246                if (symbol_conf.report_hierarchy) {
2247                        printed += hist_browser__fprintf_hierarchy_entry(browser,
2248                                                                         h, fp,
2249                                                                         h->depth);
2250                } else {
2251                        printed += hist_browser__fprintf_entry(browser, h, fp);
2252                }
2253
2254                nd = hists__filter_entries(rb_hierarchy_next(nd),
2255                                           browser->min_pcnt);
2256        }
2257
2258        return printed;
2259}
2260
2261static int hist_browser__dump(struct hist_browser *browser)
2262{
2263        char filename[64];
2264        FILE *fp;
2265
2266        while (1) {
2267                scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2268                if (access(filename, F_OK))
2269                        break;
2270                /*
2271                 * XXX: Just an arbitrary lazy upper limit
2272                 */
2273                if (++browser->print_seq == 8192) {
2274                        ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2275                        return -1;
2276                }
2277        }
2278
2279        fp = fopen(filename, "w");
2280        if (fp == NULL) {
2281                char bf[64];
2282                const char *err = str_error_r(errno, bf, sizeof(bf));
2283                ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2284                return -1;
2285        }
2286
2287        ++browser->print_seq;
2288        hist_browser__fprintf(browser, fp);
2289        fclose(fp);
2290        ui_helpline__fpush("%s written!", filename);
2291
2292        return 0;
2293}
2294
2295void hist_browser__init(struct hist_browser *browser,
2296                        struct hists *hists)
2297{
2298        struct perf_hpp_fmt *fmt;
2299
2300        browser->hists                  = hists;
2301        browser->b.refresh              = hist_browser__refresh;
2302        browser->b.refresh_dimensions   = hist_browser__refresh_dimensions;
2303        browser->b.seek                 = ui_browser__hists_seek;
2304        browser->b.use_navkeypressed    = true;
2305        browser->show_headers           = symbol_conf.show_hist_headers;
2306
2307        if (symbol_conf.report_hierarchy) {
2308                struct perf_hpp_list_node *fmt_node;
2309
2310                /* count overhead columns (in the first node) */
2311                fmt_node = list_first_entry(&hists->hpp_formats,
2312                                            struct perf_hpp_list_node, list);
2313                perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2314                        ++browser->b.columns;
2315
2316                /* add a single column for whole hierarchy sort keys*/
2317                ++browser->b.columns;
2318        } else {
2319                hists__for_each_format(hists, fmt)
2320                        ++browser->b.columns;
2321        }
2322
2323        hists__reset_column_width(hists);
2324}
2325
2326struct hist_browser *hist_browser__new(struct hists *hists)
2327{
2328        struct hist_browser *browser = zalloc(sizeof(*browser));
2329
2330        if (browser)
2331                hist_browser__init(browser, hists);
2332
2333        return browser;
2334}
2335
2336static struct hist_browser *
2337perf_evsel_browser__new(struct perf_evsel *evsel,
2338                        struct hist_browser_timer *hbt,
2339                        struct perf_env *env)
2340{
2341        struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2342
2343        if (browser) {
2344                browser->hbt   = hbt;
2345                browser->env   = env;
2346                browser->title = perf_evsel_browser_title;
2347        }
2348        return browser;
2349}
2350
2351void hist_browser__delete(struct hist_browser *browser)
2352{
2353        free(browser);
2354}
2355
2356static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2357{
2358        return browser->he_selection;
2359}
2360
2361static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2362{
2363        return browser->he_selection->thread;
2364}
2365
2366/* Check whether the browser is for 'top' or 'report' */
2367static inline bool is_report_browser(void *timer)
2368{
2369        return timer == NULL;
2370}
2371
2372static int perf_evsel_browser_title(struct hist_browser *browser,
2373                                char *bf, size_t size)
2374{
2375        struct hist_browser_timer *hbt = browser->hbt;
2376        struct hists *hists = browser->hists;
2377        char unit;
2378        int printed;
2379        const struct dso *dso = hists->dso_filter;
2380        const struct thread *thread = hists->thread_filter;
2381        int socket_id = hists->socket_filter;
2382        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2383        u64 nr_events = hists->stats.total_period;
2384        struct perf_evsel *evsel = hists_to_evsel(hists);
2385        const char *ev_name = perf_evsel__name(evsel);
2386        char buf[512];
2387        size_t buflen = sizeof(buf);
2388        char ref[30] = " show reference callgraph, ";
2389        bool enable_ref = false;
2390
2391        if (symbol_conf.filter_relative) {
2392                nr_samples = hists->stats.nr_non_filtered_samples;
2393                nr_events = hists->stats.total_non_filtered_period;
2394        }
2395
2396        if (perf_evsel__is_group_event(evsel)) {
2397                struct perf_evsel *pos;
2398
2399                perf_evsel__group_desc(evsel, buf, buflen);
2400                ev_name = buf;
2401
2402                for_each_group_member(pos, evsel) {
2403                        struct hists *pos_hists = evsel__hists(pos);
2404
2405                        if (symbol_conf.filter_relative) {
2406                                nr_samples += pos_hists->stats.nr_non_filtered_samples;
2407                                nr_events += pos_hists->stats.total_non_filtered_period;
2408                        } else {
2409                                nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2410                                nr_events += pos_hists->stats.total_period;
2411                        }
2412                }
2413        }
2414
2415        if (symbol_conf.show_ref_callgraph &&
2416            strstr(ev_name, "call-graph=no"))
2417                enable_ref = true;
2418        nr_samples = convert_unit(nr_samples, &unit);
2419        printed = scnprintf(bf, size,
2420                           "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2421                           nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2422
2423
2424        if (hists->uid_filter_str)
2425                printed += snprintf(bf + printed, size - printed,
2426                                    ", UID: %s", hists->uid_filter_str);
2427        if (thread) {
2428                if (hists__has(hists, thread)) {
2429                        printed += scnprintf(bf + printed, size - printed,
2430                                    ", Thread: %s(%d)",
2431                                     (thread->comm_set ? thread__comm_str(thread) : ""),
2432                                    thread->tid);
2433                } else {
2434                        printed += scnprintf(bf + printed, size - printed,
2435                                    ", Thread: %s",
2436                                     (thread->comm_set ? thread__comm_str(thread) : ""));
2437                }
2438        }
2439        if (dso)
2440                printed += scnprintf(bf + printed, size - printed,
2441                                    ", DSO: %s", dso->short_name);
2442        if (socket_id > -1)
2443                printed += scnprintf(bf + printed, size - printed,
2444                                    ", Processor Socket: %d", socket_id);
2445        if (!is_report_browser(hbt)) {
2446                struct perf_top *top = hbt->arg;
2447
2448                if (top->zero)
2449                        printed += scnprintf(bf + printed, size - printed, " [z]");
2450        }
2451
2452        return printed;
2453}
2454
2455static inline void free_popup_options(char **options, int n)
2456{
2457        int i;
2458
2459        for (i = 0; i < n; ++i)
2460                zfree(&options[i]);
2461}
2462
2463/*
2464 * Only runtime switching of perf data file will make "input_name" point
2465 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2466 * whether we need to call free() for current "input_name" during the switch.
2467 */
2468static bool is_input_name_malloced = false;
2469
2470static int switch_data_file(void)
2471{
2472        char *pwd, *options[32], *abs_path[32], *tmp;
2473        DIR *pwd_dir;
2474        int nr_options = 0, choice = -1, ret = -1;
2475        struct dirent *dent;
2476
2477        pwd = getenv("PWD");
2478        if (!pwd)
2479                return ret;
2480
2481        pwd_dir = opendir(pwd);
2482        if (!pwd_dir)
2483                return ret;
2484
2485        memset(options, 0, sizeof(options));
2486        memset(abs_path, 0, sizeof(abs_path));
2487
2488        while ((dent = readdir(pwd_dir))) {
2489                char path[PATH_MAX];
2490                u64 magic;
2491                char *name = dent->d_name;
2492                FILE *file;
2493
2494                if (!(dent->d_type == DT_REG))
2495                        continue;
2496
2497                snprintf(path, sizeof(path), "%s/%s", pwd, name);
2498
2499                file = fopen(path, "r");
2500                if (!file)
2501                        continue;
2502
2503                if (fread(&magic, 1, 8, file) < 8)
2504                        goto close_file_and_continue;
2505
2506                if (is_perf_magic(magic)) {
2507                        options[nr_options] = strdup(name);
2508                        if (!options[nr_options])
2509                                goto close_file_and_continue;
2510
2511                        abs_path[nr_options] = strdup(path);
2512                        if (!abs_path[nr_options]) {
2513                                zfree(&options[nr_options]);
2514                                ui__warning("Can't search all data files due to memory shortage.\n");
2515                                fclose(file);
2516                                break;
2517                        }
2518
2519                        nr_options++;
2520                }
2521
2522close_file_and_continue:
2523                fclose(file);
2524                if (nr_options >= 32) {
2525                        ui__warning("Too many perf data files in PWD!\n"
2526                                    "Only the first 32 files will be listed.\n");
2527                        break;
2528                }
2529        }
2530        closedir(pwd_dir);
2531
2532        if (nr_options) {
2533                choice = ui__popup_menu(nr_options, options);
2534                if (choice < nr_options && choice >= 0) {
2535                        tmp = strdup(abs_path[choice]);
2536                        if (tmp) {
2537                                if (is_input_name_malloced)
2538                                        free((void *)input_name);
2539                                input_name = tmp;
2540                                is_input_name_malloced = true;
2541                                ret = 0;
2542                        } else
2543                                ui__warning("Data switch failed due to memory shortage!\n");
2544                }
2545        }
2546
2547        free_popup_options(options, nr_options);
2548        free_popup_options(abs_path, nr_options);
2549        return ret;
2550}
2551
2552struct popup_action {
2553        struct thread           *thread;
2554        struct map_symbol       ms;
2555        int                     socket;
2556
2557        int (*fn)(struct hist_browser *browser, struct popup_action *act);
2558};
2559
2560static int
2561do_annotate(struct hist_browser *browser, struct popup_action *act)
2562{
2563        struct perf_evsel *evsel;
2564        struct annotation *notes;
2565        struct hist_entry *he;
2566        int err;
2567
2568        if (!objdump_path && perf_env__lookup_objdump(browser->env))
2569                return 0;
2570
2571        notes = symbol__annotation(act->ms.sym);
2572        if (!notes->src)
2573                return 0;
2574
2575        evsel = hists_to_evsel(browser->hists);
2576        err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2577        he = hist_browser__selected_entry(browser);
2578        /*
2579         * offer option to annotate the other branch source or target
2580         * (if they exists) when returning from annotate
2581         */
2582        if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2583                return 1;
2584
2585        ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2586        if (err)
2587                ui_browser__handle_resize(&browser->b);
2588        return 0;
2589}
2590
2591static int
2592add_annotate_opt(struct hist_browser *browser __maybe_unused,
2593                 struct popup_action *act, char **optstr,
2594                 struct map *map, struct symbol *sym)
2595{
2596        if (sym == NULL || map->dso->annotate_warned)
2597                return 0;
2598
2599        if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2600                return 0;
2601
2602        act->ms.map = map;
2603        act->ms.sym = sym;
2604        act->fn = do_annotate;
2605        return 1;
2606}
2607
2608static int
2609do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2610{
2611        struct thread *thread = act->thread;
2612
2613        if ((!hists__has(browser->hists, thread) &&
2614             !hists__has(browser->hists, comm)) || thread == NULL)
2615                return 0;
2616
2617        if (browser->hists->thread_filter) {
2618                pstack__remove(browser->pstack, &browser->hists->thread_filter);
2619                perf_hpp__set_elide(HISTC_THREAD, false);
2620                thread__zput(browser->hists->thread_filter);
2621                ui_helpline__pop();
2622        } else {
2623                if (hists__has(browser->hists, thread)) {
2624                        ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2625                                           thread->comm_set ? thread__comm_str(thread) : "",
2626                                           thread->tid);
2627                } else {
2628                        ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2629                                           thread->comm_set ? thread__comm_str(thread) : "");
2630                }
2631
2632                browser->hists->thread_filter = thread__get(thread);
2633                perf_hpp__set_elide(HISTC_THREAD, false);
2634                pstack__push(browser->pstack, &browser->hists->thread_filter);
2635        }
2636
2637        hists__filter_by_thread(browser->hists);
2638        hist_browser__reset(browser);
2639        return 0;
2640}
2641
2642static int
2643add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2644               char **optstr, struct thread *thread)
2645{
2646        int ret;
2647
2648        if ((!hists__has(browser->hists, thread) &&
2649             !hists__has(browser->hists, comm)) || thread == NULL)
2650                return 0;
2651
2652        if (hists__has(browser->hists, thread)) {
2653                ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2654                               browser->hists->thread_filter ? "out of" : "into",
2655                               thread->comm_set ? thread__comm_str(thread) : "",
2656                               thread->tid);
2657        } else {
2658                ret = asprintf(optstr, "Zoom %s %s thread",
2659                               browser->hists->thread_filter ? "out of" : "into",
2660                               thread->comm_set ? thread__comm_str(thread) : "");
2661        }
2662        if (ret < 0)
2663                return 0;
2664
2665        act->thread = thread;
2666        act->fn = do_zoom_thread;
2667        return 1;
2668}
2669
2670static int
2671do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2672{
2673        struct map *map = act->ms.map;
2674
2675        if (!hists__has(browser->hists, dso) || map == NULL)
2676                return 0;
2677
2678        if (browser->hists->dso_filter) {
2679                pstack__remove(browser->pstack, &browser->hists->dso_filter);
2680                perf_hpp__set_elide(HISTC_DSO, false);
2681                browser->hists->dso_filter = NULL;
2682                ui_helpline__pop();
2683        } else {
2684                ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2685                                   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2686                browser->hists->dso_filter = map->dso;
2687                perf_hpp__set_elide(HISTC_DSO, true);
2688                pstack__push(browser->pstack, &browser->hists->dso_filter);
2689        }
2690
2691        hists__filter_by_dso(browser->hists);
2692        hist_browser__reset(browser);
2693        return 0;
2694}
2695
2696static int
2697add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2698            char **optstr, struct map *map)
2699{
2700        if (!hists__has(browser->hists, dso) || map == NULL)
2701                return 0;
2702
2703        if (asprintf(optstr, "Zoom %s %s DSO",
2704                     browser->hists->dso_filter ? "out of" : "into",
2705                     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2706                return 0;
2707
2708        act->ms.map = map;
2709        act->fn = do_zoom_dso;
2710        return 1;
2711}
2712
2713static int
2714do_browse_map(struct hist_browser *browser __maybe_unused,
2715              struct popup_action *act)
2716{
2717        map__browse(act->ms.map);
2718        return 0;
2719}
2720
2721static int
2722add_map_opt(struct hist_browser *browser,
2723            struct popup_action *act, char **optstr, struct map *map)
2724{
2725        if (!hists__has(browser->hists, dso) || map == NULL)
2726                return 0;
2727
2728        if (asprintf(optstr, "Browse map details") < 0)
2729                return 0;
2730
2731        act->ms.map = map;
2732        act->fn = do_browse_map;
2733        return 1;
2734}
2735
2736static int
2737do_run_script(struct hist_browser *browser __maybe_unused,
2738              struct popup_action *act)
2739{
2740        char script_opt[64];
2741        memset(script_opt, 0, sizeof(script_opt));
2742
2743        if (act->thread) {
2744                scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2745                          thread__comm_str(act->thread));
2746        } else if (act->ms.sym) {
2747                scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2748                          act->ms.sym->name);
2749        }
2750
2751        script_browse(script_opt);
2752        return 0;
2753}
2754
2755static int
2756add_script_opt(struct hist_browser *browser __maybe_unused,
2757               struct popup_action *act, char **optstr,
2758               struct thread *thread, struct symbol *sym)
2759{
2760        if (thread) {
2761                if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2762                             thread__comm_str(thread)) < 0)
2763                        return 0;
2764        } else if (sym) {
2765                if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2766                             sym->name) < 0)
2767                        return 0;
2768        } else {
2769                if (asprintf(optstr, "Run scripts for all samples") < 0)
2770                        return 0;
2771        }
2772
2773        act->thread = thread;
2774        act->ms.sym = sym;
2775        act->fn = do_run_script;
2776        return 1;
2777}
2778
2779static int
2780do_switch_data(struct hist_browser *browser __maybe_unused,
2781               struct popup_action *act __maybe_unused)
2782{
2783        if (switch_data_file()) {
2784                ui__warning("Won't switch the data files due to\n"
2785                            "no valid data file get selected!\n");
2786                return 0;
2787        }
2788
2789        return K_SWITCH_INPUT_DATA;
2790}
2791
2792static int
2793add_switch_opt(struct hist_browser *browser,
2794               struct popup_action *act, char **optstr)
2795{
2796        if (!is_report_browser(browser->hbt))
2797                return 0;
2798
2799        if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2800                return 0;
2801
2802        act->fn = do_switch_data;
2803        return 1;
2804}
2805
2806static int
2807do_exit_browser(struct hist_browser *browser __maybe_unused,
2808                struct popup_action *act __maybe_unused)
2809{
2810        return 0;
2811}
2812
2813static int
2814add_exit_opt(struct hist_browser *browser __maybe_unused,
2815             struct popup_action *act, char **optstr)
2816{
2817        if (asprintf(optstr, "Exit") < 0)
2818                return 0;
2819
2820        act->fn = do_exit_browser;
2821        return 1;
2822}
2823
2824static int
2825do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2826{
2827        if (!hists__has(browser->hists, socket) || act->socket < 0)
2828                return 0;
2829
2830        if (browser->hists->socket_filter > -1) {
2831                pstack__remove(browser->pstack, &browser->hists->socket_filter);
2832                browser->hists->socket_filter = -1;
2833                perf_hpp__set_elide(HISTC_SOCKET, false);
2834        } else {
2835                browser->hists->socket_filter = act->socket;
2836                perf_hpp__set_elide(HISTC_SOCKET, true);
2837                pstack__push(browser->pstack, &browser->hists->socket_filter);
2838        }
2839
2840        hists__filter_by_socket(browser->hists);
2841        hist_browser__reset(browser);
2842        return 0;
2843}
2844
2845static int
2846add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2847               char **optstr, int socket_id)
2848{
2849        if (!hists__has(browser->hists, socket) || socket_id < 0)
2850                return 0;
2851
2852        if (asprintf(optstr, "Zoom %s Processor Socket %d",
2853                     (browser->hists->socket_filter > -1) ? "out of" : "into",
2854                     socket_id) < 0)
2855                return 0;
2856
2857        act->socket = socket_id;
2858        act->fn = do_zoom_socket;
2859        return 1;
2860}
2861
2862static void hist_browser__update_nr_entries(struct hist_browser *hb)
2863{
2864        u64 nr_entries = 0;
2865        struct rb_node *nd = rb_first(&hb->hists->entries);
2866
2867        if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2868                hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2869                return;
2870        }
2871
2872        while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2873                nr_entries++;
2874                nd = rb_hierarchy_next(nd);
2875        }
2876
2877        hb->nr_non_filtered_entries = nr_entries;
2878        hb->nr_hierarchy_entries = nr_entries;
2879}
2880
2881static void hist_browser__update_percent_limit(struct hist_browser *hb,
2882                                               double percent)
2883{
2884        struct hist_entry *he;
2885        struct rb_node *nd = rb_first(&hb->hists->entries);
2886        u64 total = hists__total_period(hb->hists);
2887        u64 min_callchain_hits = total * (percent / 100);
2888
2889        hb->min_pcnt = callchain_param.min_percent = percent;
2890
2891        while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2892                he = rb_entry(nd, struct hist_entry, rb_node);
2893
2894                if (he->has_no_entry) {
2895                        he->has_no_entry = false;
2896                        he->nr_rows = 0;
2897                }
2898
2899                if (!he->leaf || !symbol_conf.use_callchain)
2900                        goto next;
2901
2902                if (callchain_param.mode == CHAIN_GRAPH_REL) {
2903                        total = he->stat.period;
2904
2905                        if (symbol_conf.cumulate_callchain)
2906                                total = he->stat_acc->period;
2907
2908                        min_callchain_hits = total * (percent / 100);
2909                }
2910
2911                callchain_param.sort(&he->sorted_chain, he->callchain,
2912                                     min_callchain_hits, &callchain_param);
2913
2914next:
2915                nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2916
2917                /* force to re-evaluate folding state of callchains */
2918                he->init_have_children = false;
2919                hist_entry__set_folding(he, hb, false);
2920        }
2921}
2922
2923static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2924                                    const char *helpline,
2925                                    bool left_exits,
2926                                    struct hist_browser_timer *hbt,
2927                                    float min_pcnt,
2928                                    struct perf_env *env)
2929{
2930        struct hists *hists = evsel__hists(evsel);
2931        struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2932        struct branch_info *bi;
2933#define MAX_OPTIONS  16
2934        char *options[MAX_OPTIONS];
2935        struct popup_action actions[MAX_OPTIONS];
2936        int nr_options = 0;
2937        int key = -1;
2938        char buf[64];
2939        int delay_secs = hbt ? hbt->refresh : 0;
2940
2941#define HIST_BROWSER_HELP_COMMON                                        \
2942        "h/?/F1        Show this window\n"                              \
2943        "UP/DOWN/PGUP\n"                                                \
2944        "PGDN/SPACE    Navigate\n"                                      \
2945        "q/ESC/CTRL+C  Exit browser\n\n"                                \
2946        "For multiple event sessions:\n\n"                              \
2947        "TAB/UNTAB     Switch events\n\n"                               \
2948        "For symbolic views (--sort has sym):\n\n"                      \
2949        "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2950        "ESC           Zoom out\n"                                      \
2951        "a             Annotate current symbol\n"                       \
2952        "C             Collapse all callchains\n"                       \
2953        "d             Zoom into current DSO\n"                         \
2954        "E             Expand all callchains\n"                         \
2955        "F             Toggle percentage of filtered entries\n"         \
2956        "H             Display column headers\n"                        \
2957        "L             Change percent limit\n"                          \
2958        "m             Display context menu\n"                          \
2959        "S             Zoom into current Processor Socket\n"            \
2960
2961        /* help messages are sorted by lexical order of the hotkey */
2962        const char report_help[] = HIST_BROWSER_HELP_COMMON
2963        "i             Show header information\n"
2964        "P             Print histograms to perf.hist.N\n"
2965        "r             Run available scripts\n"
2966        "s             Switch to another data file in PWD\n"
2967        "t             Zoom into current Thread\n"
2968        "V             Verbose (DSO names in callchains, etc)\n"
2969        "/             Filter symbol by name";
2970        const char top_help[] = HIST_BROWSER_HELP_COMMON
2971        "P             Print histograms to perf.hist.N\n"
2972        "t             Zoom into current Thread\n"
2973        "V             Verbose (DSO names in callchains, etc)\n"
2974        "z             Toggle zeroing of samples\n"
2975        "f             Enable/Disable events\n"
2976        "/             Filter symbol by name";
2977
2978        if (browser == NULL)
2979                return -1;
2980
2981        /* reset abort key so that it can get Ctrl-C as a key */
2982        SLang_reset_tty();
2983        SLang_init_tty(0, 0, 0);
2984
2985        if (min_pcnt)
2986                browser->min_pcnt = min_pcnt;
2987        hist_browser__update_nr_entries(browser);
2988
2989        browser->pstack = pstack__new(3);
2990        if (browser->pstack == NULL)
2991                goto out;
2992
2993        ui_helpline__push(helpline);
2994
2995        memset(options, 0, sizeof(options));
2996        memset(actions, 0, sizeof(actions));
2997
2998        if (symbol_conf.col_width_list_str)
2999                perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3000
3001        while (1) {
3002                struct thread *thread = NULL;
3003                struct map *map = NULL;
3004                int choice = 0;
3005                int socked_id = -1;
3006
3007                nr_options = 0;
3008
3009                key = hist_browser__run(browser, helpline);
3010
3011                if (browser->he_selection != NULL) {
3012                        thread = hist_browser__selected_thread(browser);
3013                        map = browser->selection->map;
3014                        socked_id = browser->he_selection->socket;
3015                }
3016                switch (key) {
3017                case K_TAB:
3018                case K_UNTAB:
3019                        if (nr_events == 1)
3020                                continue;
3021                        /*
3022                         * Exit the browser, let hists__browser_tree
3023                         * go to the next or previous
3024                         */
3025                        goto out_free_stack;
3026                case 'a':
3027                        if (!hists__has(hists, sym)) {
3028                                ui_browser__warning(&browser->b, delay_secs * 2,
3029                        "Annotation is only available for symbolic views, "
3030                        "include \"sym*\" in --sort to use it.");
3031                                continue;
3032                        }
3033
3034                        if (browser->selection == NULL ||
3035                            browser->selection->sym == NULL ||
3036                            browser->selection->map->dso->annotate_warned)
3037                                continue;
3038
3039                        actions->ms.map = browser->selection->map;
3040                        actions->ms.sym = browser->selection->sym;
3041                        do_annotate(browser, actions);
3042                        continue;
3043                case 'P':
3044                        hist_browser__dump(browser);
3045                        continue;
3046                case 'd':
3047                        actions->ms.map = map;
3048                        do_zoom_dso(browser, actions);
3049                        continue;
3050                case 'V':
3051                        verbose = (verbose + 1) % 4;
3052                        browser->show_dso = verbose > 0;
3053                        ui_helpline__fpush("Verbosity level set to %d\n",
3054                                           verbose);
3055                        continue;
3056                case 't':
3057                        actions->thread = thread;
3058                        do_zoom_thread(browser, actions);
3059                        continue;
3060                case 'S':
3061                        actions->socket = socked_id;
3062                        do_zoom_socket(browser, actions);
3063                        continue;
3064                case '/':
3065                        if (ui_browser__input_window("Symbol to show",
3066                                        "Please enter the name of symbol you want to see.\n"
3067                                        "To remove the filter later, press / + ENTER.",
3068                                        buf, "ENTER: OK, ESC: Cancel",
3069                                        delay_secs * 2) == K_ENTER) {
3070                                hists->symbol_filter_str = *buf ? buf : NULL;
3071                                hists__filter_by_symbol(hists);
3072                                hist_browser__reset(browser);
3073                        }
3074                        continue;
3075                case 'r':
3076                        if (is_report_browser(hbt)) {
3077                                actions->thread = NULL;
3078                                actions->ms.sym = NULL;
3079                                do_run_script(browser, actions);
3080                        }
3081                        continue;
3082                case 's':
3083                        if (is_report_browser(hbt)) {
3084                                key = do_switch_data(browser, actions);
3085                                if (key == K_SWITCH_INPUT_DATA)
3086                                        goto out_free_stack;
3087                        }
3088                        continue;
3089                case 'i':
3090                        /* env->arch is NULL for live-mode (i.e. perf top) */
3091                        if (env->arch)
3092                                tui__header_window(env);
3093                        continue;
3094                case 'F':
3095                        symbol_conf.filter_relative ^= 1;
3096                        continue;
3097                case 'z':
3098                        if (!is_report_browser(hbt)) {
3099                                struct perf_top *top = hbt->arg;
3100
3101                                top->zero = !top->zero;
3102                        }
3103                        continue;
3104                case 'L':
3105                        if (ui_browser__input_window("Percent Limit",
3106                                        "Please enter the value you want to hide entries under that percent.",
3107                                        buf, "ENTER: OK, ESC: Cancel",
3108                                        delay_secs * 2) == K_ENTER) {
3109                                char *end;
3110                                double new_percent = strtod(buf, &end);
3111
3112                                if (new_percent < 0 || new_percent > 100) {
3113                                        ui_browser__warning(&browser->b, delay_secs * 2,
3114                                                "Invalid percent: %.2f", new_percent);
3115                                        continue;
3116                                }
3117
3118                                hist_browser__update_percent_limit(browser, new_percent);
3119                                hist_browser__reset(browser);
3120                        }
3121                        continue;
3122                case K_F1:
3123                case 'h':
3124                case '?':
3125                        ui_browser__help_window(&browser->b,
3126                                is_report_browser(hbt) ? report_help : top_help);
3127                        continue;
3128                case K_ENTER:
3129                case K_RIGHT:
3130                case 'm':
3131                        /* menu */
3132                        break;
3133                case K_ESC:
3134                case K_LEFT: {
3135                        const void *top;
3136
3137                        if (pstack__empty(browser->pstack)) {
3138                                /*
3139                                 * Go back to the perf_evsel_menu__run or other user
3140                                 */
3141                                if (left_exits)
3142                                        goto out_free_stack;
3143
3144                                if (key == K_ESC &&
3145                                    ui_browser__dialog_yesno(&browser->b,
3146                                                             "Do you really want to exit?"))
3147                                        goto out_free_stack;
3148
3149                                continue;
3150                        }
3151                        top = pstack__peek(browser->pstack);
3152                        if (top == &browser->hists->dso_filter) {
3153                                /*
3154                                 * No need to set actions->dso here since
3155                                 * it's just to remove the current filter.
3156                                 * Ditto for thread below.
3157                                 */
3158                                do_zoom_dso(browser, actions);
3159                        } else if (top == &browser->hists->thread_filter) {
3160                                do_zoom_thread(browser, actions);
3161                        } else if (top == &browser->hists->socket_filter) {
3162                                do_zoom_socket(browser, actions);
3163                        }
3164                        continue;
3165                }
3166                case 'q':
3167                case CTRL('c'):
3168                        goto out_free_stack;
3169                case 'f':
3170                        if (!is_report_browser(hbt)) {
3171                                struct perf_top *top = hbt->arg;
3172
3173                                perf_evlist__toggle_enable(top->evlist);
3174                                /*
3175                                 * No need to refresh, resort/decay histogram
3176                                 * entries if we are not collecting samples:
3177                                 */
3178                                if (top->evlist->enabled) {
3179                                        helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3180                                        hbt->refresh = delay_secs;
3181                                } else {
3182                                        helpline = "Press 'f' again to re-enable the events";
3183                                        hbt->refresh = 0;
3184                                }
3185                                continue;
3186                        }
3187                        /* Fall thru */
3188                default:
3189                        helpline = "Press '?' for help on key bindings";
3190                        continue;
3191                }
3192
3193                if (!hists__has(hists, sym) || browser->selection == NULL)
3194                        goto skip_annotation;
3195
3196                if (sort__mode == SORT_MODE__BRANCH) {
3197                        bi = browser->he_selection->branch_info;
3198
3199                        if (bi == NULL)
3200                                goto skip_annotation;
3201
3202                        nr_options += add_annotate_opt(browser,
3203                                                       &actions[nr_options],
3204                                                       &options[nr_options],
3205                                                       bi->from.map,
3206                                                       bi->from.sym);
3207                        if (bi->to.sym != bi->from.sym)
3208                                nr_options += add_annotate_opt(browser,
3209                                                        &actions[nr_options],
3210                                                        &options[nr_options],
3211                                                        bi->to.map,
3212                                                        bi->to.sym);
3213                } else {
3214                        nr_options += add_annotate_opt(browser,
3215                                                       &actions[nr_options],
3216                                                       &options[nr_options],
3217                                                       browser->selection->map,
3218                                                       browser->selection->sym);
3219                }
3220skip_annotation:
3221                nr_options += add_thread_opt(browser, &actions[nr_options],
3222                                             &options[nr_options], thread);
3223                nr_options += add_dso_opt(browser, &actions[nr_options],
3224                                          &options[nr_options], map);
3225                nr_options += add_map_opt(browser, &actions[nr_options],
3226                                          &options[nr_options],
3227                                          browser->selection ?
3228                                                browser->selection->map : NULL);
3229                nr_options += add_socket_opt(browser, &actions[nr_options],
3230                                             &options[nr_options],
3231                                             socked_id);
3232                /* perf script support */
3233                if (!is_report_browser(hbt))
3234                        goto skip_scripting;
3235
3236                if (browser->he_selection) {
3237                        if (hists__has(hists, thread) && thread) {
3238                                nr_options += add_script_opt(browser,
3239                                                             &actions[nr_options],
3240                                                             &options[nr_options],
3241                                                             thread, NULL);
3242                        }
3243                        /*
3244                         * Note that browser->selection != NULL
3245                         * when browser->he_selection is not NULL,
3246                         * so we don't need to check browser->selection
3247                         * before fetching browser->selection->sym like what
3248                         * we do before fetching browser->selection->map.
3249                         *
3250                         * See hist_browser__show_entry.
3251                         */
3252                        if (hists__has(hists, sym) && browser->selection->sym) {
3253                                nr_options += add_script_opt(browser,
3254                                                             &actions[nr_options],
3255                                                             &options[nr_options],
3256                                                             NULL, browser->selection->sym);
3257                        }
3258                }
3259                nr_options += add_script_opt(browser, &actions[nr_options],
3260                                             &options[nr_options], NULL, NULL);
3261                nr_options += add_switch_opt(browser, &actions[nr_options],
3262                                             &options[nr_options]);
3263skip_scripting:
3264                nr_options += add_exit_opt(browser, &actions[nr_options],
3265                                           &options[nr_options]);
3266
3267                do {
3268                        struct popup_action *act;
3269
3270                        choice = ui__popup_menu(nr_options, options);
3271                        if (choice == -1 || choice >= nr_options)
3272                                break;
3273
3274                        act = &actions[choice];
3275                        key = act->fn(browser, act);
3276                } while (key == 1);
3277
3278                if (key == K_SWITCH_INPUT_DATA)
3279                        break;
3280        }
3281out_free_stack:
3282        pstack__delete(browser->pstack);
3283out:
3284        hist_browser__delete(browser);
3285        free_popup_options(options, MAX_OPTIONS);
3286        return key;
3287}
3288
3289struct perf_evsel_menu {
3290        struct ui_browser b;
3291        struct perf_evsel *selection;
3292        bool lost_events, lost_events_warned;
3293        float min_pcnt;
3294        struct perf_env *env;
3295};
3296
3297static void perf_evsel_menu__write(struct ui_browser *browser,
3298                                   void *entry, int row)
3299{
3300        struct perf_evsel_menu *menu = container_of(browser,
3301                                                    struct perf_evsel_menu, b);
3302        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3303        struct hists *hists = evsel__hists(evsel);
3304        bool current_entry = ui_browser__is_current_entry(browser, row);
3305        unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3306        const char *ev_name = perf_evsel__name(evsel);
3307        char bf[256], unit;
3308        const char *warn = " ";
3309        size_t printed;
3310
3311        ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3312                                                       HE_COLORSET_NORMAL);
3313
3314        if (perf_evsel__is_group_event(evsel)) {
3315                struct perf_evsel *pos;
3316
3317                ev_name = perf_evsel__group_name(evsel);
3318
3319                for_each_group_member(pos, evsel) {
3320                        struct hists *pos_hists = evsel__hists(pos);
3321                        nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3322                }
3323        }
3324
3325        nr_events = convert_unit(nr_events, &unit);
3326        printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3327                           unit, unit == ' ' ? "" : " ", ev_name);
3328        ui_browser__printf(browser, "%s", bf);
3329
3330        nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3331        if (nr_events != 0) {
3332                menu->lost_events = true;
3333                if (!current_entry)
3334                        ui_browser__set_color(browser, HE_COLORSET_TOP);
3335                nr_events = convert_unit(nr_events, &unit);
3336                printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3337                                     nr_events, unit, unit == ' ' ? "" : " ");
3338                warn = bf;
3339        }
3340
3341        ui_browser__write_nstring(browser, warn, browser->width - printed);
3342
3343        if (current_entry)
3344                menu->selection = evsel;
3345}
3346
3347static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3348                                int nr_events, const char *help,
3349                                struct hist_browser_timer *hbt)
3350{
3351        struct perf_evlist *evlist = menu->b.priv;
3352        struct perf_evsel *pos;
3353        const char *title = "Available samples";
3354        int delay_secs = hbt ? hbt->refresh : 0;
3355        int key;
3356
3357        if (ui_browser__show(&menu->b, title,
3358                             "ESC: exit, ENTER|->: Browse histograms") < 0)
3359                return -1;
3360
3361        while (1) {
3362                key = ui_browser__run(&menu->b, delay_secs);
3363
3364                switch (key) {
3365                case K_TIMER:
3366                        hbt->timer(hbt->arg);
3367
3368                        if (!menu->lost_events_warned && menu->lost_events) {
3369                                ui_browser__warn_lost_events(&menu->b);
3370                                menu->lost_events_warned = true;
3371                        }
3372                        continue;
3373                case K_RIGHT:
3374                case K_ENTER:
3375                        if (!menu->selection)
3376                                continue;
3377                        pos = menu->selection;
3378browse_hists:
3379                        perf_evlist__set_selected(evlist, pos);
3380                        /*
3381                         * Give the calling tool a chance to populate the non
3382                         * default evsel resorted hists tree.
3383                         */
3384                        if (hbt)
3385                                hbt->timer(hbt->arg);
3386                        key = perf_evsel__hists_browse(pos, nr_events, help,
3387                                                       true, hbt,
3388                                                       menu->min_pcnt,
3389                                                       menu->env);
3390                        ui_browser__show_title(&menu->b, title);
3391                        switch (key) {
3392                        case K_TAB:
3393                                if (pos->node.next == &evlist->entries)
3394                                        pos = perf_evlist__first(evlist);
3395                                else
3396                                        pos = perf_evsel__next(pos);
3397                                goto browse_hists;
3398                        case K_UNTAB:
3399                                if (pos->node.prev == &evlist->entries)
3400                                        pos = perf_evlist__last(evlist);
3401                                else
3402                                        pos = perf_evsel__prev(pos);
3403                                goto browse_hists;
3404                        case K_SWITCH_INPUT_DATA:
3405                        case 'q':
3406                        case CTRL('c'):
3407                                goto out;
3408                        case K_ESC:
3409                        default:
3410                                continue;
3411                        }
3412                case K_LEFT:
3413                        continue;
3414                case K_ESC:
3415                        if (!ui_browser__dialog_yesno(&menu->b,
3416                                               "Do you really want to exit?"))
3417                                continue;
3418                        /* Fall thru */
3419                case 'q':
3420                case CTRL('c'):
3421                        goto out;
3422                default:
3423                        continue;
3424                }
3425        }
3426
3427out:
3428        ui_browser__hide(&menu->b);
3429        return key;
3430}
3431
3432static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3433                                 void *entry)
3434{
3435        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3436
3437        if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3438                return true;
3439
3440        return false;
3441}
3442
3443static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3444                                           int nr_entries, const char *help,
3445                                           struct hist_browser_timer *hbt,
3446                                           float min_pcnt,
3447                                           struct perf_env *env)
3448{
3449        struct perf_evsel *pos;
3450        struct perf_evsel_menu menu = {
3451                .b = {
3452                        .entries    = &evlist->entries,
3453                        .refresh    = ui_browser__list_head_refresh,
3454                        .seek       = ui_browser__list_head_seek,
3455                        .write      = perf_evsel_menu__write,
3456                        .filter     = filter_group_entries,
3457                        .nr_entries = nr_entries,
3458                        .priv       = evlist,
3459                },
3460                .min_pcnt = min_pcnt,
3461                .env = env,
3462        };
3463
3464        ui_helpline__push("Press ESC to exit");
3465
3466        evlist__for_each_entry(evlist, pos) {
3467                const char *ev_name = perf_evsel__name(pos);
3468                size_t line_len = strlen(ev_name) + 7;
3469
3470                if (menu.b.width < line_len)
3471                        menu.b.width = line_len;
3472        }
3473
3474        return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3475}
3476
3477int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3478                                  struct hist_browser_timer *hbt,
3479                                  float min_pcnt,
3480                                  struct perf_env *env)
3481{
3482        int nr_entries = evlist->nr_entries;
3483
3484single_entry:
3485        if (nr_entries == 1) {
3486                struct perf_evsel *first = perf_evlist__first(evlist);
3487
3488                return perf_evsel__hists_browse(first, nr_entries, help,
3489                                                false, hbt, min_pcnt,
3490                                                env);
3491        }
3492
3493        if (symbol_conf.event_group) {
3494                struct perf_evsel *pos;
3495
3496                nr_entries = 0;
3497                evlist__for_each_entry(evlist, pos) {
3498                        if (perf_evsel__is_group_leader(pos))
3499                                nr_entries++;
3500                }
3501
3502                if (nr_entries == 1)
3503                        goto single_entry;
3504        }
3505
3506        return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3507                                               hbt, min_pcnt, env);
3508}
3509