linux/tools/perf/ui/browsers/hists.c
<<
>>
Prefs
   1#include <stdio.h>
   2#include "../libslang.h"
   3#include <stdlib.h>
   4#include <string.h>
   5#include <linux/rbtree.h>
   6
   7#include "../../util/evsel.h"
   8#include "../../util/evlist.h"
   9#include "../../util/hist.h"
  10#include "../../util/pstack.h"
  11#include "../../util/sort.h"
  12#include "../../util/util.h"
  13#include "../../arch/common.h"
  14
  15#include "../browser.h"
  16#include "../helpline.h"
  17#include "../util.h"
  18#include "../ui.h"
  19#include "map.h"
  20
  21struct hist_browser {
  22        struct ui_browser   b;
  23        struct hists        *hists;
  24        struct hist_entry   *he_selection;
  25        struct map_symbol   *selection;
  26        int                  print_seq;
  27        bool                 show_dso;
  28        float                min_pcnt;
  29        u64                  nr_pcnt_entries;
  30};
  31
  32extern void hist_browser__init_hpp(void);
  33
  34static int hists__browser_title(struct hists *hists, char *bf, size_t size,
  35                                const char *ev_name);
  36
  37static void hist_browser__refresh_dimensions(struct hist_browser *browser)
  38{
  39        /* 3 == +/- toggle symbol before actual hist_entry rendering */
  40        browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
  41                             sizeof("[k]"));
  42}
  43
  44static void hist_browser__reset(struct hist_browser *browser)
  45{
  46        browser->b.nr_entries = browser->hists->nr_entries;
  47        hist_browser__refresh_dimensions(browser);
  48        ui_browser__reset_index(&browser->b);
  49}
  50
  51static char tree__folded_sign(bool unfolded)
  52{
  53        return unfolded ? '-' : '+';
  54}
  55
  56static char map_symbol__folded(const struct map_symbol *ms)
  57{
  58        return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
  59}
  60
  61static char hist_entry__folded(const struct hist_entry *he)
  62{
  63        return map_symbol__folded(&he->ms);
  64}
  65
  66static char callchain_list__folded(const struct callchain_list *cl)
  67{
  68        return map_symbol__folded(&cl->ms);
  69}
  70
  71static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
  72{
  73        ms->unfolded = unfold ? ms->has_children : false;
  74}
  75
  76static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
  77{
  78        int n = 0;
  79        struct rb_node *nd;
  80
  81        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
  82                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  83                struct callchain_list *chain;
  84                char folded_sign = ' '; /* No children */
  85
  86                list_for_each_entry(chain, &child->val, list) {
  87                        ++n;
  88                        /* We need this because we may not have children */
  89                        folded_sign = callchain_list__folded(chain);
  90                        if (folded_sign == '+')
  91                                break;
  92                }
  93
  94                if (folded_sign == '-') /* Have children and they're unfolded */
  95                        n += callchain_node__count_rows_rb_tree(child);
  96        }
  97
  98        return n;
  99}
 100
 101static int callchain_node__count_rows(struct callchain_node *node)
 102{
 103        struct callchain_list *chain;
 104        bool unfolded = false;
 105        int n = 0;
 106
 107        list_for_each_entry(chain, &node->val, list) {
 108                ++n;
 109                unfolded = chain->ms.unfolded;
 110        }
 111
 112        if (unfolded)
 113                n += callchain_node__count_rows_rb_tree(node);
 114
 115        return n;
 116}
 117
 118static int callchain__count_rows(struct rb_root *chain)
 119{
 120        struct rb_node *nd;
 121        int n = 0;
 122
 123        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 124                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 125                n += callchain_node__count_rows(node);
 126        }
 127
 128        return n;
 129}
 130
 131static bool map_symbol__toggle_fold(struct map_symbol *ms)
 132{
 133        if (!ms)
 134                return false;
 135
 136        if (!ms->has_children)
 137                return false;
 138
 139        ms->unfolded = !ms->unfolded;
 140        return true;
 141}
 142
 143static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
 144{
 145        struct rb_node *nd = rb_first(&node->rb_root);
 146
 147        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 148                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 149                struct callchain_list *chain;
 150                bool first = true;
 151
 152                list_for_each_entry(chain, &child->val, list) {
 153                        if (first) {
 154                                first = false;
 155                                chain->ms.has_children = chain->list.next != &child->val ||
 156                                                         !RB_EMPTY_ROOT(&child->rb_root);
 157                        } else
 158                                chain->ms.has_children = chain->list.next == &child->val &&
 159                                                         !RB_EMPTY_ROOT(&child->rb_root);
 160                }
 161
 162                callchain_node__init_have_children_rb_tree(child);
 163        }
 164}
 165
 166static void callchain_node__init_have_children(struct callchain_node *node)
 167{
 168        struct callchain_list *chain;
 169
 170        list_for_each_entry(chain, &node->val, list)
 171                chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
 172
 173        callchain_node__init_have_children_rb_tree(node);
 174}
 175
 176static void callchain__init_have_children(struct rb_root *root)
 177{
 178        struct rb_node *nd;
 179
 180        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
 181                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 182                callchain_node__init_have_children(node);
 183        }
 184}
 185
 186static void hist_entry__init_have_children(struct hist_entry *he)
 187{
 188        if (!he->init_have_children) {
 189                he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
 190                callchain__init_have_children(&he->sorted_chain);
 191                he->init_have_children = true;
 192        }
 193}
 194
 195static bool hist_browser__toggle_fold(struct hist_browser *browser)
 196{
 197        if (map_symbol__toggle_fold(browser->selection)) {
 198                struct hist_entry *he = browser->he_selection;
 199
 200                hist_entry__init_have_children(he);
 201                browser->hists->nr_entries -= he->nr_rows;
 202
 203                if (he->ms.unfolded)
 204                        he->nr_rows = callchain__count_rows(&he->sorted_chain);
 205                else
 206                        he->nr_rows = 0;
 207                browser->hists->nr_entries += he->nr_rows;
 208                browser->b.nr_entries = browser->hists->nr_entries;
 209
 210                return true;
 211        }
 212
 213        /* If it doesn't have children, no toggling performed */
 214        return false;
 215}
 216
 217static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
 218{
 219        int n = 0;
 220        struct rb_node *nd;
 221
 222        for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 223                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 224                struct callchain_list *chain;
 225                bool has_children = false;
 226
 227                list_for_each_entry(chain, &child->val, list) {
 228                        ++n;
 229                        map_symbol__set_folding(&chain->ms, unfold);
 230                        has_children = chain->ms.has_children;
 231                }
 232
 233                if (has_children)
 234                        n += callchain_node__set_folding_rb_tree(child, unfold);
 235        }
 236
 237        return n;
 238}
 239
 240static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
 241{
 242        struct callchain_list *chain;
 243        bool has_children = false;
 244        int n = 0;
 245
 246        list_for_each_entry(chain, &node->val, list) {
 247                ++n;
 248                map_symbol__set_folding(&chain->ms, unfold);
 249                has_children = chain->ms.has_children;
 250        }
 251
 252        if (has_children)
 253                n += callchain_node__set_folding_rb_tree(node, unfold);
 254
 255        return n;
 256}
 257
 258static int callchain__set_folding(struct rb_root *chain, bool unfold)
 259{
 260        struct rb_node *nd;
 261        int n = 0;
 262
 263        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 264                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 265                n += callchain_node__set_folding(node, unfold);
 266        }
 267
 268        return n;
 269}
 270
 271static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
 272{
 273        hist_entry__init_have_children(he);
 274        map_symbol__set_folding(&he->ms, unfold);
 275
 276        if (he->ms.has_children) {
 277                int n = callchain__set_folding(&he->sorted_chain, unfold);
 278                he->nr_rows = unfold ? n : 0;
 279        } else
 280                he->nr_rows = 0;
 281}
 282
 283static void hists__set_folding(struct hists *hists, bool unfold)
 284{
 285        struct rb_node *nd;
 286
 287        hists->nr_entries = 0;
 288
 289        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 290                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
 291                hist_entry__set_folding(he, unfold);
 292                hists->nr_entries += 1 + he->nr_rows;
 293        }
 294}
 295
 296static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 297{
 298        hists__set_folding(browser->hists, unfold);
 299        browser->b.nr_entries = browser->hists->nr_entries;
 300        /* Go to the start, we may be way after valid entries after a collapse */
 301        ui_browser__reset_index(&browser->b);
 302}
 303
 304static void ui_browser__warn_lost_events(struct ui_browser *browser)
 305{
 306        ui_browser__warning(browser, 4,
 307                "Events are being lost, check IO/CPU overload!\n\n"
 308                "You may want to run 'perf' using a RT scheduler policy:\n\n"
 309                " perf top -r 80\n\n"
 310                "Or reduce the sampling frequency.");
 311}
 312
 313static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
 314
 315static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
 316                             struct hist_browser_timer *hbt)
 317{
 318        int key;
 319        char title[160];
 320        int delay_secs = hbt ? hbt->refresh : 0;
 321
 322        browser->b.entries = &browser->hists->entries;
 323        browser->b.nr_entries = browser->hists->nr_entries;
 324        if (browser->min_pcnt)
 325                browser->b.nr_entries = browser->nr_pcnt_entries;
 326
 327        hist_browser__refresh_dimensions(browser);
 328        hists__browser_title(browser->hists, title, sizeof(title), ev_name);
 329
 330        if (ui_browser__show(&browser->b, title,
 331                             "Press '?' for help on key bindings") < 0)
 332                return -1;
 333
 334        while (1) {
 335                key = ui_browser__run(&browser->b, delay_secs);
 336
 337                switch (key) {
 338                case K_TIMER: {
 339                        u64 nr_entries;
 340                        hbt->timer(hbt->arg);
 341
 342                        if (browser->min_pcnt) {
 343                                hist_browser__update_pcnt_entries(browser);
 344                                nr_entries = browser->nr_pcnt_entries;
 345                        } else {
 346                                nr_entries = browser->hists->nr_entries;
 347                        }
 348
 349                        ui_browser__update_nr_entries(&browser->b, nr_entries);
 350
 351                        if (browser->hists->stats.nr_lost_warned !=
 352                            browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
 353                                browser->hists->stats.nr_lost_warned =
 354                                        browser->hists->stats.nr_events[PERF_RECORD_LOST];
 355                                ui_browser__warn_lost_events(&browser->b);
 356                        }
 357
 358                        hists__browser_title(browser->hists, title, sizeof(title), ev_name);
 359                        ui_browser__show_title(&browser->b, title);
 360                        continue;
 361                }
 362                case 'D': { /* Debug */
 363                        static int seq;
 364                        struct hist_entry *h = rb_entry(browser->b.top,
 365                                                        struct hist_entry, rb_node);
 366                        ui_helpline__pop();
 367                        ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
 368                                           seq++, browser->b.nr_entries,
 369                                           browser->hists->nr_entries,
 370                                           browser->b.height,
 371                                           browser->b.index,
 372                                           browser->b.top_idx,
 373                                           h->row_offset, h->nr_rows);
 374                }
 375                        break;
 376                case 'C':
 377                        /* Collapse the whole world. */
 378                        hist_browser__set_folding(browser, false);
 379                        break;
 380                case 'E':
 381                        /* Expand the whole world. */
 382                        hist_browser__set_folding(browser, true);
 383                        break;
 384                case K_ENTER:
 385                        if (hist_browser__toggle_fold(browser))
 386                                break;
 387                        /* fall thru */
 388                default:
 389                        goto out;
 390                }
 391        }
 392out:
 393        ui_browser__hide(&browser->b);
 394        return key;
 395}
 396
 397static char *callchain_list__sym_name(struct callchain_list *cl,
 398                                      char *bf, size_t bfsize, bool show_dso)
 399{
 400        int printed;
 401
 402        if (cl->ms.sym)
 403                printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
 404        else
 405                printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
 406
 407        if (show_dso)
 408                scnprintf(bf + printed, bfsize - printed, " %s",
 409                          cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
 410
 411        return bf;
 412}
 413
 414#define LEVEL_OFFSET_STEP 3
 415
 416static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
 417                                                     struct callchain_node *chain_node,
 418                                                     u64 total, int level,
 419                                                     unsigned short row,
 420                                                     off_t *row_offset,
 421                                                     bool *is_current_entry)
 422{
 423        struct rb_node *node;
 424        int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
 425        u64 new_total, remaining;
 426
 427        if (callchain_param.mode == CHAIN_GRAPH_REL)
 428                new_total = chain_node->children_hit;
 429        else
 430                new_total = total;
 431
 432        remaining = new_total;
 433        node = rb_first(&chain_node->rb_root);
 434        while (node) {
 435                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
 436                struct rb_node *next = rb_next(node);
 437                u64 cumul = callchain_cumul_hits(child);
 438                struct callchain_list *chain;
 439                char folded_sign = ' ';
 440                int first = true;
 441                int extra_offset = 0;
 442
 443                remaining -= cumul;
 444
 445                list_for_each_entry(chain, &child->val, list) {
 446                        char bf[1024], *alloc_str;
 447                        const char *str;
 448                        int color;
 449                        bool was_first = first;
 450
 451                        if (first)
 452                                first = false;
 453                        else
 454                                extra_offset = LEVEL_OFFSET_STEP;
 455
 456                        folded_sign = callchain_list__folded(chain);
 457                        if (*row_offset != 0) {
 458                                --*row_offset;
 459                                goto do_next;
 460                        }
 461
 462                        alloc_str = NULL;
 463                        str = callchain_list__sym_name(chain, bf, sizeof(bf),
 464                                                       browser->show_dso);
 465                        if (was_first) {
 466                                double percent = cumul * 100.0 / new_total;
 467
 468                                if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
 469                                        str = "Not enough memory!";
 470                                else
 471                                        str = alloc_str;
 472                        }
 473
 474                        color = HE_COLORSET_NORMAL;
 475                        width = browser->b.width - (offset + extra_offset + 2);
 476                        if (ui_browser__is_current_entry(&browser->b, row)) {
 477                                browser->selection = &chain->ms;
 478                                color = HE_COLORSET_SELECTED;
 479                                *is_current_entry = true;
 480                        }
 481
 482                        ui_browser__set_color(&browser->b, color);
 483                        ui_browser__gotorc(&browser->b, row, 0);
 484                        slsmg_write_nstring(" ", offset + extra_offset);
 485                        slsmg_printf("%c ", folded_sign);
 486                        slsmg_write_nstring(str, width);
 487                        free(alloc_str);
 488
 489                        if (++row == browser->b.height)
 490                                goto out;
 491do_next:
 492                        if (folded_sign == '+')
 493                                break;
 494                }
 495
 496                if (folded_sign == '-') {
 497                        const int new_level = level + (extra_offset ? 2 : 1);
 498                        row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
 499                                                                         new_level, row, row_offset,
 500                                                                         is_current_entry);
 501                }
 502                if (row == browser->b.height)
 503                        goto out;
 504                node = next;
 505        }
 506out:
 507        return row - first_row;
 508}
 509
 510static int hist_browser__show_callchain_node(struct hist_browser *browser,
 511                                             struct callchain_node *node,
 512                                             int level, unsigned short row,
 513                                             off_t *row_offset,
 514                                             bool *is_current_entry)
 515{
 516        struct callchain_list *chain;
 517        int first_row = row,
 518             offset = level * LEVEL_OFFSET_STEP,
 519             width = browser->b.width - offset;
 520        char folded_sign = ' ';
 521
 522        list_for_each_entry(chain, &node->val, list) {
 523                char bf[1024], *s;
 524                int color;
 525
 526                folded_sign = callchain_list__folded(chain);
 527
 528                if (*row_offset != 0) {
 529                        --*row_offset;
 530                        continue;
 531                }
 532
 533                color = HE_COLORSET_NORMAL;
 534                if (ui_browser__is_current_entry(&browser->b, row)) {
 535                        browser->selection = &chain->ms;
 536                        color = HE_COLORSET_SELECTED;
 537                        *is_current_entry = true;
 538                }
 539
 540                s = callchain_list__sym_name(chain, bf, sizeof(bf),
 541                                             browser->show_dso);
 542                ui_browser__gotorc(&browser->b, row, 0);
 543                ui_browser__set_color(&browser->b, color);
 544                slsmg_write_nstring(" ", offset);
 545                slsmg_printf("%c ", folded_sign);
 546                slsmg_write_nstring(s, width - 2);
 547
 548                if (++row == browser->b.height)
 549                        goto out;
 550        }
 551
 552        if (folded_sign == '-')
 553                row += hist_browser__show_callchain_node_rb_tree(browser, node,
 554                                                                 browser->hists->stats.total_period,
 555                                                                 level + 1, row,
 556                                                                 row_offset,
 557                                                                 is_current_entry);
 558out:
 559        return row - first_row;
 560}
 561
 562static int hist_browser__show_callchain(struct hist_browser *browser,
 563                                        struct rb_root *chain,
 564                                        int level, unsigned short row,
 565                                        off_t *row_offset,
 566                                        bool *is_current_entry)
 567{
 568        struct rb_node *nd;
 569        int first_row = row;
 570
 571        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 572                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 573
 574                row += hist_browser__show_callchain_node(browser, node, level,
 575                                                         row, row_offset,
 576                                                         is_current_entry);
 577                if (row == browser->b.height)
 578                        break;
 579        }
 580
 581        return row - first_row;
 582}
 583
 584struct hpp_arg {
 585        struct ui_browser *b;
 586        char folded_sign;
 587        bool current_entry;
 588};
 589
 590static int __hpp__color_callchain(struct hpp_arg *arg)
 591{
 592        if (!symbol_conf.use_callchain)
 593                return 0;
 594
 595        slsmg_printf("%c ", arg->folded_sign);
 596        return 2;
 597}
 598
 599static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
 600                            u64 (*get_field)(struct hist_entry *),
 601                            int (*callchain_cb)(struct hpp_arg *))
 602{
 603        int ret = 0;
 604        double percent = 0.0;
 605        struct hists *hists = he->hists;
 606        struct hpp_arg *arg = hpp->ptr;
 607
 608        if (hists->stats.total_period)
 609                percent = 100.0 * get_field(he) / hists->stats.total_period;
 610
 611        ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
 612
 613        if (callchain_cb)
 614                ret += callchain_cb(arg);
 615
 616        ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
 617        slsmg_printf("%s", hpp->buf);
 618
 619        if (symbol_conf.event_group) {
 620                int prev_idx, idx_delta;
 621                struct perf_evsel *evsel = hists_to_evsel(hists);
 622                struct hist_entry *pair;
 623                int nr_members = evsel->nr_members;
 624
 625                if (nr_members <= 1)
 626                        goto out;
 627
 628                prev_idx = perf_evsel__group_idx(evsel);
 629
 630                list_for_each_entry(pair, &he->pairs.head, pairs.node) {
 631                        u64 period = get_field(pair);
 632                        u64 total = pair->hists->stats.total_period;
 633
 634                        if (!total)
 635                                continue;
 636
 637                        evsel = hists_to_evsel(pair->hists);
 638                        idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
 639
 640                        while (idx_delta--) {
 641                                /*
 642                                 * zero-fill group members in the middle which
 643                                 * have no sample
 644                                 */
 645                                ui_browser__set_percent_color(arg->b, 0.0,
 646                                                        arg->current_entry);
 647                                ret += scnprintf(hpp->buf, hpp->size,
 648                                                 " %6.2f%%", 0.0);
 649                                slsmg_printf("%s", hpp->buf);
 650                        }
 651
 652                        percent = 100.0 * period / total;
 653                        ui_browser__set_percent_color(arg->b, percent,
 654                                                      arg->current_entry);
 655                        ret += scnprintf(hpp->buf, hpp->size,
 656                                         " %6.2f%%", percent);
 657                        slsmg_printf("%s", hpp->buf);
 658
 659                        prev_idx = perf_evsel__group_idx(evsel);
 660                }
 661
 662                idx_delta = nr_members - prev_idx - 1;
 663
 664                while (idx_delta--) {
 665                        /*
 666                         * zero-fill group members at last which have no sample
 667                         */
 668                        ui_browser__set_percent_color(arg->b, 0.0,
 669                                                      arg->current_entry);
 670                        ret += scnprintf(hpp->buf, hpp->size,
 671                                         " %6.2f%%", 0.0);
 672                        slsmg_printf("%s", hpp->buf);
 673                }
 674        }
 675out:
 676        if (!arg->current_entry || !arg->b->navkeypressed)
 677                ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
 678
 679        return ret;
 680}
 681
 682#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)                      \
 683static u64 __hpp_get_##_field(struct hist_entry *he)                    \
 684{                                                                       \
 685        return he->stat._field;                                         \
 686}                                                                       \
 687                                                                        \
 688static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,        \
 689                                           struct hist_entry *he)       \
 690{                                                                       \
 691        return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);      \
 692}
 693
 694__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
 695__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
 696__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
 697__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
 698__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
 699
 700#undef __HPP_COLOR_PERCENT_FN
 701
 702void hist_browser__init_hpp(void)
 703{
 704        perf_hpp__column_enable(PERF_HPP__OVERHEAD);
 705
 706        perf_hpp__init();
 707
 708        perf_hpp__format[PERF_HPP__OVERHEAD].color =
 709                                hist_browser__hpp_color_overhead;
 710        perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
 711                                hist_browser__hpp_color_overhead_sys;
 712        perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
 713                                hist_browser__hpp_color_overhead_us;
 714        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
 715                                hist_browser__hpp_color_overhead_guest_sys;
 716        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
 717                                hist_browser__hpp_color_overhead_guest_us;
 718}
 719
 720static int hist_browser__show_entry(struct hist_browser *browser,
 721                                    struct hist_entry *entry,
 722                                    unsigned short row)
 723{
 724        char s[256];
 725        int printed = 0;
 726        int width = browser->b.width;
 727        char folded_sign = ' ';
 728        bool current_entry = ui_browser__is_current_entry(&browser->b, row);
 729        off_t row_offset = entry->row_offset;
 730        bool first = true;
 731        struct perf_hpp_fmt *fmt;
 732
 733        if (current_entry) {
 734                browser->he_selection = entry;
 735                browser->selection = &entry->ms;
 736        }
 737
 738        if (symbol_conf.use_callchain) {
 739                hist_entry__init_have_children(entry);
 740                folded_sign = hist_entry__folded(entry);
 741        }
 742
 743        if (row_offset == 0) {
 744                struct hpp_arg arg = {
 745                        .b              = &browser->b,
 746                        .folded_sign    = folded_sign,
 747                        .current_entry  = current_entry,
 748                };
 749                struct perf_hpp hpp = {
 750                        .buf            = s,
 751                        .size           = sizeof(s),
 752                        .ptr            = &arg,
 753                };
 754
 755                ui_browser__gotorc(&browser->b, row, 0);
 756
 757                perf_hpp__for_each_format(fmt) {
 758                        if (!first) {
 759                                slsmg_printf("  ");
 760                                width -= 2;
 761                        }
 762                        first = false;
 763
 764                        if (fmt->color) {
 765                                width -= fmt->color(&hpp, entry);
 766                        } else {
 767                                width -= fmt->entry(&hpp, entry);
 768                                slsmg_printf("%s", s);
 769                        }
 770                }
 771
 772                /* The scroll bar isn't being used */
 773                if (!browser->b.navkeypressed)
 774                        width += 1;
 775
 776                hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
 777                slsmg_write_nstring(s, width);
 778                ++row;
 779                ++printed;
 780        } else
 781                --row_offset;
 782
 783        if (folded_sign == '-' && row != browser->b.height) {
 784                printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
 785                                                        1, row, &row_offset,
 786                                                        &current_entry);
 787                if (current_entry)
 788                        browser->he_selection = entry;
 789        }
 790
 791        return printed;
 792}
 793
 794static void ui_browser__hists_init_top(struct ui_browser *browser)
 795{
 796        if (browser->top == NULL) {
 797                struct hist_browser *hb;
 798
 799                hb = container_of(browser, struct hist_browser, b);
 800                browser->top = rb_first(&hb->hists->entries);
 801        }
 802}
 803
 804static unsigned int hist_browser__refresh(struct ui_browser *browser)
 805{
 806        unsigned row = 0;
 807        struct rb_node *nd;
 808        struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 809
 810        ui_browser__hists_init_top(browser);
 811
 812        for (nd = browser->top; nd; nd = rb_next(nd)) {
 813                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 814                float percent = h->stat.period * 100.0 /
 815                                        hb->hists->stats.total_period;
 816
 817                if (h->filtered)
 818                        continue;
 819
 820                if (percent < hb->min_pcnt)
 821                        continue;
 822
 823                row += hist_browser__show_entry(hb, h, row);
 824                if (row == browser->height)
 825                        break;
 826        }
 827
 828        return row;
 829}
 830
 831static struct rb_node *hists__filter_entries(struct rb_node *nd,
 832                                             struct hists *hists,
 833                                             float min_pcnt)
 834{
 835        while (nd != NULL) {
 836                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 837                float percent = h->stat.period * 100.0 /
 838                                        hists->stats.total_period;
 839
 840                if (percent < min_pcnt)
 841                        return NULL;
 842
 843                if (!h->filtered)
 844                        return nd;
 845
 846                nd = rb_next(nd);
 847        }
 848
 849        return NULL;
 850}
 851
 852static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
 853                                                  struct hists *hists,
 854                                                  float min_pcnt)
 855{
 856        while (nd != NULL) {
 857                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 858                float percent = h->stat.period * 100.0 /
 859                                        hists->stats.total_period;
 860
 861                if (!h->filtered && percent >= min_pcnt)
 862                        return nd;
 863
 864                nd = rb_prev(nd);
 865        }
 866
 867        return NULL;
 868}
 869
 870static void ui_browser__hists_seek(struct ui_browser *browser,
 871                                   off_t offset, int whence)
 872{
 873        struct hist_entry *h;
 874        struct rb_node *nd;
 875        bool first = true;
 876        struct hist_browser *hb;
 877
 878        hb = container_of(browser, struct hist_browser, b);
 879
 880        if (browser->nr_entries == 0)
 881                return;
 882
 883        ui_browser__hists_init_top(browser);
 884
 885        switch (whence) {
 886        case SEEK_SET:
 887                nd = hists__filter_entries(rb_first(browser->entries),
 888                                           hb->hists, hb->min_pcnt);
 889                break;
 890        case SEEK_CUR:
 891                nd = browser->top;
 892                goto do_offset;
 893        case SEEK_END:
 894                nd = hists__filter_prev_entries(rb_last(browser->entries),
 895                                                hb->hists, hb->min_pcnt);
 896                first = false;
 897                break;
 898        default:
 899                return;
 900        }
 901
 902        /*
 903         * Moves not relative to the first visible entry invalidates its
 904         * row_offset:
 905         */
 906        h = rb_entry(browser->top, struct hist_entry, rb_node);
 907        h->row_offset = 0;
 908
 909        /*
 910         * Here we have to check if nd is expanded (+), if it is we can't go
 911         * the next top level hist_entry, instead we must compute an offset of
 912         * what _not_ to show and not change the first visible entry.
 913         *
 914         * This offset increments when we are going from top to bottom and
 915         * decreases when we're going from bottom to top.
 916         *
 917         * As we don't have backpointers to the top level in the callchains
 918         * structure, we need to always print the whole hist_entry callchain,
 919         * skipping the first ones that are before the first visible entry
 920         * and stop when we printed enough lines to fill the screen.
 921         */
 922do_offset:
 923        if (offset > 0) {
 924                do {
 925                        h = rb_entry(nd, struct hist_entry, rb_node);
 926                        if (h->ms.unfolded) {
 927                                u16 remaining = h->nr_rows - h->row_offset;
 928                                if (offset > remaining) {
 929                                        offset -= remaining;
 930                                        h->row_offset = 0;
 931                                } else {
 932                                        h->row_offset += offset;
 933                                        offset = 0;
 934                                        browser->top = nd;
 935                                        break;
 936                                }
 937                        }
 938                        nd = hists__filter_entries(rb_next(nd), hb->hists,
 939                                                   hb->min_pcnt);
 940                        if (nd == NULL)
 941                                break;
 942                        --offset;
 943                        browser->top = nd;
 944                } while (offset != 0);
 945        } else if (offset < 0) {
 946                while (1) {
 947                        h = rb_entry(nd, struct hist_entry, rb_node);
 948                        if (h->ms.unfolded) {
 949                                if (first) {
 950                                        if (-offset > h->row_offset) {
 951                                                offset += h->row_offset;
 952                                                h->row_offset = 0;
 953                                        } else {
 954                                                h->row_offset += offset;
 955                                                offset = 0;
 956                                                browser->top = nd;
 957                                                break;
 958                                        }
 959                                } else {
 960                                        if (-offset > h->nr_rows) {
 961                                                offset += h->nr_rows;
 962                                                h->row_offset = 0;
 963                                        } else {
 964                                                h->row_offset = h->nr_rows + offset;
 965                                                offset = 0;
 966                                                browser->top = nd;
 967                                                break;
 968                                        }
 969                                }
 970                        }
 971
 972                        nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
 973                                                        hb->min_pcnt);
 974                        if (nd == NULL)
 975                                break;
 976                        ++offset;
 977                        browser->top = nd;
 978                        if (offset == 0) {
 979                                /*
 980                                 * Last unfiltered hist_entry, check if it is
 981                                 * unfolded, if it is then we should have
 982                                 * row_offset at its last entry.
 983                                 */
 984                                h = rb_entry(nd, struct hist_entry, rb_node);
 985                                if (h->ms.unfolded)
 986                                        h->row_offset = h->nr_rows;
 987                                break;
 988                        }
 989                        first = false;
 990                }
 991        } else {
 992                browser->top = nd;
 993                h = rb_entry(nd, struct hist_entry, rb_node);
 994                h->row_offset = 0;
 995        }
 996}
 997
 998static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
 999                                                        struct callchain_node *chain_node,
