Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <dirent.h>
0003 #include <errno.h>
0004 #include <inttypes.h>
0005 #include <stdio.h>
0006 #include <stdlib.h>
0007 #include <string.h>
0008 #include <linux/rbtree.h>
0009 #include <linux/string.h>
0010 #include <sys/ttydefaults.h>
0011 #include <linux/time64.h>
0012 #include <linux/zalloc.h>
0013 
0014 #include "../../util/debug.h"
0015 #include "../../util/dso.h"
0016 #include "../../util/callchain.h"
0017 #include "../../util/evsel.h"
0018 #include "../../util/evlist.h"
0019 #include "../../util/header.h"
0020 #include "../../util/hist.h"
0021 #include "../../util/machine.h"
0022 #include "../../util/map.h"
0023 #include "../../util/maps.h"
0024 #include "../../util/symbol.h"
0025 #include "../../util/map_symbol.h"
0026 #include "../../util/branch.h"
0027 #include "../../util/pstack.h"
0028 #include "../../util/sort.h"
0029 #include "../../util/top.h"
0030 #include "../../util/thread.h"
0031 #include "../../util/block-info.h"
0032 #include "../../arch/common.h"
0033 #include "../../perf.h"
0034 
0035 #include "../browsers/hists.h"
0036 #include "../helpline.h"
0037 #include "../util.h"
0038 #include "../ui.h"
0039 #include "map.h"
0040 #include "annotate.h"
0041 #include "srcline.h"
0042 #include "string2.h"
0043 #include "units.h"
0044 #include "time-utils.h"
0045 
0046 #include <linux/ctype.h>
0047 
0048 extern void hist_browser__init_hpp(void);
0049 
0050 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
0051 static void hist_browser__update_nr_entries(struct hist_browser *hb);
0052 
0053 static struct rb_node *hists__filter_entries(struct rb_node *nd,
0054                          float min_pcnt);
0055 
0056 static bool hist_browser__has_filter(struct hist_browser *hb)
0057 {
0058     return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
0059 }
0060 
0061 static int hist_browser__get_folding(struct hist_browser *browser)
0062 {
0063     struct rb_node *nd;
0064     struct hists *hists = browser->hists;
0065     int unfolded_rows = 0;
0066 
0067     for (nd = rb_first_cached(&hists->entries);
0068          (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
0069          nd = rb_hierarchy_next(nd)) {
0070         struct hist_entry *he =
0071             rb_entry(nd, struct hist_entry, rb_node);
0072 
0073         if (he->leaf && he->unfolded)
0074             unfolded_rows += he->nr_rows;
0075     }
0076     return unfolded_rows;
0077 }
0078 
0079 static void hist_browser__set_title_space(struct hist_browser *hb)
0080 {
0081     struct ui_browser *browser = &hb->b;
0082     struct hists *hists = hb->hists;
0083     struct perf_hpp_list *hpp_list = hists->hpp_list;
0084 
0085     browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
0086 }
0087 
0088 static u32 hist_browser__nr_entries(struct hist_browser *hb)
0089 {
0090     u32 nr_entries;
0091 
0092     if (symbol_conf.report_hierarchy)
0093         nr_entries = hb->nr_hierarchy_entries;
0094     else if (hist_browser__has_filter(hb))
0095         nr_entries = hb->nr_non_filtered_entries;
0096     else
0097         nr_entries = hb->hists->nr_entries;
0098 
0099     hb->nr_callchain_rows = hist_browser__get_folding(hb);
0100     return nr_entries + hb->nr_callchain_rows;
0101 }
0102 
0103 static void hist_browser__update_rows(struct hist_browser *hb)
0104 {
0105     struct ui_browser *browser = &hb->b;
0106     struct hists *hists = hb->hists;
0107     struct perf_hpp_list *hpp_list = hists->hpp_list;
0108     u16 index_row;
0109 
0110     if (!hb->show_headers) {
0111         browser->rows += browser->extra_title_lines;
0112         browser->extra_title_lines = 0;
0113         return;
0114     }
0115 
0116     browser->extra_title_lines = hpp_list->nr_header_lines;
0117     browser->rows -= browser->extra_title_lines;
0118     /*
0119      * Verify if we were at the last line and that line isn't
0120      * visible because we now show the header line(s).
0121      */
0122     index_row = browser->index - browser->top_idx;
0123     if (index_row >= browser->rows)
0124         browser->index -= index_row - browser->rows + 1;
0125 }
0126 
0127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
0128 {
0129     struct hist_browser *hb = container_of(browser, struct hist_browser, b);
0130 
0131     /* 3 == +/- toggle symbol before actual hist_entry rendering */
0132     browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
0133     /*
0134      * FIXME: Just keeping existing behaviour, but this really should be
0135      *    before updating browser->width, as it will invalidate the
0136      *    calculation above. Fix this and the fallout in another
0137      *    changeset.
0138      */
0139     ui_browser__refresh_dimensions(browser);
0140 }
0141 
0142 static void hist_browser__reset(struct hist_browser *browser)
0143 {
0144     /*
0145      * The hists__remove_entry_filter() already folds non-filtered
0146      * entries so we can assume it has 0 callchain rows.
0147      */
0148     browser->nr_callchain_rows = 0;
0149 
0150     hist_browser__update_nr_entries(browser);
0151     browser->b.nr_entries = hist_browser__nr_entries(browser);
0152     hist_browser__refresh_dimensions(&browser->b);
0153     ui_browser__reset_index(&browser->b);
0154 }
0155 
0156 static char tree__folded_sign(bool unfolded)
0157 {
0158     return unfolded ? '-' : '+';
0159 }
0160 
0161 static char hist_entry__folded(const struct hist_entry *he)
0162 {
0163     return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
0164 }
0165 
0166 static char callchain_list__folded(const struct callchain_list *cl)
0167 {
0168     return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
0169 }
0170 
0171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
0172 {
0173     cl->unfolded = unfold ? cl->has_children : false;
0174 }
0175 
0176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
0177 {
0178     int n = 0;
0179     struct rb_node *nd;
0180 
0181     for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
0182         struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
0183         struct callchain_list *chain;
0184         char folded_sign = ' '; /* No children */
0185 
0186         list_for_each_entry(chain, &child->val, list) {
0187             ++n;
0188 
0189             /* We need this because we may not have children */
0190             folded_sign = callchain_list__folded(chain);
0191             if (folded_sign == '+')
0192                 break;
0193         }
0194 
0195         if (folded_sign == '-') /* Have children and they're unfolded */
0196             n += callchain_node__count_rows_rb_tree(child);
0197     }
0198 
0199     return n;
0200 }
0201 
0202 static int callchain_node__count_flat_rows(struct callchain_node *node)
0203 {
0204     struct callchain_list *chain;
0205     char folded_sign = 0;
0206     int n = 0;
0207 
0208     list_for_each_entry(chain, &node->parent_val, list) {
0209         if (!folded_sign) {
0210             /* only check first chain list entry */
0211             folded_sign = callchain_list__folded(chain);
0212             if (folded_sign == '+')
0213                 return 1;
0214         }
0215         n++;
0216     }
0217 
0218     list_for_each_entry(chain, &node->val, list) {
0219         if (!folded_sign) {
0220             /* node->parent_val list might be empty */
0221             folded_sign = callchain_list__folded(chain);
0222             if (folded_sign == '+')
0223                 return 1;
0224         }
0225         n++;
0226     }
0227 
0228     return n;
0229 }
0230 
0231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
0232 {
0233     return 1;
0234 }
0235 
0236 static int callchain_node__count_rows(struct callchain_node *node)
0237 {
0238     struct callchain_list *chain;
0239     bool unfolded = false;
0240     int n = 0;
0241 
0242     if (callchain_param.mode == CHAIN_FLAT)
0243         return callchain_node__count_flat_rows(node);
0244     else if (callchain_param.mode == CHAIN_FOLDED)
0245         return callchain_node__count_folded_rows(node);
0246 
0247     list_for_each_entry(chain, &node->val, list) {
0248         ++n;
0249 
0250         unfolded = chain->unfolded;
0251     }
0252 
0253     if (unfolded)
0254         n += callchain_node__count_rows_rb_tree(node);
0255 
0256     return n;
0257 }
0258 
0259 static int callchain__count_rows(struct rb_root *chain)
0260 {
0261     struct rb_node *nd;
0262     int n = 0;
0263 
0264     for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
0265         struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
0266         n += callchain_node__count_rows(node);
0267     }
0268 
0269     return n;
0270 }
0271 
0272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
0273                 bool include_children)
0274 {
0275     int count = 0;
0276     struct rb_node *node;
0277     struct hist_entry *child;
0278 
0279     if (he->leaf)
0280         return callchain__count_rows(&he->sorted_chain);
0281 
0282     if (he->has_no_entry)
0283         return 1;
0284 
0285     node = rb_first_cached(&he->hroot_out);
0286     while (node) {
0287         float percent;
0288 
0289         child = rb_entry(node, struct hist_entry, rb_node);
0290         percent = hist_entry__get_percent_limit(child);
0291 
0292         if (!child->filtered && percent >= hb->min_pcnt) {
0293             count++;
0294 
0295             if (include_children && child->unfolded)
0296                 count += hierarchy_count_rows(hb, child, true);
0297         }
0298 
0299         node = rb_next(node);
0300     }
0301     return count;
0302 }
0303 
0304 static bool hist_entry__toggle_fold(struct hist_entry *he)
0305 {
0306     if (!he)
0307         return false;
0308 
0309     if (!he->has_children)
0310         return false;
0311 
0312     he->unfolded = !he->unfolded;
0313     return true;
0314 }
0315 
0316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
0317 {
0318     if (!cl)
0319         return false;
0320 
0321     if (!cl->has_children)
0322         return false;
0323 
0324     cl->unfolded = !cl->unfolded;
0325     return true;
0326 }
0327 
0328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
0329 {
0330     struct rb_node *nd = rb_first(&node->rb_root);
0331 
0332     for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
0333         struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
0334         struct callchain_list *chain;
0335         bool first = true;
0336 
0337         list_for_each_entry(chain, &child->val, list) {
0338             if (first) {
0339                 first = false;
0340                 chain->has_children = chain->list.next != &child->val ||
0341                              !RB_EMPTY_ROOT(&child->rb_root);
0342             } else
0343                 chain->has_children = chain->list.next == &child->val &&
0344                              !RB_EMPTY_ROOT(&child->rb_root);
0345         }
0346 
0347         callchain_node__init_have_children_rb_tree(child);
0348     }
0349 }
0350 
0351 static void callchain_node__init_have_children(struct callchain_node *node,
0352                            bool has_sibling)
0353 {
0354     struct callchain_list *chain;
0355 
0356     chain = list_entry(node->val.next, struct callchain_list, list);
0357     chain->has_children = has_sibling;
0358 
0359     if (!list_empty(&node->val)) {
0360         chain = list_entry(node->val.prev, struct callchain_list, list);
0361         chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
0362     }
0363 
0364     callchain_node__init_have_children_rb_tree(node);
0365 }
0366 
0367 static void callchain__init_have_children(struct rb_root *root)
0368 {
0369     struct rb_node *nd = rb_first(root);
0370     bool has_sibling = nd && rb_next(nd);
0371 
0372     for (nd = rb_first(root); nd; nd = rb_next(nd)) {
0373         struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
0374         callchain_node__init_have_children(node, has_sibling);
0375         if (callchain_param.mode == CHAIN_FLAT ||
0376             callchain_param.mode == CHAIN_FOLDED)
0377             callchain_node__make_parent_list(node);
0378     }
0379 }
0380 
0381 static void hist_entry__init_have_children(struct hist_entry *he)
0382 {
0383     if (he->init_have_children)
0384         return;
0385 
0386     if (he->leaf) {
0387         he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
0388         callchain__init_have_children(&he->sorted_chain);
0389     } else {
0390         he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
0391     }
0392 
0393     he->init_have_children = true;
0394 }
0395 
0396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
0397 {
0398     struct hist_entry *he = browser->he_selection;
0399     struct map_symbol *ms = browser->selection;
0400 
0401     if (!he || !ms)
0402         return false;
0403 
0404     if (ms == &he->ms)
0405            return he->has_children;
0406 
0407     return container_of(ms, struct callchain_list, ms)->has_children;
0408 }
0409 
0410 static bool hist_browser__he_selection_unfolded(struct hist_browser *browser)
0411 {
0412     return browser->he_selection ? browser->he_selection->unfolded : false;
0413 }
0414 
0415 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
0416 {
0417     struct hist_entry *he = browser->he_selection;
0418     struct map_symbol *ms = browser->selection;
0419 
0420     if (!he || !ms)
0421         return false;
0422 
0423     if (ms == &he->ms)
0424            return he->unfolded;
0425 
0426     return container_of(ms, struct callchain_list, ms)->unfolded;
0427 }
0428 
0429 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
0430 {
0431     struct hist_entry *he = browser->he_selection;
0432     struct map_symbol *ms = browser->selection;
0433     struct callchain_list *callchain_entry;
0434 
0435     if (!he || !ms)
0436         return NULL;
0437 
0438     if (ms == &he->ms) {
0439            hist_entry__sym_snprintf(he, bf, size, 0);
0440            return bf + 4; // skip the level, e.g. '[k] '
0441     }
0442 
0443     callchain_entry = container_of(ms, struct callchain_list, ms);
0444     return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
0445 }
0446 
0447 static bool hist_browser__toggle_fold(struct hist_browser *browser)
0448 {
0449     struct hist_entry *he = browser->he_selection;
0450     struct map_symbol *ms = browser->selection;
0451     struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
0452     bool has_children;
0453 
0454     if (!he || !ms)
0455         return false;
0456 
0457     if (ms == &he->ms)
0458         has_children = hist_entry__toggle_fold(he);
0459     else
0460         has_children = callchain_list__toggle_fold(cl);
0461 
0462     if (has_children) {
0463         int child_rows = 0;
0464 
0465         hist_entry__init_have_children(he);
0466         browser->b.nr_entries -= he->nr_rows;
0467 
0468         if (he->leaf)
0469             browser->nr_callchain_rows -= he->nr_rows;
0470         else
0471             browser->nr_hierarchy_entries -= he->nr_rows;
0472 
0473         if (symbol_conf.report_hierarchy)
0474             child_rows = hierarchy_count_rows(browser, he, true);
0475 
0476         if (he->unfolded) {
0477             if (he->leaf)
0478                 he->nr_rows = callchain__count_rows(
0479                         &he->sorted_chain);
0480             else
0481                 he->nr_rows = hierarchy_count_rows(browser, he, false);
0482 
0483             /* account grand children */
0484             if (symbol_conf.report_hierarchy)
0485                 browser->b.nr_entries += child_rows - he->nr_rows;
0486 
0487             if (!he->leaf && he->nr_rows == 0) {
0488                 he->has_no_entry = true;
0489                 he->nr_rows = 1;
0490             }
0491         } else {
0492             if (symbol_conf.report_hierarchy)
0493                 browser->b.nr_entries -= child_rows - he->nr_rows;
0494 
0495             if (he->has_no_entry)
0496                 he->has_no_entry = false;
0497 
0498             he->nr_rows = 0;
0499         }
0500 
0501         browser->b.nr_entries += he->nr_rows;
0502 
0503         if (he->leaf)
0504             browser->nr_callchain_rows += he->nr_rows;
0505         else
0506             browser->nr_hierarchy_entries += he->nr_rows;
0507 
0508         return true;
0509     }
0510 
0511     /* If it doesn't have children, no toggling performed */
0512     return false;
0513 }
0514 
0515 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
0516 {
0517     int n = 0;
0518     struct rb_node *nd;
0519 
0520     for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
0521         struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
0522         struct callchain_list *chain;
0523         bool has_children = false;
0524 
0525         list_for_each_entry(chain, &child->val, list) {
0526             ++n;
0527             callchain_list__set_folding(chain, unfold);
0528             has_children = chain->has_children;
0529         }
0530 
0531         if (has_children)
0532             n += callchain_node__set_folding_rb_tree(child, unfold);
0533     }
0534 
0535     return n;
0536 }
0537 
0538 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
0539 {
0540     struct callchain_list *chain;
0541     bool has_children = false;
0542     int n = 0;
0543 
0544     list_for_each_entry(chain, &node->val, list) {
0545         ++n;
0546         callchain_list__set_folding(chain, unfold);
0547         has_children = chain->has_children;
0548     }
0549 
0550     if (has_children)
0551         n += callchain_node__set_folding_rb_tree(node, unfold);
0552 
0553     return n;
0554 }
0555 
0556 static int callchain__set_folding(struct rb_root *chain, bool unfold)
0557 {
0558     struct rb_node *nd;
0559     int n = 0;
0560 
0561     for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
0562         struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
0563         n += callchain_node__set_folding(node, unfold);
0564     }
0565 
0566     return n;
0567 }
0568 
0569 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
0570                  bool unfold __maybe_unused)
0571 {
0572     float percent;
0573     struct rb_node *nd;
0574     struct hist_entry *child;
0575     int n = 0;
0576 
0577     for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
0578         child = rb_entry(nd, struct hist_entry, rb_node);
0579         percent = hist_entry__get_percent_limit(child);
0580         if (!child->filtered && percent >= hb->min_pcnt)
0581             n++;
0582     }
0583 
0584     return n;
0585 }
0586 
0587 static void __hist_entry__set_folding(struct hist_entry *he,
0588                       struct hist_browser *hb, bool unfold)
0589 {
0590     hist_entry__init_have_children(he);
0591     he->unfolded = unfold ? he->has_children : false;
0592 
0593     if (he->has_children) {
0594         int n;
0595 
0596         if (he->leaf)
0597             n = callchain__set_folding(&he->sorted_chain, unfold);
0598         else
0599             n = hierarchy_set_folding(hb, he, unfold);
0600 
0601         he->nr_rows = unfold ? n : 0;
0602     } else
0603         he->nr_rows = 0;
0604 }
0605 
0606 static void hist_entry__set_folding(struct hist_entry *he,
0607                     struct hist_browser *browser, bool unfold)
0608 {
0609     double percent;
0610 
0611     percent = hist_entry__get_percent_limit(he);
0612     if (he->filtered || percent < browser->min_pcnt)
0613         return;
0614 
0615     __hist_entry__set_folding(he, browser, unfold);
0616 
0617     if (!he->depth || unfold)
0618         browser->nr_hierarchy_entries++;
0619     if (he->leaf)
0620         browser->nr_callchain_rows += he->nr_rows;
0621     else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
0622         browser->nr_hierarchy_entries++;
0623         he->has_no_entry = true;
0624         he->nr_rows = 1;
0625     } else
0626         he->has_no_entry = false;
0627 }
0628 
0629 static void
0630 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
0631 {
0632     struct rb_node *nd;
0633     struct hist_entry *he;
0634 
0635     nd = rb_first_cached(&browser->hists->entries);
0636     while (nd) {
0637         he = rb_entry(nd, struct hist_entry, rb_node);
0638 
0639         /* set folding state even if it's currently folded */
0640         nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
0641 
0642         hist_entry__set_folding(he, browser, unfold);
0643     }
0644 }
0645 
0646 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
0647 {
0648     browser->nr_hierarchy_entries = 0;
0649     browser->nr_callchain_rows = 0;
0650     __hist_browser__set_folding(browser, unfold);
0651 
0652     browser->b.nr_entries = hist_browser__nr_entries(browser);
0653     /* Go to the start, we may be way after valid entries after a collapse */
0654     ui_browser__reset_index(&browser->b);
0655 }
0656 
0657 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
0658 {
0659     if (!browser->he_selection)
0660         return;
0661 
0662     hist_entry__set_folding(browser->he_selection, browser, unfold);
0663     browser->b.nr_entries = hist_browser__nr_entries(browser);
0664 }
0665 
0666 static void ui_browser__warn_lost_events(struct ui_browser *browser)
0667 {
0668     ui_browser__warning(browser, 4,
0669         "Events are being lost, check IO/CPU overload!\n\n"
0670         "You may want to run 'perf' using a RT scheduler policy:\n\n"
0671         " perf top -r 80\n\n"
0672         "Or reduce the sampling frequency.");
0673 }
0674 
0675 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
0676 {
0677     return browser->title ? browser->title(browser, bf, size) : 0;
0678 }
0679 
0680 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
0681 {
0682     switch (key) {
0683     case K_TIMER: {
0684         struct hist_browser_timer *hbt = browser->hbt;
0685         struct evsel *evsel = hists_to_evsel(browser->hists);
0686         u64 nr_entries;
0687 
0688         WARN_ON_ONCE(!hbt);
0689 
0690         if (hbt)
0691             hbt->timer(hbt->arg);
0692 
0693         if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
0694             hist_browser__update_nr_entries(browser);
0695 
0696         nr_entries = hist_browser__nr_entries(browser);
0697         ui_browser__update_nr_entries(&browser->b, nr_entries);
0698 
0699         if (warn_lost_event &&
0700             (evsel->evlist->stats.nr_lost_warned !=
0701              evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
0702             evsel->evlist->stats.nr_lost_warned =
0703                 evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
0704             ui_browser__warn_lost_events(&browser->b);
0705         }
0706 
0707         hist_browser__title(browser, title, size);
0708         ui_browser__show_title(&browser->b, title);
0709         break;
0710     }
0711     case 'D': { /* Debug */
0712         struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
0713         static int seq;
0714 
0715         ui_helpline__pop();
0716         ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
0717                    seq++, browser->b.nr_entries, browser->hists->nr_entries,
0718                    browser->b.extra_title_lines, browser->b.rows,
0719                    browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
0720     }
0721         break;
0722     case 'C':
0723         /* Collapse the whole world. */
0724         hist_browser__set_folding(browser, false);
0725         break;
0726     case 'c':
0727         /* Collapse the selected entry. */
0728         hist_browser__set_folding_selected(browser, false);
0729         break;
0730     case 'E':
0731         /* Expand the whole world. */
0732         hist_browser__set_folding(browser, true);
0733         break;
0734     case 'e':
0735         /* Expand the selected entry. */
0736         hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
0737         break;
0738     case 'H':
0739         browser->show_headers = !browser->show_headers;
0740         hist_browser__update_rows(browser);
0741         break;
0742     case '+':
0743         if (hist_browser__toggle_fold(browser))
0744             break;
0745         /* fall thru */
0746     default:
0747         return -1;
0748     }
0749 
0750     return 0;
0751 }
0752 
0753 int hist_browser__run(struct hist_browser *browser, const char *help,
0754               bool warn_lost_event, int key)
0755 {
0756     char title[160];
0757     struct hist_browser_timer *hbt = browser->hbt;
0758     int delay_secs = hbt ? hbt->refresh : 0;
0759 
0760     browser->b.entries = &browser->hists->entries;
0761     browser->b.nr_entries = hist_browser__nr_entries(browser);
0762 
0763     hist_browser__title(browser, title, sizeof(title));
0764 
0765     if (ui_browser__show(&browser->b, title, "%s", help) < 0)
0766         return -1;
0767 
0768     if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
0769         goto out;
0770 
0771     while (1) {
0772         key = ui_browser__run(&browser->b, delay_secs);
0773 
0774         if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
0775             break;
0776     }
0777 out:
0778     ui_browser__hide(&browser->b);
0779     return key;
0780 }
0781 
0782 struct callchain_print_arg {
0783     /* for hists browser */
0784     off_t   row_offset;
0785     bool    is_current_entry;
0786 
0787     /* for file dump */
0788     FILE    *fp;
0789     int printed;
0790 };
0791 
0792 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
0793                      struct callchain_list *chain,
0794                      const char *str, int offset,
0795                      unsigned short row,
0796                      struct callchain_print_arg *arg);
0797 
0798 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
0799                            struct callchain_list *chain,
0800                            const char *str, int offset,
0801                            unsigned short row,
0802                            struct callchain_print_arg *arg)
0803 {
0804     int color, width;
0805     char folded_sign = callchain_list__folded(chain);
0806     bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
0807 
0808     color = HE_COLORSET_NORMAL;
0809     width = browser->b.width - (offset + 2);
0810     if (ui_browser__is_current_entry(&browser->b, row)) {
0811         browser->selection = &chain->ms;
0812         color = HE_COLORSET_SELECTED;
0813         arg->is_current_entry = true;
0814     }
0815 
0816     ui_browser__set_color(&browser->b, color);
0817     ui_browser__gotorc(&browser->b, row, 0);
0818     ui_browser__write_nstring(&browser->b, " ", offset);
0819     ui_browser__printf(&browser->b, "%c", folded_sign);
0820     ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
0821     ui_browser__write_nstring(&browser->b, str, width);
0822 }
0823 
0824 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
0825                           struct callchain_list *chain,
0826                           const char *str, int offset,
0827                           unsigned short row __maybe_unused,
0828                           struct callchain_print_arg *arg)
0829 {
0830     char folded_sign = callchain_list__folded(chain);
0831 
0832     arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
0833                 folded_sign, str);
0834 }
0835 
0836 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
0837                      unsigned short row);
0838 
0839 static bool hist_browser__check_output_full(struct hist_browser *browser,
0840                         unsigned short row)
0841 {
0842     return browser->b.rows == row;
0843 }
0844 
0845 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
0846                       unsigned short row __maybe_unused)
0847 {
0848     return false;
0849 }
0850 
0851 #define LEVEL_OFFSET_STEP 3
0852 
0853 static int hist_browser__show_callchain_list(struct hist_browser *browser,
0854                          struct callchain_node *node,
0855                          struct callchain_list *chain,
0856                          unsigned short row, u64 total,
0857                          bool need_percent, int offset,
0858                          print_callchain_entry_fn print,
0859                          struct callchain_print_arg *arg)
0860 {
0861     char bf[1024], *alloc_str;
0862     char buf[64], *alloc_str2;
0863     const char *str;
0864     int ret = 1;
0865 
0866     if (arg->row_offset != 0) {
0867         arg->row_offset--;
0868         return 0;
0869     }
0870 
0871     alloc_str = NULL;
0872     alloc_str2 = NULL;
0873 
0874     str = callchain_list__sym_name(chain, bf, sizeof(bf),
0875                        browser->show_dso);
0876 
0877     if (symbol_conf.show_branchflag_count) {
0878         callchain_list_counts__printf_value(chain, NULL,
0879                             buf, sizeof(buf));
0880 
0881         if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
0882             str = "Not enough memory!";
0883         else
0884             str = alloc_str2;
0885     }
0886 
0887     if (need_percent) {
0888         callchain_node__scnprintf_value(node, buf, sizeof(buf),
0889                         total);
0890 
0891         if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
0892             str = "Not enough memory!";
0893         else
0894             str = alloc_str;
0895     }
0896 
0897     print(browser, chain, str, offset, row, arg);
0898     free(alloc_str);
0899     free(alloc_str2);
0900 
0901     return ret;
0902 }
0903 
0904 static bool check_percent_display(struct rb_node *node, u64 parent_total)
0905 {
0906     struct callchain_node *child;
0907 
0908     if (node == NULL)
0909         return false;
0910 
0911     if (rb_next(node))
0912         return true;
0913 
0914     child = rb_entry(node, struct callchain_node, rb_node);
0915     return callchain_cumul_hits(child) != parent_total;
0916 }
0917 
0918 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
0919                          struct rb_root *root,
0920                          unsigned short row, u64 total,
0921                          u64 parent_total,
0922                          print_callchain_entry_fn print,
0923                          struct callchain_print_arg *arg,
0924                          check_output_full_fn is_output_full)
0925 {
0926     struct rb_node *node;
0927     int first_row = row, offset = LEVEL_OFFSET_STEP;
0928     bool need_percent;
0929 
0930     node = rb_first(root);
0931     need_percent = check_percent_display(node, parent_total);
0932 
0933     while (node) {
0934         struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
0935         struct rb_node *next = rb_next(node);
0936         struct callchain_list *chain;
0937         char folded_sign = ' ';
0938         int first = true;
0939         int extra_offset = 0;
0940 
0941         list_for_each_entry(chain, &child->parent_val, list) {
0942             bool was_first = first;
0943 
0944             if (first)
0945                 first = false;
0946             else if (need_percent)
0947                 extra_offset = LEVEL_OFFSET_STEP;
0948 
0949             folded_sign = callchain_list__folded(chain);
0950 
0951             row += hist_browser__show_callchain_list(browser, child,
0952                             chain, row, total,
0953                             was_first && need_percent,
0954                             offset + extra_offset,
0955                             print, arg);
0956 
0957             if (is_output_full(browser, row))
0958                 goto out;
0959 
0960             if (folded_sign == '+')
0961                 goto next;
0962         }
0963 
0964         list_for_each_entry(chain, &child->val, list) {
0965             bool was_first = first;
0966 
0967             if (first)
0968                 first = false;
0969             else if (need_percent)
0970                 extra_offset = LEVEL_OFFSET_STEP;
0971 
0972             folded_sign = callchain_list__folded(chain);
0973 
0974             row += hist_browser__show_callchain_list(browser, child,
0975                             chain, row, total,
0976                             was_first && need_percent,
0977                             offset + extra_offset,
0978                             print, arg);
0979 
0980             if (is_output_full(browser, row))
0981                 goto out;
0982 
0983             if (folded_sign == '+')
0984                 break;
0985         }
0986 
0987 next:
0988         if (is_output_full(browser, row))
0989             break;
0990         node = next;
0991     }
0992 out:
0993     return row - first_row;
0994 }
0995 
0996 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
0997                         struct callchain_list *chain,
0998                         char *value_str, char *old_str)
0999 {
1000     char bf[1024];
1001     const char *str;
1002     char *new;
1003 
1004     str = callchain_list__sym_name(chain, bf, sizeof(bf),
1005                        browser->show_dso);
1006     if (old_str) {
1007         if (asprintf(&new, "%s%s%s", old_str,
1008                  symbol_conf.field_sep ?: ";", str) < 0)
1009             new = NULL;
1010     } else {
1011         if (value_str) {
1012             if (asprintf(&new, "%s %s", value_str, str) < 0)
1013                 new = NULL;
1014         } else {
1015             if (asprintf(&new, "%s", str) < 0)
1016                 new = NULL;
1017         }
1018     }
1019     return new;
1020 }
1021 
1022 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1023                            struct rb_root *root,
1024                            unsigned short row, u64 total,
1025                            u64 parent_total,
1026                            print_callchain_entry_fn print,
1027                            struct callchain_print_arg *arg,
1028                            check_output_full_fn is_output_full)
1029 {
1030     struct rb_node *node;
1031     int first_row = row, offset = LEVEL_OFFSET_STEP;
1032     bool need_percent;
1033 
1034     node = rb_first(root);
1035     need_percent = check_percent_display(node, parent_total);
1036 
1037     while (node) {
1038         struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1039         struct rb_node *next = rb_next(node);
1040         struct callchain_list *chain, *first_chain = NULL;
1041         int first = true;
1042         char *value_str = NULL, *value_str_alloc = NULL;
1043         char *chain_str = NULL, *chain_str_alloc = NULL;
1044 
1045         if (arg->row_offset != 0) {
1046             arg->row_offset--;
1047             goto next;
1048         }
1049 
1050         if (need_percent) {
1051             char buf[64];
1052 
1053             callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1054             if (asprintf(&value_str, "%s", buf) < 0) {
1055                 value_str = (char *)"<...>";
1056                 goto do_print;
1057             }
1058             value_str_alloc = value_str;
1059         }
1060 
1061         list_for_each_entry(chain, &child->parent_val, list) {
1062             chain_str = hist_browser__folded_callchain_str(browser,
1063                         chain, value_str, chain_str);
1064             if (first) {
1065                 first = false;
1066                 first_chain = chain;
1067             }
1068 
1069             if (chain_str == NULL) {
1070                 chain_str = (char *)"Not enough memory!";
1071                 goto do_print;
1072             }
1073 
1074             chain_str_alloc = chain_str;
1075         }
1076 
1077         list_for_each_entry(chain, &child->val, list) {
1078             chain_str = hist_browser__folded_callchain_str(browser,
1079                         chain, value_str, chain_str);
1080             if (first) {
1081                 first = false;
1082                 first_chain = chain;
1083             }
1084 
1085             if (chain_str == NULL) {
1086                 chain_str = (char *)"Not enough memory!";
1087                 goto do_print;
1088             }
1089 
1090             chain_str_alloc = chain_str;
1091         }
1092 
1093 do_print:
1094         print(browser, first_chain, chain_str, offset, row++, arg);
1095         free(value_str_alloc);
1096         free(chain_str_alloc);
1097 
1098 next:
1099         if (is_output_full(browser, row))
1100             break;
1101         node = next;
1102     }
1103 
1104     return row - first_row;
1105 }
1106 
1107 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1108                     struct rb_root *root, int level,
1109                     unsigned short row, u64 total,
1110                     u64 parent_total,
1111                     print_callchain_entry_fn print,
1112                     struct callchain_print_arg *arg,
1113                     check_output_full_fn is_output_full)
1114 {
1115     struct rb_node *node;
1116     int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1117     bool need_percent;
1118     u64 percent_total = total;
1119 
1120     if (callchain_param.mode == CHAIN_GRAPH_REL)
1121         percent_total = parent_total;
1122 
1123     node = rb_first(root);
1124     need_percent = check_percent_display(node, parent_total);
1125 
1126     while (node) {
1127         struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1128         struct rb_node *next = rb_next(node);
1129         struct callchain_list *chain;
1130         char folded_sign = ' ';
1131         int first = true;
1132         int extra_offset = 0;
1133 
1134         list_for_each_entry(chain, &child->val, list) {
1135             bool was_first = first;
1136 
1137             if (first)
1138                 first = false;
1139             else if (need_percent)
1140                 extra_offset = LEVEL_OFFSET_STEP;
1141 
1142             folded_sign = callchain_list__folded(chain);
1143 
1144             row += hist_browser__show_callchain_list(browser, child,
1145                             chain, row, percent_total,
1146                             was_first && need_percent,
1147                             offset + extra_offset,
1148                             print, arg);
1149 
1150             if (is_output_full(browser, row))
1151                 goto out;
1152 
1153             if (folded_sign == '+')
1154                 break;
1155         }
1156 
1157         if (folded_sign == '-') {
1158             const int new_level = level + (extra_offset ? 2 : 1);
1159 
1160             row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1161                                 new_level, row, total,
1162                                 child->children_hit,
1163                                 print, arg, is_output_full);
1164         }
1165         if (is_output_full(browser, row))
1166             break;
1167         node = next;
1168     }
1169 out:
1170     return row - first_row;
1171 }
1172 
1173 static int hist_browser__show_callchain(struct hist_browser *browser,
1174                     struct hist_entry *entry, int level,
1175                     unsigned short row,
1176                     print_callchain_entry_fn print,
1177                     struct callchain_print_arg *arg,
1178                     check_output_full_fn is_output_full)
1179 {
1180     u64 total = hists__total_period(entry->hists);
1181     u64 parent_total;
1182     int printed;
1183 
1184     if (symbol_conf.cumulate_callchain)
1185         parent_total = entry->stat_acc->period;
1186     else
1187         parent_total = entry->stat.period;
1188 
1189     if (callchain_param.mode == CHAIN_FLAT) {
1190         printed = hist_browser__show_callchain_flat(browser,
1191                         &entry->sorted_chain, row,
1192                         total, parent_total, print, arg,
1193                         is_output_full);
1194     } else if (callchain_param.mode == CHAIN_FOLDED) {
1195         printed = hist_browser__show_callchain_folded(browser,
1196                         &entry->sorted_chain, row,
1197                         total, parent_total, print, arg,
1198                         is_output_full);
1199     } else {
1200         printed = hist_browser__show_callchain_graph(browser,
1201                         &entry->sorted_chain, level, row,
1202                         total, parent_total, print, arg,
1203                         is_output_full);
1204     }
1205 
1206     if (arg->is_current_entry)
1207         browser->he_selection = entry;
1208 
1209     return printed;
1210 }
1211 
1212 struct hpp_arg {
1213     struct ui_browser *b;
1214     char folded_sign;
1215     bool current_entry;
1216 };
1217 
1218 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1219 {
1220     struct hpp_arg *arg = hpp->ptr;
1221     int ret, len;
1222     va_list args;
1223     double percent;
1224 
1225     va_start(args, fmt);
1226     len = va_arg(args, int);
1227     percent = va_arg(args, double);
1228     va_end(args);
1229 
1230     ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1231 
1232     ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1233     ui_browser__printf(arg->b, "%s", hpp->buf);
1234 
1235     return ret;
1236 }
1237 
1238 #define __HPP_COLOR_PERCENT_FN(_type, _field)               \
1239 static u64 __hpp_get_##_field(struct hist_entry *he)            \
1240 {                                   \
1241     return he->stat._field;                     \
1242 }                                   \
1243                                     \
1244 static int                              \
1245 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,       \
1246                 struct perf_hpp *hpp,           \
1247                 struct hist_entry *he)          \
1248 {                                   \
1249     return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
1250             __hpp__slsmg_color_printf, true);       \
1251 }
1252 
1253 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)           \
1254 static u64 __hpp_get_acc_##_field(struct hist_entry *he)        \
1255 {                                   \
1256     return he->stat_acc->_field;                    \
1257 }                                   \
1258                                     \
1259 static int                              \
1260 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,       \
1261                 struct perf_hpp *hpp,           \
1262                 struct hist_entry *he)          \
1263 {                                   \
1264     if (!symbol_conf.cumulate_callchain) {              \
1265         struct hpp_arg *arg = hpp->ptr;             \
1266         int len = fmt->user_len ?: fmt->len;            \
1267         int ret = scnprintf(hpp->buf, hpp->size,        \
1268                     "%*s", len, "N/A");         \
1269         ui_browser__printf(arg->b, "%s", hpp->buf);     \
1270                                     \
1271         return ret;                     \
1272     }                               \
1273     return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,       \
1274             " %*.2f%%", __hpp__slsmg_color_printf, true);   \
1275 }
1276 
1277 __HPP_COLOR_PERCENT_FN(overhead, period)
1278 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1279 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1280 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1281 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1282 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1283 
1284 #undef __HPP_COLOR_PERCENT_FN
1285 #undef __HPP_COLOR_ACC_PERCENT_FN
1286 
1287 void hist_browser__init_hpp(void)
1288 {
1289     perf_hpp__format[PERF_HPP__OVERHEAD].color =
1290                 hist_browser__hpp_color_overhead;
1291     perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1292                 hist_browser__hpp_color_overhead_sys;
1293     perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1294                 hist_browser__hpp_color_overhead_us;
1295     perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1296                 hist_browser__hpp_color_overhead_guest_sys;
1297     perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1298                 hist_browser__hpp_color_overhead_guest_us;
1299     perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1300                 hist_browser__hpp_color_overhead_acc;
1301 
1302     res_sample_init();
1303 }
1304 
1305 static int hist_browser__show_entry(struct hist_browser *browser,
1306                     struct hist_entry *entry,
1307                     unsigned short row)
1308 {
1309     int printed = 0;
1310     int width = browser->b.width;
1311     char folded_sign = ' ';
1312     bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1313     bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1314     off_t row_offset = entry->row_offset;
1315     bool first = true;
1316     struct perf_hpp_fmt *fmt;
1317 
1318     if (current_entry) {
1319         browser->he_selection = entry;
1320         browser->selection = &entry->ms;
1321     }
1322 
1323     if (use_callchain) {
1324         hist_entry__init_have_children(entry);
1325         folded_sign = hist_entry__folded(entry);
1326     }
1327 
1328     if (row_offset == 0) {
1329         struct hpp_arg arg = {
1330             .b      = &browser->b,
1331             .folded_sign    = folded_sign,
1332             .current_entry  = current_entry,
1333         };
1334         int column = 0;
1335 
1336         ui_browser__gotorc(&browser->b, row, 0);
1337 
1338         hists__for_each_format(browser->hists, fmt) {
1339             char s[2048];
1340             struct perf_hpp hpp = {
1341                 .buf    = s,
1342                 .size   = sizeof(s),
1343                 .ptr    = &arg,
1344             };
1345 
1346             if (perf_hpp__should_skip(fmt, entry->hists) ||
1347                 column++ < browser->b.horiz_scroll)
1348                 continue;
1349 
1350             if (current_entry && browser->b.navkeypressed) {
1351                 ui_browser__set_color(&browser->b,
1352                               HE_COLORSET_SELECTED);
1353             } else {
1354                 ui_browser__set_color(&browser->b,
1355                               HE_COLORSET_NORMAL);
1356             }
1357 
1358             if (first) {
1359                 if (use_callchain) {
1360                     ui_browser__printf(&browser->b, "%c ", folded_sign);
1361                     width -= 2;
1362                 }
1363                 first = false;
1364             } else {
1365                 ui_browser__printf(&browser->b, "  ");
1366                 width -= 2;
1367             }
1368 
1369             if (fmt->color) {
1370                 int ret = fmt->color(fmt, &hpp, entry);
1371                 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1372                 /*
1373                  * fmt->color() already used ui_browser to
1374                  * print the non alignment bits, skip it (+ret):
1375                  */
1376                 ui_browser__printf(&browser->b, "%s", s + ret);
1377             } else {
1378                 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1379                 ui_browser__printf(&browser->b, "%s", s);
1380             }
1381             width -= hpp.buf - s;
1382         }
1383 
1384         /* The scroll bar isn't being used */
1385         if (!browser->b.navkeypressed)
1386             width += 1;
1387 
1388         ui_browser__write_nstring(&browser->b, "", width);
1389 
1390         ++row;
1391         ++printed;
1392     } else
1393         --row_offset;
1394 
1395     if (folded_sign == '-' && row != browser->b.rows) {
1396         struct callchain_print_arg arg = {
1397             .row_offset = row_offset,
1398             .is_current_entry = current_entry,
1399         };
1400 
1401         printed += hist_browser__show_callchain(browser,
1402                 entry, 1, row,
1403                 hist_browser__show_callchain_entry,
1404                 &arg,
1405                 hist_browser__check_output_full);
1406     }
1407 
1408     return printed;
1409 }
1410 
1411 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1412                           struct hist_entry *entry,
1413                           unsigned short row,
1414                           int level)
1415 {
1416     int printed = 0;
1417     int width = browser->b.width;
1418     char folded_sign = ' ';
1419     bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1420     off_t row_offset = entry->row_offset;
1421     bool first = true;
1422     struct perf_hpp_fmt *fmt;
1423     struct perf_hpp_list_node *fmt_node;
1424     struct hpp_arg arg = {
1425         .b      = &browser->b,
1426         .current_entry  = current_entry,
1427     };
1428     int column = 0;
1429     int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1430 
1431     if (current_entry) {
1432         browser->he_selection = entry;
1433         browser->selection = &entry->ms;
1434     }
1435 
1436     hist_entry__init_have_children(entry);
1437     folded_sign = hist_entry__folded(entry);
1438     arg.folded_sign = folded_sign;
1439 
1440     if (entry->leaf && row_offset) {
1441         row_offset--;
1442         goto show_callchain;
1443     }
1444 
1445     ui_browser__gotorc(&browser->b, row, 0);
1446 
1447     if (current_entry && browser->b.navkeypressed)
1448         ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1449     else
1450         ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1451 
1452     ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1453     width -= level * HIERARCHY_INDENT;
1454 
1455     /* the first hpp_list_node is for overhead columns */
1456     fmt_node = list_first_entry(&entry->hists->hpp_formats,
1457                     struct perf_hpp_list_node, list);
1458     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1459         char s[2048];
1460         struct perf_hpp hpp = {
1461             .buf        = s,
1462             .size       = sizeof(s),
1463             .ptr        = &arg,
1464         };
1465 
1466         if (perf_hpp__should_skip(fmt, entry->hists) ||
1467             column++ < browser->b.horiz_scroll)
1468             continue;
1469 
1470         if (current_entry && browser->b.navkeypressed) {
1471             ui_browser__set_color(&browser->b,
1472                           HE_COLORSET_SELECTED);
1473         } else {
1474             ui_browser__set_color(&browser->b,
1475                           HE_COLORSET_NORMAL);
1476         }
1477 
1478         if (first) {
1479             ui_browser__printf(&browser->b, "%c ", folded_sign);
1480             width -= 2;
1481             first = false;
1482         } else {
1483             ui_browser__printf(&browser->b, "  ");
1484             width -= 2;
1485         }
1486 
1487         if (fmt->color) {
1488             int ret = fmt->color(fmt, &hpp, entry);
1489             hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1490             /*
1491              * fmt->color() already used ui_browser to
1492              * print the non alignment bits, skip it (+ret):
1493              */
1494             ui_browser__printf(&browser->b, "%s", s + ret);
1495         } else {
1496             int ret = fmt->entry(fmt, &hpp, entry);
1497             hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1498             ui_browser__printf(&browser->b, "%s", s);
1499         }
1500         width -= hpp.buf - s;
1501     }
1502 
1503     if (!first) {
1504         ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1505         width -= hierarchy_indent;
1506     }
1507 
1508     if (column >= browser->b.horiz_scroll) {
1509         char s[2048];
1510         struct perf_hpp hpp = {
1511             .buf        = s,
1512             .size       = sizeof(s),
1513             .ptr        = &arg,
1514         };
1515 
1516         if (current_entry && browser->b.navkeypressed) {
1517             ui_browser__set_color(&browser->b,
1518                           HE_COLORSET_SELECTED);
1519         } else {
1520             ui_browser__set_color(&browser->b,
1521                           HE_COLORSET_NORMAL);
1522         }
1523 
1524         perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1525             if (first) {
1526                 ui_browser__printf(&browser->b, "%c ", folded_sign);
1527                 first = false;
1528             } else {
1529                 ui_browser__write_nstring(&browser->b, "", 2);
1530             }
1531 
1532             width -= 2;
1533 
1534             /*
1535              * No need to call hist_entry__snprintf_alignment()
1536              * since this fmt is always the last column in the
1537              * hierarchy mode.
1538              */
1539             if (fmt->color) {
1540                 width -= fmt->color(fmt, &hpp, entry);
1541             } else {
1542                 int i = 0;
1543 
1544                 width -= fmt->entry(fmt, &hpp, entry);
1545                 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1546 
1547                 while (isspace(s[i++]))
1548                     width++;
1549             }
1550         }
1551     }
1552 
1553     /* The scroll bar isn't being used */
1554     if (!browser->b.navkeypressed)
1555         width += 1;
1556 
1557     ui_browser__write_nstring(&browser->b, "", width);
1558 
1559     ++row;
1560     ++printed;
1561 
1562 show_callchain:
1563     if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1564         struct callchain_print_arg carg = {
1565             .row_offset = row_offset,
1566         };
1567 
1568         printed += hist_browser__show_callchain(browser, entry,
1569                     level + 1, row,
1570                     hist_browser__show_callchain_entry, &carg,
1571                     hist_browser__check_output_full);
1572     }
1573 
1574     return printed;
1575 }
1576 
1577 static int hist_browser__show_no_entry(struct hist_browser *browser,
1578                        unsigned short row, int level)
1579 {
1580     int width = browser->b.width;
1581     bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1582     bool first = true;
1583     int column = 0;
1584     int ret;
1585     struct perf_hpp_fmt *fmt;
1586     struct perf_hpp_list_node *fmt_node;
1587     int indent = browser->hists->nr_hpp_node - 2;
1588 
1589     if (current_entry) {
1590         browser->he_selection = NULL;
1591         browser->selection = NULL;
1592     }
1593 
1594     ui_browser__gotorc(&browser->b, row, 0);
1595 
1596     if (current_entry && browser->b.navkeypressed)
1597         ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1598     else
1599         ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1600 
1601     ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1602     width -= level * HIERARCHY_INDENT;
1603 
1604     /* the first hpp_list_node is for overhead columns */
1605     fmt_node = list_first_entry(&browser->hists->hpp_formats,
1606                     struct perf_hpp_list_node, list);
1607     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1608         if (perf_hpp__should_skip(fmt, browser->hists) ||
1609             column++ < browser->b.horiz_scroll)
1610             continue;
1611 
1612         ret = fmt->width(fmt, NULL, browser->hists);
1613 
1614         if (first) {
1615             /* for folded sign */
1616             first = false;
1617             ret++;
1618         } else {
1619             /* space between columns */
1620             ret += 2;
1621         }
1622 
1623         ui_browser__write_nstring(&browser->b, "", ret);
1624         width -= ret;
1625     }
1626 
1627     ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1628     width -= indent * HIERARCHY_INDENT;
1629 
1630     if (column >= browser->b.horiz_scroll) {
1631         char buf[32];
1632 
1633         ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1634         ui_browser__printf(&browser->b, "  %s", buf);
1635         width -= ret + 2;
1636     }
1637 
1638     /* The scroll bar isn't being used */
1639     if (!browser->b.navkeypressed)
1640         width += 1;
1641 
1642     ui_browser__write_nstring(&browser->b, "", width);
1643     return 1;
1644 }
1645 
1646 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1647 {
1648     advance_hpp(hpp, inc);
1649     return hpp->size <= 0;
1650 }
1651 
1652 static int
1653 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1654                  size_t size, int line)
1655 {
1656     struct hists *hists = browser->hists;
1657     struct perf_hpp dummy_hpp = {
1658         .buf    = buf,
1659         .size   = size,
1660     };
1661     struct perf_hpp_fmt *fmt;
1662     size_t ret = 0;
1663     int column = 0;
1664     int span = 0;
1665 
1666     if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1667         ret = scnprintf(buf, size, "  ");
1668         if (advance_hpp_check(&dummy_hpp, ret))
1669             return ret;
1670     }
1671 
1672     hists__for_each_format(browser->hists, fmt) {
1673         if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1674             continue;
1675 
1676         ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1677         if (advance_hpp_check(&dummy_hpp, ret))
1678             break;
1679 
1680         if (span)
1681             continue;
1682 
1683         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1684         if (advance_hpp_check(&dummy_hpp, ret))
1685             break;
1686     }
1687 
1688     return ret;
1689 }
1690 
1691 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1692 {
1693     struct hists *hists = browser->hists;
1694     struct perf_hpp dummy_hpp = {
1695         .buf    = buf,
1696         .size   = size,
1697     };
1698     struct perf_hpp_fmt *fmt;
1699     struct perf_hpp_list_node *fmt_node;
1700     size_t ret = 0;
1701     int column = 0;
1702     int indent = hists->nr_hpp_node - 2;
1703     bool first_node, first_col;
1704 
1705     ret = scnprintf(buf, size, "  ");
1706     if (advance_hpp_check(&dummy_hpp, ret))
1707         return ret;
1708 
1709     first_node = true;
1710     /* the first hpp_list_node is for overhead columns */
1711     fmt_node = list_first_entry(&hists->hpp_formats,
1712                     struct perf_hpp_list_node, list);
1713     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1714         if (column++ < browser->b.horiz_scroll)
1715             continue;
1716 
1717         ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1718         if (advance_hpp_check(&dummy_hpp, ret))
1719             break;
1720 
1721         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1722         if (advance_hpp_check(&dummy_hpp, ret))
1723             break;
1724 
1725         first_node = false;
1726     }
1727 
1728     if (!first_node) {
1729         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1730                 indent * HIERARCHY_INDENT, "");
1731         if (advance_hpp_check(&dummy_hpp, ret))
1732             return ret;
1733     }
1734 
1735     first_node = true;
1736     list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1737         if (!first_node) {
1738             ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1739             if (advance_hpp_check(&dummy_hpp, ret))
1740                 break;
1741         }
1742         first_node = false;
1743 
1744         first_col = true;
1745         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1746             char *start;
1747 
1748             if (perf_hpp__should_skip(fmt, hists))
1749                 continue;
1750 
1751             if (!first_col) {
1752                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1753                 if (advance_hpp_check(&dummy_hpp, ret))
1754                     break;
1755             }
1756             first_col = false;
1757 
1758             ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1759             dummy_hpp.buf[ret] = '\0';
1760 
1761             start = strim(dummy_hpp.buf);
1762             ret = strlen(start);
1763 
1764             if (start != dummy_hpp.buf)
1765                 memmove(dummy_hpp.buf, start, ret + 1);
1766 
1767             if (advance_hpp_check(&dummy_hpp, ret))
1768                 break;
1769         }
1770     }
1771 
1772     return ret;
1773 }
1774 
1775 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1776 {
1777     char headers[1024];
1778 
1779     hists_browser__scnprintf_hierarchy_headers(browser, headers,
1780                            sizeof(headers));
1781 
1782     ui_browser__gotorc(&browser->b, 0, 0);
1783     ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1784     ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1785 }
1786 
1787 static void hists_browser__headers(struct hist_browser *browser)
1788 {
1789     struct hists *hists = browser->hists;
1790     struct perf_hpp_list *hpp_list = hists->hpp_list;
1791 
1792     int line;
1793 
1794     for (line = 0; line < hpp_list->nr_header_lines; line++) {
1795         char headers[1024];
1796 
1797         hists_browser__scnprintf_headers(browser, headers,
1798                          sizeof(headers), line);
1799 
1800         ui_browser__gotorc_title(&browser->b, line, 0);
1801         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1802         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1803     }
1804 }
1805 
1806 static void hist_browser__show_headers(struct hist_browser *browser)
1807 {
1808     if (symbol_conf.report_hierarchy)
1809         hists_browser__hierarchy_headers(browser);
1810     else
1811         hists_browser__headers(browser);
1812 }
1813 
1814 static void ui_browser__hists_init_top(struct ui_browser *browser)
1815 {
1816     if (browser->top == NULL) {
1817         struct hist_browser *hb;
1818 
1819         hb = container_of(browser, struct hist_browser, b);
1820         browser->top = rb_first_cached(&hb->hists->entries);
1821     }
1822 }
1823 
1824 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1825 {
1826     unsigned row = 0;
1827     struct rb_node *nd;
1828     struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1829 
1830     if (hb->show_headers)
1831         hist_browser__show_headers(hb);
1832 
1833     ui_browser__hists_init_top(browser);
1834     hb->he_selection = NULL;
1835     hb->selection = NULL;
1836 
1837     for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1838         struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1839         float percent;
1840 
1841         if (h->filtered) {
1842             /* let it move to sibling */
1843             h->unfolded = false;
1844             continue;
1845         }
1846 
1847         if (symbol_conf.report_individual_block)
1848             percent = block_info__total_cycles_percent(h);
1849         else
1850             percent = hist_entry__get_percent_limit(h);
1851 
1852         if (percent < hb->min_pcnt)
1853             continue;
1854 
1855         if (symbol_conf.report_hierarchy) {
1856             row += hist_browser__show_hierarchy_entry(hb, h, row,
1857                                   h->depth);
1858             if (row == browser->rows)
1859                 break;
1860 
1861             if (h->has_no_entry) {
1862                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1863                 row++;
1864             }
1865         } else {
1866             row += hist_browser__show_entry(hb, h, row);
1867         }
1868 
1869         if (row == browser->rows)
1870             break;
1871     }
1872 
1873     return row;
1874 }
1875 
1876 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1877                          float min_pcnt)
1878 {
1879     while (nd != NULL) {
1880         struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1881         float percent = hist_entry__get_percent_limit(h);
1882 
1883         if (!h->filtered && percent >= min_pcnt)
1884             return nd;
1885 
1886         /*
1887          * If it's filtered, its all children also were filtered.
1888          * So move to sibling node.
1889          */
1890         if (rb_next(nd))
1891             nd = rb_next(nd);
1892         else
1893             nd = rb_hierarchy_next(nd);
1894     }
1895 
1896     return NULL;
1897 }
1898 
1899 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1900                           float min_pcnt)
1901 {
1902     while (nd != NULL) {
1903         struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1904         float percent = hist_entry__get_percent_limit(h);
1905 
1906         if (!h->filtered && percent >= min_pcnt)
1907             return nd;
1908 
1909         nd = rb_hierarchy_prev(nd);
1910     }
1911 
1912     return NULL;
1913 }
1914 
1915 static void ui_browser__hists_seek(struct ui_browser *browser,
1916                    off_t offset, int whence)
1917 {
1918     struct hist_entry *h;
1919     struct rb_node *nd;
1920     bool first = true;
1921     struct hist_browser *hb;
1922 
1923     hb = container_of(browser, struct hist_browser, b);
1924 
1925     if (browser->nr_entries == 0)
1926         return;
1927 
1928     ui_browser__hists_init_top(browser);
1929 
1930     switch (whence) {
1931     case SEEK_SET:
1932         nd = hists__filter_entries(rb_first(browser->entries),
1933                        hb->min_pcnt);
1934         break;
1935     case SEEK_CUR:
1936         nd = browser->top;
1937         goto do_offset;
1938     case SEEK_END:
1939         nd = rb_hierarchy_last(rb_last(browser->entries));
1940         nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1941         first = false;
1942         break;
1943     default:
1944         return;
1945     }
1946 
1947     /*
1948      * Moves not relative to the first visible entry invalidates its
1949      * row_offset:
1950      */
1951     h = rb_entry(browser->top, struct hist_entry, rb_node);
1952     h->row_offset = 0;
1953 
1954     /*
1955      * Here we have to check if nd is expanded (+), if it is we can't go
1956      * the next top level hist_entry, instead we must compute an offset of
1957      * what _not_ to show and not change the first visible entry.
1958      *
1959      * This offset increments when we are going from top to bottom and
1960      * decreases when we're going from bottom to top.
1961      *
1962      * As we don't have backpointers to the top level in the callchains
1963      * structure, we need to always print the whole hist_entry callchain,
1964      * skipping the first ones that are before the first visible entry
1965      * and stop when we printed enough lines to fill the screen.
1966      */
1967 do_offset:
1968     if (!nd)
1969         return;
1970 
1971     if (offset > 0) {
1972         do {
1973             h = rb_entry(nd, struct hist_entry, rb_node);
1974             if (h->unfolded && h->leaf) {
1975                 u16 remaining = h->nr_rows - h->row_offset;
1976                 if (offset > remaining) {
1977                     offset -= remaining;
1978                     h->row_offset = 0;
1979                 } else {
1980                     h->row_offset += offset;
1981                     offset = 0;
1982                     browser->top = nd;
1983                     break;
1984                 }
1985             }
1986             nd = hists__filter_entries(rb_hierarchy_next(nd),
1987                            hb->min_pcnt);
1988             if (nd == NULL)
1989                 break;
1990             --offset;
1991             browser->top = nd;
1992         } while (offset != 0);
1993     } else if (offset < 0) {
1994         while (1) {
1995             h = rb_entry(nd, struct hist_entry, rb_node);
1996             if (h->unfolded && h->leaf) {
1997                 if (first) {
1998                     if (-offset > h->row_offset) {
1999                         offset += h->row_offset;
2000                         h->row_offset = 0;
2001                     } else {
2002                         h->row_offset += offset;
2003                         offset = 0;
2004                         browser->top = nd;
2005                         break;
2006                     }
2007                 } else {
2008                     if (-offset > h->nr_rows) {
2009                         offset += h->nr_rows;
2010                         h->row_offset = 0;
2011                     } else {
2012                         h->row_offset = h->nr_rows + offset;
2013                         offset = 0;
2014                         browser->top = nd;
2015                         break;
2016                     }
2017                 }
2018             }
2019 
2020             nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2021                             hb->min_pcnt);
2022             if (nd == NULL)
2023                 break;
2024             ++offset;
2025             browser->top = nd;
2026             if (offset == 0) {
2027                 /*
2028                  * Last unfiltered hist_entry, check if it is
2029                  * unfolded, if it is then we should have
2030                  * row_offset at its last entry.
2031                  */
2032                 h = rb_entry(nd, struct hist_entry, rb_node);
2033                 if (h->unfolded && h->leaf)
2034                     h->row_offset = h->nr_rows;
2035                 break;
2036             }
2037             first = false;
2038         }
2039     } else {
2040         browser->top = nd;
2041         h = rb_entry(nd, struct hist_entry, rb_node);
2042         h->row_offset = 0;
2043     }
2044 }
2045 
2046 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2047                        struct hist_entry *he, FILE *fp,
2048                        int level)
2049 {
2050     struct callchain_print_arg arg  = {
2051         .fp = fp,
2052     };
2053 
2054     hist_browser__show_callchain(browser, he, level, 0,
2055                      hist_browser__fprintf_callchain_entry, &arg,
2056                      hist_browser__check_dump_full);
2057     return arg.printed;
2058 }
2059 
2060 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2061                        struct hist_entry *he, FILE *fp)
2062 {
2063     char s[8192];
2064     int printed = 0;
2065     char folded_sign = ' ';
2066     struct perf_hpp hpp = {
2067         .buf = s,
2068         .size = sizeof(s),
2069     };
2070     struct perf_hpp_fmt *fmt;
2071     bool first = true;
2072     int ret;
2073 
2074     if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2075         folded_sign = hist_entry__folded(he);
2076         printed += fprintf(fp, "%c ", folded_sign);
2077     }
2078 
2079     hists__for_each_format(browser->hists, fmt) {
2080         if (perf_hpp__should_skip(fmt, he->hists))
2081             continue;
2082 
2083         if (!first) {
2084             ret = scnprintf(hpp.buf, hpp.size, "  ");
2085             advance_hpp(&hpp, ret);
2086         } else
2087             first = false;
2088 
2089         ret = fmt->entry(fmt, &hpp, he);
2090         ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2091         advance_hpp(&hpp, ret);
2092     }
2093     printed += fprintf(fp, "%s\n", s);
2094 
2095     if (folded_sign == '-')
2096         printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2097 
2098     return printed;
2099 }
2100 
2101 
2102 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2103                          struct hist_entry *he,
2104                          FILE *fp, int level)
2105 {
2106     char s[8192];
2107     int printed = 0;
2108     char folded_sign = ' ';
2109     struct perf_hpp hpp = {
2110         .buf = s,
2111         .size = sizeof(s),
2112     };
2113     struct perf_hpp_fmt *fmt;
2114     struct perf_hpp_list_node *fmt_node;
2115     bool first = true;
2116     int ret;
2117     int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2118 
2119     printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2120 
2121     folded_sign = hist_entry__folded(he);
2122     printed += fprintf(fp, "%c", folded_sign);
2123 
2124     /* the first hpp_list_node is for overhead columns */
2125     fmt_node = list_first_entry(&he->hists->hpp_formats,
2126                     struct perf_hpp_list_node, list);
2127     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2128         if (!first) {
2129             ret = scnprintf(hpp.buf, hpp.size, "  ");
2130             advance_hpp(&hpp, ret);
2131         } else
2132             first = false;
2133 
2134         ret = fmt->entry(fmt, &hpp, he);
2135         advance_hpp(&hpp, ret);
2136     }
2137 
2138     ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2139     advance_hpp(&hpp, ret);
2140 
2141     perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2142         ret = scnprintf(hpp.buf, hpp.size, "  ");
2143         advance_hpp(&hpp, ret);
2144 
2145         ret = fmt->entry(fmt, &hpp, he);
2146         advance_hpp(&hpp, ret);
2147     }
2148 
2149     strim(s);
2150     printed += fprintf(fp, "%s\n", s);
2151 
2152     if (he->leaf && folded_sign == '-') {
2153         printed += hist_browser__fprintf_callchain(browser, he, fp,
2154                                he->depth + 1);
2155     }
2156 
2157     return printed;
2158 }
2159 
2160 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2161 {
2162     struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2163                            browser->min_pcnt);
2164     int printed = 0;
2165 
2166     while (nd) {
2167         struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2168 
2169         if (symbol_conf.report_hierarchy) {
2170             printed += hist_browser__fprintf_hierarchy_entry(browser,
2171                                      h, fp,
2172                                      h->depth);
2173         } else {
2174             printed += hist_browser__fprintf_entry(browser, h, fp);
2175         }
2176 
2177         nd = hists__filter_entries(rb_hierarchy_next(nd),
2178                        browser->min_pcnt);
2179     }
2180 
2181     return printed;
2182 }
2183 
2184 static int hist_browser__dump(struct hist_browser *browser)
2185 {
2186     char filename[64];
2187     FILE *fp;
2188 
2189     while (1) {
2190         scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2191         if (access(filename, F_OK))
2192             break;
2193         /*
2194          * XXX: Just an arbitrary lazy upper limit
2195          */
2196         if (++browser->print_seq == 8192) {
2197             ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2198             return -1;
2199         }
2200     }
2201 
2202     fp = fopen(filename, "w");
2203     if (fp == NULL) {
2204         char bf[64];
2205         const char *err = str_error_r(errno, bf, sizeof(bf));
2206         ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2207         return -1;
2208     }
2209 
2210     ++browser->print_seq;
2211     hist_browser__fprintf(browser, fp);
2212     fclose(fp);
2213     ui_helpline__fpush("%s written!", filename);
2214 
2215     return 0;
2216 }
2217 
2218 void hist_browser__init(struct hist_browser *browser,
2219             struct hists *hists)
2220 {
2221     struct perf_hpp_fmt *fmt;
2222 
2223     browser->hists          = hists;
2224     browser->b.refresh      = hist_browser__refresh;
2225     browser->b.refresh_dimensions   = hist_browser__refresh_dimensions;
2226     browser->b.seek         = ui_browser__hists_seek;
2227     browser->b.use_navkeypressed    = true;
2228     browser->show_headers       = symbol_conf.show_hist_headers;
2229     hist_browser__set_title_space(browser);
2230 
2231     if (symbol_conf.report_hierarchy) {
2232         struct perf_hpp_list_node *fmt_node;
2233 
2234         /* count overhead columns (in the first node) */
2235         fmt_node = list_first_entry(&hists->hpp_formats,
2236                         struct perf_hpp_list_node, list);
2237         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2238             ++browser->b.columns;
2239 
2240         /* add a single column for whole hierarchy sort keys*/
2241         ++browser->b.columns;
2242     } else {
2243         hists__for_each_format(hists, fmt)
2244             ++browser->b.columns;
2245     }
2246 
2247     hists__reset_column_width(hists);
2248 }
2249 
2250 struct hist_browser *hist_browser__new(struct hists *hists)
2251 {
2252     struct hist_browser *browser = zalloc(sizeof(*browser));
2253 
2254     if (browser)
2255         hist_browser__init(browser, hists);
2256 
2257     return browser;
2258 }
2259 
2260 static struct hist_browser *
2261 perf_evsel_browser__new(struct evsel *evsel,
2262             struct hist_browser_timer *hbt,
2263             struct perf_env *env,
2264             struct annotation_options *annotation_opts)
2265 {
2266     struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2267 
2268     if (browser) {
2269         browser->hbt   = hbt;
2270         browser->env   = env;
2271         browser->title = hists_browser__scnprintf_title;
2272         browser->annotation_opts = annotation_opts;
2273     }
2274     return browser;
2275 }
2276 
2277 void hist_browser__delete(struct hist_browser *browser)
2278 {
2279     free(browser);
2280 }
2281 
2282 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2283 {
2284     return browser->he_selection;
2285 }
2286 
2287 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2288 {
2289     return browser->he_selection->thread;
2290 }
2291 
2292 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2293 {
2294     return browser->he_selection ? browser->he_selection->res_samples : NULL;
2295 }
2296 
2297 /* Check whether the browser is for 'top' or 'report' */
2298 static inline bool is_report_browser(void *timer)
2299 {
2300     return timer == NULL;
2301 }
2302 
2303 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2304 {
2305     struct hist_browser_timer *hbt = browser->hbt;
2306     int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2307 
2308     if (!is_report_browser(hbt)) {
2309         struct perf_top *top = hbt->arg;
2310 
2311         printed += scnprintf(bf + printed, size - printed,
2312                      " lost: %" PRIu64 "/%" PRIu64,
2313                      top->lost, top->lost_total);
2314 
2315         printed += scnprintf(bf + printed, size - printed,
2316                      " drop: %" PRIu64 "/%" PRIu64,
2317                      top->drop, top->drop_total);
2318 
2319         if (top->zero)
2320             printed += scnprintf(bf + printed, size - printed, " [z]");
2321 
2322         perf_top__reset_sample_counters(top);
2323     }
2324 
2325 
2326     return printed;
2327 }
2328 
2329 static inline void free_popup_options(char **options, int n)
2330 {
2331     int i;
2332 
2333     for (i = 0; i < n; ++i)
2334         zfree(&options[i]);
2335 }
2336 
2337 /*
2338  * Only runtime switching of perf data file will make "input_name" point
2339  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2340  * whether we need to call free() for current "input_name" during the switch.
2341  */
2342 static bool is_input_name_malloced = false;
2343 
2344 static int switch_data_file(void)
2345 {
2346     char *pwd, *options[32], *abs_path[32], *tmp;
2347     DIR *pwd_dir;
2348     int nr_options = 0, choice = -1, ret = -1;
2349     struct dirent *dent;
2350 
2351     pwd = getenv("PWD");
2352     if (!pwd)
2353         return ret;
2354 
2355     pwd_dir = opendir(pwd);
2356     if (!pwd_dir)
2357         return ret;
2358 
2359     memset(options, 0, sizeof(options));
2360     memset(abs_path, 0, sizeof(abs_path));
2361 
2362     while ((dent = readdir(pwd_dir))) {
2363         char path[PATH_MAX];
2364         u64 magic;
2365         char *name = dent->d_name;
2366         FILE *file;
2367 
2368         if (!(dent->d_type == DT_REG))
2369             continue;
2370 
2371         snprintf(path, sizeof(path), "%s/%s", pwd, name);
2372 
2373         file = fopen(path, "r");
2374         if (!file)
2375             continue;
2376 
2377         if (fread(&magic, 1, 8, file) < 8)
2378             goto close_file_and_continue;
2379 
2380         if (is_perf_magic(magic)) {
2381             options[nr_options] = strdup(name);
2382             if (!options[nr_options])
2383                 goto close_file_and_continue;
2384 
2385             abs_path[nr_options] = strdup(path);
2386             if (!abs_path[nr_options]) {
2387                 zfree(&options[nr_options]);
2388                 ui__warning("Can't search all data files due to memory shortage.\n");
2389                 fclose(file);
2390                 break;
2391             }
2392 
2393             nr_options++;
2394         }
2395 
2396 close_file_and_continue:
2397         fclose(file);
2398         if (nr_options >= 32) {
2399             ui__warning("Too many perf data files in PWD!\n"
2400                     "Only the first 32 files will be listed.\n");
2401             break;
2402         }
2403     }
2404     closedir(pwd_dir);
2405 
2406     if (nr_options) {
2407         choice = ui__popup_menu(nr_options, options, NULL);
2408         if (choice < nr_options && choice >= 0) {
2409             tmp = strdup(abs_path[choice]);
2410             if (tmp) {
2411                 if (is_input_name_malloced)
2412                     free((void *)input_name);
2413                 input_name = tmp;
2414                 is_input_name_malloced = true;
2415                 ret = 0;
2416             } else
2417                 ui__warning("Data switch failed due to memory shortage!\n");
2418         }
2419     }
2420 
2421     free_popup_options(options, nr_options);
2422     free_popup_options(abs_path, nr_options);
2423     return ret;
2424 }
2425 
2426 struct popup_action {
2427     unsigned long       time;
2428     struct thread       *thread;
2429     struct map_symbol   ms;
2430     int         socket;
2431     struct evsel    *evsel;
2432     enum rstype     rstype;
2433 
2434     int (*fn)(struct hist_browser *browser, struct popup_action *act);
2435 };
2436 
2437 static int
2438 do_annotate(struct hist_browser *browser, struct popup_action *act)
2439 {
2440     struct evsel *evsel;
2441     struct annotation *notes;
2442     struct hist_entry *he;
2443     int err;
2444 
2445     if (!browser->annotation_opts->objdump_path &&
2446         perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2447         return 0;
2448 
2449     notes = symbol__annotation(act->ms.sym);
2450     if (!notes->src)
2451         return 0;
2452 
2453     if (browser->block_evsel)
2454         evsel = browser->block_evsel;
2455     else
2456         evsel = hists_to_evsel(browser->hists);
2457 
2458     err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2459                        browser->annotation_opts);
2460     he = hist_browser__selected_entry(browser);
2461     /*
2462      * offer option to annotate the other branch source or target
2463      * (if they exists) when returning from annotate
2464      */
2465     if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2466         return 1;
2467 
2468     ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2469     if (err)
2470         ui_browser__handle_resize(&browser->b);
2471     return 0;
2472 }
2473 
2474 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2475 {
2476     struct annotated_source *src;
2477     struct symbol *sym;
2478     char name[64];
2479 
2480     snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2481 
2482     sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2483     if (sym) {
2484         src = symbol__hists(sym, 1);
2485         if (!src) {
2486             symbol__delete(sym);
2487             return NULL;
2488         }
2489 
2490         dso__insert_symbol(map->dso, sym);
2491     }
2492 
2493     return sym;
2494 }
2495 
2496 static int
2497 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2498          struct popup_action *act, char **optstr,
2499          struct map_symbol *ms,
2500          u64 addr)
2501 {
2502     if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2503         return 0;
2504 
2505     if (!ms->sym)
2506         ms->sym = symbol__new_unresolved(addr, ms->map);
2507 
2508     if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2509         return 0;
2510 
2511     if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2512         return 0;
2513 
2514     act->ms = *ms;
2515     act->fn = do_annotate;
2516     return 1;
2517 }
2518 
2519 static int
2520 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2521 {
2522     struct thread *thread = act->thread;
2523 
2524     if ((!hists__has(browser->hists, thread) &&
2525          !hists__has(browser->hists, comm)) || thread == NULL)
2526         return 0;
2527 
2528     if (browser->hists->thread_filter) {
2529         pstack__remove(browser->pstack, &browser->hists->thread_filter);
2530         perf_hpp__set_elide(HISTC_THREAD, false);
2531         thread__zput(browser->hists->thread_filter);
2532         ui_helpline__pop();
2533     } else {
2534         if (hists__has(browser->hists, thread)) {
2535             ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2536                        thread->comm_set ? thread__comm_str(thread) : "",
2537                        thread->tid);
2538         } else {
2539             ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2540                        thread->comm_set ? thread__comm_str(thread) : "");
2541         }
2542 
2543         browser->hists->thread_filter = thread__get(thread);
2544         perf_hpp__set_elide(HISTC_THREAD, false);
2545         pstack__push(browser->pstack, &browser->hists->thread_filter);
2546     }
2547 
2548     hists__filter_by_thread(browser->hists);
2549     hist_browser__reset(browser);
2550     return 0;
2551 }
2552 
2553 static int
2554 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2555            char **optstr, struct thread *thread)
2556 {
2557     int ret;
2558 
2559     if ((!hists__has(browser->hists, thread) &&
2560          !hists__has(browser->hists, comm)) || thread == NULL)
2561         return 0;
2562 
2563     if (hists__has(browser->hists, thread)) {
2564         ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2565                    browser->hists->thread_filter ? "out of" : "into",
2566                    thread->comm_set ? thread__comm_str(thread) : "",
2567                    thread->tid);
2568     } else {
2569         ret = asprintf(optstr, "Zoom %s %s thread",
2570                    browser->hists->thread_filter ? "out of" : "into",
2571                    thread->comm_set ? thread__comm_str(thread) : "");
2572     }
2573     if (ret < 0)
2574         return 0;
2575 
2576     act->thread = thread;
2577     act->fn = do_zoom_thread;
2578     return 1;
2579 }
2580 
2581 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2582 {
2583     if (!hists__has(browser->hists, dso) || map == NULL)
2584         return 0;
2585 
2586     if (browser->hists->dso_filter) {
2587         pstack__remove(browser->pstack, &browser->hists->dso_filter);
2588         perf_hpp__set_elide(HISTC_DSO, false);
2589         browser->hists->dso_filter = NULL;
2590         ui_helpline__pop();
2591     } else {
2592         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2593                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2594         browser->hists->dso_filter = map->dso;
2595         perf_hpp__set_elide(HISTC_DSO, true);
2596         pstack__push(browser->pstack, &browser->hists->dso_filter);
2597     }
2598 
2599     hists__filter_by_dso(browser->hists);
2600     hist_browser__reset(browser);
2601     return 0;
2602 }
2603 
2604 static int
2605 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2606 {
2607     return hists_browser__zoom_map(browser, act->ms.map);
2608 }
2609 
2610 static int
2611 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2612         char **optstr, struct map *map)
2613 {
2614     if (!hists__has(browser->hists, dso) || map == NULL)
2615         return 0;
2616 
2617     if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2618              browser->hists->dso_filter ? "out of" : "into",
2619              __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2620         return 0;
2621 
2622     act->ms.map = map;
2623     act->fn = do_zoom_dso;
2624     return 1;
2625 }
2626 
2627 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2628 {
2629     hist_browser__toggle_fold(browser);
2630     return 0;
2631 }
2632 
2633 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2634 {
2635     char sym_name[512];
2636 
2637         if (!hist_browser__selection_has_children(browser))
2638                 return 0;
2639 
2640     if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2641              hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2642              hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2643         return 0;
2644 
2645     act->fn = do_toggle_callchain;
2646     return 1;
2647 }
2648 
2649 static int
2650 do_browse_map(struct hist_browser *browser __maybe_unused,
2651           struct popup_action *act)
2652 {
2653     map__browse(act->ms.map);
2654     return 0;
2655 }
2656 
2657 static int
2658 add_map_opt(struct hist_browser *browser,
2659         struct popup_action *act, char **optstr, struct map *map)
2660 {
2661     if (!hists__has(browser->hists, dso) || map == NULL)
2662         return 0;
2663 
2664     if (asprintf(optstr, "Browse map details") < 0)
2665         return 0;
2666 
2667     act->ms.map = map;
2668     act->fn = do_browse_map;
2669     return 1;
2670 }
2671 
2672 static int
2673 do_run_script(struct hist_browser *browser __maybe_unused,
2674           struct popup_action *act)
2675 {
2676     char *script_opt;
2677     int len;
2678     int n = 0;
2679 
2680     len = 100;
2681     if (act->thread)
2682         len += strlen(thread__comm_str(act->thread));
2683     else if (act->ms.sym)
2684         len += strlen(act->ms.sym->name);
2685     script_opt = malloc(len);
2686     if (!script_opt)
2687         return -1;
2688 
2689     script_opt[0] = 0;
2690     if (act->thread) {
2691         n = scnprintf(script_opt, len, " -c %s ",
2692               thread__comm_str(act->thread));
2693     } else if (act->ms.sym) {
2694         n = scnprintf(script_opt, len, " -S %s ",
2695               act->ms.sym->name);
2696     }
2697 
2698     if (act->time) {
2699         char start[32], end[32];
2700         unsigned long starttime = act->time;
2701         unsigned long endtime = act->time + symbol_conf.time_quantum;
2702 
2703         if (starttime == endtime) { /* Display 1ms as fallback */
2704             starttime -= 1*NSEC_PER_MSEC;
2705             endtime += 1*NSEC_PER_MSEC;
2706         }
2707         timestamp__scnprintf_usec(starttime, start, sizeof start);
2708         timestamp__scnprintf_usec(endtime, end, sizeof end);
2709         n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2710     }
2711 
2712     script_browse(script_opt, act->evsel);
2713     free(script_opt);
2714     return 0;
2715 }
2716 
2717 static int
2718 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2719              struct popup_action *act)
2720 {
2721     struct hist_entry *he;
2722 
2723     he = hist_browser__selected_entry(browser);
2724     res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2725     return 0;
2726 }
2727 
2728 static int
2729 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2730            struct popup_action *act, char **optstr,
2731            struct thread *thread, struct symbol *sym,
2732            struct evsel *evsel, const char *tstr)
2733 {
2734 
2735     if (thread) {
2736         if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2737                  thread__comm_str(thread), tstr) < 0)
2738             return 0;
2739     } else if (sym) {
2740         if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2741                  sym->name, tstr) < 0)
2742             return 0;
2743     } else {
2744         if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2745             return 0;
2746     }
2747 
2748     act->thread = thread;
2749     act->ms.sym = sym;
2750     act->evsel = evsel;
2751     act->fn = do_run_script;
2752     return 1;
2753 }
2754 
2755 static int
2756 add_script_opt(struct hist_browser *browser,
2757            struct popup_action *act, char **optstr,
2758            struct thread *thread, struct symbol *sym,
2759            struct evsel *evsel)
2760 {
2761     int n, j;
2762     struct hist_entry *he;
2763 
2764     n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2765 
2766     he = hist_browser__selected_entry(browser);
2767     if (sort_order && strstr(sort_order, "time")) {
2768         char tstr[128];
2769 
2770         optstr++;
2771         act++;
2772         j = sprintf(tstr, " in ");
2773         j += timestamp__scnprintf_usec(he->time, tstr + j,
2774                            sizeof tstr - j);
2775         j += sprintf(tstr + j, "-");
2776         timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2777                           tstr + j, sizeof tstr - j);
2778         n += add_script_opt_2(browser, act, optstr, thread, sym,
2779                       evsel, tstr);
2780         act->time = he->time;
2781     }
2782     return n;
2783 }
2784 
2785 static int
2786 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2787            struct popup_action *act, char **optstr,
2788            struct res_sample *res_sample,
2789            struct evsel *evsel,
2790            enum rstype type)
2791 {
2792     if (!res_sample)
2793         return 0;
2794 
2795     if (asprintf(optstr, "Show context for individual samples %s",
2796         type == A_ASM ? "with assembler" :
2797         type == A_SOURCE ? "with source" : "") < 0)
2798         return 0;
2799 
2800     act->fn = do_res_sample_script;
2801     act->evsel = evsel;
2802     act->rstype = type;
2803     return 1;
2804 }
2805 
2806 static int
2807 do_switch_data(struct hist_browser *browser __maybe_unused,
2808            struct popup_action *act __maybe_unused)
2809 {
2810     if (switch_data_file()) {
2811         ui__warning("Won't switch the data files due to\n"
2812                 "no valid data file get selected!\n");
2813         return 0;
2814     }
2815 
2816     return K_SWITCH_INPUT_DATA;
2817 }
2818 
2819 static int
2820 add_switch_opt(struct hist_browser *browser,
2821            struct popup_action *act, char **optstr)
2822 {
2823     if (!is_report_browser(browser->hbt))
2824         return 0;
2825 
2826     if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2827         return 0;
2828 
2829     act->fn = do_switch_data;
2830     return 1;
2831 }
2832 
2833 static int
2834 do_exit_browser(struct hist_browser *browser __maybe_unused,
2835         struct popup_action *act __maybe_unused)
2836 {
2837     return 0;
2838 }
2839 
2840 static int
2841 add_exit_opt(struct hist_browser *browser __maybe_unused,
2842          struct popup_action *act, char **optstr)
2843 {
2844     if (asprintf(optstr, "Exit") < 0)
2845         return 0;
2846 
2847     act->fn = do_exit_browser;
2848     return 1;
2849 }
2850 
2851 static int
2852 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2853 {
2854     if (!hists__has(browser->hists, socket) || act->socket < 0)
2855         return 0;
2856 
2857     if (browser->hists->socket_filter > -1) {
2858         pstack__remove(browser->pstack, &browser->hists->socket_filter);
2859         browser->hists->socket_filter = -1;
2860         perf_hpp__set_elide(HISTC_SOCKET, false);
2861     } else {
2862         browser->hists->socket_filter = act->socket;
2863         perf_hpp__set_elide(HISTC_SOCKET, true);
2864         pstack__push(browser->pstack, &browser->hists->socket_filter);
2865     }
2866 
2867     hists__filter_by_socket(browser->hists);
2868     hist_browser__reset(browser);
2869     return 0;
2870 }
2871 
2872 static int
2873 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2874            char **optstr, int socket_id)
2875 {
2876     if (!hists__has(browser->hists, socket) || socket_id < 0)
2877         return 0;
2878 
2879     if (asprintf(optstr, "Zoom %s Processor Socket %d",
2880              (browser->hists->socket_filter > -1) ? "out of" : "into",
2881              socket_id) < 0)
2882         return 0;
2883 
2884     act->socket = socket_id;
2885     act->fn = do_zoom_socket;
2886     return 1;
2887 }
2888 
2889 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2890 {
2891     u64 nr_entries = 0;
2892     struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2893 
2894     if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2895         hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2896         return;
2897     }
2898 
2899     while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2900         nr_entries++;
2901         nd = rb_hierarchy_next(nd);
2902     }
2903 
2904     hb->nr_non_filtered_entries = nr_entries;
2905     hb->nr_hierarchy_entries = nr_entries;
2906 }
2907 
2908 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2909                            double percent)
2910 {
2911     struct hist_entry *he;
2912     struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2913     u64 total = hists__total_period(hb->hists);
2914     u64 min_callchain_hits = total * (percent / 100);
2915 
2916     hb->min_pcnt = callchain_param.min_percent = percent;
2917 
2918     while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2919         he = rb_entry(nd, struct hist_entry, rb_node);
2920 
2921         if (he->has_no_entry) {
2922             he->has_no_entry = false;
2923             he->nr_rows = 0;
2924         }
2925 
2926         if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2927             goto next;
2928 
2929         if (callchain_param.mode == CHAIN_GRAPH_REL) {
2930             total = he->stat.period;
2931 
2932             if (symbol_conf.cumulate_callchain)
2933                 total = he->stat_acc->period;
2934 
2935             min_callchain_hits = total * (percent / 100);
2936         }
2937 
2938         callchain_param.sort(&he->sorted_chain, he->callchain,
2939                      min_callchain_hits, &callchain_param);
2940 
2941 next:
2942         nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2943 
2944         /* force to re-evaluate folding state of callchains */
2945         he->init_have_children = false;
2946         hist_entry__set_folding(he, hb, false);
2947     }
2948 }
2949 
2950 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
2951                    bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
2952                    struct perf_env *env, bool warn_lost_event,
2953                    struct annotation_options *annotation_opts)
2954 {
2955     struct hists *hists = evsel__hists(evsel);
2956     struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2957     struct branch_info *bi = NULL;
2958 #define MAX_OPTIONS  16
2959     char *options[MAX_OPTIONS];
2960     struct popup_action actions[MAX_OPTIONS];
2961     int nr_options = 0;
2962     int key = -1;
2963     char buf[128];
2964     int delay_secs = hbt ? hbt->refresh : 0;
2965 
2966 #define HIST_BROWSER_HELP_COMMON                    \
2967     "h/?/F1        Show this window\n"              \
2968     "UP/DOWN/PGUP\n"                        \
2969     "PGDN/SPACE    Navigate\n"                  \
2970     "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
2971     "For multiple event sessions:\n\n"              \
2972     "TAB/UNTAB     Switch events\n\n"               \
2973     "For symbolic views (--sort has sym):\n\n"          \
2974     "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2975     "ESC           Zoom out\n"                  \
2976     "+             Expand/Collapse one callchain level\n"       \
2977     "a             Annotate current symbol\n"           \
2978     "C             Collapse all callchains\n"           \
2979     "d             Zoom into current DSO\n"             \
2980     "e             Expand/Collapse main entry callchains\n" \
2981     "E             Expand all callchains\n"             \
2982     "F             Toggle percentage of filtered entries\n"     \
2983     "H             Display column headers\n"            \
2984     "k             Zoom into the kernel map\n"          \
2985     "L             Change percent limit\n"              \
2986     "m             Display context menu\n"              \
2987     "S             Zoom into current Processor Socket\n"        \
2988 
2989     /* help messages are sorted by lexical order of the hotkey */
2990     static const char report_help[] = HIST_BROWSER_HELP_COMMON
2991     "i             Show header information\n"
2992     "P             Print histograms to perf.hist.N\n"
2993     "r             Run available scripts\n"
2994     "s             Switch to another data file in PWD\n"
2995     "t             Zoom into current Thread\n"
2996     "V             Verbose (DSO names in callchains, etc)\n"
2997     "/             Filter symbol by name\n"
2998     "0-9           Sort by event n in group";
2999     static const char top_help[] = HIST_BROWSER_HELP_COMMON
3000     "P             Print histograms to perf.hist.N\n"
3001     "t             Zoom into current Thread\n"
3002     "V             Verbose (DSO names in callchains, etc)\n"
3003     "z             Toggle zeroing of samples\n"
3004     "f             Enable/Disable events\n"
3005     "/             Filter symbol by name";
3006 
3007     if (browser == NULL)
3008         return -1;
3009 
3010     /* reset abort key so that it can get Ctrl-C as a key */
3011     SLang_reset_tty();
3012     SLang_init_tty(0, 0, 0);
3013 
3014     if (min_pcnt)
3015         browser->min_pcnt = min_pcnt;
3016     hist_browser__update_nr_entries(browser);
3017 
3018     browser->pstack = pstack__new(3);
3019     if (browser->pstack == NULL)
3020         goto out;
3021 
3022     ui_helpline__push(helpline);
3023 
3024     memset(options, 0, sizeof(options));
3025     memset(actions, 0, sizeof(actions));
3026 
3027     if (symbol_conf.col_width_list_str)
3028         perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3029 
3030     if (!is_report_browser(hbt))
3031         browser->b.no_samples_msg = "Collecting samples...";
3032 
3033     while (1) {
3034         struct thread *thread = NULL;
3035         struct map *map = NULL;
3036         int choice;
3037         int socked_id = -1;
3038 
3039         key = 0; // reset key
3040 do_hotkey:       // key came straight from options ui__popup_menu()
3041         choice = nr_options = 0;
3042         key = hist_browser__run(browser, helpline, warn_lost_event, key);
3043 
3044         if (browser->he_selection != NULL) {
3045             thread = hist_browser__selected_thread(browser);
3046             map = browser->selection->map;
3047             socked_id = browser->he_selection->socket;
3048         }
3049         switch (key) {
3050         case K_TAB:
3051         case K_UNTAB:
3052             if (nr_events == 1)
3053                 continue;
3054             /*
3055              * Exit the browser, let hists__browser_tree
3056              * go to the next or previous
3057              */
3058             goto out_free_stack;
3059         case '0' ... '9':
3060             if (!symbol_conf.event_group ||
3061                 evsel->core.nr_members < 2) {
3062                 snprintf(buf, sizeof(buf),
3063                      "Sort by index only available with group events!");
3064                 helpline = buf;
3065                 continue;
3066             }
3067 
3068             if (key - '0' == symbol_conf.group_sort_idx)
3069                 continue;
3070 
3071             symbol_conf.group_sort_idx = key - '0';
3072 
3073             if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3074                 snprintf(buf, sizeof(buf),
3075                      "Max event group index to sort is %d (index from 0 to %d)",
3076                      evsel->core.nr_members - 1,
3077                      evsel->core.nr_members - 1);
3078                 helpline = buf;
3079                 continue;
3080             }
3081 
3082             key = K_RELOAD;
3083             goto out_free_stack;
3084         case 'a':
3085             if (!hists__has(hists, sym)) {
3086                 ui_browser__warning(&browser->b, delay_secs * 2,
3087             "Annotation is only available for symbolic views, "
3088             "include \"sym*\" in --sort to use it.");
3089                 continue;
3090             }
3091 
3092             if (!browser->selection ||
3093                 !browser->selection->map ||
3094                 !browser->selection->map->dso ||
3095                 browser->selection->map->dso->annotate_warned) {
3096                 continue;
3097             }
3098 
3099             if (!browser->selection->sym) {
3100                 if (!browser->he_selection)
3101                     continue;
3102 
3103                 if (sort__mode == SORT_MODE__BRANCH) {
3104                     bi = browser->he_selection->branch_info;
3105                     if (!bi || !bi->to.ms.map)
3106                         continue;
3107 
3108                     actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3109                     actions->ms.map = bi->to.ms.map;
3110                 } else {
3111                     actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3112                                          browser->selection->map);
3113                     actions->ms.map = browser->selection->map;
3114                 }
3115 
3116                 if (!actions->ms.sym)
3117                     continue;
3118             } else {
3119                 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3120                     ui_browser__warning(&browser->b, delay_secs * 2,
3121                         "No samples for the \"%s\" symbol.\n\n"
3122                         "Probably appeared just in a callchain",
3123                         browser->selection->sym->name);
3124                     continue;
3125                 }
3126 
3127                 actions->ms.map = browser->selection->map;
3128                 actions->ms.sym = browser->selection->sym;
3129             }
3130 
3131             do_annotate(browser, actions);
3132             continue;
3133         case 'P':
3134             hist_browser__dump(browser);
3135             continue;
3136         case 'd':
3137             actions->ms.map = map;
3138             do_zoom_dso(browser, actions);
3139             continue;
3140         case 'k':
3141             if (browser->selection != NULL)
3142                 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3143             continue;
3144         case 'V':
3145             verbose = (verbose + 1) % 4;
3146             browser->show_dso = verbose > 0;
3147             ui_helpline__fpush("Verbosity level set to %d\n",
3148                        verbose);
3149             continue;
3150         case 't':
3151             actions->thread = thread;
3152             do_zoom_thread(browser, actions);
3153             continue;
3154         case 'S':
3155             actions->socket = socked_id;
3156             do_zoom_socket(browser, actions);
3157             continue;
3158         case '/':
3159             if (ui_browser__input_window("Symbol to show",
3160                     "Please enter the name of symbol you want to see.\n"
3161                     "To remove the filter later, press / + ENTER.",
3162                     buf, "ENTER: OK, ESC: Cancel",
3163                     delay_secs * 2) == K_ENTER) {
3164                 hists->symbol_filter_str = *buf ? buf : NULL;
3165                 hists__filter_by_symbol(hists);
3166                 hist_browser__reset(browser);
3167             }
3168             continue;
3169         case 'r':
3170             if (is_report_browser(hbt)) {
3171                 actions->thread = NULL;
3172                 actions->ms.sym = NULL;
3173                 do_run_script(browser, actions);
3174             }
3175             continue;
3176         case 's':
3177             if (is_report_browser(hbt)) {
3178                 key = do_switch_data(browser, actions);
3179                 if (key == K_SWITCH_INPUT_DATA)
3180                     goto out_free_stack;
3181             }
3182             continue;
3183         case 'i':
3184             /* env->arch is NULL for live-mode (i.e. perf top) */
3185             if (env->arch)
3186                 tui__header_window(env);
3187             continue;
3188         case 'F':
3189             symbol_conf.filter_relative ^= 1;
3190             continue;
3191         case 'z':
3192             if (!is_report_browser(hbt)) {
3193                 struct perf_top *top = hbt->arg;
3194 
3195                 top->zero = !top->zero;
3196             }
3197             continue;
3198         case 'L':
3199             if (ui_browser__input_window("Percent Limit",
3200                     "Please enter the value you want to hide entries under that percent.",
3201                     buf, "ENTER: OK, ESC: Cancel",
3202                     delay_secs * 2) == K_ENTER) {
3203                 char *end;
3204                 double new_percent = strtod(buf, &end);
3205 
3206                 if (new_percent < 0 || new_percent > 100) {
3207                     ui_browser__warning(&browser->b, delay_secs * 2,
3208                         "Invalid percent: %.2f", new_percent);
3209                     continue;
3210                 }
3211 
3212                 hist_browser__update_percent_limit(browser, new_percent);
3213                 hist_browser__reset(browser);
3214             }
3215             continue;
3216         case K_F1:
3217         case 'h':
3218         case '?':
3219             ui_browser__help_window(&browser->b,
3220                 is_report_browser(hbt) ? report_help : top_help);
3221             continue;
3222         case K_ENTER:
3223         case K_RIGHT:
3224         case 'm':
3225             /* menu */
3226             break;
3227         case K_ESC:
3228         case K_LEFT: {
3229             const void *top;
3230 
3231             if (pstack__empty(browser->pstack)) {
3232                 /*
3233                  * Go back to the perf_evsel_menu__run or other user
3234                  */
3235                 if (left_exits)
3236                     goto out_free_stack;
3237 
3238                 if (key == K_ESC &&
3239                     ui_browser__dialog_yesno(&browser->b,
3240                                  "Do you really want to exit?"))
3241                     goto out_free_stack;
3242 
3243                 continue;
3244             }
3245             actions->ms.map = map;
3246             top = pstack__peek(browser->pstack);
3247             if (top == &browser->hists->dso_filter) {
3248                 /*
3249                  * No need to set actions->dso here since
3250                  * it's just to remove the current filter.
3251                  * Ditto for thread below.
3252                  */
3253                 do_zoom_dso(browser, actions);
3254             } else if (top == &browser->hists->thread_filter) {
3255                 do_zoom_thread(browser, actions);
3256             } else if (top == &browser->hists->socket_filter) {
3257                 do_zoom_socket(browser, actions);
3258             }
3259             continue;
3260         }
3261         case 'q':
3262         case CTRL('c'):
3263             goto out_free_stack;
3264         case 'f':
3265             if (!is_report_browser(hbt)) {
3266                 struct perf_top *top = hbt->arg;
3267 
3268                 evlist__toggle_enable(top->evlist);
3269                 /*
3270                  * No need to refresh, resort/decay histogram
3271                  * entries if we are not collecting samples:
3272                  */
3273                 if (top->evlist->enabled) {
3274                     helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3275                     hbt->refresh = delay_secs;
3276                 } else {
3277                     helpline = "Press 'f' again to re-enable the events";
3278                     hbt->refresh = 0;
3279                 }
3280                 continue;
3281             }
3282             /* Fall thru */
3283         default:
3284             helpline = "Press '?' for help on key bindings";
3285             continue;
3286         }
3287 
3288         if (!hists__has(hists, sym) || browser->selection == NULL)
3289             goto skip_annotation;
3290 
3291         if (sort__mode == SORT_MODE__BRANCH) {
3292 
3293             if (browser->he_selection)
3294                 bi = browser->he_selection->branch_info;
3295 
3296             if (bi == NULL)
3297                 goto skip_annotation;
3298 
3299             nr_options += add_annotate_opt(browser,
3300                                &actions[nr_options],
3301                                &options[nr_options],
3302                                &bi->from.ms,
3303                                bi->from.al_addr);
3304             if (bi->to.ms.sym != bi->from.ms.sym)
3305                 nr_options += add_annotate_opt(browser,
3306                             &actions[nr_options],
3307                             &options[nr_options],
3308                             &bi->to.ms,
3309                             bi->to.al_addr);
3310         } else {
3311             nr_options += add_annotate_opt(browser,
3312                                &actions[nr_options],
3313                                &options[nr_options],
3314                                browser->selection,
3315                                browser->he_selection->ip);
3316         }
3317 skip_annotation:
3318         nr_options += add_thread_opt(browser, &actions[nr_options],
3319                          &options[nr_options], thread);
3320         nr_options += add_dso_opt(browser, &actions[nr_options],
3321                       &options[nr_options], map);
3322         nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3323         nr_options += add_map_opt(browser, &actions[nr_options],
3324                       &options[nr_options],
3325                       browser->selection ?
3326                         browser->selection->map : NULL);
3327         nr_options += add_socket_opt(browser, &actions[nr_options],
3328                          &options[nr_options],
3329                          socked_id);
3330         /* perf script support */
3331         if (!is_report_browser(hbt))
3332             goto skip_scripting;
3333 
3334         if (browser->he_selection) {
3335             if (hists__has(hists, thread) && thread) {
3336                 nr_options += add_script_opt(browser,
3337                                  &actions[nr_options],
3338                                  &options[nr_options],
3339                                  thread, NULL, evsel);
3340             }
3341             /*
3342              * Note that browser->selection != NULL
3343              * when browser->he_selection is not NULL,
3344              * so we don't need to check browser->selection
3345              * before fetching browser->selection->sym like what
3346              * we do before fetching browser->selection->map.
3347              *
3348              * See hist_browser__show_entry.
3349              */
3350             if (hists__has(hists, sym) && browser->selection->sym) {
3351                 nr_options += add_script_opt(browser,
3352                                  &actions[nr_options],
3353                                  &options[nr_options],
3354                                  NULL, browser->selection->sym,
3355                                  evsel);
3356             }
3357         }
3358         nr_options += add_script_opt(browser, &actions[nr_options],
3359                          &options[nr_options], NULL, NULL, evsel);
3360         nr_options += add_res_sample_opt(browser, &actions[nr_options],
3361                          &options[nr_options],
3362                          hist_browser__selected_res_sample(browser),
3363                          evsel, A_NORMAL);
3364         nr_options += add_res_sample_opt(browser, &actions[nr_options],
3365                          &options[nr_options],
3366                          hist_browser__selected_res_sample(browser),
3367                          evsel, A_ASM);
3368         nr_options += add_res_sample_opt(browser, &actions[nr_options],
3369                          &options[nr_options],
3370                          hist_browser__selected_res_sample(browser),
3371                          evsel, A_SOURCE);
3372         nr_options += add_switch_opt(browser, &actions[nr_options],
3373                          &options[nr_options]);
3374 skip_scripting:
3375         nr_options += add_exit_opt(browser, &actions[nr_options],
3376                        &options[nr_options]);
3377 
3378         do {
3379             struct popup_action *act;
3380 
3381             choice = ui__popup_menu(nr_options, options, &key);
3382             if (choice == -1)
3383                 break;
3384 
3385             if (choice == nr_options)
3386                 goto do_hotkey;
3387 
3388             act = &actions[choice];
3389             key = act->fn(browser, act);
3390         } while (key == 1);
3391 
3392         if (key == K_SWITCH_INPUT_DATA)
3393             break;
3394     }
3395 out_free_stack:
3396     pstack__delete(browser->pstack);
3397 out:
3398     hist_browser__delete(browser);
3399     free_popup_options(options, MAX_OPTIONS);
3400     return key;
3401 }
3402 
3403 struct evsel_menu {
3404     struct ui_browser b;
3405     struct evsel *selection;
3406     struct annotation_options *annotation_opts;
3407     bool lost_events, lost_events_warned;
3408     float min_pcnt;
3409     struct perf_env *env;
3410 };
3411 
3412 static void perf_evsel_menu__write(struct ui_browser *browser,
3413                    void *entry, int row)
3414 {
3415     struct evsel_menu *menu = container_of(browser,
3416                             struct evsel_menu, b);
3417     struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3418     struct hists *hists = evsel__hists(evsel);
3419     bool current_entry = ui_browser__is_current_entry(browser, row);
3420     unsigned long nr_events = hists->stats.nr_samples;
3421     const char *ev_name = evsel__name(evsel);
3422     char bf[256], unit;
3423     const char *warn = " ";
3424     size_t printed;
3425 
3426     ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3427                                HE_COLORSET_NORMAL);
3428 
3429     if (evsel__is_group_event(evsel)) {
3430         struct evsel *pos;
3431 
3432         ev_name = evsel__group_name(evsel);
3433 
3434         for_each_group_member(pos, evsel) {
3435             struct hists *pos_hists = evsel__hists(pos);
3436             nr_events += pos_hists->stats.nr_samples;
3437         }
3438     }
3439 
3440     nr_events = convert_unit(nr_events, &unit);
3441     printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3442                unit, unit == ' ' ? "" : " ", ev_name);
3443     ui_browser__printf(browser, "%s", bf);
3444 
3445     nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3446     if (nr_events != 0) {
3447         menu->lost_events = true;
3448         if (!current_entry)
3449             ui_browser__set_color(browser, HE_COLORSET_TOP);
3450         nr_events = convert_unit(nr_events, &unit);
3451         printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3452                      nr_events, unit, unit == ' ' ? "" : " ");
3453         warn = bf;
3454     }
3455 
3456     ui_browser__write_nstring(browser, warn, browser->width - printed);
3457 
3458     if (current_entry)
3459         menu->selection = evsel;
3460 }
3461 
3462 static int perf_evsel_menu__run(struct evsel_menu *menu,
3463                 int nr_events, const char *help,
3464                 struct hist_browser_timer *hbt,
3465                 bool warn_lost_event)
3466 {
3467     struct evlist *evlist = menu->b.priv;
3468     struct evsel *pos;
3469     const char *title = "Available samples";
3470     int delay_secs = hbt ? hbt->refresh : 0;
3471     int key;
3472 
3473     if (ui_browser__show(&menu->b, title,
3474                  "ESC: exit, ENTER|->: Browse histograms") < 0)
3475         return -1;
3476 
3477     while (1) {
3478         key = ui_browser__run(&menu->b, delay_secs);
3479 
3480         switch (key) {
3481         case K_TIMER:
3482             if (hbt)
3483                 hbt->timer(hbt->arg);
3484 
3485             if (!menu->lost_events_warned &&
3486                 menu->lost_events &&
3487                 warn_lost_event) {
3488                 ui_browser__warn_lost_events(&menu->b);
3489                 menu->lost_events_warned = true;
3490             }
3491             continue;
3492         case K_RIGHT:
3493         case K_ENTER:
3494             if (!menu->selection)
3495                 continue;
3496             pos = menu->selection;
3497 browse_hists:
3498             evlist__set_selected(evlist, pos);
3499             /*
3500              * Give the calling tool a chance to populate the non
3501              * default evsel resorted hists tree.
3502              */
3503             if (hbt)
3504                 hbt->timer(hbt->arg);
3505             key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3506                           menu->min_pcnt, menu->env,
3507                           warn_lost_event,
3508                           menu->annotation_opts);
3509             ui_browser__show_title(&menu->b, title);
3510             switch (key) {
3511             case K_TAB:
3512                 if (pos->core.node.next == &evlist->core.entries)
3513                     pos = evlist__first(evlist);
3514                 else
3515                     pos = evsel__next(pos);
3516                 goto browse_hists;
3517             case K_UNTAB:
3518                 if (pos->core.node.prev == &evlist->core.entries)
3519                     pos = evlist__last(evlist);
3520                 else
3521                     pos = evsel__prev(pos);
3522                 goto browse_hists;
3523             case K_SWITCH_INPUT_DATA:
3524             case K_RELOAD:
3525             case 'q':
3526             case CTRL('c'):
3527                 goto out;
3528             case K_ESC:
3529             default:
3530                 continue;
3531             }
3532         case K_LEFT:
3533             continue;
3534         case K_ESC:
3535             if (!ui_browser__dialog_yesno(&menu->b,
3536                            "Do you really want to exit?"))
3537                 continue;
3538             /* Fall thru */
3539         case 'q':
3540         case CTRL('c'):
3541             goto out;
3542         default:
3543             continue;
3544         }
3545     }
3546 
3547 out:
3548     ui_browser__hide(&menu->b);
3549     return key;
3550 }
3551 
3552 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3553                  void *entry)
3554 {
3555     struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3556 
3557     if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3558         return true;
3559 
3560     return false;
3561 }
3562 
3563 static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3564                       struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3565                       bool warn_lost_event, struct annotation_options *annotation_opts)
3566 {
3567     struct evsel *pos;
3568     struct evsel_menu menu = {
3569         .b = {
3570             .entries    = &evlist->core.entries,
3571             .refresh    = ui_browser__list_head_refresh,
3572             .seek       = ui_browser__list_head_seek,
3573             .write      = perf_evsel_menu__write,
3574             .filter     = filter_group_entries,
3575             .nr_entries = nr_entries,
3576             .priv       = evlist,
3577         },
3578         .min_pcnt = min_pcnt,
3579         .env = env,
3580         .annotation_opts = annotation_opts,
3581     };
3582 
3583     ui_helpline__push("Press ESC to exit");
3584 
3585     evlist__for_each_entry(evlist, pos) {
3586         const char *ev_name = evsel__name(pos);
3587         size_t line_len = strlen(ev_name) + 7;
3588 
3589         if (menu.b.width < line_len)
3590             menu.b.width = line_len;
3591     }
3592 
3593     return perf_evsel_menu__run(&menu, nr_entries, help,
3594                     hbt, warn_lost_event);
3595 }
3596 
3597 static bool evlist__single_entry(struct evlist *evlist)
3598 {
3599     int nr_entries = evlist->core.nr_entries;
3600 
3601     if (nr_entries == 1)
3602            return true;
3603 
3604     if (nr_entries == 2) {
3605         struct evsel *last = evlist__last(evlist);
3606 
3607         if (evsel__is_dummy_event(last))
3608             return true;
3609     }
3610 
3611     return false;
3612 }
3613 
3614 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3615                  float min_pcnt, struct perf_env *env, bool warn_lost_event,
3616                  struct annotation_options *annotation_opts)
3617 {
3618     int nr_entries = evlist->core.nr_entries;
3619 
3620     if (evlist__single_entry(evlist)) {
3621 single_entry: {
3622         struct evsel *first = evlist__first(evlist);
3623 
3624         return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3625                        env, warn_lost_event, annotation_opts);
3626     }
3627     }
3628 
3629     if (symbol_conf.event_group) {
3630         struct evsel *pos;
3631 
3632         nr_entries = 0;
3633         evlist__for_each_entry(evlist, pos) {
3634             if (evsel__is_group_leader(pos))
3635                 nr_entries++;
3636         }
3637 
3638         if (nr_entries == 1)
3639             goto single_entry;
3640     }
3641 
3642     return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3643                       warn_lost_event, annotation_opts);
3644 }
3645 
3646 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3647                       size_t size)
3648 {
3649     struct hists *hists = evsel__hists(browser->block_evsel);
3650     const char *evname = evsel__name(browser->block_evsel);
3651     unsigned long nr_samples = hists->stats.nr_samples;
3652     int ret;
3653 
3654     ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3655     if (evname)
3656         scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3657 
3658     return 0;
3659 }
3660 
3661 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3662                float min_percent, struct perf_env *env,
3663                struct annotation_options *annotation_opts)
3664 {
3665     struct hists *hists = &bh->block_hists;
3666     struct hist_browser *browser;
3667     int key = -1;
3668     struct popup_action action;
3669     static const char help[] =
3670     " q             Quit \n";
3671 
3672     browser = hist_browser__new(hists);
3673     if (!browser)
3674         return -1;
3675 
3676     browser->block_evsel = evsel;
3677     browser->title = block_hists_browser__title;
3678     browser->min_pcnt = min_percent;
3679     browser->env = env;
3680     browser->annotation_opts = annotation_opts;
3681 
3682     /* reset abort key so that it can get Ctrl-C as a key */
3683     SLang_reset_tty();
3684     SLang_init_tty(0, 0, 0);
3685 
3686     memset(&action, 0, sizeof(action));
3687 
3688     while (1) {
3689         key = hist_browser__run(browser, "? - help", true, 0);
3690 
3691         switch (key) {
3692         case 'q':
3693             goto out;
3694         case '?':
3695             ui_browser__help_window(&browser->b, help);
3696             break;
3697         case 'a':
3698         case K_ENTER:
3699             if (!browser->selection ||
3700                 !browser->selection->sym) {
3701                 continue;
3702             }
3703 
3704             action.ms.map = browser->selection->map;
3705             action.ms.sym = browser->selection->sym;
3706             do_annotate(browser, &action);
3707             continue;
3708         default:
3709             break;
3710         }
3711     }
3712 
3713 out:
3714     hist_browser__delete(browser);
3715     return 0;
3716 }