linux/tools/perf/ui/browsers/hists.c
<<
>>
Prefs
   1#include <stdio.h>
   2#include <stdlib.h>
   3#include <string.h>
   4#include <linux/rbtree.h>
   5
   6#include "../../util/evsel.h"
   7#include "../../util/evlist.h"
   8#include "../../util/hist.h"
   9#include "../../util/pstack.h"
  10#include "../../util/sort.h"
  11#include "../../util/util.h"
  12#include "../../util/top.h"
  13#include "../../arch/common.h"
  14
  15#include "../browser.h"
  16#include "../helpline.h"
  17#include "../util.h"
  18#include "../ui.h"
  19#include "map.h"
  20#include "annotate.h"
  21
  22struct hist_browser {
  23        struct ui_browser   b;
  24        struct hists        *hists;
  25        struct hist_entry   *he_selection;
  26        struct map_symbol   *selection;
  27        struct hist_browser_timer *hbt;
  28        struct pstack       *pstack;
  29        struct perf_env *env;
  30        int                  print_seq;
  31        bool                 show_dso;
  32        bool                 show_headers;
  33        float                min_pcnt;
  34        u64                  nr_non_filtered_entries;
  35        u64                  nr_callchain_rows;
  36};
  37
  38extern void hist_browser__init_hpp(void);
  39
  40static int hists__browser_title(struct hists *hists,
  41                                struct hist_browser_timer *hbt,
  42                                char *bf, size_t size);
  43static void hist_browser__update_nr_entries(struct hist_browser *hb);
  44
  45static struct rb_node *hists__filter_entries(struct rb_node *nd,
  46                                             float min_pcnt);
  47
  48static bool hist_browser__has_filter(struct hist_browser *hb)
  49{
  50        return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
  51}
  52
  53static int hist_browser__get_folding(struct hist_browser *browser)
  54{
  55        struct rb_node *nd;
  56        struct hists *hists = browser->hists;
  57        int unfolded_rows = 0;
  58
  59        for (nd = rb_first(&hists->entries);
  60             (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
  61             nd = rb_next(nd)) {
  62                struct hist_entry *he =
  63                        rb_entry(nd, struct hist_entry, rb_node);
  64
  65                if (he->unfolded)
  66                        unfolded_rows += he->nr_rows;
  67        }
  68        return unfolded_rows;
  69}
  70
  71static u32 hist_browser__nr_entries(struct hist_browser *hb)
  72{
  73        u32 nr_entries;
  74
  75        if (hist_browser__has_filter(hb))
  76                nr_entries = hb->nr_non_filtered_entries;
  77        else
  78                nr_entries = hb->hists->nr_entries;
  79
  80        hb->nr_callchain_rows = hist_browser__get_folding(hb);
  81        return nr_entries + hb->nr_callchain_rows;
  82}
  83
  84static void hist_browser__update_rows(struct hist_browser *hb)
  85{
  86        struct ui_browser *browser = &hb->b;
  87        u16 header_offset = hb->show_headers ? 1 : 0, index_row;
  88
  89        browser->rows = browser->height - header_offset;
  90        /*
  91         * Verify if we were at the last line and that line isn't
  92         * visibe because we now show the header line(s).
  93         */
  94        index_row = browser->index - browser->top_idx;
  95        if (index_row >= browser->rows)
  96                browser->index -= index_row - browser->rows + 1;
  97}
  98
  99static void hist_browser__refresh_dimensions(struct ui_browser *browser)
 100{
 101        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 102
 103        /* 3 == +/- toggle symbol before actual hist_entry rendering */
 104        browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
 105        /*
 106         * FIXME: Just keeping existing behaviour, but this really should be
 107         *        before updating browser->width, as it will invalidate the
 108         *        calculation above. Fix this and the fallout in another
 109         *        changeset.
 110         */
 111        ui_browser__refresh_dimensions(browser);
 112        hist_browser__update_rows(hb);
 113}
 114
 115static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
 116{
 117        u16 header_offset = browser->show_headers ? 1 : 0;
 118
 119        ui_browser__gotorc(&browser->b, row + header_offset, column);
 120}
 121
 122static void hist_browser__reset(struct hist_browser *browser)
 123{
 124        /*
 125         * The hists__remove_entry_filter() already folds non-filtered
 126         * entries so we can assume it has 0 callchain rows.
 127         */
 128        browser->nr_callchain_rows = 0;
 129
 130        hist_browser__update_nr_entries(browser);
 131        browser->b.nr_entries = hist_browser__nr_entries(browser);
 132        hist_browser__refresh_dimensions(&browser->b);
 133        ui_browser__reset_index(&browser->b);
 134}
 135
 136static char tree__folded_sign(bool unfolded)
 137{
 138        return unfolded ? '-' : '+';
 139}
 140
 141static char hist_entry__folded(const struct hist_entry *he)
 142{
 143        return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
 144}
 145
 146static char callchain_list__folded(const struct callchain_list *cl)
 147{
 148        return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
 149}
 150
 151static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
 152{
 153        cl->unfolded = unfold ? cl->has_children : false;
 154}
 155
 156static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
 157{
 158        int n = 0;
 159        struct rb_node *nd;
 160
 161        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 162                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 163                struct callchain_list *chain;
 164                char folded_sign = ' '; /* No children */
 165
 166                list_for_each_entry(chain, &child->val, list) {
 167                        ++n;
 168                        /* We need this because we may not have children */
 169                        folded_sign = callchain_list__folded(chain);
 170                        if (folded_sign == '+')
 171                                break;
 172                }
 173
 174                if (folded_sign == '-') /* Have children and they're unfolded */
 175                        n += callchain_node__count_rows_rb_tree(child);
 176        }
 177
 178        return n;
 179}
 180
 181static int callchain_node__count_rows(struct callchain_node *node)
 182{
 183        struct callchain_list *chain;
 184        bool unfolded = false;
 185        int n = 0;
 186
 187        list_for_each_entry(chain, &node->val, list) {
 188                ++n;
 189                unfolded = chain->unfolded;
 190        }
 191
 192        if (unfolded)
 193                n += callchain_node__count_rows_rb_tree(node);
 194
 195        return n;
 196}
 197
 198static int callchain__count_rows(struct rb_root *chain)
 199{
 200        struct rb_node *nd;
 201        int n = 0;
 202
 203        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 204                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 205                n += callchain_node__count_rows(node);
 206        }
 207
 208        return n;
 209}
 210
 211static bool hist_entry__toggle_fold(struct hist_entry *he)
 212{
 213        if (!he)
 214                return false;
 215
 216        if (!he->has_children)
 217                return false;
 218
 219        he->unfolded = !he->unfolded;
 220        return true;
 221}
 222
 223static bool callchain_list__toggle_fold(struct callchain_list *cl)
 224{
 225        if (!cl)
 226                return false;
 227
 228        if (!cl->has_children)
 229                return false;
 230
 231        cl->unfolded = !cl->unfolded;
 232        return true;
 233}
 234
 235static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
 236{
 237        struct rb_node *nd = rb_first(&node->rb_root);
 238
 239        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 240                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 241                struct callchain_list *chain;
 242                bool first = true;
 243
 244                list_for_each_entry(chain, &child->val, list) {
 245                        if (first) {
 246                                first = false;
 247                                chain->has_children = chain->list.next != &child->val ||
 248                                                         !RB_EMPTY_ROOT(&child->rb_root);
 249                        } else
 250                                chain->has_children = chain->list.next == &child->val &&
 251                                                         !RB_EMPTY_ROOT(&child->rb_root);
 252                }
 253
 254                callchain_node__init_have_children_rb_tree(child);
 255        }
 256}
 257
 258static void callchain_node__init_have_children(struct callchain_node *node,
 259                                               bool has_sibling)
 260{
 261        struct callchain_list *chain;
 262
 263        chain = list_entry(node->val.next, struct callchain_list, list);
 264        chain->has_children = has_sibling;
 265
 266        if (!list_empty(&node->val)) {
 267                chain = list_entry(node->val.prev, struct callchain_list, list);
 268                chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
 269        }
 270
 271        callchain_node__init_have_children_rb_tree(node);
 272}
 273
 274static void callchain__init_have_children(struct rb_root *root)
 275{
 276        struct rb_node *nd = rb_first(root);
 277        bool has_sibling = nd && rb_next(nd);
 278
 279        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
 280                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 281                callchain_node__init_have_children(node, has_sibling);
 282        }
 283}
 284
 285static void hist_entry__init_have_children(struct hist_entry *he)
 286{
 287        if (!he->init_have_children) {
 288                he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
 289                callchain__init_have_children(&he->sorted_chain);
 290                he->init_have_children = true;
 291        }
 292}
 293
 294static bool hist_browser__toggle_fold(struct hist_browser *browser)
 295{
 296        struct hist_entry *he = browser->he_selection;
 297        struct map_symbol *ms = browser->selection;
 298        struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
 299        bool has_children;
 300
 301        if (!he || !ms)
 302                return false;
 303
 304        if (ms == &he->ms)
 305                has_children = hist_entry__toggle_fold(he);
 306        else
 307                has_children = callchain_list__toggle_fold(cl);
 308
 309        if (has_children) {
 310                hist_entry__init_have_children(he);
 311                browser->b.nr_entries -= he->nr_rows;
 312                browser->nr_callchain_rows -= he->nr_rows;
 313
 314                if (he->unfolded)
 315                        he->nr_rows = callchain__count_rows(&he->sorted_chain);
 316                else
 317                        he->nr_rows = 0;
 318
 319                browser->b.nr_entries += he->nr_rows;
 320                browser->nr_callchain_rows += he->nr_rows;
 321
 322                return true;
 323        }
 324
 325        /* If it doesn't have children, no toggling performed */
 326        return false;
 327}
 328
 329static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
 330{
 331        int n = 0;
 332        struct rb_node *nd;
 333
 334        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 335                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 336                struct callchain_list *chain;
 337                bool has_children = false;
 338
 339                list_for_each_entry(chain, &child->val, list) {
 340                        ++n;
 341                        callchain_list__set_folding(chain, unfold);
 342                        has_children = chain->has_children;
 343                }
 344
 345                if (has_children)
 346                        n += callchain_node__set_folding_rb_tree(child, unfold);
 347        }
 348
 349        return n;
 350}
 351
 352static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
 353{
 354        struct callchain_list *chain;
 355        bool has_children = false;
 356        int n = 0;
 357
 358        list_for_each_entry(chain, &node->val, list) {
 359                ++n;
 360                callchain_list__set_folding(chain, unfold);
 361                has_children = chain->has_children;
 362        }
 363
 364        if (has_children)
 365                n += callchain_node__set_folding_rb_tree(node, unfold);
 366
 367        return n;
 368}
 369
 370static int callchain__set_folding(struct rb_root *chain, bool unfold)
 371{
 372        struct rb_node *nd;
 373        int n = 0;
 374
 375        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 376                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 377                n += callchain_node__set_folding(node, unfold);
 378        }
 379
 380        return n;
 381}
 382
 383static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
 384{
 385        hist_entry__init_have_children(he);
 386        he->unfolded = unfold ? he->has_children : false;
 387
 388        if (he->has_children) {
 389                int n = callchain__set_folding(&he->sorted_chain, unfold);
 390                he->nr_rows = unfold ? n : 0;
 391        } else
 392                he->nr_rows = 0;
 393}
 394
 395static void
 396__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 397{
 398        struct rb_node *nd;
 399        struct hists *hists = browser->hists;
 400
 401        for (nd = rb_first(&hists->entries);
 402             (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
 403             nd = rb_next(nd)) {
 404                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
 405                hist_entry__set_folding(he, unfold);
 406                browser->nr_callchain_rows += he->nr_rows;
 407        }
 408}
 409
 410static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 411{
 412        browser->nr_callchain_rows = 0;
 413        __hist_browser__set_folding(browser, unfold);
 414
 415        browser->b.nr_entries = hist_browser__nr_entries(browser);
 416        /* Go to the start, we may be way after valid entries after a collapse */
 417        ui_browser__reset_index(&browser->b);
 418}
 419
 420static void ui_browser__warn_lost_events(struct ui_browser *browser)
 421{
 422        ui_browser__warning(browser, 4,
 423                "Events are being lost, check IO/CPU overload!\n\n"
 424                "You may want to run 'perf' using a RT scheduler policy:\n\n"
 425                " perf top -r 80\n\n"
 426                "Or reduce the sampling frequency.");
 427}
 428
 429static int hist_browser__run(struct hist_browser *browser, const char *help)
 430{
 431        int key;
 432        char title[160];
 433        struct hist_browser_timer *hbt = browser->hbt;
 434        int delay_secs = hbt ? hbt->refresh : 0;
 435
 436        browser->b.entries = &browser->hists->entries;
 437        browser->b.nr_entries = hist_browser__nr_entries(browser);
 438
 439        hists__browser_title(browser->hists, hbt, title, sizeof(title));
 440
 441        if (ui_browser__show(&browser->b, title, help) < 0)
 442                return -1;
 443
 444        while (1) {
 445                key = ui_browser__run(&browser->b, delay_secs);
 446
 447                switch (key) {
 448                case K_TIMER: {
 449                        u64 nr_entries;
 450                        hbt->timer(hbt->arg);
 451
 452                        if (hist_browser__has_filter(browser))
 453                                hist_browser__update_nr_entries(browser);
 454
 455                        nr_entries = hist_browser__nr_entries(browser);
 456                        ui_browser__update_nr_entries(&browser->b, nr_entries);
 457
 458                        if (browser->hists->stats.nr_lost_warned !=
 459                            browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
 460                                browser->hists->stats.nr_lost_warned =
 461                                        browser->hists->stats.nr_events[PERF_RECORD_LOST];
 462                                ui_browser__warn_lost_events(&browser->b);
 463                        }
 464
 465                        hists__browser_title(browser->hists,
 466                                             hbt, title, sizeof(title));
 467                        ui_browser__show_title(&browser->b, title);
 468                        continue;
 469                }
 470                case 'D': { /* Debug */
 471                        static int seq;
 472                        struct hist_entry *h = rb_entry(browser->b.top,
 473                                                        struct hist_entry, rb_node);
 474                        ui_helpline__pop();
 475                        ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
 476                                           seq++, browser->b.nr_entries,
 477                                           browser->hists->nr_entries,
 478                                           browser->b.rows,
 479                                           browser->b.index,
 480                                           browser->b.top_idx,
 481                                           h->row_offset, h->nr_rows);
 482                }
 483                        break;
 484                case 'C':
 485                        /* Collapse the whole world. */
 486                        hist_browser__set_folding(browser, false);
 487                        break;
 488                case 'E':
 489                        /* Expand the whole world. */
 490                        hist_browser__set_folding(browser, true);
 491                        break;
 492                case 'H':
 493                        browser->show_headers = !browser->show_headers;
 494                        hist_browser__update_rows(browser);
 495                        break;
 496                case K_ENTER:
 497                        if (hist_browser__toggle_fold(browser))
 498                                break;
 499                        /* fall thru */
 500                default:
 501                        goto out;
 502                }
 503        }
 504out:
 505        ui_browser__hide(&browser->b);
 506        return key;
 507}
 508
 509struct callchain_print_arg {
 510        /* for hists browser */
 511        off_t   row_offset;
 512        bool    is_current_entry;
 513
 514        /* for file dump */
 515        FILE    *fp;
 516        int     printed;
 517};
 518
 519typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
 520                                         struct callchain_list *chain,
 521                                         const char *str, int offset,
 522                                         unsigned short row,
 523                                         struct callchain_print_arg *arg);
 524
 525static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 526                                               struct callchain_list *chain,
 527                                               const char *str, int offset,
 528                                               unsigned short row,
 529                                               struct callchain_print_arg *arg)
 530{
 531        int color, width;
 532        char folded_sign = callchain_list__folded(chain);
 533        bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
 534
 535        color = HE_COLORSET_NORMAL;
 536        width = browser->b.width - (offset + 2);
 537        if (ui_browser__is_current_entry(&browser->b, row)) {
 538                browser->selection = &chain->ms;
 539                color = HE_COLORSET_SELECTED;
 540                arg->is_current_entry = true;
 541        }
 542
 543        ui_browser__set_color(&browser->b, color);
 544        hist_browser__gotorc(browser, row, 0);
 545        ui_browser__write_nstring(&browser->b, " ", offset);
 546        ui_browser__printf(&browser->b, "%c", folded_sign);
 547        ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
 548        ui_browser__write_nstring(&browser->b, str, width);
 549}
 550
 551static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
 552                                                  struct callchain_list *chain,
 553                                                  const char *str, int offset,
 554                                                  unsigned short row __maybe_unused,
 555                                                  struct callchain_print_arg *arg)
 556{
 557        char folded_sign = callchain_list__folded(chain);
 558
 559        arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
 560                                folded_sign, str);
 561}
 562
 563typedef bool (*check_output_full_fn)(struct hist_browser *browser,
 564                                     unsigned short row);
 565
 566static bool hist_browser__check_output_full(struct hist_browser *browser,
 567                                            unsigned short row)
 568{
 569        return browser->b.rows == row;
 570}
 571
 572static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
 573                                          unsigned short row __maybe_unused)
 574{
 575        return false;
 576}
 577
 578#define LEVEL_OFFSET_STEP 3
 579
 580static int hist_browser__show_callchain(struct hist_browser *browser,
 581                                        struct rb_root *root, int level,
 582                                        unsigned short row, u64 total,
 583                                        print_callchain_entry_fn print,
 584                                        struct callchain_print_arg *arg,
 585                                        check_output_full_fn is_output_full)
 586{
 587        struct rb_node *node;
 588        int first_row = row, offset = level * LEVEL_OFFSET_STEP;
 589        u64 new_total;
 590        bool need_percent;
 591
 592        node = rb_first(root);
 593        need_percent = node && rb_next(node);
 594
 595        while (node) {
 596                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
 597                struct rb_node *next = rb_next(node);
 598                u64 cumul = callchain_cumul_hits(child);
 599                struct callchain_list *chain;
 600                char folded_sign = ' ';
 601                int first = true;
 602                int extra_offset = 0;
 603
 604                list_for_each_entry(chain, &child->val, list) {
 605                        char bf[1024], *alloc_str;
 606                        const char *str;
 607                        bool was_first = first;
 608
 609                        if (first)
 610                                first = false;
 611                        else if (need_percent)
 612                                extra_offset = LEVEL_OFFSET_STEP;
 613
 614                        folded_sign = callchain_list__folded(chain);
 615                        if (arg->row_offset != 0) {
 616                                arg->row_offset--;
 617                                goto do_next;
 618                        }
 619
 620                        alloc_str = NULL;
 621                        str = callchain_list__sym_name(chain, bf, sizeof(bf),
 622                                                       browser->show_dso);
 623
 624                        if (was_first && need_percent) {
 625                                double percent = cumul * 100.0 / total;
 626
 627                                if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
 628                                        str = "Not enough memory!";
 629                                else
 630                                        str = alloc_str;
 631                        }
 632
 633                        print(browser, chain, str, offset + extra_offset, row, arg);
 634
 635                        free(alloc_str);
 636
 637                        if (is_output_full(browser, ++row))
 638                                goto out;
 639do_next:
 640                        if (folded_sign == '+')
 641                                break;
 642                }
 643
 644                if (folded_sign == '-') {
 645                        const int new_level = level + (extra_offset ? 2 : 1);
 646
 647                        if (callchain_param.mode == CHAIN_GRAPH_REL)
 648                                new_total = child->children_hit;
 649                        else
 650                                new_total = total;
 651
 652                        row += hist_browser__show_callchain(browser, &child->rb_root,
 653                                                            new_level, row, new_total,
 654                                                            print, arg, is_output_full);
 655                }
 656                if (is_output_full(browser, row))
 657                        break;
 658                node = next;
 659        }
 660out:
 661        return row - first_row;
 662}
 663
 664struct hpp_arg {
 665        struct ui_browser *b;
 666        char folded_sign;
 667        bool current_entry;
 668};
 669
 670static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
 671{
 672        struct hpp_arg *arg = hpp->ptr;
 673        int ret, len;
 674        va_list args;
 675        double percent;
 676
 677        va_start(args, fmt);
 678        len = va_arg(args, int);
 679        percent = va_arg(args, double);
 680        va_end(args);
 681
 682        ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
 683
 684        ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
 685        ui_browser__printf(arg->b, "%s", hpp->buf);
 686
 687        advance_hpp(hpp, ret);
 688        return ret;
 689}
 690
 691#define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
 692static u64 __hpp_get_##_field(struct hist_entry *he)                    \
 693{                                                                       \
 694        return he->stat._field;                                         \
 695}                                                                       \
 696                                                                        \
 697static int                                                              \
 698hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
 699                                struct perf_hpp *hpp,                   \
 700                                struct hist_entry *he)                  \
 701{                                                                       \
 702        return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
 703                        __hpp__slsmg_color_printf, true);               \
 704}
 705
 706#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
 707static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
 708{                                                                       \
 709        return he->stat_acc->_field;                                    \
 710}                                                                       \
 711                                                                        \
 712static int                                                              \
 713hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
 714                                struct perf_hpp *hpp,                   \
 715                                struct hist_entry *he)                  \
 716{                                                                       \
 717        if (!symbol_conf.cumulate_callchain) {                          \
 718                struct hpp_arg *arg = hpp->ptr;                         \
 719                int len = fmt->user_len ?: fmt->len;                    \
 720                int ret = scnprintf(hpp->buf, hpp->size,                \
 721                                    "%*s", len, "N/A");                 \
 722                ui_browser__printf(arg->b, "%s", hpp->buf);             \
 723                                                                        \
 724                return ret;                                             \
 725        }                                                               \
 726        return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
 727                        " %*.2f%%", __hpp__slsmg_color_printf, true);   \
 728}
 729
 730__HPP_COLOR_PERCENT_FN(overhead, period)
 731__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 732__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 733__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 734__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
 735__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
 736
 737#undef __HPP_COLOR_PERCENT_FN
 738#undef __HPP_COLOR_ACC_PERCENT_FN
 739
 740void hist_browser__init_hpp(void)
 741{
 742        perf_hpp__format[PERF_HPP__OVERHEAD].color =
 743                                hist_browser__hpp_color_overhead;
 744        perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
 745                                hist_browser__hpp_color_overhead_sys;
 746        perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
 747                                hist_browser__hpp_color_overhead_us;
 748        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
 749                                hist_browser__hpp_color_overhead_guest_sys;
 750        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
 751                                hist_browser__hpp_color_overhead_guest_us;
 752        perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
 753                                hist_browser__hpp_color_overhead_acc;
 754}
 755
 756static int hist_browser__show_entry(struct hist_browser *browser,
 757                                    struct hist_entry *entry,
 758                                    unsigned short row)
 759{
 760        char s[256];
 761        int printed = 0;
 762        int width = browser->b.width;
 763        char folded_sign = ' ';
 764        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
 765        off_t row_offset = entry->row_offset;
 766        bool first = true;
 767        struct perf_hpp_fmt *fmt;
 768
 769        if (current_entry) {
 770                browser->he_selection = entry;
 771                browser->selection = &entry->ms;
 772        }
 773
 774        if (symbol_conf.use_callchain) {
 775                hist_entry__init_have_children(entry);
 776                folded_sign = hist_entry__folded(entry);
 777        }
 778
 779        if (row_offset == 0) {
 780                struct hpp_arg arg = {
 781                        .b              = &browser->b,
 782                        .folded_sign    = folded_sign,
 783                        .current_entry  = current_entry,
 784                };
 785                struct perf_hpp hpp = {
 786                        .buf            = s,
 787                        .size           = sizeof(s),
 788                        .ptr            = &arg,
 789                };
 790                int column = 0;
 791
 792                hist_browser__gotorc(browser, row, 0);
 793
 794                perf_hpp__for_each_format(fmt) {
 795                        if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
 796                                continue;
 797
 798                        if (current_entry && browser->b.navkeypressed) {
 799                                ui_browser__set_color(&browser->b,
 800                                                      HE_COLORSET_SELECTED);
 801                        } else {
 802                                ui_browser__set_color(&browser->b,
 803                                                      HE_COLORSET_NORMAL);
 804                        }
 805
 806                        if (first) {
 807                                if (symbol_conf.use_callchain) {
 808                                        ui_browser__printf(&browser->b, "%c ", folded_sign);
 809                                        width -= 2;
 810                                }
 811                                first = false;
 812                        } else {
 813                                ui_browser__printf(&browser->b, "  ");
 814                                width -= 2;
 815                        }
 816
 817                        if (fmt->color) {
 818                                width -= fmt->color(fmt, &hpp, entry);
 819                        } else {
 820                                width -= fmt->entry(fmt, &hpp, entry);
 821                                ui_browser__printf(&browser->b, "%s", s);
 822                        }
 823                }
 824
 825                /* The scroll bar isn't being used */
 826                if (!browser->b.navkeypressed)
 827                        width += 1;
 828
 829                ui_browser__write_nstring(&browser->b, "", width);
 830
 831                ++row;
 832                ++printed;
 833        } else
 834                --row_offset;
 835
 836        if (folded_sign == '-' && row != browser->b.rows) {
 837                u64 total = hists__total_period(entry->hists);
 838                struct callchain_print_arg arg = {
 839                        .row_offset = row_offset,
 840                        .is_current_entry = current_entry,
 841                };
 842
 843                if (callchain_param.mode == CHAIN_GRAPH_REL) {
 844                        if (symbol_conf.cumulate_callchain)
 845                                total = entry->stat_acc->period;
 846                        else
 847                                total = entry->stat.period;
 848                }
 849
 850                printed += hist_browser__show_callchain(browser,
 851                                        &entry->sorted_chain, 1, row, total,
 852                                        hist_browser__show_callchain_entry, &arg,
 853                                        hist_browser__check_output_full);
 854
 855                if (arg.is_current_entry)
 856                        browser->he_selection = entry;
 857        }
 858
 859        return printed;
 860}
 861
 862static int advance_hpp_check(struct perf_hpp *hpp, int inc)
 863{
 864        advance_hpp(hpp, inc);
 865        return hpp->size <= 0;
 866}
 867
 868static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
 869{
 870        struct hists *hists = browser->hists;
 871        struct perf_hpp dummy_hpp = {
 872                .buf    = buf,
 873                .size   = size,
 874        };
 875        struct perf_hpp_fmt *fmt;
 876        size_t ret = 0;
 877        int column = 0;
 878
 879        if (symbol_conf.use_callchain) {
 880                ret = scnprintf(buf, size, "  ");
 881                if (advance_hpp_check(&dummy_hpp, ret))
 882                        return ret;
 883        }
 884
 885        perf_hpp__for_each_format(fmt) {
 886                if (perf_hpp__should_skip(fmt)  || column++ < browser->b.horiz_scroll)
 887                        continue;
 888
 889                ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
 890                if (advance_hpp_check(&dummy_hpp, ret))
 891                        break;
 892
 893                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
 894                if (advance_hpp_check(&dummy_hpp, ret))
 895                        break;
 896        }
 897
 898        return ret;
 899}
 900
 901static void hist_browser__show_headers(struct hist_browser *browser)
 902{
 903        char headers[1024];
 904
 905        hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
 906        ui_browser__gotorc(&browser->b, 0, 0);
 907        ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
 908        ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
 909}
 910
 911static void ui_browser__hists_init_top(struct ui_browser *browser)
 912{
 913        if (browser->top == NULL) {
 914                struct hist_browser *hb;
 915
 916                hb = container_of(browser, struct hist_browser, b);
 917                browser->top = rb_first(&hb->hists->entries);
 918        }
 919}
 920
 921static unsigned int hist_browser__refresh(struct ui_browser *browser)
 922{
 923        unsigned row = 0;
 924        u16 header_offset = 0;
 925        struct rb_node *nd;
 926        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 927
 928        if (hb->show_headers) {
 929                hist_browser__show_headers(hb);
 930                header_offset = 1;
 931        }
 932
 933        ui_browser__hists_init_top(browser);
 934        hb->he_selection = NULL;
 935        hb->selection = NULL;
 936
 937        for (nd = browser->top; nd; nd = rb_next(nd)) {
 938                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 939                float percent;
 940
 941                if (h->filtered)
 942                        continue;
 943
 944                percent = hist_entry__get_percent_limit(h);
 945                if (percent < hb->min_pcnt)
 946                        continue;
 947
 948                row += hist_browser__show_entry(hb, h, row);
 949                if (row == browser->rows)
 950                        break;
 951        }
 952
 953        return row + header_offset;
 954}
 955
 956static struct rb_node *hists__filter_entries(struct rb_node *nd,
 957                                             float min_pcnt)
 958{
 959        while (nd != NULL) {
 960                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 961                float percent = hist_entry__get_percent_limit(h);
 962
 963                if (!h->filtered && percent >= min_pcnt)
 964                        return nd;
 965
 966                nd = rb_next(nd);
 967        }
 968
 969        return NULL;
 970}
 971
 972static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
 973                                                  float min_pcnt)
 974{
 975        while (nd != NULL) {
 976                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 977                float percent = hist_entry__get_percent_limit(h);
 978
 979                if (!h->filtered && percent >= min_pcnt)
 980                        return nd;
 981
 982                nd = rb_prev(nd);
 983        }
 984
 985        return NULL;
 986}
 987
 988static void ui_browser__hists_seek(struct ui_browser *browser,
 989                                   off_t offset, int whence)
 990{
 991        struct hist_entry *h;
 992        struct rb_node *nd;
 993        bool first = true;
 994        struct hist_browser *hb;
 995
 996        hb = container_of(browser, struct hist_browser, b);
 997
 998        if (browser->nr_entries == 0)
 999                return;
1000
1001        ui_browser__hists_init_top(browser);
1002
1003        switch (whence) {
1004        case SEEK_SET:
1005                nd = hists__filter_entries(rb_first(browser->entries),
1006                                           hb->min_pcnt);
1007                break;
1008        case SEEK_CUR:
1009                nd = browser->top;
1010                goto do_offset;
1011        case SEEK_END:
1012                nd = hists__filter_prev_entries(rb_last(browser->entries),
1013                                                hb->min_pcnt);
1014                first = false;
1015                break;
1016        default:
1017                return;
1018        }
1019
1020        /*
1021         * Moves not relative to the first visible entry invalidates its
1022         * row_offset:
1023         */
1024        h = rb_entry(browser->top, struct hist_entry, rb_node);
1025        h->row_offset = 0;
1026
1027        /*
1028         * Here we have to check if nd is expanded (+), if it is we can't go
1029         * the next top level hist_entry, instead we must compute an offset of
1030         * what _not_ to show and not change the first visible entry.
1031         *
1032         * This offset increments when we are going from top to bottom and
1033         * decreases when we're going from bottom to top.
1034         *
1035         * As we don't have backpointers to the top level in the callchains
1036         * structure, we need to always print the whole hist_entry callchain,
1037         * skipping the first ones that are before the first visible entry
1038         * and stop when we printed enough lines to fill the screen.
1039         */
1040do_offset:
1041        if (!nd)
1042                return;
1043
1044        if (offset > 0) {
1045                do {
1046                        h = rb_entry(nd, struct hist_entry, rb_node);
1047                        if (h->unfolded) {
1048                                u16 remaining = h->nr_rows - h->row_offset;
1049                                if (offset > remaining) {
1050                                        offset -= remaining;
1051                                        h->row_offset = 0;
1052                                } else {
1053                                        h->row_offset += offset;
1054                                        offset = 0;
1055                                        browser->top = nd;
1056                                        break;
1057                                }
1058                        }
1059                        nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1060                        if (nd == NULL)
1061                                break;
1062                        --offset;
1063                        browser->top = nd;
1064                } while (offset != 0);
1065        } else if (offset < 0) {
1066                while (1) {
1067                        h = rb_entry(nd, struct hist_entry, rb_node);
1068                        if (h->unfolded) {
1069                                if (first) {
1070                                        if (-offset > h->row_offset) {
1071                                                offset += h->row_offset;
1072                                                h->row_offset = 0;
1073                                        } else {
1074                                                h->row_offset += offset;
1075                                                offset = 0;
1076                                                browser->top = nd;
1077                                                break;
1078                                        }
1079                                } else {
1080                                        if (-offset > h->nr_rows) {
1081                                                offset += h->nr_rows;
1082                                                h->row_offset = 0;
1083                                        } else {
1084                                                h->row_offset = h->nr_rows + offset;
1085                                                offset = 0;
1086                                                browser->top = nd;
1087                                                break;
1088                                        }
1089                                }
1090                        }
1091
1092                        nd = hists__filter_prev_entries(rb_prev(nd),
1093                                                        hb->min_pcnt);
1094                        if (nd == NULL)
1095                                break;
1096                        ++offset;
1097                        browser->top = nd;
1098                        if (offset == 0) {
1099                                /*
1100                                 * Last unfiltered hist_entry, check if it is
1101                                 * unfolded, if it is then we should have
1102                                 * row_offset at its last entry.
1103                                 */
1104                                h = rb_entry(nd, struct hist_entry, rb_node);
1105                                if (h->unfolded)
1106                                        h->row_offset = h->nr_rows;
1107                                break;
1108                        }
1109                        first = false;
1110                }
1111        } else {
1112                browser->top = nd;
1113                h = rb_entry(nd, struct hist_entry, rb_node);
1114                h->row_offset = 0;
1115        }
1116}
1117
1118static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1119                                           struct hist_entry *he, FILE *fp)
1120{
1121        u64 total = hists__total_period(he->hists);
1122        struct callchain_print_arg arg  = {
1123                .fp = fp,
1124        };
1125
1126        if (symbol_conf.cumulate_callchain)
1127                total = he->stat_acc->period;
1128
1129        hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1130                                     hist_browser__fprintf_callchain_entry, &arg,
1131                                     hist_browser__check_dump_full);
1132        return arg.printed;
1133}
1134
1135static int hist_browser__fprintf_entry(struct hist_browser *browser,
1136                                       struct hist_entry *he, FILE *fp)
1137{
1138        char s[8192];
1139        int printed = 0;
1140        char folded_sign = ' ';
1141        struct perf_hpp hpp = {
1142                .buf = s,
1143                .size = sizeof(s),
1144        };
1145        struct perf_hpp_fmt *fmt;
1146        bool first = true;
1147        int ret;
1148
1149        if (symbol_conf.use_callchain)
1150                folded_sign = hist_entry__folded(he);
1151
1152        if (symbol_conf.use_callchain)
1153                printed += fprintf(fp, "%c ", folded_sign);
1154
1155        perf_hpp__for_each_format(fmt) {
1156                if (perf_hpp__should_skip(fmt))
1157                        continue;
1158
1159                if (!first) {
1160                        ret = scnprintf(hpp.buf, hpp.size, "  ");
1161                        advance_hpp(&hpp, ret);
1162                } else
1163                        first = false;
1164
1165                ret = fmt->entry(fmt, &hpp, he);
1166                advance_hpp(&hpp, ret);
1167        }
1168        printed += fprintf(fp, "%s\n", rtrim(s));
1169
1170        if (folded_sign == '-')
1171                printed += hist_browser__fprintf_callchain(browser, he, fp);
1172
1173        return printed;
1174}
1175
1176static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1177{
1178        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1179                                                   browser->min_pcnt);
1180        int printed = 0;
1181
1182        while (nd) {
1183                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1184
1185                printed += hist_browser__fprintf_entry(browser, h, fp);
1186                nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1187        }
1188
1189        return printed;
1190}
1191
1192static int hist_browser__dump(struct hist_browser *browser)
1193{
1194        char filename[64];
1195        FILE *fp;
1196
1197        while (1) {
1198                scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1199                if (access(filename, F_OK))
1200                        break;
1201                /*
1202                 * XXX: Just an arbitrary lazy upper limit
1203                 */
1204                if (++browser->print_seq == 8192) {
1205                        ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1206                        return -1;
1207                }
1208        }
1209
1210        fp = fopen(filename, "w");
1211        if (fp == NULL) {
1212                char bf[64];
1213                const char *err = strerror_r(errno, bf, sizeof(bf));
1214                ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1215                return -1;
1216        }
1217
1218        ++browser->print_seq;
1219        hist_browser__fprintf(browser, fp);
1220        fclose(fp);
1221        ui_helpline__fpush("%s written!", filename);
1222
1223        return 0;
1224}
1225
1226static struct hist_browser *hist_browser__new(struct hists *hists,
1227                                              struct hist_browser_timer *hbt,
1228                                              struct perf_env *env)
1229{
1230        struct hist_browser *browser = zalloc(sizeof(*browser));
1231
1232        if (browser) {
1233                browser->hists = hists;
1234                browser->b.refresh = hist_browser__refresh;
1235                browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1236                browser->b.seek = ui_browser__hists_seek;
1237                browser->b.use_navkeypressed = true;
1238                browser->show_headers = symbol_conf.show_hist_headers;
1239                browser->hbt = hbt;
1240                browser->env = env;
1241        }
1242
1243        return browser;
1244}
1245
1246static void hist_browser__delete(struct hist_browser *browser)
1247{
1248        free(browser);
1249}
1250
1251static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1252{
1253        return browser->he_selection;
1254}
1255
1256static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1257{
1258        return browser->he_selection->thread;
1259}
1260
1261/* Check whether the browser is for 'top' or 'report' */
1262static inline bool is_report_browser(void *timer)
1263{
1264        return timer == NULL;
1265}
1266
1267static int hists__browser_title(struct hists *hists,
1268                                struct hist_browser_timer *hbt,
1269                                char *bf, size_t size)
1270{
1271        char unit;
1272        int printed;
1273        const struct dso *dso = hists->dso_filter;
1274        const struct thread *thread = hists->thread_filter;
1275        int socket_id = hists->socket_filter;
1276        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1277        u64 nr_events = hists->stats.total_period;
1278        struct perf_evsel *evsel = hists_to_evsel(hists);
1279        const char *ev_name = perf_evsel__name(evsel);
1280        char buf[512];
1281        size_t buflen = sizeof(buf);
1282        char ref[30] = " show reference callgraph, ";
1283        bool enable_ref = false;
1284
1285        if (symbol_conf.filter_relative) {
1286                nr_samples = hists->stats.nr_non_filtered_samples;
1287                nr_events = hists->stats.total_non_filtered_period;
1288        }
1289
1290        if (perf_evsel__is_group_event(evsel)) {
1291                struct perf_evsel *pos;
1292
1293                perf_evsel__group_desc(evsel, buf, buflen);
1294                ev_name = buf;
1295
1296                for_each_group_member(pos, evsel) {
1297                        struct hists *pos_hists = evsel__hists(pos);
1298
1299                        if (symbol_conf.filter_relative) {
1300                                nr_samples += pos_hists->stats.nr_non_filtered_samples;
1301                                nr_events += pos_hists->stats.total_non_filtered_period;
1302                        } else {
1303                                nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1304                                nr_events += pos_hists->stats.total_period;
1305                        }
1306                }
1307        }
1308
1309        if (symbol_conf.show_ref_callgraph &&
1310            strstr(ev_name, "call-graph=no"))
1311                enable_ref = true;
1312        nr_samples = convert_unit(nr_samples, &unit);
1313        printed = scnprintf(bf, size,
1314                           "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1315                           nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1316
1317
1318        if (hists->uid_filter_str)
1319                printed += snprintf(bf + printed, size - printed,
1320                                    ", UID: %s", hists->uid_filter_str);
1321        if (thread)
1322                printed += scnprintf(bf + printed, size - printed,
1323                                    ", Thread: %s(%d)",
1324                                     (thread->comm_set ? thread__comm_str(thread) : ""),
1325                                    thread->tid);
1326        if (dso)
1327                printed += scnprintf(bf + printed, size - printed,
1328                                    ", DSO: %s", dso->short_name);
1329        if (socket_id > -1)
1330                printed += scnprintf(bf + printed, size - printed,
1331                                    ", Processor Socket: %d", socket_id);
1332        if (!is_report_browser(hbt)) {
1333                struct perf_top *top = hbt->arg;
1334
1335                if (top->zero)
1336                        printed += scnprintf(bf + printed, size - printed, " [z]");
1337        }
1338
1339        return printed;
1340}
1341
1342static inline void free_popup_options(char **options, int n)
1343{
1344        int i;
1345
1346        for (i = 0; i < n; ++i)
1347                zfree(&options[i]);
1348}
1349
1350/*
1351 * Only runtime switching of perf data file will make "input_name" point
1352 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1353 * whether we need to call free() for current "input_name" during the switch.
1354 */
1355static bool is_input_name_malloced = false;
1356
1357static int switch_data_file(void)
1358{
1359        char *pwd, *options[32], *abs_path[32], *tmp;
1360        DIR *pwd_dir;
1361        int nr_options = 0, choice = -1, ret = -1;
1362        struct dirent *dent;
1363
1364        pwd = getenv("PWD");
1365        if (!pwd)
1366                return ret;
1367
1368        pwd_dir = opendir(pwd);
1369        if (!pwd_dir)
1370                return ret;
1371
1372        memset(options, 0, sizeof(options));
1373        memset(options, 0, sizeof(abs_path));
1374
1375        while ((dent = readdir(pwd_dir))) {
1376                char path[PATH_MAX];
1377                u64 magic;
1378                char *name = dent->d_name;
1379                FILE *file;
1380
1381                if (!(dent->d_type == DT_REG))
1382                        continue;
1383
1384                snprintf(path, sizeof(path), "%s/%s", pwd, name);
1385
1386                file = fopen(path, "r");
1387                if (!file)
1388                        continue;
1389
1390                if (fread(&magic, 1, 8, file) < 8)
1391                        goto close_file_and_continue;
1392
1393                if (is_perf_magic(magic)) {
1394                        options[nr_options] = strdup(name);
1395                        if (!options[nr_options])
1396                                goto close_file_and_continue;
1397
1398                        abs_path[nr_options] = strdup(path);
1399                        if (!abs_path[nr_options]) {
1400                                zfree(&options[nr_options]);
1401                                ui__warning("Can't search all data files due to memory shortage.\n");
1402                                fclose(file);
1403                                break;
1404                        }
1405
1406                        nr_options++;
1407                }
1408
1409close_file_and_continue:
1410                fclose(file);
1411                if (nr_options >= 32) {
1412                        ui__warning("Too many perf data files in PWD!\n"
1413                                    "Only the first 32 files will be listed.\n");
1414                        break;
1415                }
1416        }
1417        closedir(pwd_dir);
1418
1419        if (nr_options) {
1420                choice = ui__popup_menu(nr_options, options);
1421                if (choice < nr_options && choice >= 0) {
1422                        tmp = strdup(abs_path[choice]);
1423                        if (tmp) {
1424                                if (is_input_name_malloced)
1425                                        free((void *)input_name);
1426                                input_name = tmp;
1427                                is_input_name_malloced = true;
1428                                ret = 0;
1429                        } else
1430                                ui__warning("Data switch failed due to memory shortage!\n");
1431                }
1432        }
1433
1434        free_popup_options(options, nr_options);
1435        free_popup_options(abs_path, nr_options);
1436        return ret;
1437}
1438
1439struct popup_action {
1440        struct thread           *thread;
1441        struct map_symbol       ms;
1442        int                     socket;
1443
1444        int (*fn)(struct hist_browser *browser, struct popup_action *act);
1445};
1446
1447static int
1448do_annotate(struct hist_browser *browser, struct popup_action *act)
1449{
1450        struct perf_evsel *evsel;
1451        struct annotation *notes;
1452        struct hist_entry *he;
1453        int err;
1454
1455        if (!objdump_path && perf_env__lookup_objdump(browser->env))
1456                return 0;
1457
1458        notes = symbol__annotation(act->ms.sym);
1459        if (!notes->src)
1460                return 0;
1461
1462        evsel = hists_to_evsel(browser->hists);
1463        err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1464        he = hist_browser__selected_entry(browser);
1465        /*
1466         * offer option to annotate the other branch source or target
1467         * (if they exists) when returning from annotate
1468         */
1469        if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1470                return 1;
1471
1472        ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1473        if (err)
1474                ui_browser__handle_resize(&browser->b);
1475        return 0;
1476}
1477
1478static int
1479add_annotate_opt(struct hist_browser *browser __maybe_unused,
1480                 struct popup_action *act, char **optstr,
1481                 struct map *map, struct symbol *sym)
1482{
1483        if (sym == NULL || map->dso->annotate_warned)
1484                return 0;
1485
1486        if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1487                return 0;
1488
1489        act->ms.map = map;
1490        act->ms.sym = sym;
1491        act->fn = do_annotate;
1492        return 1;
1493}
1494
1495static int
1496do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1497{
1498        struct thread *thread = act->thread;
1499
1500        if (browser->hists->thread_filter) {
1501                pstack__remove(browser->pstack, &browser->hists->thread_filter);
1502                perf_hpp__set_elide(HISTC_THREAD, false);
1503                thread__zput(browser->hists->thread_filter);
1504                ui_helpline__pop();
1505        } else {
1506                ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1507                                   thread->comm_set ? thread__comm_str(thread) : "",
1508                                   thread->tid);
1509                browser->hists->thread_filter = thread__get(thread);
1510                perf_hpp__set_elide(HISTC_THREAD, false);
1511                pstack__push(browser->pstack, &browser->hists->thread_filter);
1512        }
1513
1514        hists__filter_by_thread(browser->hists);
1515        hist_browser__reset(browser);
1516        return 0;
1517}
1518
1519static int
1520add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1521               char **optstr, struct thread *thread)
1522{
1523        if (thread == NULL)
1524                return 0;
1525
1526        if (asprintf(optstr, "Zoom %s %s(%d) thread",
1527                     browser->hists->thread_filter ? "out of" : "into",
1528                     thread->comm_set ? thread__comm_str(thread) : "",
1529                     thread->tid) < 0)
1530                return 0;
1531
1532        act->thread = thread;
1533        act->fn = do_zoom_thread;
1534        return 1;
1535}
1536
1537static int
1538do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1539{
1540        struct map *map = act->ms.map;
1541
1542        if (browser->hists->dso_filter) {
1543                pstack__remove(browser->pstack, &browser->hists->dso_filter);
1544                perf_hpp__set_elide(HISTC_DSO, false);
1545                browser->hists->dso_filter = NULL;
1546                ui_helpline__pop();
1547        } else {
1548                if (map == NULL)
1549                        return 0;
1550                ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1551                                   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1552                browser->hists->dso_filter = map->dso;
1553                perf_hpp__set_elide(HISTC_DSO, true);
1554                pstack__push(browser->pstack, &browser->hists->dso_filter);
1555        }
1556
1557        hists__filter_by_dso(browser->hists);
1558        hist_browser__reset(browser);
1559        return 0;
1560}
1561
1562static int
1563add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1564            char **optstr, struct map *map)
1565{
1566        if (map == NULL)
1567                return 0;
1568
1569        if (asprintf(optstr, "Zoom %s %s DSO",
1570                     browser->hists->dso_filter ? "out of" : "into",
1571                     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1572                return 0;
1573
1574        act->ms.map = map;
1575        act->fn = do_zoom_dso;
1576        return 1;
1577}
1578
1579static int
1580do_browse_map(struct hist_browser *browser __maybe_unused,
1581              struct popup_action *act)
1582{
1583        map__browse(act->ms.map);
1584        return 0;
1585}
1586
1587static int
1588add_map_opt(struct hist_browser *browser __maybe_unused,
1589            struct popup_action *act, char **optstr, struct map *map)
1590{
1591        if (map == NULL)
1592                return 0;
1593
1594        if (asprintf(optstr, "Browse map details") < 0)
1595                return 0;
1596
1597        act->ms.map = map;
1598        act->fn = do_browse_map;
1599        return 1;
1600}
1601
1602static int
1603do_run_script(struct hist_browser *browser __maybe_unused,
1604              struct popup_action *act)
1605{
1606        char script_opt[64];
1607        memset(script_opt, 0, sizeof(script_opt));
1608
1609        if (act->thread) {
1610                scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1611                          thread__comm_str(act->thread));
1612        } else if (act->ms.sym) {
1613                scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1614                          act->ms.sym->name);
1615        }
1616
1617        script_browse(script_opt);
1618        return 0;
1619}
1620
1621static int
1622add_script_opt(struct hist_browser *browser __maybe_unused,
1623               struct popup_action *act, char **optstr,
1624               struct thread *thread, struct symbol *sym)
1625{
1626        if (thread) {
1627                if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1628                             thread__comm_str(thread)) < 0)
1629                        return 0;
1630        } else if (sym) {
1631                if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1632                             sym->name) < 0)
1633                        return 0;
1634        } else {
1635                if (asprintf(optstr, "Run scripts for all samples") < 0)
1636                        return 0;
1637        }
1638
1639        act->thread = thread;
1640        act->ms.sym = sym;
1641        act->fn = do_run_script;
1642        return 1;
1643}
1644
1645static int
1646do_switch_data(struct hist_browser *browser __maybe_unused,
1647               struct popup_action *act __maybe_unused)
1648{
1649        if (switch_data_file()) {
1650                ui__warning("Won't switch the data files due to\n"
1651                            "no valid data file get selected!\n");
1652                return 0;
1653        }
1654
1655        return K_SWITCH_INPUT_DATA;
1656}
1657
1658static int
1659add_switch_opt(struct hist_browser *browser,
1660               struct popup_action *act, char **optstr)
1661{
1662        if (!is_report_browser(browser->hbt))
1663                return 0;
1664
1665        if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1666                return 0;
1667
1668        act->fn = do_switch_data;
1669        return 1;
1670}
1671
1672static int
1673do_exit_browser(struct hist_browser *browser __maybe_unused,
1674                struct popup_action *act __maybe_unused)
1675{
1676        return 0;
1677}
1678
1679static int
1680add_exit_opt(struct hist_browser *browser __maybe_unused,
1681             struct popup_action *act, char **optstr)
1682{
1683        if (asprintf(optstr, "Exit") < 0)
1684                return 0;
1685
1686        act->fn = do_exit_browser;
1687        return 1;
1688}
1689
1690static int
1691do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1692{
1693        if (browser->hists->socket_filter > -1) {
1694                pstack__remove(browser->pstack, &browser->hists->socket_filter);
1695                browser->hists->socket_filter = -1;
1696                perf_hpp__set_elide(HISTC_SOCKET, false);
1697        } else {
1698                browser->hists->socket_filter = act->socket;
1699                perf_hpp__set_elide(HISTC_SOCKET, true);
1700                pstack__push(browser->pstack, &browser->hists->socket_filter);
1701        }
1702
1703        hists__filter_by_socket(browser->hists);
1704        hist_browser__reset(browser);
1705        return 0;
1706}
1707
1708static int
1709add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1710               char **optstr, int socket_id)
1711{
1712        if (socket_id < 0)
1713                return 0;
1714
1715        if (asprintf(optstr, "Zoom %s Processor Socket %d",
1716                     (browser->hists->socket_filter > -1) ? "out of" : "into",
1717                     socket_id) < 0)
1718                return 0;
1719
1720        act->socket = socket_id;
1721        act->fn = do_zoom_socket;
1722        return 1;
1723}
1724
1725static void hist_browser__update_nr_entries(struct hist_browser *hb)
1726{
1727        u64 nr_entries = 0;
1728        struct rb_node *nd = rb_first(&hb->hists->entries);
1729
1730        if (hb->min_pcnt == 0) {
1731                hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1732                return;
1733        }
1734
1735        while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1736                nr_entries++;
1737                nd = rb_next(nd);
1738        }
1739
1740        hb->nr_non_filtered_entries = nr_entries;
1741}
1742
1743static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1744                                    const char *helpline,
1745                                    bool left_exits,
1746                                    struct hist_browser_timer *hbt,
1747                                    float min_pcnt,
1748                                    struct perf_env *env)
1749{
1750        struct hists *hists = evsel__hists(evsel);
1751        struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1752        struct branch_info *bi;
1753#define MAX_OPTIONS  16
1754        char *options[MAX_OPTIONS];
1755        struct popup_action actions[MAX_OPTIONS];
1756        int nr_options = 0;
1757        int key = -1;
1758        char buf[64];
1759        int delay_secs = hbt ? hbt->refresh : 0;
1760        struct perf_hpp_fmt *fmt;
1761
1762#define HIST_BROWSER_HELP_COMMON                                        \
1763        "h/?/F1        Show this window\n"                              \
1764        "UP/DOWN/PGUP\n"                                                \
1765        "PGDN/SPACE    Navigate\n"                                      \
1766        "q/ESC/CTRL+C  Exit browser\n\n"                                \
1767        "For multiple event sessions:\n\n"                              \
1768        "TAB/UNTAB     Switch events\n\n"                               \
1769        "For symbolic views (--sort has sym):\n\n"                      \
1770        "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
1771        "ESC           Zoom out\n"                                      \
1772        "a             Annotate current symbol\n"                       \
1773        "C             Collapse all callchains\n"                       \
1774        "d             Zoom into current DSO\n"                         \
1775        "E             Expand all callchains\n"                         \
1776        "F             Toggle percentage of filtered entries\n"         \
1777        "H             Display column headers\n"                        \
1778        "m             Display context menu\n"                          \
1779        "S             Zoom into current Processor Socket\n"            \
1780
1781        /* help messages are sorted by lexical order of the hotkey */
1782        const char report_help[] = HIST_BROWSER_HELP_COMMON
1783        "i             Show header information\n"
1784        "P             Print histograms to perf.hist.N\n"
1785        "r             Run available scripts\n"
1786        "s             Switch to another data file in PWD\n"
1787        "t             Zoom into current Thread\n"
1788        "V             Verbose (DSO names in callchains, etc)\n"
1789        "/             Filter symbol by name";
1790        const char top_help[] = HIST_BROWSER_HELP_COMMON
1791        "P             Print histograms to perf.hist.N\n"
1792        "t             Zoom into current Thread\n"
1793        "V             Verbose (DSO names in callchains, etc)\n"
1794        "z             Toggle zeroing of samples\n"
1795        "f             Enable/Disable events\n"
1796        "/             Filter symbol by name";
1797
1798        if (browser == NULL)
1799                return -1;
1800
1801        /* reset abort key so that it can get Ctrl-C as a key */
1802        SLang_reset_tty();
1803        SLang_init_tty(0, 0, 0);
1804
1805        if (min_pcnt) {
1806                browser->min_pcnt = min_pcnt;
1807                hist_browser__update_nr_entries(browser);
1808        }
1809
1810        browser->pstack = pstack__new(3);
1811        if (browser->pstack == NULL)
1812                goto out;
1813
1814        ui_helpline__push(helpline);
1815
1816        memset(options, 0, sizeof(options));
1817        memset(actions, 0, sizeof(actions));
1818
1819        perf_hpp__for_each_format(fmt) {
1820                perf_hpp__reset_width(fmt, hists);
1821                /*
1822                 * This is done just once, and activates the horizontal scrolling
1823                 * code in the ui_browser code, it would be better to have a the
1824                 * counter in the perf_hpp code, but I couldn't find doing it here
1825                 * works, FIXME by setting this in hist_browser__new, for now, be
1826                 * clever 8-)
1827                 */
1828                ++browser->b.columns;
1829        }
1830
1831        if (symbol_conf.col_width_list_str)
1832                perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1833
1834        while (1) {
1835                struct thread *thread = NULL;
1836                struct map *map = NULL;
1837                int choice = 0;
1838                int socked_id = -1;
1839
1840                nr_options = 0;
1841
1842                key = hist_browser__run(browser, helpline);
1843
1844                if (browser->he_selection != NULL) {
1845                        thread = hist_browser__selected_thread(browser);
1846                        map = browser->selection->map;
1847                        socked_id = browser->he_selection->socket;
1848                }
1849                switch (key) {
1850                case K_TAB:
1851                case K_UNTAB:
1852                        if (nr_events == 1)
1853                                continue;
1854                        /*
1855                         * Exit the browser, let hists__browser_tree
1856                         * go to the next or previous
1857                         */
1858                        goto out_free_stack;
1859                case 'a':
1860                        if (!sort__has_sym) {
1861                                ui_browser__warning(&browser->b, delay_secs * 2,
1862                        "Annotation is only available for symbolic views, "
1863                        "include \"sym*\" in --sort to use it.");
1864                                continue;
1865                        }
1866
1867                        if (browser->selection == NULL ||
1868                            browser->selection->sym == NULL ||
1869                            browser->selection->map->dso->annotate_warned)
1870                                continue;
1871
1872                        actions->ms.map = browser->selection->map;
1873                        actions->ms.sym = browser->selection->sym;
1874                        do_annotate(browser, actions);
1875                        continue;
1876                case 'P':
1877                        hist_browser__dump(browser);
1878                        continue;
1879                case 'd':
1880                        actions->ms.map = map;
1881                        do_zoom_dso(browser, actions);
1882                        continue;
1883                case 'V':
1884                        browser->show_dso = !browser->show_dso;
1885                        continue;
1886                case 't':
1887                        actions->thread = thread;
1888                        do_zoom_thread(browser, actions);
1889                        continue;
1890                case 'S':
1891                        actions->socket = socked_id;
1892                        do_zoom_socket(browser, actions);
1893                        continue;
1894                case '/':
1895                        if (ui_browser__input_window("Symbol to show",
1896                                        "Please enter the name of symbol you want to see.\n"
1897                                        "To remove the filter later, press / + ENTER.",
1898                                        buf, "ENTER: OK, ESC: Cancel",
1899                                        delay_secs * 2) == K_ENTER) {
1900                                hists->symbol_filter_str = *buf ? buf : NULL;
1901                                hists__filter_by_symbol(hists);
1902                                hist_browser__reset(browser);
1903                        }
1904                        continue;
1905                case 'r':
1906                        if (is_report_browser(hbt)) {
1907                                actions->thread = NULL;
1908                                actions->ms.sym = NULL;
1909                                do_run_script(browser, actions);
1910                        }
1911                        continue;
1912                case 's':
1913                        if (is_report_browser(hbt)) {
1914                                key = do_switch_data(browser, actions);
1915                                if (key == K_SWITCH_INPUT_DATA)
1916                                        goto out_free_stack;
1917                        }
1918                        continue;
1919                case 'i':
1920                        /* env->arch is NULL for live-mode (i.e. perf top) */
1921                        if (env->arch)
1922                                tui__header_window(env);
1923                        continue;
1924                case 'F':
1925                        symbol_conf.filter_relative ^= 1;
1926                        continue;
1927                case 'z':
1928                        if (!is_report_browser(hbt)) {
1929                                struct perf_top *top = hbt->arg;
1930
1931                                top->zero = !top->zero;
1932                        }
1933                        continue;
1934                case K_F1:
1935                case 'h':
1936                case '?':
1937                        ui_browser__help_window(&browser->b,
1938                                is_report_browser(hbt) ? report_help : top_help);
1939                        continue;
1940                case K_ENTER:
1941                case K_RIGHT:
1942                case 'm':
1943                        /* menu */
1944                        break;
1945                case K_ESC:
1946                case K_LEFT: {
1947                        const void *top;
1948
1949                        if (pstack__empty(browser->pstack)) {
1950                                /*
1951                                 * Go back to the perf_evsel_menu__run or other user
1952                                 */
1953                                if (left_exits)
1954                                        goto out_free_stack;
1955
1956                                if (key == K_ESC &&
1957                                    ui_browser__dialog_yesno(&browser->b,
1958                                                             "Do you really want to exit?"))
1959                                        goto out_free_stack;
1960
1961                                continue;
1962                        }
1963                        top = pstack__peek(browser->pstack);
1964                        if (top == &browser->hists->dso_filter) {
1965                                /*
1966                                 * No need to set actions->dso here since
1967                                 * it's just to remove the current filter.
1968                                 * Ditto for thread below.
1969                                 */
1970                                do_zoom_dso(browser, actions);
1971                        } else if (top == &browser->hists->thread_filter) {
1972                                do_zoom_thread(browser, actions);
1973                        } else if (top == &browser->hists->socket_filter) {
1974                                do_zoom_socket(browser, actions);
1975                        }
1976                        continue;
1977                }
1978                case 'q':
1979                case CTRL('c'):
1980                        goto out_free_stack;
1981                case 'f':
1982                        if (!is_report_browser(hbt)) {
1983                                struct perf_top *top = hbt->arg;
1984
1985                                perf_evlist__toggle_enable(top->evlist);
1986                                /*
1987                                 * No need to refresh, resort/decay histogram
1988                                 * entries if we are not collecting samples:
1989                                 */
1990                                if (top->evlist->enabled) {
1991                                        helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1992                                        hbt->refresh = delay_secs;
1993                                } else {
1994                                        helpline = "Press 'f' again to re-enable the events";
1995                                        hbt->refresh = 0;
1996                                }
1997                                continue;
1998                        }
1999                        /* Fall thru */
2000                default:
2001                        helpline = "Press '?' for help on key bindings";
2002                        continue;
2003                }
2004
2005                if (!sort__has_sym)
2006                        goto add_exit_option;
2007
2008                if (browser->selection == NULL)
2009                        goto skip_annotation;
2010
2011                if (sort__mode == SORT_MODE__BRANCH) {
2012                        bi = browser->he_selection->branch_info;
2013
2014                        if (bi == NULL)
2015                                goto skip_annotation;
2016
2017                        nr_options += add_annotate_opt(browser,
2018                                                       &actions[nr_options],
2019                                                       &options[nr_options],
2020                                                       bi->from.map,
2021                                                       bi->from.sym);
2022                        if (bi->to.sym != bi->from.sym)
2023                                nr_options += add_annotate_opt(browser,
2024                                                        &actions[nr_options],
2025                                                        &options[nr_options],
2026                                                        bi->to.map,
2027                                                        bi->to.sym);
2028                } else {
2029                        nr_options += add_annotate_opt(browser,
2030                                                       &actions[nr_options],
2031                                                       &options[nr_options],
2032                                                       browser->selection->map,
2033                                                       browser->selection->sym);
2034                }
2035skip_annotation:
2036                nr_options += add_thread_opt(browser, &actions[nr_options],
2037                                             &options[nr_options], thread);
2038                nr_options += add_dso_opt(browser, &actions[nr_options],
2039                                          &options[nr_options], map);
2040                nr_options += add_map_opt(browser, &actions[nr_options],
2041                                          &options[nr_options],
2042                                          browser->selection ?
2043                                                browser->selection->map : NULL);
2044                nr_options += add_socket_opt(browser, &actions[nr_options],
2045                                             &options[nr_options],
2046                                             socked_id);
2047                /* perf script support */
2048                if (browser->he_selection) {
2049                        nr_options += add_script_opt(browser,
2050                                                     &actions[nr_options],
2051                                                     &options[nr_options],
2052                                                     thread, NULL);
2053                        /*
2054                         * Note that browser->selection != NULL
2055                         * when browser->he_selection is not NULL,
2056                         * so we don't need to check browser->selection
2057                         * before fetching browser->selection->sym like what
2058                         * we do before fetching browser->selection->map.
2059                         *
2060                         * See hist_browser__show_entry.
2061                         */
2062                        nr_options += add_script_opt(browser,
2063                                                     &actions[nr_options],
2064                                                     &options[nr_options],
2065                                                     NULL, browser->selection->sym);
2066                }
2067                nr_options += add_script_opt(browser, &actions[nr_options],
2068                                             &options[nr_options], NULL, NULL);
2069                nr_options += add_switch_opt(browser, &actions[nr_options],
2070                                             &options[nr_options]);
2071add_exit_option:
2072                nr_options += add_exit_opt(browser, &actions[nr_options],
2073                                           &options[nr_options]);
2074
2075                do {
2076                        struct popup_action *act;
2077
2078                        choice = ui__popup_menu(nr_options, options);
2079                        if (choice == -1 || choice >= nr_options)
2080                                break;
2081
2082                        act = &actions[choice];
2083                        key = act->fn(browser, act);
2084                } while (key == 1);
2085
2086                if (key == K_SWITCH_INPUT_DATA)
2087                        break;
2088        }
2089out_free_stack:
2090        pstack__delete(browser->pstack);
2091out:
2092        hist_browser__delete(browser);
2093        free_popup_options(options, MAX_OPTIONS);
2094        return key;
2095}
2096
2097struct perf_evsel_menu {
2098        struct ui_browser b;
2099        struct perf_evsel *selection;
2100        bool lost_events, lost_events_warned;
2101        float min_pcnt;
2102        struct perf_env *env;
2103};
2104
2105static void perf_evsel_menu__write(struct ui_browser *browser,
2106                                   void *entry, int row)
2107{
2108        struct perf_evsel_menu *menu = container_of(browser,
2109                                                    struct perf_evsel_menu, b);
2110        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2111        struct hists *hists = evsel__hists(evsel);
2112        bool current_entry = ui_browser__is_current_entry(browser, row);
2113        unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2114        const char *ev_name = perf_evsel__name(evsel);
2115        char bf[256], unit;
2116        const char *warn = " ";
2117        size_t printed;
2118
2119        ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2120                                                       HE_COLORSET_NORMAL);
2121
2122        if (perf_evsel__is_group_event(evsel)) {
2123                struct perf_evsel *pos;
2124
2125                ev_name = perf_evsel__group_name(evsel);
2126
2127                for_each_group_member(pos, evsel) {
2128                        struct hists *pos_hists = evsel__hists(pos);
2129                        nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2130                }
2131        }
2132
2133        nr_events = convert_unit(nr_events, &unit);
2134        printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2135                           unit, unit == ' ' ? "" : " ", ev_name);
2136        ui_browser__printf(browser, "%s", bf);
2137
2138        nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2139        if (nr_events != 0) {
2140                menu->lost_events = true;
2141                if (!current_entry)
2142                        ui_browser__set_color(browser, HE_COLORSET_TOP);
2143                nr_events = convert_unit(nr_events, &unit);
2144                printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2145                                     nr_events, unit, unit == ' ' ? "" : " ");
2146                warn = bf;
2147        }
2148
2149        ui_browser__write_nstring(browser, warn, browser->width - printed);
2150
2151        if (current_entry)
2152                menu->selection = evsel;
2153}
2154
2155static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2156                                int nr_events, const char *help,
2157                                struct hist_browser_timer *hbt)
2158{
2159        struct perf_evlist *evlist = menu->b.priv;
2160        struct perf_evsel *pos;
2161        const char *title = "Available samples";
2162        int delay_secs = hbt ? hbt->refresh : 0;
2163        int key;
2164
2165        if (ui_browser__show(&menu->b, title,
2166                             "ESC: exit, ENTER|->: Browse histograms") < 0)
2167                return -1;
2168
2169        while (1) {
2170                key = ui_browser__run(&menu->b, delay_secs);
2171
2172                switch (key) {
2173                case K_TIMER:
2174                        hbt->timer(hbt->arg);
2175
2176                        if (!menu->lost_events_warned && menu->lost_events) {
2177                                ui_browser__warn_lost_events(&menu->b);
2178                                menu->lost_events_warned = true;
2179                        }
2180                        continue;
2181                case K_RIGHT:
2182                case K_ENTER:
2183                        if (!menu->selection)
2184                                continue;
2185                        pos = menu->selection;
2186browse_hists:
2187                        perf_evlist__set_selected(evlist, pos);
2188                        /*
2189                         * Give the calling tool a chance to populate the non
2190                         * default evsel resorted hists tree.
2191                         */
2192                        if (hbt)
2193                                hbt->timer(hbt->arg);
2194                        key = perf_evsel__hists_browse(pos, nr_events, help,
2195                                                       true, hbt,
2196                                                       menu->min_pcnt,
2197                                                       menu->env);
2198                        ui_browser__show_title(&menu->b, title);
2199                        switch (key) {
2200                        case K_TAB:
2201                                if (pos->node.next == &evlist->entries)
2202                                        pos = perf_evlist__first(evlist);
2203                                else
2204                                        pos = perf_evsel__next(pos);
2205                                goto browse_hists;
2206                        case K_UNTAB:
2207                                if (pos->node.prev == &evlist->entries)
2208                                        pos = perf_evlist__last(evlist);
2209                                else
2210                                        pos = perf_evsel__prev(pos);
2211                                goto browse_hists;
2212                        case K_SWITCH_INPUT_DATA:
2213                        case 'q':
2214                        case CTRL('c'):
2215                                goto out;
2216                        case K_ESC:
2217                        default:
2218                                continue;
2219                        }
2220                case K_LEFT:
2221                        continue;
2222                case K_ESC:
2223                        if (!ui_browser__dialog_yesno(&menu->b,
2224                                               "Do you really want to exit?"))
2225                                continue;
2226                        /* Fall thru */
2227                case 'q':
2228                case CTRL('c'):
2229                        goto out;
2230                default:
2231                        continue;
2232                }
2233        }
2234
2235out:
2236        ui_browser__hide(&menu->b);
2237        return key;
2238}
2239
2240static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2241                                 void *entry)
2242{
2243        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2244
2245        if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2246                return true;
2247
2248        return false;
2249}
2250
2251static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2252                                           int nr_entries, const char *help,
2253                                           struct hist_browser_timer *hbt,
2254                                           float min_pcnt,
2255                                           struct perf_env *env)
2256{
2257        struct perf_evsel *pos;
2258        struct perf_evsel_menu menu = {
2259                .b = {
2260                        .entries    = &evlist->entries,
2261                        .refresh    = ui_browser__list_head_refresh,
2262                        .seek       = ui_browser__list_head_seek,
2263                        .write      = perf_evsel_menu__write,
2264                        .filter     = filter_group_entries,
2265                        .nr_entries = nr_entries,
2266                        .priv       = evlist,
2267                },
2268                .min_pcnt = min_pcnt,
2269                .env = env,
2270        };
2271
2272        ui_helpline__push("Press ESC to exit");
2273
2274        evlist__for_each(evlist, pos) {
2275                const char *ev_name = perf_evsel__name(pos);
2276                size_t line_len = strlen(ev_name) + 7;
2277
2278                if (menu.b.width < line_len)
2279                        menu.b.width = line_len;
2280        }
2281
2282        return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2283}
2284
2285int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2286                                  struct hist_browser_timer *hbt,
2287                                  float min_pcnt,
2288                                  struct perf_env *env)
2289{
2290        int nr_entries = evlist->nr_entries;
2291
2292single_entry:
2293        if (nr_entries == 1) {
2294                struct perf_evsel *first = perf_evlist__first(evlist);
2295
2296                return perf_evsel__hists_browse(first, nr_entries, help,
2297                                                false, hbt, min_pcnt,
2298                                                env);
2299        }
2300
2301        if (symbol_conf.event_group) {
2302                struct perf_evsel *pos;
2303
2304                nr_entries = 0;
2305                evlist__for_each(evlist, pos) {
2306                        if (perf_evsel__is_group_leader(pos))
2307                                nr_entries++;
2308                }
2309
2310                if (nr_entries == 1)
2311                        goto single_entry;
2312        }
2313
2314        return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2315                                               hbt, min_pcnt, env);
2316}
2317