1000                                                        u64 total, int level,
1001                                                        FILE *fp)
1002{
1003        struct rb_node *node;
1004        int offset = level * LEVEL_OFFSET_STEP;
1005        u64 new_total, remaining;
1006        int printed = 0;
1007
1008        if (callchain_param.mode == CHAIN_GRAPH_REL)
1009                new_total = chain_node->children_hit;
1010        else
1011                new_total = total;
1012
1013        remaining = new_total;
1014        node = rb_first(&chain_node->rb_root);
1015        while (node) {
1016                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017                struct rb_node *next = rb_next(node);
1018                u64 cumul = callchain_cumul_hits(child);
1019                struct callchain_list *chain;
1020                char folded_sign = ' ';
1021                int first = true;
1022                int extra_offset = 0;
1023
1024                remaining -= cumul;
1025
1026                list_for_each_entry(chain, &child->val, list) {
1027                        char bf[1024], *alloc_str;
1028                        const char *str;
1029                        bool was_first = first;
1030
1031                        if (first)
1032                                first = false;
1033                        else
1034                                extra_offset = LEVEL_OFFSET_STEP;
1035
1036                        folded_sign = callchain_list__folded(chain);
1037
1038                        alloc_str = NULL;
1039                        str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040                                                       browser->show_dso);
1041                        if (was_first) {
1042                                double percent = cumul * 100.0 / new_total;
1043
1044                                if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045                                        str = "Not enough memory!";
1046                                else
1047                                        str = alloc_str;
1048                        }
1049
1050                        printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051                        free(alloc_str);
1052                        if (folded_sign == '+')
1053                                break;
1054                }
1055
1056                if (folded_sign == '-') {
1057                        const int new_level = level + (extra_offset ? 2 : 1);
1058                        printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059                                                                                new_level, fp);
1060                }
1061
1062                node = next;
1063        }
1064
1065        return printed;
1066}
1067
1068static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069                                                struct callchain_node *node,
1070                                                int level, FILE *fp)
1071{
1072        struct callchain_list *chain;
1073        int offset = level * LEVEL_OFFSET_STEP;
1074        char folded_sign = ' ';
1075        int printed = 0;
1076
1077        list_for_each_entry(chain, &node->val, list) {
1078                char bf[1024], *s;
1079
1080                folded_sign = callchain_list__folded(chain);
1081                s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082                printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1083        }
1084
1085        if (folded_sign == '-')
1086                printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087                                                                        browser->hists->stats.total_period,
1088                                                                        level + 1,  fp);
1089        return printed;
1090}
1091
1092static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093                                           struct rb_root *chain, int level, FILE *fp)
1094{
1095        struct rb_node *nd;
1096        int printed = 0;
1097
1098        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1100
1101                printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1102        }
1103
1104        return printed;
1105}
1106
1107static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108                                       struct hist_entry *he, FILE *fp)
1109{
1110        char s[8192];
1111        double percent;
1112        int printed = 0;
1113        char folded_sign = ' ';
1114
1115        if (symbol_conf.use_callchain)
1116                folded_sign = hist_entry__folded(he);
1117
1118        hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119        percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1120
1121        if (symbol_conf.use_callchain)
1122                printed += fprintf(fp, "%c ", folded_sign);
1123
1124        printed += fprintf(fp, " %5.2f%%", percent);
1125
1126        if (symbol_conf.show_nr_samples)
1127                printed += fprintf(fp, " %11u", he->stat.nr_events);
1128
1129        if (symbol_conf.show_total_period)
1130                printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1131
1132        printed += fprintf(fp, "%s\n", rtrim(s));
1133
1134        if (folded_sign == '-')
1135                printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136
1137        return printed;
1138}
1139
1140static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1141{
1142        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143                                                   browser->hists,
1144                                                   browser->min_pcnt);
1145        int printed = 0;
1146
1147        while (nd) {
1148                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149
1150                printed += hist_browser__fprintf_entry(browser, h, fp);
1151                nd = hists__filter_entries(rb_next(nd), browser->hists,
1152                                           browser->min_pcnt);
1153        }
1154
1155        return printed;
1156}
1157
1158static int hist_browser__dump(struct hist_browser *browser)
1159{
1160        char filename[64];
1161        FILE *fp;
1162
1163        while (1) {
1164                scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165                if (access(filename, F_OK))
1166                        break;
1167                /*
1168                 * XXX: Just an arbitrary lazy upper limit
1169                 */
1170                if (++browser->print_seq == 8192) {
1171                        ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172                        return -1;
1173                }
1174        }
1175
1176        fp = fopen(filename, "w");
1177        if (fp == NULL) {
1178                char bf[64];
1179                const char *err = strerror_r(errno, bf, sizeof(bf));
1180                ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181                return -1;
1182        }
1183
1184        ++browser->print_seq;
1185        hist_browser__fprintf(browser, fp);
1186        fclose(fp);
1187        ui_helpline__fpush("%s written!", filename);
1188
1189        return 0;
1190}
1191
1192static struct hist_browser *hist_browser__new(struct hists *hists)
1193{
1194        struct hist_browser *browser = zalloc(sizeof(*browser));
1195
1196        if (browser) {
1197                browser->hists = hists;
1198                browser->b.refresh = hist_browser__refresh;
1199                browser->b.seek = ui_browser__hists_seek;
1200                browser->b.use_navkeypressed = true;
1201        }
1202
1203        return browser;
1204}
1205
1206static void hist_browser__delete(struct hist_browser *browser)
1207{
1208        free(browser);
1209}
1210
1211static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1212{
1213        return browser->he_selection;
1214}
1215
1216static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1217{
1218        return browser->he_selection->thread;
1219}
1220
1221static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222                                const char *ev_name)
1223{
1224        char unit;
1225        int printed;
1226        const struct dso *dso = hists->dso_filter;
1227        const struct thread *thread = hists->thread_filter;
1228        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1229        u64 nr_events = hists->stats.total_period;
1230        struct perf_evsel *evsel = hists_to_evsel(hists);
1231        char buf[512];
1232        size_t buflen = sizeof(buf);
1233
1234        if (perf_evsel__is_group_event(evsel)) {
1235                struct perf_evsel *pos;
1236
1237                perf_evsel__group_desc(evsel, buf, buflen);
1238                ev_name = buf;
1239
1240                for_each_group_member(pos, evsel) {
1241                        nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242                        nr_events += pos->hists.stats.total_period;
1243                }
1244        }
1245
1246        nr_samples = convert_unit(nr_samples, &unit);
1247        printed = scnprintf(bf, size,
1248                           "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249                           nr_samples, unit, ev_name, nr_events);
1250
1251
1252        if (hists->uid_filter_str)
1253                printed += snprintf(bf + printed, size - printed,
1254                                    ", UID: %s", hists->uid_filter_str);
1255        if (thread)
1256                printed += scnprintf(bf + printed, size - printed,
1257                                    ", Thread: %s(%d)",
1258                                    (thread->comm_set ? thread->comm : ""),
1259                                    thread->pid);
1260        if (dso)
1261                printed += scnprintf(bf + printed, size - printed,
1262                                    ", DSO: %s", dso->short_name);
1263        return printed;
1264}
1265
1266static inline void free_popup_options(char **options, int n)
1267{
1268        int i;
1269
1270        for (i = 0; i < n; ++i) {
1271                free(options[i]);
1272                options[i] = NULL;
1273        }
1274}
1275
1276/* Check whether the browser is for 'top' or 'report' */
1277static inline bool is_report_browser(void *timer)
1278{
1279        return timer == NULL;
1280}
1281
1282/*
1283 * Only runtime switching of perf data file will make "input_name" point
1284 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1285 * whether we need to call free() for current "input_name" during the switch.
1286 */
1287static bool is_input_name_malloced = false;
1288
1289static int switch_data_file(void)
1290{
1291        char *pwd, *options[32], *abs_path[32], *tmp;
1292        DIR *pwd_dir;
1293        int nr_options = 0, choice = -1, ret = -1;
1294        struct dirent *dent;
1295
1296        pwd = getenv("PWD");
1297        if (!pwd)
1298                return ret;
1299
1300        pwd_dir = opendir(pwd);
1301        if (!pwd_dir)
1302                return ret;
1303
1304        memset(options, 0, sizeof(options));
1305        memset(options, 0, sizeof(abs_path));
1306
1307        while ((dent = readdir(pwd_dir))) {
1308                char path[PATH_MAX];
1309                u64 magic;
1310                char *name = dent->d_name;
1311                FILE *file;
1312
1313                if (!(dent->d_type == DT_REG))
1314                        continue;
1315
1316                snprintf(path, sizeof(path), "%s/%s", pwd, name);
1317
1318                file = fopen(path, "r");
1319                if (!file)
1320                        continue;
1321
1322                if (fread(&magic, 1, 8, file) < 8)
1323                        goto close_file_and_continue;
1324
1325                if (is_perf_magic(magic)) {
1326                        options[nr_options] = strdup(name);
1327                        if (!options[nr_options])
1328                                goto close_file_and_continue;
1329
1330                        abs_path[nr_options] = strdup(path);
1331                        if (!abs_path[nr_options]) {
1332                                free(options[nr_options]);
1333                                ui__warning("Can't search all data files due to memory shortage.\n");
1334                                fclose(file);
1335                                break;
1336                        }
1337
1338                        nr_options++;
1339                }
1340
1341close_file_and_continue:
1342                fclose(file);
1343                if (nr_options >= 32) {
1344                        ui__warning("Too many perf data files in PWD!\n"
1345                                    "Only the first 32 files will be listed.\n");
1346                        break;
1347                }
1348        }
1349        closedir(pwd_dir);
1350
1351        if (nr_options) {
1352                choice = ui__popup_menu(nr_options, options);
1353                if (choice < nr_options && choice >= 0) {
1354                        tmp = strdup(abs_path[choice]);
1355                        if (tmp) {
1356                                if (is_input_name_malloced)
1357                                        free((void *)input_name);
1358                                input_name = tmp;
1359                                is_input_name_malloced = true;
1360                                ret = 0;
1361                        } else
1362                                ui__warning("Data switch failed due to memory shortage!\n");
1363                }
1364        }
1365
1366        free_popup_options(options, nr_options);
1367        free_popup_options(abs_path, nr_options);
1368        return ret;
1369}
1370
1371static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1372{
1373        u64 nr_entries = 0;
1374        struct rb_node *nd = rb_first(&hb->hists->entries);
1375
1376        while (nd) {
1377                nr_entries++;
1378                nd = hists__filter_entries(rb_next(nd), hb->hists,
1379                                           hb->min_pcnt);
1380        }
1381
1382        hb->nr_pcnt_entries = nr_entries;
1383}
1384
1385static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1386                                    const char *helpline, const char *ev_name,
1387                                    bool left_exits,
1388                                    struct hist_browser_timer *hbt,
1389                                    float min_pcnt,
1390                                    struct perf_session_env *env)
1391{
1392        struct hists *hists = &evsel->hists;
1393        struct hist_browser *browser = hist_browser__new(hists);
1394        struct branch_info *bi;
1395        struct pstack *fstack;
1396        char *options[16];
1397        int nr_options = 0;
1398        int key = -1;
1399        char buf[64];
1400        char script_opt[64];
1401        int delay_secs = hbt ? hbt->refresh : 0;
1402
1403        if (browser == NULL)
1404                return -1;
1405
1406        if (min_pcnt) {
1407                browser->min_pcnt = min_pcnt;
1408                hist_browser__update_pcnt_entries(browser);
1409        }
1410
1411        fstack = pstack__new(2);
1412        if (fstack == NULL)
1413                goto out;
1414
1415        ui_helpline__push(helpline);
1416
1417        memset(options, 0, sizeof(options));
1418
1419        while (1) {
1420                const struct thread *thread = NULL;
1421                const struct dso *dso = NULL;
1422                int choice = 0,
1423                    annotate = -2, zoom_dso = -2, zoom_thread = -2,
1424                    annotate_f = -2, annotate_t = -2, browse_map = -2;
1425                int scripts_comm = -2, scripts_symbol = -2,
1426                    scripts_all = -2, switch_data = -2;
1427
1428                nr_options = 0;
1429
1430                key = hist_browser__run(browser, ev_name, hbt);
1431
1432                if (browser->he_selection != NULL) {
1433                        thread = hist_browser__selected_thread(browser);
1434                        dso = browser->selection->map ? browser->selection->map->dso : NULL;
1435                }
1436                switch (key) {
1437                case K_TAB:
1438                case K_UNTAB:
1439                        if (nr_events == 1)
1440                                continue;
1441                        /*
1442                         * Exit the browser, let hists__browser_tree
1443                         * go to the next or previous
1444                         */
1445                        goto out_free_stack;
1446                case 'a':
1447                        if (!sort__has_sym) {
1448                                ui_browser__warning(&browser->b, delay_secs * 2,
1449                        "Annotation is only available for symbolic views, "
1450                        "include \"sym*\" in --sort to use it.");
1451                                continue;
1452                        }
1453
1454                        if (browser->selection == NULL ||
1455                            browser->selection->sym == NULL ||
1456                            browser->selection->map->dso->annotate_warned)
1457                                continue;
1458                        goto do_annotate;
1459                case 'P':
1460                        hist_browser__dump(browser);
1461                        continue;
1462                case 'd':
1463                        goto zoom_dso;
1464                case 'V':
1465                        browser->show_dso = !browser->show_dso;
1466                        continue;
1467                case 't':
1468                        goto zoom_thread;
1469                case '/':
1470                        if (ui_browser__input_window("Symbol to show",
1471                                        "Please enter the name of symbol you want to see",
1472                                        buf, "ENTER: OK, ESC: Cancel",
1473                                        delay_secs * 2) == K_ENTER) {
1474                                hists->symbol_filter_str = *buf ? buf : NULL;
1475                                hists__filter_by_symbol(hists);
1476                                hist_browser__reset(browser);
1477                        }
1478                        continue;
1479                case 'r':
1480                        if (is_report_browser(hbt))
1481                                goto do_scripts;
1482                        continue;
1483                case 's':
1484                        if (is_report_browser(hbt))
1485                                goto do_data_switch;
1486                        continue;
1487                case K_F1:
1488                case 'h':
1489                case '?':
1490                        ui_browser__help_window(&browser->b,
1491                                        "h/?/F1        Show this window\n"
1492                                        "UP/DOWN/PGUP\n"
1493                                        "PGDN/SPACE    Navigate\n"
1494                                        "q/ESC/CTRL+C  Exit browser\n\n"
1495                                        "For multiple event sessions:\n\n"
1496                                        "TAB/UNTAB Switch events\n\n"
1497                                        "For symbolic views (--sort has sym):\n\n"
1498                                        "->            Zoom into DSO/Threads & Annotate current symbol\n"
1499                                        "<-            Zoom out\n"
1500                                        "a             Annotate current symbol\n"
1501                                        "C             Collapse all callchains\n"
1502                                        "E             Expand all callchains\n"
1503                                        "d             Zoom into current DSO\n"
1504                                        "t             Zoom into current Thread\n"
1505                                        "r             Run available scripts('perf report' only)\n"
1506                                        "s             Switch to another data file in PWD ('perf report' only)\n"
1507                                        "P             Print histograms to perf.hist.N\n"
1508                                        "V             Verbose (DSO names in callchains, etc)\n"
1509                                        "/             Filter symbol by name");
1510                        continue;
1511                case K_ENTER:
1512                case K_RIGHT:
1513                        /* menu */
1514                        break;
1515                case K_LEFT: {
1516                        const void *top;
1517
1518                        if (pstack__empty(fstack)) {
1519                                /*
1520                                 * Go back to the perf_evsel_menu__run or other user
1521                                 */
1522                                if (left_exits)
1523                                        goto out_free_stack;
1524                                continue;
1525                        }
1526                        top = pstack__pop(fstack);
1527                        if (top == &browser->hists->dso_filter)
1528                                goto zoom_out_dso;
1529                        if (top == &browser->hists->thread_filter)
1530                                goto zoom_out_thread;
1531                        continue;
1532                }
1533                case K_ESC:
1534                        if (!left_exits &&
1535                            !ui_browser__dialog_yesno(&browser->b,
1536                                               "Do you really want to exit?"))
1537                                continue;
1538                        /* Fall thru */
1539                case 'q':
1540                case CTRL('c'):
1541                        goto out_free_stack;
1542                default:
1543                        continue;
1544                }
1545
1546                if (!sort__has_sym)
1547                        goto add_exit_option;
1548
1549                if (sort__mode == SORT_MODE__BRANCH) {
1550                        bi = browser->he_selection->branch_info;
1551                        if (browser->selection != NULL &&
1552                            bi &&
1553                            bi->from.sym != NULL &&
1554                            !bi->from.map->dso->annotate_warned &&
1555                                asprintf(&options[nr_options], "Annotate %s",
1556                                         bi->from.sym->name) > 0)
1557                                annotate_f = nr_options++;
1558
1559                        if (browser->selection != NULL &&
1560                            bi &&
1561                            bi->to.sym != NULL &&
1562                            !bi->to.map->dso->annotate_warned &&
1563                            (bi->to.sym != bi->from.sym ||
1564                             bi->to.map->dso != bi->from.map->dso) &&
1565                                asprintf(&options[nr_options], "Annotate %s",
1566                                         bi->to.sym->name) > 0)
1567                                annotate_t = nr_options++;
1568                } else {
1569
1570                        if (browser->selection != NULL &&
1571                            browser->selection->sym != NULL &&
1572                            !browser->selection->map->dso->annotate_warned &&
1573                                asprintf(&options[nr_options], "Annotate %s",
1574                                         browser->selection->sym->name) > 0)
1575                                annotate = nr_options++;
1576                }
1577
1578                if (thread != NULL &&
1579                    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1580                             (browser->hists->thread_filter ? "out of" : "into"),
1581                             (thread->comm_set ? thread->comm : ""),
1582                             thread->pid) > 0)
1583                        zoom_thread = nr_options++;
1584
1585                if (dso != NULL &&
1586                    asprintf(&options[nr_options], "Zoom %s %s DSO",
1587                             (browser->hists->dso_filter ? "out of" : "into"),
1588                             (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1589                        zoom_dso = nr_options++;
1590
1591                if (browser->selection != NULL &&
1592                    browser->selection->map != NULL &&
1593                    asprintf(&options[nr_options], "Browse map details") > 0)
1594                        browse_map = nr_options++;
1595
1596                /* perf script support */
1597                if (browser->he_selection) {
1598                        struct symbol *sym;
1599
1600                        if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1601                                browser->he_selection->thread->comm) > 0)
1602                                scripts_comm = nr_options++;
1603
1604                        sym = browser->he_selection->ms.sym;
1605                        if (sym && sym->namelen &&
1606                                asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1607                                                sym->name) > 0)
1608                                scripts_symbol = nr_options++;
1609                }
1610
1611                if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1612                        scripts_all = nr_options++;
1613
1614                if (is_report_browser(hbt) && asprintf(&options[nr_options],
1615                                "Switch to another data file in PWD") > 0)
1616                        switch_data = nr_options++;
1617add_exit_option:
1618                options[nr_options++] = (char *)"Exit";
1619retry_popup_menu:
1620                choice = ui__popup_menu(nr_options, options);
1621
1622                if (choice == nr_options - 1)
1623                        break;
1624
1625                if (choice == -1) {
1626                        free_popup_options(options, nr_options - 1);
1627                        continue;
1628                }
1629
1630                if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1631                        struct hist_entry *he;
1632                        int err;
1633do_annotate:
1634                        if (!objdump_path && perf_session_env__lookup_objdump(env))
1635                                continue;
1636
1637                        he = hist_browser__selected_entry(browser);
1638                        if (he == NULL)
1639                                continue;
1640
1641                        /*
1642                         * we stash the branch_info symbol + map into the
1643                         * the ms so we don't have to rewrite all the annotation
1644                         * code to use branch_info.
1645                         * in branch mode, the ms struct is not used
1646                         */
1647                        if (choice == annotate_f) {
1648                                he->ms.sym = he->branch_info->from.sym;
1649                                he->ms.map = he->branch_info->from.map;
1650                        }  else if (choice == annotate_t) {
1651                                he->ms.sym = he->branch_info->to.sym;
1652                                he->ms.map = he->branch_info->to.map;
1653                        }
1654
1655                        /*
1656                         * Don't let this be freed, say, by hists__decay_entry.
1657                         */
1658                        he->used = true;
1659                        err = hist_entry__tui_annotate(he, evsel, hbt);
1660                        he->used = false;
1661                        /*
1662                         * offer option to annotate the other branch source or target
1663                         * (if they exists) when returning from annotate
1664                         */
1665                        if ((err == 'q' || err == CTRL('c'))
1666                            && annotate_t != -2 && annotate_f != -2)
1667                                goto retry_popup_menu;
1668
1669                        ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1670                        if (err)
1671                                ui_browser__handle_resize(&browser->b);
1672
1673                } else if (choice == browse_map)
1674                        map__browse(browser->selection->map);
1675                else if (choice == zoom_dso) {
1676zoom_dso:
1677                        if (browser->hists->dso_filter) {
1678                                pstack__remove(fstack, &browser->hists->dso_filter);
1679zoom_out_dso:
1680                                ui_helpline__pop();
1681                                browser->hists->dso_filter = NULL;
1682                                sort_dso.elide = false;
1683                        } else {
1684                                if (dso == NULL)
1685                                        continue;
1686                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1687                                                   dso->kernel ? "the Kernel" : dso->short_name);
1688                                browser->hists->dso_filter = dso;
1689                                sort_dso.elide = true;
1690                                pstack__push(fstack, &browser->hists->dso_filter);
1691                        }
1692                        hists__filter_by_dso(hists);
1693                        hist_browser__reset(browser);
1694                } else if (choice == zoom_thread) {
1695zoom_thread:
1696                        if (browser->hists->thread_filter) {
1697                                pstack__remove(fstack, &browser->hists->thread_filter);
1698zoom_out_thread:
1699                                ui_helpline__pop();
1700                                browser->hists->thread_filter = NULL;
1701                                sort_thread.elide = false;
1702                        } else {
1703                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1704                                                   thread->comm_set ? thread->comm : "",
1705                                                   thread->pid);
1706                                browser->hists->thread_filter = thread;
1707                                sort_thread.elide = true;
1708                                pstack__push(fstack, &browser->hists->thread_filter);
1709                        }
1710                        hists__filter_by_thread(hists);
1711                        hist_browser__reset(browser);
1712                }
1713                /* perf scripts support */
1714                else if (choice == scripts_all || choice == scripts_comm ||
1715                                choice == scripts_symbol) {
1716do_scripts:
1717                        memset(script_opt, 0, 64);
1718
1719                        if (choice == scripts_comm)
1720                                sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1721
1722                        if (choice == scripts_symbol)
1723                                sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1724
1725                        script_browse(script_opt);
1726                }
1727                /* Switch to another data file */
1728                else if (choice == switch_data) {
1729do_data_switch:
1730                        if (!switch_data_file()) {
1731                                key = K_SWITCH_INPUT_DATA;
1732                                break;
1733                        } else
1734                                ui__warning("Won't switch the data files due to\n"
1735                                        "no valid data file get selected!\n");
1736                }
1737        }
1738out_free_stack:
1739        pstack__delete(fstack);
1740out:
1741        hist_browser__delete(browser);
1742        free_popup_options(options, nr_options - 1);
1743        return key;
1744}
1745
1746struct perf_evsel_menu {
1747        struct ui_browser b;
1748        struct perf_evsel *selection;
1749        bool lost_events, lost_events_warned;
1750        float min_pcnt;
1751        struct perf_session_env *env;
1752};
1753
1754static void perf_evsel_menu__write(struct ui_browser *browser,
1755                                   void *entry, int row)
1756{
1757        struct perf_evsel_menu *menu = container_of(browser,
1758                                                    struct perf_evsel_menu, b);
1759        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1760        bool current_entry = ui_browser__is_current_entry(browser, row);
1761        unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1762        const char *ev_name = perf_evsel__name(evsel);
1763        char bf[256], unit;
1764        const char *warn = " ";
1765        size_t printed;
1766
1767        ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1768                                                       HE_COLORSET_NORMAL);
1769
1770        if (perf_evsel__is_group_event(evsel)) {
1771                struct perf_evsel *pos;
1772
1773                ev_name = perf_evsel__group_name(evsel);
1774
1775                for_each_group_member(pos, evsel) {
1776                        nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777                }
1778        }
1779
1780        nr_events = convert_unit(nr_events, &unit);
1781        printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1782                           unit, unit == ' ' ? "" : " ", ev_name);
1783        slsmg_printf("%s", bf);
1784
1785        nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1786        if (nr_events != 0) {
1787                menu->lost_events = true;
1788                if (!current_entry)
1789                        ui_browser__set_color(browser, HE_COLORSET_TOP);
1790                nr_events = convert_unit(nr_events, &unit);
1791                printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1792                                     nr_events, unit, unit == ' ' ? "" : " ");
1793                warn = bf;
1794        }
1795
1796        slsmg_write_nstring(warn, browser->width - printed);
1797
1798        if (current_entry)
1799                menu->selection = evsel;
1800}
1801
1802static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1803                                int nr_events, const char *help,
1804                                struct hist_browser_timer *hbt)
1805{
1806        struct perf_evlist *evlist = menu->b.priv;
1807        struct perf_evsel *pos;
1808        const char *ev_name, *title = "Available samples";
1809        int delay_secs = hbt ? hbt->refresh : 0;
1810        int key;
1811
1812        if (ui_browser__show(&menu->b, title,
1813                             "ESC: exit, ENTER|->: Browse histograms") < 0)
1814                return -1;
1815
1816        while (1) {
1817                key = ui_browser__run(&menu->b, delay_secs);
1818
1819                switch (key) {
1820                case K_TIMER:
1821                        hbt->timer(hbt->arg);
1822
1823                        if (!menu->lost_events_warned && menu->lost_events) {
1824                                ui_browser__warn_lost_events(&menu->b);
1825                                menu->lost_events_warned = true;
1826                        }
1827                        continue;
1828                case K_RIGHT:
1829                case K_ENTER:
1830                        if (!menu->selection)
1831                                continue;
1832                        pos = menu->selection;
1833browse_hists:
1834                        perf_evlist__set_selected(evlist, pos);
1835                        /*
1836                         * Give the calling tool a chance to populate the non
1837                         * default evsel resorted hists tree.
1838                         */
1839                        if (hbt)
1840                                hbt->timer(hbt->arg);
1841                        ev_name = perf_evsel__name(pos);
1842                        key = perf_evsel__hists_browse(pos, nr_events, help,
1843                                                       ev_name, true, hbt,
1844                                                       menu->min_pcnt,
1845                                                       menu->env);
1846                        ui_browser__show_title(&menu->b, title);
1847                        switch (key) {
1848                        case K_TAB:
1849                                if (pos->node.next == &evlist->entries)
1850                                        pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1851                                else
1852                                        pos = list_entry(pos->node.next, struct perf_evsel, node);
1853                                goto browse_hists;
1854                        case K_UNTAB:
1855                                if (pos->node.prev == &evlist->entries)
1856                                        pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1857                                else
1858                                        pos = list_entry(pos->node.prev, struct perf_evsel, node);
1859                                goto browse_hists;
1860                        case K_ESC:
1861                                if (!ui_browser__dialog_yesno(&menu->b,
1862                                                "Do you really want to exit?"))
1863                                        continue;
1864                                /* Fall thru */
1865                        case K_SWITCH_INPUT_DATA:
1866                        case 'q':
1867                        case CTRL('c'):
1868                                goto out;
1869                        default:
1870                                continue;
1871                        }
1872                case K_LEFT:
1873                        continue;
1874                case K_ESC:
1875                        if (!ui_browser__dialog_yesno(&menu->b,
1876                                               "Do you really want to exit?"))
1877                                continue;
1878                        /* Fall thru */
1879                case 'q':
1880                case CTRL('c'):
1881                        goto out;
1882                default:
1883                        continue;
1884                }
1885        }
1886
1887out:
1888        ui_browser__hide(&menu->b);
1889        return key;
1890}
1891
1892static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1893                                 void *entry)
1894{
1895        struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1896
1897        if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1898                return true;
1899
1900        return false;
1901}
1902
1903static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1904                                           int nr_entries, const char *help,
1905                                           struct hist_browser_timer *hbt,
1906                                           float min_pcnt,
1907                                           struct perf_session_env *env)
1908{
1909        struct perf_evsel *pos;
1910        struct perf_evsel_menu menu = {
1911                .b = {
1912                        .entries    = &evlist->entries,
1913                        .refresh    = ui_browser__list_head_refresh,
1914                        .seek       = ui_browser__list_head_seek,
1915                        .write      = perf_evsel_menu__write,
1916                        .filter     = filter_group_entries,
1917                        .nr_entries = nr_entries,
1918                        .priv       = evlist,
1919                },
1920                .min_pcnt = min_pcnt,
1921                .env = env,
1922        };
1923
1924        ui_helpline__push("Press ESC to exit");
1925
1926        list_for_each_entry(pos, &evlist->entries, node) {
1927                const char *ev_name = perf_evsel__name(pos);
1928                size_t line_len = strlen(ev_name) + 7;
1929
1930                if (menu.b.width < line_len)
1931                        menu.b.width = line_len;
1932        }
1933
1934        return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1935}
1936
1937int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1938                                  struct hist_browser_timer *hbt,
1939                                  float min_pcnt,
1940                                  struct perf_session_env *env)
1941{
1942        int nr_entries = evlist->nr_entries;
1943
1944single_entry:
1945        if (nr_entries == 1) {
1946                struct perf_evsel *first = list_entry(evlist->entries.next,
1947                                                      struct perf_evsel, node);
1948                const char *ev_name = perf_evsel__name(first);
1949
1950                return perf_evsel__hists_browse(first, nr_entries, help,
1951                                                ev_name, false, hbt, min_pcnt,
1952                                                env);
1953        }
1954
1955        if (symbol_conf.event_group) {
1956                struct perf_evsel *pos;
1957
1958                nr_entries = 0;
1959                list_for_each_entry(pos, &evlist->entries, node)
1960                        if (perf_evsel__is_group_leader(pos))
1961                                nr_entries++;
1962
1963                if (nr_entries == 1)
1964                        goto single_entry;
1965        }
1966
1967        return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1968                                               hbt, min_pcnt, env);
1969}
1970