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

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

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ 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-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

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