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