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        bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1235        off_t row_offset = entry->row_offset;
1236        bool first = true;
1237        struct perf_hpp_fmt *fmt;
1238
1239        if (current_entry) {
1240                browser->he_selection = entry;
1241                browser->selection = &entry->ms;
1242        }
1243
1244        if (use_callchain) {
1245                hist_entry__init_have_children(entry);
1246                folded_sign = hist_entry__folded(entry);
1247        }
1248
1249        if (row_offset == 0) {
1250                struct hpp_arg arg = {
1251                        .b              = &browser->b,
1252                        .folded_sign    = folded_sign,
1253                        .current_entry  = current_entry,
1254                };
1255                int column = 0;
1256
1257                ui_browser__gotorc(&browser->b, row, 0);
1258
1259                hists__for_each_format(browser->hists, fmt) {
1260                        char s[2048];
1261                        struct perf_hpp hpp = {
1262                                .buf    = s,
1263                                .size   = sizeof(s),
1264                                .ptr    = &arg,
1265                        };
1266
1267                        if (perf_hpp__should_skip(fmt, entry->hists) ||
1268                            column++ < browser->b.horiz_scroll)
1269                                continue;
1270
1271                        if (current_entry && browser->b.navkeypressed) {
1272                                ui_browser__set_color(&browser->b,
1273                                                      HE_COLORSET_SELECTED);
1274                        } else {
1275                                ui_browser__set_color(&browser->b,
1276                                                      HE_COLORSET_NORMAL);
1277                        }
1278
1279                        if (first) {
1280                                if (use_callchain) {
1281                                        ui_browser__printf(&browser->b, "%c ", folded_sign);
1282                                        width -= 2;
1283                                }
1284                                first = false;
1285                        } else {
1286                                ui_browser__printf(&browser->b, "  ");
1287                                width -= 2;
1288                        }
1289
1290                        if (fmt->color) {
1291                                int ret = fmt->color(fmt, &hpp, entry);
1292                                hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1293                                /*
1294                                 * fmt->color() already used ui_browser to
1295                                 * print the non alignment bits, skip it (+ret):
1296                                 */
1297                                ui_browser__printf(&browser->b, "%s", s + ret);
1298                        } else {
1299                                hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1300                                ui_browser__printf(&browser->b, "%s", s);
1301                        }
1302                        width -= hpp.buf - s;
1303                }
1304
1305                /* The scroll bar isn't being used */
1306                if (!browser->b.navkeypressed)
1307                        width += 1;
1308
1309                ui_browser__write_nstring(&browser->b, "", width);
1310
1311                ++row;
1312                ++printed;
1313        } else
1314                --row_offset;
1315
1316        if (folded_sign == '-' && row != browser->b.rows) {
1317                struct callchain_print_arg arg = {
1318                        .row_offset = row_offset,
1319                        .is_current_entry = current_entry,
1320                };
1321
1322                printed += hist_browser__show_callchain(browser,
1323                                entry, 1, row,
1324                                hist_browser__show_callchain_entry,
1325                                &arg,
1326                                hist_browser__check_output_full);
1327        }
1328
1329        return printed;
1330}
1331
1332static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1333                                              struct hist_entry *entry,
1334                                              unsigned short row,
1335                                              int level)
1336{
1337        int printed = 0;
1338        int width = browser->b.width;
1339        char folded_sign = ' ';
1340        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1341        off_t row_offset = entry->row_offset;
1342        bool first = true;
1343        struct perf_hpp_fmt *fmt;
1344        struct perf_hpp_list_node *fmt_node;
1345        struct hpp_arg arg = {
1346                .b              = &browser->b,
1347                .current_entry  = current_entry,
1348        };
1349        int column = 0;
1350        int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1351
1352        if (current_entry) {
1353                browser->he_selection = entry;
1354                browser->selection = &entry->ms;
1355        }
1356
1357        hist_entry__init_have_children(entry);
1358        folded_sign = hist_entry__folded(entry);
1359        arg.folded_sign = folded_sign;
1360
1361        if (entry->leaf && row_offset) {
1362                row_offset--;
1363                goto show_callchain;
1364        }
1365
1366        ui_browser__gotorc(&browser->b, row, 0);
1367
1368        if (current_entry && browser->b.navkeypressed)
1369                ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1370        else
1371                ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1372
1373        ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1374        width -= level * HIERARCHY_INDENT;
1375
1376        /* the first hpp_list_node is for overhead columns */
1377        fmt_node = list_first_entry(&entry->hists->hpp_formats,
1378                                    struct perf_hpp_list_node, list);
1379        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1380                char s[2048];
1381                struct perf_hpp hpp = {
1382                        .buf            = s,
1383                        .size           = sizeof(s),
1384                        .ptr            = &arg,
1385                };
1386
1387                if (perf_hpp__should_skip(fmt, entry->hists) ||
1388                    column++ < browser->b.horiz_scroll)
1389                        continue;
1390
1391                if (current_entry && browser->b.navkeypressed) {
1392                        ui_browser__set_color(&browser->b,
1393                                              HE_COLORSET_SELECTED);
1394                } else {
1395                        ui_browser__set_color(&browser->b,
1396                                              HE_COLORSET_NORMAL);
1397                }
1398
1399                if (first) {
1400                        ui_browser__printf(&browser->b, "%c ", folded_sign);
1401                        width -= 2;
1402                        first = false;
1403                } else {
1404                        ui_browser__printf(&browser->b, "  ");
1405                        width -= 2;
1406                }
1407
1408                if (fmt->color) {
1409                        int ret = fmt->color(fmt, &hpp, entry);
1410                        hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1411                        /*
1412                         * fmt->color() already used ui_browser to
1413                         * print the non alignment bits, skip it (+ret):
1414                         */
1415                        ui_browser__printf(&browser->b, "%s", s + ret);
1416                } else {
1417                        int ret = fmt->entry(fmt, &hpp, entry);
1418                        hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1419                        ui_browser__printf(&browser->b, "%s", s);
1420                }
1421                width -= hpp.buf - s;
1422        }
1423
1424        if (!first) {
1425                ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1426                width -= hierarchy_indent;
1427        }
1428
1429        if (column >= browser->b.horiz_scroll) {
1430                char s[2048];
1431                struct perf_hpp hpp = {
1432                        .buf            = s,
1433                        .size           = sizeof(s),
1434                        .ptr            = &arg,
1435                };
1436
1437                if (current_entry && browser->b.navkeypressed) {
1438                        ui_browser__set_color(&browser->b,
1439                                              HE_COLORSET_SELECTED);
1440                } else {
1441                        ui_browser__set_color(&browser->b,
1442                                              HE_COLORSET_NORMAL);
1443                }
1444
1445                perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1446                        if (first) {
1447                                ui_browser__printf(&browser->b, "%c ", folded_sign);
1448                                first = false;
1449                        } else {
1450                                ui_browser__write_nstring(&browser->b, "", 2);
1451                        }
1452
1453                        width -= 2;
1454
1455                        /*
1456                         * No need to call hist_entry__snprintf_alignment()
1457                         * since this fmt is always the last column in the
1458                         * hierarchy mode.
1459                         */
1460                        if (fmt->color) {
1461                                width -= fmt->color(fmt, &hpp, entry);
1462                        } else {
1463                                int i = 0;
1464
1465                                width -= fmt->entry(fmt, &hpp, entry);
1466                                ui_browser__printf(&browser->b, "%s", ltrim(s));
1467
1468                                while (isspace(s[i++]))
1469                                        width++;
1470                        }
1471                }
1472        }
1473
1474        /* The scroll bar isn't being used */
1475        if (!browser->b.navkeypressed)
1476                width += 1;
1477
1478        ui_browser__write_nstring(&browser->b, "", width);
1479
1480        ++row;
1481        ++printed;
1482
1483show_callchain:
1484        if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1485                struct callchain_print_arg carg = {
1486                        .row_offset = row_offset,
1487                };
1488
1489                printed += hist_browser__show_callchain(browser, entry,
1490                                        level + 1, row,
1491                                        hist_browser__show_callchain_entry, &carg,
1492                                        hist_browser__check_output_full);
1493        }
1494
1495        return printed;
1496}
1497
1498static int hist_browser__show_no_entry(struct hist_browser *browser,
1499                                       unsigned short row, int level)
1500{
1501        int width = browser->b.width;
1502        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1503        bool first = true;
1504        int column = 0;
1505        int ret;
1506        struct perf_hpp_fmt *fmt;
1507        struct perf_hpp_list_node *fmt_node;
1508        int indent = browser->hists->nr_hpp_node - 2;
1509
1510        if (current_entry) {
1511                browser->he_selection = NULL;
1512                browser->selection = NULL;
1513        }
1514
1515        ui_browser__gotorc(&browser->b, row, 0);
1516
1517        if (current_entry && browser->b.navkeypressed)
1518                ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1519        else
1520                ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1521
1522        ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1523        width -= level * HIERARCHY_INDENT;
1524
1525        /* the first hpp_list_node is for overhead columns */
1526        fmt_node = list_first_entry(&browser->hists->hpp_formats,
1527                                    struct perf_hpp_list_node, list);
1528        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1529                if (perf_hpp__should_skip(fmt, browser->hists) ||
1530                    column++ < browser->b.horiz_scroll)
1531                        continue;
1532
1533                ret = fmt->width(fmt, NULL, browser->hists);
1534
1535                if (first) {
1536                        /* for folded sign */
1537                        first = false;
1538                        ret++;
1539                } else {
1540                        /* space between columns */
1541                        ret += 2;
1542                }
1543
1544                ui_browser__write_nstring(&browser->b, "", ret);
1545                width -= ret;
1546        }
1547
1548        ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1549        width -= indent * HIERARCHY_INDENT;
1550
1551        if (column >= browser->b.horiz_scroll) {
1552                char buf[32];
1553
1554                ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1555                ui_browser__printf(&browser->b, "  %s", buf);
1556                width -= ret + 2;
1557        }
1558
1559        /* The scroll bar isn't being used */
1560        if (!browser->b.navkeypressed)
1561                width += 1;
1562
1563        ui_browser__write_nstring(&browser->b, "", width);
1564        return 1;
1565}
1566
1567static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1568{
1569        advance_hpp(hpp, inc);
1570        return hpp->size <= 0;
1571}
1572
1573static int
1574hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1575                                 size_t size, int line)
1576{
1577        struct hists *hists = browser->hists;
1578        struct perf_hpp dummy_hpp = {
1579                .buf    = buf,
1580                .size   = size,
1581        };
1582        struct perf_hpp_fmt *fmt;
1583        size_t ret = 0;
1584        int column = 0;
1585        int span = 0;
1586
1587        if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1588                ret = scnprintf(buf, size, "  ");
1589                if (advance_hpp_check(&dummy_hpp, ret))
1590                        return ret;
1591        }
1592
1593        hists__for_each_format(browser->hists, fmt) {
1594                if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1595                        continue;
1596
1597                ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1598                if (advance_hpp_check(&dummy_hpp, ret))
1599                        break;
1600
1601                if (span)
1602                        continue;
1603
1604                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1605                if (advance_hpp_check(&dummy_hpp, ret))
1606                        break;
1607        }
1608
1609        return ret;
1610}
1611
1612static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1613{
1614        struct hists *hists = browser->hists;
1615        struct perf_hpp dummy_hpp = {
1616                .buf    = buf,
1617                .size   = size,
1618        };
1619        struct perf_hpp_fmt *fmt;
1620        struct perf_hpp_list_node *fmt_node;
1621        size_t ret = 0;
1622        int column = 0;
1623        int indent = hists->nr_hpp_node - 2;
1624        bool first_node, first_col;
1625
1626        ret = scnprintf(buf, size, "  ");
1627        if (advance_hpp_check(&dummy_hpp, ret))
1628                return ret;
1629
1630        first_node = true;
1631        /* the first hpp_list_node is for overhead columns */
1632        fmt_node = list_first_entry(&hists->hpp_formats,
1633                                    struct perf_hpp_list_node, list);
1634        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1635                if (column++ < browser->b.horiz_scroll)
1636                        continue;
1637
1638                ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1639                if (advance_hpp_check(&dummy_hpp, ret))
1640                        break;
1641
1642                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1643                if (advance_hpp_check(&dummy_hpp, ret))
1644                        break;
1645
1646                first_node = false;
1647        }
1648
1649        if (!first_node) {
1650                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1651                                indent * HIERARCHY_INDENT, "");
1652                if (advance_hpp_check(&dummy_hpp, ret))
1653                        return ret;
1654        }
1655
1656        first_node = true;
1657        list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1658                if (!first_node) {
1659                        ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1660                        if (advance_hpp_check(&dummy_hpp, ret))
1661                                break;
1662                }
1663                first_node = false;
1664
1665                first_col = true;
1666                perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1667                        char *start;
1668
1669                        if (perf_hpp__should_skip(fmt, hists))
1670                                continue;
1671
1672                        if (!first_col) {
1673                                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1674                                if (advance_hpp_check(&dummy_hpp, ret))
1675                                        break;
1676                        }
1677                        first_col = false;
1678
1679                        ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1680                        dummy_hpp.buf[ret] = '\0';
1681
1682                        start = trim(dummy_hpp.buf);
1683                        ret = strlen(start);
1684
1685                        if (start != dummy_hpp.buf)
1686                                memmove(dummy_hpp.buf, start, ret + 1);
1687
1688                        if (advance_hpp_check(&dummy_hpp, ret))
1689                                break;
1690                }
1691        }
1692
1693        return ret;
1694}
1695
1696static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1697{
1698        char headers[1024];
1699
1700        hists_browser__scnprintf_hierarchy_headers(browser, headers,
1701                                                   sizeof(headers));
1702
1703        ui_browser__gotorc(&browser->b, 0, 0);
1704        ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1705        ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1706}
1707
1708static void hists_browser__headers(struct hist_browser *browser)
1709{
1710        struct hists *hists = browser->hists;
1711        struct perf_hpp_list *hpp_list = hists->hpp_list;
1712
1713        int line;
1714
1715        for (line = 0; line < hpp_list->nr_header_lines; line++) {
1716                char headers[1024];
1717
1718                hists_browser__scnprintf_headers(browser, headers,
1719                                                 sizeof(headers), line);
1720
1721                ui_browser__gotorc_title(&browser->b, line, 0);
1722                ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1723                ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1724        }
1725}
1726
1727static void hist_browser__show_headers(struct hist_browser *browser)
1728{
1729        if (symbol_conf.report_hierarchy)
1730                hists_browser__hierarchy_headers(browser);
1731        else
1732                hists_browser__headers(browser);
1733}
1734
1735static void ui_browser__hists_init_top(struct ui_browser *browser)
1736{
1737        if (browser->top == NULL) {
1738                struct hist_browser *hb;
1739
1740                hb = container_of(browser, struct hist_browser, b);
1741                browser->top = rb_first(&hb->hists->entries);
1742        }
1743}
1744
1745static unsigned int hist_browser__refresh(struct ui_browser *browser)
1746{
1747        unsigned row = 0;
1748        struct rb_node *nd;
1749        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1750
1751        if (hb->show_headers)
1752                hist_browser__show_headers(hb);
1753
1754        ui_browser__hists_init_top(browser);
1755        hb->he_selection = NULL;
1756        hb->selection = NULL;
1757
1758        for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1759                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1760                float percent;
1761
1762                if (h->filtered) {
1763                        /* let it move to sibling */
1764                        h->unfolded = false;
1765                        continue;
1766                }
1767
1768                percent = hist_entry__get_percent_limit(h);
1769                if (percent < hb->min_pcnt)
1770                        continue;
1771
1772                if (symbol_conf.report_hierarchy) {
1773                        row += hist_browser__show_hierarchy_entry(hb, h, row,
1774                                                                  h->depth);
1775                        if (row == browser->rows)
1776                                break;
1777
1778                        if (h->has_no_entry) {
1779                                hist_browser__show_no_entry(hb, row, h->depth + 1);
1780                                row++;
1781                        }
1782                } else {
1783                        row += hist_browser__show_entry(hb, h, row);
1784                }
1785
1786                if (row == browser->rows)
1787                        break;
1788        }
1789
1790        return row;
1791}
1792
1793static struct rb_node *hists__filter_entries(struct rb_node *nd,
1794                                             float min_pcnt)
1795{
1796        while (nd != NULL) {
1797                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1798                float percent = hist_entry__get_percent_limit(h);
1799
1800                if (!h->filtered && percent >= min_pcnt)
1801                        return nd;
1802
1803                /*
1804                 * If it's filtered, its all children also were filtered.
1805                 * So move to sibling node.
1806                 */
1807                if (rb_next(nd))
1808                        nd = rb_next(nd);
1809                else
1810                        nd = rb_hierarchy_next(nd);
1811        }
1812
1813        return NULL;
1814}
1815
1816static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1817                                                  float min_pcnt)
1818{
1819        while (nd != NULL) {
1820                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1821                float percent = hist_entry__get_percent_limit(h);
1822
1823                if (!h->filtered && percent >= min_pcnt)
1824                        return nd;
1825
1826                nd = rb_hierarchy_prev(nd);
1827        }
1828
1829        return NULL;
1830}
1831
1832static void ui_browser__hists_seek(struct ui_browser *browser,
1833                                   off_t offset, int whence)
1834{
1835        struct hist_entry *h;
1836        struct rb_node *nd;
1837        bool first = true;
1838        struct hist_browser *hb;
1839
1840        hb = container_of(browser, struct hist_browser, b);
1841
1842        if (browser->nr_entries == 0)
1843                return;
1844
1845        ui_browser__hists_init_top(browser);
1846
1847        switch (whence) {
1848        case SEEK_SET:
1849                nd = hists__filter_entries(rb_first(browser->entries),
1850                                           hb->min_pcnt);
1851                break;
1852        case SEEK_CUR:
1853                nd = browser->top;
1854                goto do_offset;
1855        case SEEK_END:
1856                nd = rb_hierarchy_last(rb_last(browser->entries));
1857                nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1858                first = false;
1859                break;
1860        default:
1861                return;
1862        }
1863
1864        /*
1865         * Moves not relative to the first visible entry invalidates its
1866         * row_offset:
1867         */
1868        h = rb_entry(browser->top, struct hist_entry, rb_node);
1869        h->row_offset = 0;
1870
1871        /*
1872         * Here we have to check if nd is expanded (+), if it is we can't go
1873         * the next top level hist_entry, instead we must compute an offset of
1874         * what _not_ to show and not change the first visible entry.
1875         *
1876         * This offset increments when we are going from top to bottom and
1877         * decreases when we're going from bottom to top.
1878         *
1879         * As we don't have backpointers to the top level in the callchains
1880         * structure, we need to always print the whole hist_entry callchain,
1881         * skipping the first ones that are before the first visible entry
1882         * and stop when we printed enough lines to fill the screen.
1883         */
1884do_offset:
1885        if (!nd)
1886                return;
1887
1888        if (offset > 0) {
1889                do {
1890                        h = rb_entry(nd, struct hist_entry, rb_node);
1891                        if (h->unfolded && h->leaf) {
1892                                u16 remaining = h->nr_rows - h->row_offset;
1893                                if (offset > remaining) {
1894                                        offset -= remaining;
1895                                        h->row_offset = 0;
1896                                } else {
1897                                        h->row_offset += offset;
1898                                        offset = 0;
1899                                        browser->top = nd;
1900                                        break;
1901                                }
1902                        }
1903                        nd = hists__filter_entries(rb_hierarchy_next(nd),
1904                                                   hb->min_pcnt);
1905                        if (nd == NULL)
1906                                break;
1907                        --offset;
1908                        browser->top = nd;
1909                } while (offset != 0);
1910        } else if (offset < 0) {
1911                while (1) {
1912                        h = rb_entry(nd, struct hist_entry, rb_node);
1913                        if (h->unfolded && h->leaf) {
1914                                if (first) {
1915                                        if (-offset > h->row_offset) {
1916                                                offset += h->row_offset;
1917                                                h->row_offset = 0;
1918                                        } else {
1919                                                h->row_offset += offset;
1920                                                offset = 0;
1921                                                browser->top = nd;
1922                                                break;
1923                                        }
1924                                } else {
1925                                        if (-offset > h->nr_rows) {
1926                                                offset += h->nr_rows;
1927                                                h->row_offset = 0;
1928                                        } else {
1929                                                h->row_offset = h->nr_rows + offset;
1930                                                offset = 0;
1931                                                browser->top = nd;
1932                                                break;
1933                                        }
1934                                }
1935                        }
1936
1937                        nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1938                                                        hb->min_pcnt);
1939                        if (nd == NULL)
1940                                break;
1941                        ++offset;
1942                        browser->top = nd;
1943                        if (offset == 0) {
1944                                /*
1945                                 * Last unfiltered hist_entry, check if it is
1946                                 * unfolded, if it is then we should have
1947                                 * row_offset at its last entry.
1948                                 */
1949                                h = rb_entry(nd, struct hist_entry, rb_node);
1950                                if (h->unfolded && h->leaf)
1951                                        h->row_offset = h->nr_rows;
1952                                break;
1953                        }
1954                        first = false;
1955                }
1956        } else {
1957                browser->top = nd;
1958                h = rb_entry(nd, struct hist_entry, rb_node);
1959                h->row_offset = 0;
1960        }
1961}
1962
1963static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1964                                           struct hist_entry *he, FILE *fp,
1965                                           int level)
1966{
1967        struct callchain_print_arg arg  = {
1968                .fp = fp,
1969        };
1970
1971        hist_browser__show_callchain(browser, he, level, 0,
1972                                     hist_browser__fprintf_callchain_entry, &arg,
1973                                     hist_browser__check_dump_full);
1974        return arg.printed;
1975}
1976
1977static int hist_browser__fprintf_entry(struct hist_browser *browser,
1978                                       struct hist_entry *he, FILE *fp)
1979{
1980        char s[8192];
1981        int printed = 0;
1982        char folded_sign = ' ';
1983        struct perf_hpp hpp = {
1984                .buf = s,
1985                .size = sizeof(s),
1986        };
1987        struct perf_hpp_fmt *fmt;
1988        bool first = true;
1989        int ret;
1990
1991        if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
1992                folded_sign = hist_entry__folded(he);
1993                printed += fprintf(fp, "%c ", folded_sign);
1994        }
1995
1996        hists__for_each_format(browser->hists, fmt) {
1997                if (perf_hpp__should_skip(fmt, he->hists))
1998                        continue;
1999
2000                if (!first) {
2001                        ret = scnprintf(hpp.buf, hpp.size, "  ");
2002                        advance_hpp(&hpp, ret);
2003                } else
2004                        first = false;
2005
2006                ret = fmt->entry(fmt, &hpp, he);
2007                ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2008                advance_hpp(&hpp, ret);
2009        }
2010        printed += fprintf(fp, "%s\n", s);
2011
2012        if (folded_sign == '-')
2013                printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2014
2015        return printed;
2016}
2017
2018
2019static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2020                                                 struct hist_entry *he,
2021                                                 FILE *fp, int level)
2022{
2023        char s[8192];
2024        int printed = 0;
2025        char folded_sign = ' ';
2026        struct perf_hpp hpp = {
2027                .buf = s,
2028                .size = sizeof(s),
2029        };
2030        struct perf_hpp_fmt *fmt;
2031        struct perf_hpp_list_node *fmt_node;
2032        bool first = true;
2033        int ret;
2034        int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2035
2036        printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2037
2038        folded_sign = hist_entry__folded(he);
2039        printed += fprintf(fp, "%c", folded_sign);
2040
2041        /* the first hpp_list_node is for overhead columns */
2042        fmt_node = list_first_entry(&he->hists->hpp_formats,
2043                                    struct perf_hpp_list_node, list);
2044        perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2045                if (!first) {
2046                        ret = scnprintf(hpp.buf, hpp.size, "  ");
2047                        advance_hpp(&hpp, ret);
2048                } else
2049                        first = false;
2050
2051                ret = fmt->entry(fmt, &hpp, he);
2052                advance_hpp(&hpp, ret);
2053        }
2054
2055        ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2056        advance_hpp(&hpp, ret);
2057
2058        perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2059                ret = scnprintf(hpp.buf, hpp.size, "  ");
2060                advance_hpp(&hpp, ret);
2061
2062                ret = fmt->entry(fmt, &hpp, he);
2063                advance_hpp(&hpp, ret);
2064        }
2065
2066        printed += fprintf(fp, "%s\n", rtrim(s));
2067
2068        if (he->leaf && folded_sign == '-') {
2069                printed += hist_browser__fprintf_callchain(browser, he, fp,
2070                                                           he->depth + 1);
2071        }
2072
2073        return printed;
2074}
2075
2076static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2077{
2078        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2079                                                   browser->min_pcnt);
2080        int printed = 0;
2081
2082        while (nd) {
2083                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2084
2085                if (symbol_conf.report_hierarchy) {
2086                        printed += hist_browser__fprintf_hierarchy_entry(browser,
2087                                                                         h, fp,
2088                                                                         h->depth);
2089                } else {
2090                        printed += hist_browser__fprintf_entry(browser, h, fp);
2091                }
2092
2093                nd = hists__filter_entries(rb_hierarchy_next(nd),
2094                                           browser->min_pcnt);
2095        }
2096
2097        return printed;
2098}
2099
2100static int hist_browser__dump(struct hist_browser *browser)
2101{
2102        char filename[64];
2103        FILE *fp;
2104
2105        while (1) {
2106                scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2107                if (access(filename, F_OK))
2108                        break;
2109                /*
2110                 * XXX: Just an arbitrary lazy upper limit
2111                 */
2112                if (++browser->print_seq == 8192) {
2113                        ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2114                        return -1;
2115                }
2116        }
2117
2118        fp = fopen(filename, "w");
2119        if (fp == NULL) {
2120                char bf[64];
2121                const char *err = str_error_r(errno, bf, sizeof(bf));
2122                ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2123                return -1;
2124        }
2125
2126        ++browser->print_seq;
2127        hist_browser__fprintf(browser, fp);
2128        fclose(fp);
2129        ui_helpline__fpush("%s written!", filename);
2130
2131        return 0;
2132}
2133
2134void hist_browser__init(struct hist_browser *browser,
2135                        struct hists *hists)
2136{
2137        struct perf_hpp_fmt *fmt;
2138
2139        browser->hists                  = hists;
2140        browser->b.refresh              = hist_browser__refresh;
2141        browser->b.refresh_dimensions   = hist_browser__refresh_dimensions;
2142        browser->b.seek                 = ui_browser__hists_seek;
2143        browser->b.use_navkeypressed    = true;
2144        browser->show_headers           = symbol_conf.show_hist_headers;
2145        hist_browser__set_title_space(browser);
2146
2147        if (symbol_conf.report_hierarchy) {
2148                struct perf_hpp_list_node *fmt_node;
2149
2150                /* count overhead columns (in the first node) */
2151                fmt_node = list_first_entry(&hists->hpp_formats,
2152                                            struct perf_hpp_list_node, list);
2153                perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2154                        ++browser->b.columns;
2155
2156                /* add a single column for whole hierarchy sort keys*/
2157                ++browser->b.columns;
2158        } else {
2159                hists__for_each_format(hists, fmt)
2160                        ++browser->b.columns;
2161        }
2162
2163        hists__reset_column_width(hists);
2164}
2165
2166struct hist_browser *hist_browser__new(struct hists *hists)
2167{
2168        struct hist_browser *browser = zalloc(sizeof(*browser));
2169
2170        if (browser)
2171                hist_browser__init(browser, hists);
2172
2173        return browser;
2174}
2175
2176static struct hist_browser *
2177perf_evsel_browser__new(struct perf_evsel *evsel,
2178                        struct hist_browser_timer *hbt,
2179                        struct perf_env *env,
2180                        struct annotation_options *annotation_opts)
2181{
2182        struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2183
2184        if (browser) {
2185                browser->hbt   = hbt;
2186                browser->env   = env;
2187                browser->title = hists_browser__scnprintf_title;
2188                browser->annotation_opts = annotation_opts;
2189        }
2190        return browser;
2191}
2192
2193void hist_browser__delete(struct hist_browser *browser)
2194{
2195        free(browser);
2196}
2197
2198static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2199{
2200        return browser->he_selection;
2201}
2202
2203static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2204{
2205        return browser->he_selection->thread;
2206}
2207
2208/* Check whether the browser is for 'top' or 'report' */
2209static inline bool is_report_browser(void *timer)
2210{
2211        return timer == NULL;
2212}
2213
2214static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2215{
2216        struct hist_browser_timer *hbt = browser->hbt;
2217        int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2218
2219        if (!is_report_browser(hbt)) {
2220                struct perf_top *top = hbt->arg;
2221
2222                if (top->zero)
2223                        printed += scnprintf(bf + printed, size - printed, " [z]");
2224        }
2225
2226        return printed;
2227}
2228
2229static inline void free_popup_options(char **options, int n)
2230{
2231        int i;
2232
2233        for (i = 0; i < n; ++i)
2234                zfree(&options[i]);
2235}
2236
2237/*
2238 * Only runtime switching of perf data file will make "input_name" point
2239 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2240 * whether we need to call free() for current "input_name" during the switch.
2241 */
2242static bool is_input_name_malloced = false;
2243
2244static int switch_data_file(void)
2245{
2246        char *pwd, *options[32], *abs_path[32], *tmp;
2247        DIR *pwd_dir;
2248        int nr_options = 0, choice = -1, ret = -1;
2249        struct dirent *dent;
2250
2251        pwd = getenv("PWD");
2252        if (!pwd)
2253                return ret;
2254
2255        pwd_dir = opendir(pwd);
2256        if (!pwd_dir)
2257                return ret;
2258
2259        memset(options, 0, sizeof(options));
2260        memset(abs_path, 0, sizeof(abs_path));
2261
2262        while ((dent = readdir(pwd_dir))) {
2263                char path[PATH_MAX];
2264                u64 magic;
2265                char *name = dent->d_name;
2266                FILE *file;
2267
2268                if (!(dent->d_type == DT_REG))
2269                        continue;
2270
2271                snprintf(path, sizeof(path), "%s/%s", pwd, name);
2272
2273                file = fopen(path, "r");
2274                if (!file)
2275                        continue;
2276
2277                if (fread(&magic, 1, 8, file) < 8)
2278                        goto close_file_and_continue;
2279
2280                if (is_perf_magic(magic)) {
2281                        options[nr_options] = strdup(name);
2282                        if (!options[nr_options])
2283                                goto close_file_and_continue;
2284
2285                        abs_path[nr_options] = strdup(path);
2286                        if (!abs_path[nr_options]) {
2287                                zfree(&options[nr_options]);
2288                                ui__warning("Can't search all data files due to memory shortage.\n");
2289                                fclose(file);
2290                                break;
2291                        }
2292
2293                        nr_options++;
2294                }
2295
2296close_file_and_continue:
2297                fclose(file);
2298                if (nr_options >= 32) {
2299                        ui__warning("Too many perf data files in PWD!\n"
2300                                    "Only the first 32 files will be listed.\n");
2301                        break;
2302                }
2303        }
2304        closedir(pwd_dir);
2305
2306        if (nr_options) {
2307                choice = ui__popup_menu(nr_options, options);
2308                if (choice < nr_options && choice >= 0) {
2309                        tmp = strdup(abs_path[choice]);
2310                        if (tmp) {
2311                                if (is_input_name_malloced)
2312                                        free((void *)input_name);
2313                                input_name = tmp;
2314                                is_input_name_malloced = true;
2315                                ret = 0;
2316                        } else
2317                                ui__warning("Data switch failed due to memory shortage!\n");
2318                }
2319        }
2320
2321        free_popup_options(options, nr_options);
2322        free_popup_options(abs_path, nr_options);
2323        return ret;
2324}
2325
2326struct popup_action {
2327        struct thread           *thread;
2328        struct map_symbol       ms;
2329        int                     socket;
2330
2331        int (*fn)(struct hist_browser *browser, struct popup_action *act);
2332};
2333
2334static int
2335do_annotate(struct hist_browser *browser, struct popup_action *act)
2336{
2337        struct perf_evsel *evsel;
2338        struct annotation *notes;
2339        struct hist_entry *he;
2340        int err;
2341
2342        if (!browser->annotation_opts->objdump_path &&
2343            perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2344                return 0;
2345
2346        notes = symbol__annotation(act->ms.sym);
2347        if (!notes->src)
2348                return 0;
2349
2350        evsel = hists_to_evsel(browser->hists);
2351        err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2352                                       browser->annotation_opts);
2353        he = hist_browser__selected_entry(browser);
2354        /*
2355         * offer option to annotate the other branch source or target
2356         * (if they exists) when returning from annotate
2357         */
2358        if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2359                return 1;
2360
2361        ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2362        if (err)
2363                ui_browser__handle_resize(&browser->b);
2364        return 0;
2365}
2366
2367static int
2368add_annotate_opt(struct hist_browser *browser __maybe_unused,
2369                 struct popup_action *act, char **optstr,
2370                 struct map *map, struct symbol *sym)
2371{
2372        if (sym == NULL || map->dso->annotate_warned)
2373                return 0;
2374
2375        if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2376                return 0;
2377
2378        act->ms.map = map;
2379        act->ms.sym = sym;
2380        act->fn = do_annotate;
2381        return 1;
2382}
2383
2384static int
2385do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2386{
2387        struct thread *thread = act->thread;
2388
2389        if ((!hists__has(browser->hists, thread) &&
2390             !hists__has(browser->hists, comm)) || thread == NULL)
2391                return 0;
2392
2393        if (browser->hists->thread_filter) {
2394                pstack__remove(browser->pstack, &browser->hists->thread_filter);
2395                perf_hpp__set_elide(HISTC_THREAD, false);
2396                thread__zput(browser->hists->thread_filter);
2397                ui_helpline__pop();
2398        } else {
2399                if (hists__has(browser->hists, thread)) {
2400                        ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2401                                           thread->comm_set ? thread__comm_str(thread) : "",
2402                                           thread->tid);
2403                } else {
2404                        ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2405                                           thread->comm_set ? thread__comm_str(thread) : "");
2406                }
2407
2408                browser->hists->thread_filter = thread__get(thread);
2409                perf_hpp__set_elide(HISTC_THREAD, false);
2410                pstack__push(browser->pstack, &browser->hists->thread_filter);
2411        }
2412
2413        hists__filter_by_thread(browser->hists);
2414        hist_browser__reset(browser);
2415        return 0;
2416}
2417
2418static int
2419add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2420               char **optstr, struct thread *thread)
2421{
2422        int ret;
2423
2424        if ((!hists__has(browser->hists, thread) &&
2425             !hists__has(browser->hists, comm)) || thread == NULL)
2426                return 0;
2427
2428        if (hists__has(browser->hists, thread)) {
2429                ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2430                               browser->hists->thread_filter ? "out of" : "into",
2431                               thread->comm_set ? thread__comm_str(thread) : "",
2432                               thread->tid);
2433        } else {
2434                ret = asprintf(optstr, "Zoom %s %s thread",
2435                               browser->hists->thread_filter ? "out of" : "into",
2436                               thread->comm_set ? thread__comm_str(thread) : "");
2437        }
2438        if (ret < 0)
2439                return 0;
2440
2441        act->thread = thread;
2442        act->fn = do_zoom_thread;
2443        return 1;
2444}
2445
2446static int
2447do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2448{
2449        struct map *map = act->ms.map;
2450
2451        if (!hists__has(browser->hists, dso) || map == NULL)
2452                return 0;
2453
2454        if (browser->hists->dso_filter) {
2455                pstack__remove(browser->pstack, &browser->hists->dso_filter);
2456                perf_hpp__set_elide(HISTC_DSO, false);
2457                browser->hists->dso_filter = NULL;
2458                ui_helpline__pop();
2459        } else {
2460                ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2461                                   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2462                browser->hists->dso_filter = map->dso;
2463                perf_hpp__set_elide(HISTC_DSO, true);
2464                pstack__push(browser->pstack, &browser->hists->dso_filter);
2465        }
2466
2467        hists__filter_by_dso(browser->hists);
2468        hist_browser__reset(browser);
2469        return 0;
2470}
2471
2472static int
2473add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2474            char **optstr, struct map *map)
2475{
2476        if (!hists__has(browser->hists, dso) || map == NULL)
2477                return 0;
2478
2479        if (asprintf(optstr, "Zoom %s %s DSO",
2480                     browser->hists->dso_filter ? "out of" : "into",
2481                     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2482                return 0;
2483
2484        act->ms.map = map;
2485        act->fn = do_zoom_dso;
2486        return 1;
2487}
2488
2489static int
2490do_browse_map(struct hist_browser *browser __maybe_unused,
2491              struct popup_action *act)
2492{
2493        map__browse(act->ms.map);
2494        return 0;
2495}
2496
2497static int
2498add_map_opt(struct hist_browser *browser,
2499            struct popup_action *act, char **optstr, struct map *map)
2500{
2501        if (!hists__has(browser->hists, dso) || map == NULL)
2502                return 0;
2503
2504        if (asprintf(optstr, "Browse map details") < 0)
2505                return 0;
2506
2507        act->ms.map = map;
2508        act->fn = do_browse_map;
2509        return 1;
2510}
2511
2512static int
2513do_run_script(struct hist_browser *browser __maybe_unused,
2514              struct popup_action *act)
2515{
2516        char script_opt[64];
2517        memset(script_opt, 0, sizeof(script_opt));
2518
2519        if (act->thread) {
2520                scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2521                          thread__comm_str(act->thread));
2522        } else if (act->ms.sym) {
2523                scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2524                          act->ms.sym->name);
2525        }
2526
2527        script_browse(script_opt);
2528        return 0;
2529}
2530
2531static int
2532add_script_opt(struct hist_browser *browser __maybe_unused,
2533               struct popup_action *act, char **optstr,
2534               struct thread *thread, struct symbol *sym)
2535{
2536        if (thread) {
2537                if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2538                             thread__comm_str(thread)) < 0)
2539                        return 0;
2540        } else if (sym) {
2541                if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2542                             sym->name) < 0)
2543                        return 0;
2544        } else {
2545                if (asprintf(optstr, "Run scripts for all samples") < 0)
2546                        return 0;
2547        }
2548
2549        act->thread = thread;
2550        act->ms.sym = sym;
2551        act->fn = do_run_script;
2552        return 1;
2553}
2554
2555static int
2556do_switch_data(struct hist_browser *browser __maybe_unused,
2557               struct popup_action *act __maybe_unused)
2558{
2559        if (switch_data_file()) {
2560                ui__warning("Won't switch the data files due to\n"
2561                            "no valid data file get selected!\n");
2562                return 0;
2563        }
2564
2565        return K_SWITCH_INPUT_DATA;
2566}
2567
2568static int
2569add_switch_opt(struct hist_browser *browser,
2570               struct popup_action *act, char **optstr)
2571{
2572        if (!is_report_browser(browser->hbt))
2573                return 0;
2574
2575        if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2576                return 0;
2577
2578        act->fn = do_switch_data;
2579        return 1;
2580}
2581
2582static int
2583do_exit_browser(struct hist_browser *browser __maybe_unused,
2584                struct popup_action *act __maybe_unused)
2585{
2586        return 0;
2587}
2588
2589static int
2590add_exit_opt(struct hist_browser *browser __maybe_unused,
2591             struct popup_action *act, char **optstr)
2592{
2593        if (asprintf(optstr, "Exit") < 0)
2594                return 0;
2595
2596        act->fn = do_exit_browser;
2597        return 1;
2598}
2599
2600static int
2601do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2602{
2603        if (!hists__has(browser->hists, socket) || act->socket < 0)
2604                return 0;
2605
2606        if (browser->hists->socket_filter > -1) {
2607                pstack__remove(browser->pstack, &browser->hists->socket_filter);
2608                browser->hists->socket_filter = -1;
2609                perf_hpp__set_elide(HISTC_SOCKET, false);
2610        } else {
2611                browser->hists->socket_filter = act->socket;
2612                perf_hpp__set_elide(HISTC_SOCKET, true);
2613                pstack__push(browser->pstack, &browser->hists->socket_filter);
2614        }
2615
2616        hists__filter_by_socket(browser->hists);
2617        hist_browser__reset(browser);
2618        return 0;
2619}
2620
2621static int
2622add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2623               char **optstr, int socket_id)
2624{
2625        if (!hists__has(browser->hists, socket) || socket_id < 0)
2626                return 0;
2627
2628        if (asprintf(optstr, "Zoom %s Processor Socket %d",
2629                     (browser->hists->socket_filter > -1) ? "out of" : "into",
2630                     socket_id) < 0)
2631                return 0;
2632
2633        act->socket = socket_id;
2634        act->fn = do_zoom_socket;
2635        return 1;
2636}
2637
2638static void hist_browser__update_nr_entries(struct hist_browser *hb)
2639{
2640        u64 nr_entries = 0;
2641        struct rb_node *nd = rb_first(&hb->hists->entries);
2642
2643        if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2644                hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2645                return;
2646        }
2647
2648        while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2649                nr_entries++;
2650                nd = rb_hierarchy_next(nd);
2651        }
2652
2653        hb->nr_non_filtered_entries = nr_entries;
2654        hb->nr_hierarchy_entries = nr_entries;
2655}
2656
2657static void hist_browser__update_percent_limit(struct hist_browser *hb,
2658                                               double percent)
2659{
2660        struct hist_entry *he;
2661        struct rb_node *nd = rb_first(&hb->hists->entries);
2662        u64 total = hists__total_period(hb->hists);
2663        u64 min_callchain_hits = total * (percent / 100);
2664
2665        hb->min_pcnt = callchain_param.min_percent = percent;
2666
2667        while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2668                he = rb_entry(nd, struct hist_entry, rb_node);
2669
2670                if (he->has_no_entry) {
2671                        he->has_no_entry = false;
2672                        he->nr_rows = 0;
2673                }
2674
2675                if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2676                        goto next;
2677
2678                if (callchain_param.mode == CHAIN_GRAPH_REL) {
2679                        total = he->stat.period;
2680
2681                        if (symbol_conf.cumulate_callchain)
2682                                total = he->stat_acc->period;
2683
2684                        min_callchain_hits = total * (percent / 100);
2685                }
2686
2687                callchain_param.sort(&he->sorted_chain, he->callchain,
2688                                     min_callchain_hits, &callchain_param);
2689
2690next:
2691                nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2692
2693                /* force to re-evaluate folding state of callchains */
2694                he->init_have_children = false;
2695                hist_entry__set_folding(he, hb, false);
2696        }
2697}
2698
2699static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2700                                    const char *helpline,
2701                                    bool left_exits,
2702                                    struct hist_browser_timer *hbt,
2703                                    float min_pcnt,
2704                                    struct perf_env *env,
2705                                    bool warn_lost_event,
2706                                    struct annotation_options *annotation_opts)
2707{
2708        struct hists *hists = evsel__hists(evsel);
2709        struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2710        struct branch_info *bi;
2711#define MAX_OPTIONS  16
2712        char *options[MAX_OPTIONS];
2713        struct popup_action actions[MAX_OPTIONS];
2714        int nr_options = 0;
2715        int key = -1;
2716        char buf[64];
2717        int delay_secs = hbt ? hbt->refresh : 0;
2718
2719#define HIST_BROWSER_HELP_COMMON                                        \
2720        "h/?/F1        Show this window\n"                              \
2721        "UP/DOWN/PGUP\n"                                                \
2722        "PGDN/SPACE    Navigate\n"                                      \
2723        "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
2724        "For multiple event sessions:\n\n"                              \
2725        "TAB/UNTAB     Switch events\n\n"                               \
2726        "For symbolic views (--sort has sym):\n\n"                      \
2727        "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2728        "ESC           Zoom out\n"                                      \
2729        "a             Annotate current symbol\n"                       \
2730        "C             Collapse all callchains\n"                       \
2731        "d             Zoom into current DSO\n"                         \
2732        "E             Expand all callchains\n"                         \
2733        "F             Toggle percentage of filtered entries\n"         \
2734        "H             Display column headers\n"                        \
2735        "L             Change percent limit\n"                          \
2736        "m             Display context menu\n"                          \
2737        "S             Zoom into current Processor Socket\n"            \
2738
2739        /* help messages are sorted by lexical order of the hotkey */
2740        const char report_help[] = HIST_BROWSER_HELP_COMMON
2741        "i             Show header information\n"
2742        "P             Print histograms to perf.hist.N\n"
2743        "r             Run available scripts\n"
2744        "s             Switch to another data file in PWD\n"
2745        "t             Zoom into current Thread\n"
2746        "V             Verbose (DSO names in callchains, etc)\n"
2747        "/             Filter symbol by name";
2748        const char top_help[] = HIST_BROWSER_HELP_COMMON
2749        "P             Print histograms to perf.hist.N\n"
2750        "t             Zoom into current Thread\n"
2751        "V             Verbose (DSO names in callchains, etc)\n"
2752        "z             Toggle zeroing of samples\n"
2753        "f             Enable/Disable events\n"
2754        "/             Filter symbol by name";
2755
2756        if (browser == NULL)
2757                return -1;
2758
2759        /* reset abort key so that it can get Ctrl-C as a key */
2760        SLang_reset_tty();
2761        SLang_init_tty(0, 0, 0);
2762
2763        if (min_pcnt)
2764                browser->min_pcnt = min_pcnt;
2765        hist_browser__update_nr_entries(browser);
2766
2767        browser->pstack = pstack__new(3);
2768        if (browser->pstack == NULL)
2769                goto out;
2770
2771        ui_helpline__push(helpline);
2772
2773        memset(options, 0, sizeof(options));
2774        memset(actions, 0, sizeof(actions));
2775
2776        if (symbol_conf.col_width_list_str)
2777                perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2778
2779        while (1) {
2780                struct thread *thread = NULL;
2781                struct map *map = NULL;
2782                int choice = 0;
2783                int socked_id = -1;
2784
2785                nr_options = 0;
2786
2787                key = hist_browser__run(browser, helpline,
2788                                        warn_lost_event);
2789
2790                if (browser->he_selection != NULL) {
2791                        thread = hist_browser__selected_thread(browser);
2792                        map = browser->selection->map;
2793                        socked_id = browser->he_selection->socket;
2794                }
2795                switch (key) {
2796                case K_TAB:
2797                case K_UNTAB:
2798                        if (nr_events == 1)
2799                                continue;
2800                        /*
2801                         * Exit the browser, let hists__browser_tree
2802                         * go to the next or previous
2803                         */
2804                        goto out_free_stack;
2805                case 'a':
2806                        if (!hists__has(hists, sym)) {
2807                                ui_browser__warning(&browser->b, delay_secs * 2,
2808                        "Annotation is only available for symbolic views, "
2809                        "include \"sym*\" in --sort to use it.");
2810                                continue;
2811                        }
2812
2813                        if (browser->selection == NULL ||
2814                            browser->selection->sym == NULL ||
2815                            browser->selection->map->dso->annotate_warned)
2816                                continue;
2817
2818                        actions->ms.map = browser->selection->map;
2819                        actions->ms.sym = browser->selection->sym;
2820                        do_annotate(browser, actions);
2821                        continue;
2822                case 'P':
2823                        hist_browser__dump(browser);
2824                        continue;
2825                case 'd':
2826                        actions->ms.map = map;
2827                        do_zoom_dso(browser, actions);
2828                        continue;
2829                case 'V':
2830                        verbose = (verbose + 1) % 4;
2831                        browser->show_dso = verbose > 0;
2832                        ui_helpline__fpush("Verbosity level set to %d\n",
2833                                           verbose);
2834                        continue;
2835                case 't':
2836                        actions->thread = thread;
2837                        do_zoom_thread(browser, actions);
2838                        continue;
2839                case 'S':
2840                        actions->socket = socked_id;
2841                        do_zoom_socket(browser, actions);
2842                        continue;
2843                case '/':
2844                        if (ui_browser__input_window("Symbol to show",
2845                                        "Please enter the name of symbol you want to see.\n"
2846                                        "To remove the filter later, press / + ENTER.",
2847                                        buf, "ENTER: OK, ESC: Cancel",
2848                                        delay_secs * 2) == K_ENTER) {
2849                                hists->symbol_filter_str = *buf ? buf : NULL;
2850                                hists__filter_by_symbol(hists);
2851                                hist_browser__reset(browser);
2852                        }
2853                        continue;
2854                case 'r':
2855                        if (is_report_browser(hbt)) {
2856                                actions->thread = NULL;
2857                                actions->ms.sym = NULL;
2858                                do_run_script(browser, actions);
2859                        }
2860                        continue;
2861                case 's':
2862                        if (is_report_browser(hbt)) {
2863                                key = do_switch_data(browser, actions);
2864                                if (key == K_SWITCH_INPUT_DATA)
2865                                        goto out_free_stack;
2866                        }
2867                        continue;
2868                case 'i':
2869                        /* env->arch is NULL for live-mode (i.e. perf top) */
2870                        if (env->arch)
2871                                tui__header_window(env);
2872                        continue;
2873                case 'F':
2874                        symbol_conf.filter_relative ^= 1;
2875                        continue;
2876                case 'z':
2877                        if (!is_report_browser(hbt)) {
2878                                struct perf_top *top = hbt->arg;
2879
2880                                top->zero = !top->zero;
2881                        }
2882                        continue;
2883                case 'L':
2884                        if (ui_browser__input_window("Percent Limit",
2885                                        "Please enter the value you want to hide entries under that percent.",
2886                                        buf, "ENTER: OK, ESC: Cancel",
2887                                        delay_secs * 2) == K_ENTER) {
2888                                char *end;
2889                                double new_percent = strtod(buf, &end);
2890
2891                                if (new_percent < 0 || new_percent > 100) {
2892                                        ui_browser__warning(&browser->b, delay_secs * 2,
2893                                                "Invalid percent: %.2f", new_percent);
2894                                        continue;
2895                                }
2896
2897                                hist_browser__update_percent_limit(browser, new_percent);
2898                                hist_browser__reset(browser);
2899                        }
2900                        continue;
2901                case K_F1:
2902                case 'h':
2903                case '?':
2904                        ui_browser__help_window(&browser->b,
2905                                is_report_browser(hbt) ? report_help : top_help);
2906                        continue;
2907                case K_ENTER:
2908                case K_RIGHT:
2909                case 'm':
2910                        /* menu */
2911                        break;
2912                case K_ESC:
2913                case K_LEFT: {
2914                        const void *top;
2915
2916                        if (pstack__empty(browser->pstack)) {
2917                                /*
2918                                 * Go back to the perf_evsel_menu__run or other user
2919                                 */
2920                                if (left_exits)
2921                                        goto out_free_stack;
2922
2923                                if (key == K_ESC &&
2924                                    ui_browser__dialog_yesno(&browser->b,
2925                                                             "Do you really want to exit?"))
2926                                        goto out_free_stack;
2927
2928                                continue;
2929                        }
2930                        top = pstack__peek(browser->pstack);
2931                        if (top == &browser->hists->dso_filter) {
2932                                /*
2933                                 * No need to set actions->dso here since
2934                                 * it's just to remove the current filter.
2935                                 * Ditto for thread below.
2936                                 */
2937                                do_zoom_dso(browser, actions);
2938                        } else if (top == &browser->hists->thread_filter) {
2939                                do_zoom_thread(browser, actions);
2940                        } else if (top == &browser->hists->socket_filter) {
2941                                do_zoom_socket(browser, actions);
2942                        }
2943                        continue;
2944                }
2945                case 'q':
2946                case CTRL('c'):
2947                        goto out_free_stack;
2948                case 'f':
2949                        if (!is_report_browser(hbt)) {
2950                                struct perf_top *top = hbt->arg;
2951
2952                                perf_evlist__toggle_enable(top->evlist);
2953                                /*
2954                                 * No need to refresh, resort/decay histogram
2955                                 * entries if we are not collecting samples:
2956                                 */
2957                                if (top->evlist->enabled) {
2958                                        helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2959                                        hbt->refresh = delay_secs;
2960                                } else {
2961                                        helpline = "Press 'f' again to re-enable the events";
2962                                        hbt->refresh = 0;
2963                                }
2964                                continue;
2965                        }
2966                        /* Fall thru */
2967                default:
2968                        helpline = "Press '?' for help on key bindings";
2969                        continue;
2970                }
2971
2972                if (!hists__has(hists, sym) || browser->selection == NULL)
2973                        goto skip_annotation;
2974
2975                if (sort__mode == SORT_MODE__BRANCH) {
2976                        bi = browser->he_selection->branch_info;
2977
2978                        if (bi == NULL)
2979                                goto skip_annotation;
2980
2981                        nr_options += add_annotate_opt(browser,
2982                                                       &actions[nr_options],
2983                                                       &options[nr_options],
2984                                                       bi->from.map,
2985                                                       bi->from.sym);
2986                        if (bi->to.sym != bi->from.sym)
2987                                nr_options += add_annotate_opt(browser,
2988                                                        &actions[nr_options],
2989                                                        &options[nr_options],
2990                                                        bi->to.map,
2991                                                        bi->to.sym);
2992                } else {
2993                        nr_options += add_annotate_opt(browser,
2994                                                       &actions[nr_options],
2995                                                       &options[nr_options],
2996                                                       browser->selection->map,
2997                                                       browser->selection->sym);
2998                }
2999skip_annotation:
3000                nr_options += add_thread_opt(browser, &actions[nr_options],
3001                                             &options[nr_options], thread);
3002                nr_options += add_dso_opt(browser, &actions[nr_options],
3003                                          &options[nr_options], map);
3004                nr_options += add_map_opt(browser, &actions[nr_options],
3005                                          &options[nr_options],
3006                                          browser->selection ?
3007                                                browser->selection->map : NULL);
3008                nr_options += add_socket_opt(browser, &actions[nr_options],
3009                                             &options[nr_options],
3010                                             socked_id);
3011                /* perf script support */
3012                if (!is_report_browser(hbt))
3013                        goto skip_scripting;
3014
3015                if (browser->he_selection) {
3016                        if (hists__has(hists, thread) && thread) {
3017                                nr_options += add_script_opt(browser,
3018                                                             &actions[nr_options],
3019                                                             &options[nr_options],
3020                                                             thread, NULL);
3021                        }
3022                        /*
3023                         * Note that browser->selection != NULL
3024                         * when browser->he_selection is not NULL,
3025                         * so we don't need to check browser->selection
3026                         * before fetching browser->selection->sym like what
3027                         * we do before fetching browser->selection->map.
3028                         *
3029                         * See hist_browser__show_entry.
3030                         */
3031                        if (hists__has(hists, sym) && browser->selection->sym) {
3032                                nr_options += add_script_opt(browser,
3033                                                             &actions[nr_options],
3034                                                             &options[nr_options],
3035                                                             NULL, browser->selection->sym);
3036                        }
3037                }
3038                nr_options += add_script_opt(browser, &actions[nr_options],
3039                                             &options[nr_options], NULL, NULL);
3040                nr_options += add_switch_opt(browser, &actions[nr_options],
3041                                             &options[nr_options]);
3042skip_scripting:
3043                nr_options += add_exit_opt(browser, &actions[nr_options],
3044                                           &options[nr_options]);
3045
3046                do {
3047                        struct popup_action *act;
3048
3049                        choice = ui__popup_menu(nr_options, options);
3050                        if (choice == -1 || choice >= nr_options)
3051                                break;
3052
3053                        act = &actions[choice];
3054                        key = act->fn(browser, act);
3055                } while (key == 1);
3056
3057                if (key == K_SWITCH_INPUT_DATA)
3058                        break;
3059        }
3060out_free_stack:
3061        pstack__delete(browser->pstack);
3062out:
3063        hist_browser__delete(browser);
3064        free_popup_options(options, MAX_OPTIONS);
3065        return key;
3066}
3067
3068struct perf_evsel_menu {
3069        struct ui_browser b;
3070        struct perf_evsel *selection;
3071        struct annotation_options *annotation_opts;
3072        bool lost_events, lost_events_warned;
3073        float min_pcnt;
3074        struct perf_env *env;
3075};
3076
3077static void perf_evsel_menu__write(struct ui_browser *browser,
3078                                   void *entry, int row)
3079{
3080        struct perf_evsel_menu *menu = container_of(browser,
3081                                                    struct perf_evsel_menu, b);
3082        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3083        struct hists *hists = evsel__hists(evsel);
3084        bool current_entry = ui_browser__is_current_entry(browser, row);
3085        unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3086        const char *ev_name = perf_evsel__name(evsel);
3087        char bf[256], unit;
3088        const char *warn = " ";
3089        size_t printed;
3090
3091        ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3092                                                       HE_COLORSET_NORMAL);
3093
3094        if (perf_evsel__is_group_event(evsel)) {
3095                struct perf_evsel *pos;
3096
3097                ev_name = perf_evsel__group_name(evsel);
3098
3099                for_each_group_member(pos, evsel) {
3100                        struct hists *pos_hists = evsel__hists(pos);
3101                        nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3102                }
3103        }
3104
3105        nr_events = convert_unit(nr_events, &unit);
3106        printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3107                           unit, unit == ' ' ? "" : " ", ev_name);
3108        ui_browser__printf(browser, "%s", bf);
3109
3110        nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3111        if (nr_events != 0) {
3112                menu->lost_events = true;
3113                if (!current_entry)
3114                        ui_browser__set_color(browser, HE_COLORSET_TOP);
3115                nr_events = convert_unit(nr_events, &unit);
3116                printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3117                                     nr_events, unit, unit == ' ' ? "" : " ");
3118                warn = bf;
3119        }
3120
3121        ui_browser__write_nstring(browser, warn, browser->width - printed);
3122
3123        if (current_entry)
3124                menu->selection = evsel;
3125}
3126
3127static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3128                                int nr_events, const char *help,
3129                                struct hist_browser_timer *hbt,
3130                                bool warn_lost_event)
3131{
3132        struct perf_evlist *evlist = menu->b.priv;
3133        struct perf_evsel *pos;
3134        const char *title = "Available samples";
3135        int delay_secs = hbt ? hbt->refresh : 0;
3136        int key;
3137
3138        if (ui_browser__show(&menu->b, title,
3139                             "ESC: exit, ENTER|->: Browse histograms") < 0)
3140                return -1;
3141
3142        while (1) {
3143                key = ui_browser__run(&menu->b, delay_secs);
3144
3145                switch (key) {
3146                case K_TIMER:
3147                        hbt->timer(hbt->arg);
3148
3149                        if (!menu->lost_events_warned &&
3150                            menu->lost_events &&
3151                            warn_lost_event) {
3152                                ui_browser__warn_lost_events(&menu->b);
3153                                menu->lost_events_warned = true;
3154                        }
3155                        continue;
3156                case K_RIGHT:
3157                case K_ENTER:
3158                        if (!menu->selection)
3159                                continue;
3160                        pos = menu->selection;
3161browse_hists:
3162                        perf_evlist__set_selected(evlist, pos);
3163                        /*
3164                         * Give the calling tool a chance to populate the non
3165                         * default evsel resorted hists tree.
3166                         */
3167                        if (hbt)
3168                                hbt->timer(hbt->arg);
3169                        key = perf_evsel__hists_browse(pos, nr_events, help,
3170                                                       true, hbt,
3171                                                       menu->min_pcnt,
3172                                                       menu->env,
3173                                                       warn_lost_event,
3174                                                       menu->annotation_opts);
3175                        ui_browser__show_title(&menu->b, title);
3176                        switch (key) {
3177                        case K_TAB:
3178                                if (pos->node.next == &evlist->entries)
3179                                        pos = perf_evlist__first(evlist);
3180                                else
3181                                        pos = perf_evsel__next(pos);
3182                                goto browse_hists;
3183                        case K_UNTAB:
3184                                if (pos->node.prev == &evlist->entries)
3185                                        pos = perf_evlist__last(evlist);
3186                                else
3187                                        pos = perf_evsel__prev(pos);
3188                                goto browse_hists;
3189                        case K_SWITCH_INPUT_DATA:
3190                        case 'q':
3191                        case CTRL('c'):
3192                                goto out;
3193                        case K_ESC:
3194                        default:
3195                                continue;
3196                        }
3197                case K_LEFT:
3198                        continue;
3199                case K_ESC:
3200                        if (!ui_browser__dialog_yesno(&menu->b,
3201                                               "Do you really want to exit?"))
3202                                continue;
3203                        /* Fall thru */
3204                case 'q':
3205                case CTRL('c'):
3206                        goto out;
3207                default:
3208                        continue;
3209                }
3210        }
3211
3212out:
3213        ui_browser__hide(&menu->b);
3214        return key;
3215}
3216
3217static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3218                                 void *entry)
3219{
3220        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3221
3222        if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3223                return true;
3224
3225        return false;
3226}
3227
3228static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3229                                           int nr_entries, const char *help,
3230                                           struct hist_browser_timer *hbt,
3231                                           float min_pcnt,
3232                                           struct perf_env *env,
3233                                           bool warn_lost_event,
3234                                           struct annotation_options *annotation_opts)
3235{
3236        struct perf_evsel *pos;
3237        struct perf_evsel_menu menu = {
3238                .b = {
3239                        .entries    = &evlist->entries,
3240                        .refresh    = ui_browser__list_head_refresh,
3241                        .seek       = ui_browser__list_head_seek,
3242                        .write      = perf_evsel_menu__write,
3243                        .filter     = filter_group_entries,
3244                        .nr_entries = nr_entries,
3245                        .priv       = evlist,
3246                },
3247                .min_pcnt = min_pcnt,
3248                .env = env,
3249                .annotation_opts = annotation_opts,
3250        };
3251
3252        ui_helpline__push("Press ESC to exit");
3253
3254        evlist__for_each_entry(evlist, pos) {
3255                const char *ev_name = perf_evsel__name(pos);
3256                size_t line_len = strlen(ev_name) + 7;
3257
3258                if (menu.b.width < line_len)
3259                        menu.b.width = line_len;
3260        }
3261
3262        return perf_evsel_menu__run(&menu, nr_entries, help,
3263                                    hbt, warn_lost_event);
3264}
3265
3266int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3267                                  struct hist_browser_timer *hbt,
3268                                  float min_pcnt,
3269                                  struct perf_env *env,
3270                                  bool warn_lost_event,
3271                                  struct annotation_options *annotation_opts)
3272{
3273        int nr_entries = evlist->nr_entries;
3274
3275single_entry:
3276        if (nr_entries == 1) {
3277                struct perf_evsel *first = perf_evlist__first(evlist);
3278
3279                return perf_evsel__hists_browse(first, nr_entries, help,
3280                                                false, hbt, min_pcnt,
3281                                                env, warn_lost_event,
3282                                                annotation_opts);
3283        }
3284
3285        if (symbol_conf.event_group) {
3286                struct perf_evsel *pos;
3287
3288                nr_entries = 0;
3289                evlist__for_each_entry(evlist, pos) {
3290                        if (perf_evsel__is_group_leader(pos))
3291                                nr_entries++;
3292                }
3293
3294                if (nr_entries == 1)
3295                        goto single_entry;
3296        }
3297
3298        return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3299                                               hbt, min_pcnt, env,
3300                                               warn_lost_event,
3301                                               annotation_opts);
3302}
3303