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