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

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

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php