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

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

Version: ~ [ linux-5.9.1 ] ~ [ linux-5.8.16 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.72 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.152 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.202 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.240 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.240 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

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

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

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

osdn.jp