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

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

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

~ [ 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