root/tools/perf/ui/browsers/hists.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hist_browser__has_filter
  2. hist_browser__get_folding
  3. hist_browser__set_title_space
  4. hist_browser__nr_entries
  5. hist_browser__update_rows
  6. hist_browser__refresh_dimensions
  7. hist_browser__reset
  8. tree__folded_sign
  9. hist_entry__folded
  10. callchain_list__folded
  11. callchain_list__set_folding
  12. callchain_node__count_rows_rb_tree
  13. callchain_node__count_flat_rows
  14. callchain_node__count_folded_rows
  15. callchain_node__count_rows
  16. callchain__count_rows
  17. hierarchy_count_rows
  18. hist_entry__toggle_fold
  19. callchain_list__toggle_fold
  20. callchain_node__init_have_children_rb_tree
  21. callchain_node__init_have_children
  22. callchain__init_have_children
  23. hist_entry__init_have_children
  24. hist_browser__toggle_fold
  25. callchain_node__set_folding_rb_tree
  26. callchain_node__set_folding
  27. callchain__set_folding
  28. hierarchy_set_folding
  29. __hist_entry__set_folding
  30. hist_entry__set_folding
  31. __hist_browser__set_folding
  32. hist_browser__set_folding
  33. hist_browser__set_folding_selected
  34. ui_browser__warn_lost_events
  35. hist_browser__title
  36. hist_browser__run
  37. hist_browser__show_callchain_entry
  38. hist_browser__fprintf_callchain_entry
  39. hist_browser__check_output_full
  40. hist_browser__check_dump_full
  41. hist_browser__show_callchain_list
  42. check_percent_display
  43. hist_browser__show_callchain_flat
  44. hist_browser__folded_callchain_str
  45. hist_browser__show_callchain_folded
  46. hist_browser__show_callchain_graph
  47. hist_browser__show_callchain
  48. __hpp__slsmg_color_printf
  49. __HPP_COLOR_PERCENT_FN
  50. hist_browser__show_entry
  51. hist_browser__show_hierarchy_entry
  52. hist_browser__show_no_entry
  53. advance_hpp_check
  54. hists_browser__scnprintf_headers
  55. hists_browser__scnprintf_hierarchy_headers
  56. hists_browser__hierarchy_headers
  57. hists_browser__headers
  58. hist_browser__show_headers
  59. ui_browser__hists_init_top
  60. hist_browser__refresh
  61. hists__filter_entries
  62. hists__filter_prev_entries
  63. ui_browser__hists_seek
  64. hist_browser__fprintf_callchain
  65. hist_browser__fprintf_entry
  66. hist_browser__fprintf_hierarchy_entry
  67. hist_browser__fprintf
  68. hist_browser__dump
  69. hist_browser__init
  70. hist_browser__new
  71. perf_evsel_browser__new
  72. hist_browser__delete
  73. hist_browser__selected_entry
  74. hist_browser__selected_thread
  75. is_report_browser
  76. hists_browser__scnprintf_title
  77. free_popup_options
  78. switch_data_file
  79. do_annotate
  80. add_annotate_opt
  81. do_zoom_thread
  82. add_thread_opt
  83. do_zoom_dso
  84. add_dso_opt
  85. do_browse_map
  86. add_map_opt
  87. do_run_script
  88. do_res_sample_script
  89. add_script_opt_2
  90. add_script_opt
  91. add_res_sample_opt
  92. do_switch_data
  93. add_switch_opt
  94. do_exit_browser
  95. add_exit_opt
  96. do_zoom_socket
  97. add_socket_opt
  98. hist_browser__update_nr_entries
  99. hist_browser__update_percent_limit
  100. perf_evsel__hists_browse
  101. perf_evsel_menu__write
  102. perf_evsel_menu__run
  103. filter_group_entries
  104. __perf_evlist__tui_browse_hists
  105. perf_evlist__tui_browse_hists

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

/* [<][>][^][v][top][bottom][index][help] */