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