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