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