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