~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/ui/browsers/hists.c

Version: ~ [ linux-5.17-rc1 ] ~ [ linux-5.16.2 ] ~ [ linux-5.15.16 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.93 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.173 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.225 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.262 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.297 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.299 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp