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