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

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

Version: ~ [ linux-6.0 ] ~ [ linux-5.19.12 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.71 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.146 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.215 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.260 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.295 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.330 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #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 
1611                         start = trim(dummy_hpp.buf);
1612                         ret = strlen(start);
1613 
1614                         if (start != dummy_hpp.buf)
1615                                 memmove(dummy_hpp.buf, start, ret + 1);
1616 
1617                         if (advance_hpp_check(&dummy_hpp, ret))
1618                                 break;
1619                 }
1620         }
1621 
1622         return ret;
1623 }
1624 
1625 static void hist_browser__show_headers(struct hist_browser *browser)
1626 {
1627         char headers[1024];
1628 
1629         if (symbol_conf.report_hierarchy)
1630                 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1631                                                            sizeof(headers));
1632         else
1633                 hists_browser__scnprintf_headers(browser, headers,
1634                                                  sizeof(headers));
1635         ui_browser__gotorc(&browser->b, 0, 0);
1636         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1637         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1638 }
1639 
1640 static void ui_browser__hists_init_top(struct ui_browser *browser)
1641 {
1642         if (browser->top == NULL) {
1643                 struct hist_browser *hb;
1644 
1645                 hb = container_of(browser, struct hist_browser, b);
1646                 browser->top = rb_first(&hb->hists->entries);
1647         }
1648 }
1649 
1650 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1651 {
1652         unsigned row = 0;
1653         u16 header_offset = 0;
1654         struct rb_node *nd;
1655         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1656 
1657         if (hb->show_headers) {
1658                 hist_browser__show_headers(hb);
1659                 header_offset = 1;
1660         }
1661 
1662         ui_browser__hists_init_top(browser);
1663         hb->he_selection = NULL;
1664         hb->selection = NULL;
1665 
1666         for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1667                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1668                 float percent;
1669 
1670                 if (h->filtered) {
1671                         /* let it move to sibling */
1672                         h->unfolded = false;
1673                         continue;
1674                 }
1675 
1676                 percent = hist_entry__get_percent_limit(h);
1677                 if (percent < hb->min_pcnt)
1678                         continue;
1679 
1680                 if (symbol_conf.report_hierarchy) {
1681                         row += hist_browser__show_hierarchy_entry(hb, h, row,
1682                                                                   h->depth);
1683                         if (row == browser->rows)
1684                                 break;
1685 
1686                         if (h->has_no_entry) {
1687                                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1688                                 row++;
1689                         }
1690                 } else {
1691                         row += hist_browser__show_entry(hb, h, row);
1692                 }
1693 
1694                 if (row == browser->rows)
1695                         break;
1696         }
1697 
1698         return row + header_offset;
1699 }
1700 
1701 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1702                                              float min_pcnt)
1703 {
1704         while (nd != NULL) {
1705                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1706                 float percent = hist_entry__get_percent_limit(h);
1707 
1708                 if (!h->filtered && percent >= min_pcnt)
1709                         return nd;
1710 
1711                 /*
1712                  * If it's filtered, its all children also were filtered.
1713                  * So move to sibling node.
1714                  */
1715                 if (rb_next(nd))
1716                         nd = rb_next(nd);
1717                 else
1718                         nd = rb_hierarchy_next(nd);
1719         }
1720 
1721         return NULL;
1722 }
1723 
1724 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1725                                                   float min_pcnt)
1726 {
1727         while (nd != NULL) {
1728                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1729                 float percent = hist_entry__get_percent_limit(h);
1730 
1731                 if (!h->filtered && percent >= min_pcnt)
1732                         return nd;
1733 
1734                 nd = rb_hierarchy_prev(nd);
1735         }
1736 
1737         return NULL;
1738 }
1739 
1740 static void ui_browser__hists_seek(struct ui_browser *browser,
1741                                    off_t offset, int whence)
1742 {
1743         struct hist_entry *h;
1744         struct rb_node *nd;
1745         bool first = true;
1746         struct hist_browser *hb;
1747 
1748         hb = container_of(browser, struct hist_browser, b);
1749 
1750         if (browser->nr_entries == 0)
1751                 return;
1752 
1753         ui_browser__hists_init_top(browser);
1754 
1755         switch (whence) {
1756         case SEEK_SET:
1757                 nd = hists__filter_entries(rb_first(browser->entries),
1758                                            hb->min_pcnt);
1759                 break;
1760         case SEEK_CUR:
1761                 nd = browser->top;
1762                 goto do_offset;
1763         case SEEK_END:
1764                 nd = rb_hierarchy_last(rb_last(browser->entries));
1765                 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1766                 first = false;
1767                 break;
1768         default:
1769                 return;
1770         }
1771 
1772         /*
1773          * Moves not relative to the first visible entry invalidates its
1774          * row_offset:
1775          */
1776         h = rb_entry(browser->top, struct hist_entry, rb_node);
1777         h->row_offset = 0;
1778 
1779         /*
1780          * Here we have to check if nd is expanded (+), if it is we can't go
1781          * the next top level hist_entry, instead we must compute an offset of
1782          * what _not_ to show and not change the first visible entry.
1783          *
1784          * This offset increments when we are going from top to bottom and
1785          * decreases when we're going from bottom to top.
1786          *
1787          * As we don't have backpointers to the top level in the callchains
1788          * structure, we need to always print the whole hist_entry callchain,
1789          * skipping the first ones that are before the first visible entry
1790          * and stop when we printed enough lines to fill the screen.
1791          */
1792 do_offset:
1793         if (!nd)
1794                 return;
1795 
1796         if (offset > 0) {
1797                 do {
1798                         h = rb_entry(nd, struct hist_entry, rb_node);
1799                         if (h->unfolded && h->leaf) {
1800                                 u16 remaining = h->nr_rows - h->row_offset;
1801                                 if (offset > remaining) {
1802                                         offset -= remaining;
1803                                         h->row_offset = 0;
1804                                 } else {
1805                                         h->row_offset += offset;
1806                                         offset = 0;
1807                                         browser->top = nd;
1808                                         break;
1809                                 }
1810                         }
1811                         nd = hists__filter_entries(rb_hierarchy_next(nd),
1812                                                    hb->min_pcnt);
1813                         if (nd == NULL)
1814                                 break;
1815                         --offset;
1816                         browser->top = nd;
1817                 } while (offset != 0);
1818         } else if (offset < 0) {
1819                 while (1) {
1820                         h = rb_entry(nd, struct hist_entry, rb_node);
1821                         if (h->unfolded && h->leaf) {
1822                                 if (first) {
1823                                         if (-offset > h->row_offset) {
1824                                                 offset += h->row_offset;
1825                                                 h->row_offset = 0;
1826                                         } else {
1827                                                 h->row_offset += offset;
1828                                                 offset = 0;
1829                                                 browser->top = nd;
1830                                                 break;
1831                                         }
1832                                 } else {
1833                                         if (-offset > h->nr_rows) {
1834                                                 offset += h->nr_rows;
1835                                                 h->row_offset = 0;
1836                                         } else {
1837                                                 h->row_offset = h->nr_rows + offset;
1838                                                 offset = 0;
1839                                                 browser->top = nd;
1840                                                 break;
1841                                         }
1842                                 }
1843                         }
1844 
1845                         nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1846                                                         hb->min_pcnt);
1847                         if (nd == NULL)
1848                                 break;
1849                         ++offset;
1850                         browser->top = nd;
1851                         if (offset == 0) {
1852                                 /*
1853                                  * Last unfiltered hist_entry, check if it is
1854                                  * unfolded, if it is then we should have
1855                                  * row_offset at its last entry.
1856                                  */
1857                                 h = rb_entry(nd, struct hist_entry, rb_node);
1858                                 if (h->unfolded && h->leaf)
1859                                         h->row_offset = h->nr_rows;
1860                                 break;
1861                         }
1862                         first = false;
1863                 }
1864         } else {
1865                 browser->top = nd;
1866                 h = rb_entry(nd, struct hist_entry, rb_node);
1867                 h->row_offset = 0;
1868         }
1869 }
1870 
1871 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1872                                            struct hist_entry *he, FILE *fp,
1873                                            int level)
1874 {
1875         struct callchain_print_arg arg  = {
1876                 .fp = fp,
1877         };
1878 
1879         hist_browser__show_callchain(browser, he, level, 0,
1880                                      hist_browser__fprintf_callchain_entry, &arg,
1881                                      hist_browser__check_dump_full);
1882         return arg.printed;
1883 }
1884 
1885 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1886                                        struct hist_entry *he, FILE *fp)
1887 {
1888         char s[8192];
1889         int printed = 0;
1890         char folded_sign = ' ';
1891         struct perf_hpp hpp = {
1892                 .buf = s,
1893                 .size = sizeof(s),
1894         };
1895         struct perf_hpp_fmt *fmt;
1896         bool first = true;
1897         int ret;
1898 
1899         if (symbol_conf.use_callchain) {
1900                 folded_sign = hist_entry__folded(he);
1901                 printed += fprintf(fp, "%c ", folded_sign);
1902         }
1903 
1904         hists__for_each_format(browser->hists, fmt) {
1905                 if (perf_hpp__should_skip(fmt, he->hists))
1906                         continue;
1907 
1908                 if (!first) {
1909                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1910                         advance_hpp(&hpp, ret);
1911                 } else
1912                         first = false;
1913 
1914                 ret = fmt->entry(fmt, &hpp, he);
1915                 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1916                 advance_hpp(&hpp, ret);
1917         }
1918         printed += fprintf(fp, "%s\n", s);
1919 
1920         if (folded_sign == '-')
1921                 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1922 
1923         return printed;
1924 }
1925 
1926 
1927 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1928                                                  struct hist_entry *he,
1929                                                  FILE *fp, int level)
1930 {
1931         char s[8192];
1932         int printed = 0;
1933         char folded_sign = ' ';
1934         struct perf_hpp hpp = {
1935                 .buf = s,
1936                 .size = sizeof(s),
1937         };
1938         struct perf_hpp_fmt *fmt;
1939         struct perf_hpp_list_node *fmt_node;
1940         bool first = true;
1941         int ret;
1942         int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1943 
1944         printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1945 
1946         folded_sign = hist_entry__folded(he);
1947         printed += fprintf(fp, "%c", folded_sign);
1948 
1949         /* the first hpp_list_node is for overhead columns */
1950         fmt_node = list_first_entry(&he->hists->hpp_formats,
1951                                     struct perf_hpp_list_node, list);
1952         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1953                 if (!first) {
1954                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1955                         advance_hpp(&hpp, ret);
1956                 } else
1957                         first = false;
1958 
1959                 ret = fmt->entry(fmt, &hpp, he);
1960                 advance_hpp(&hpp, ret);
1961         }
1962 
1963         ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1964         advance_hpp(&hpp, ret);
1965 
1966         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1967                 ret = scnprintf(hpp.buf, hpp.size, "  ");
1968                 advance_hpp(&hpp, ret);
1969 
1970                 ret = fmt->entry(fmt, &hpp, he);
1971                 advance_hpp(&hpp, ret);
1972         }
1973 
1974         printed += fprintf(fp, "%s\n", rtrim(s));
1975 
1976         if (he->leaf && folded_sign == '-') {
1977                 printed += hist_browser__fprintf_callchain(browser, he, fp,
1978                                                            he->depth + 1);
1979         }
1980 
1981         return printed;
1982 }
1983 
1984 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1985 {
1986         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1987                                                    browser->min_pcnt);
1988         int printed = 0;
1989 
1990         while (nd) {
1991                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1992 
1993                 if (symbol_conf.report_hierarchy) {
1994                         printed += hist_browser__fprintf_hierarchy_entry(browser,
1995                                                                          h, fp,
1996                                                                          h->depth);
1997                 } else {
1998                         printed += hist_browser__fprintf_entry(browser, h, fp);
1999                 }
2000 
2001                 nd = hists__filter_entries(rb_hierarchy_next(nd),
2002                                            browser->min_pcnt);
2003         }
2004 
2005         return printed;
2006 }
2007 
2008 static int hist_browser__dump(struct hist_browser *browser)
2009 {
2010         char filename[64];
2011         FILE *fp;
2012 
2013         while (1) {
2014                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2015                 if (access(filename, F_OK))
2016                         break;
2017                 /*
2018                  * XXX: Just an arbitrary lazy upper limit
2019                  */
2020                 if (++browser->print_seq == 8192) {
2021                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2022                         return -1;
2023                 }
2024         }
2025 
2026         fp = fopen(filename, "w");
2027         if (fp == NULL) {
2028                 char bf[64];
2029                 const char *err = strerror_r(errno, bf, sizeof(bf));
2030                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2031                 return -1;
2032         }
2033 
2034         ++browser->print_seq;
2035         hist_browser__fprintf(browser, fp);
2036         fclose(fp);
2037         ui_helpline__fpush("%s written!", filename);
2038 
2039         return 0;
2040 }
2041 
2042 static struct hist_browser *hist_browser__new(struct hists *hists,
2043                                               struct hist_browser_timer *hbt,
2044                                               struct perf_env *env)
2045 {
2046         struct hist_browser *browser = zalloc(sizeof(*browser));
2047 
2048         if (browser) {
2049                 browser->hists = hists;
2050                 browser->b.refresh = hist_browser__refresh;
2051                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2052                 browser->b.seek = ui_browser__hists_seek;
2053                 browser->b.use_navkeypressed = true;
2054                 browser->show_headers = symbol_conf.show_hist_headers;
2055                 browser->hbt = hbt;
2056                 browser->env = env;
2057         }
2058 
2059         return browser;
2060 }
2061 
2062 static void hist_browser__delete(struct hist_browser *browser)
2063 {
2064         free(browser);
2065 }
2066 
2067 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2068 {
2069         return browser->he_selection;
2070 }
2071 
2072 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2073 {
2074         return browser->he_selection->thread;
2075 }
2076 
2077 /* Check whether the browser is for 'top' or 'report' */
2078 static inline bool is_report_browser(void *timer)
2079 {
2080         return timer == NULL;
2081 }
2082 
2083 static int hists__browser_title(struct hists *hists,
2084                                 struct hist_browser_timer *hbt,
2085                                 char *bf, size_t size)
2086 {
2087         char unit;
2088         int printed;
2089         const struct dso *dso = hists->dso_filter;
2090         const struct thread *thread = hists->thread_filter;
2091         int socket_id = hists->socket_filter;
2092         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2093         u64 nr_events = hists->stats.total_period;
2094         struct perf_evsel *evsel = hists_to_evsel(hists);
2095         const char *ev_name = perf_evsel__name(evsel);
2096         char buf[512];
2097         size_t buflen = sizeof(buf);
2098         char ref[30] = " show reference callgraph, ";
2099         bool enable_ref = false;
2100 
2101         if (symbol_conf.filter_relative) {
2102                 nr_samples = hists->stats.nr_non_filtered_samples;
2103                 nr_events = hists->stats.total_non_filtered_period;
2104         }
2105 
2106         if (perf_evsel__is_group_event(evsel)) {
2107                 struct perf_evsel *pos;
2108 
2109                 perf_evsel__group_desc(evsel, buf, buflen);
2110                 ev_name = buf;
2111 
2112                 for_each_group_member(pos, evsel) {
2113                         struct hists *pos_hists = evsel__hists(pos);
2114 
2115                         if (symbol_conf.filter_relative) {
2116                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2117                                 nr_events += pos_hists->stats.total_non_filtered_period;
2118                         } else {
2119                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2120                                 nr_events += pos_hists->stats.total_period;
2121                         }
2122                 }
2123         }
2124 
2125         if (symbol_conf.show_ref_callgraph &&
2126             strstr(ev_name, "call-graph=no"))
2127                 enable_ref = true;
2128         nr_samples = convert_unit(nr_samples, &unit);
2129         printed = scnprintf(bf, size,
2130                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2131                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2132 
2133 
2134         if (hists->uid_filter_str)
2135                 printed += snprintf(bf + printed, size - printed,
2136                                     ", UID: %s", hists->uid_filter_str);
2137         if (thread) {
2138                 if (hists__has(hists, thread)) {
2139                         printed += scnprintf(bf + printed, size - printed,
2140                                     ", Thread: %s(%d)",
2141                                      (thread->comm_set ? thread__comm_str(thread) : ""),
2142                                     thread->tid);
2143                 } else {
2144                         printed += scnprintf(bf + printed, size - printed,
2145                                     ", Thread: %s",
2146                                      (thread->comm_set ? thread__comm_str(thread) : ""));
2147                 }
2148         }
2149         if (dso)
2150                 printed += scnprintf(bf + printed, size - printed,
2151                                     ", DSO: %s", dso->short_name);
2152         if (socket_id > -1)
2153                 printed += scnprintf(bf + printed, size - printed,
2154                                     ", Processor Socket: %d", socket_id);
2155         if (!is_report_browser(hbt)) {
2156                 struct perf_top *top = hbt->arg;
2157 
2158                 if (top->zero)
2159                         printed += scnprintf(bf + printed, size - printed, " [z]");
2160         }
2161 
2162         return printed;
2163 }
2164 
2165 static inline void free_popup_options(char **options, int n)
2166 {
2167         int i;
2168 
2169         for (i = 0; i < n; ++i)
2170                 zfree(&options[i]);
2171 }
2172 
2173 /*
2174  * Only runtime switching of perf data file will make "input_name" point
2175  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2176  * whether we need to call free() for current "input_name" during the switch.
2177  */
2178 static bool is_input_name_malloced = false;
2179 
2180 static int switch_data_file(void)
2181 {
2182         char *pwd, *options[32], *abs_path[32], *tmp;
2183         DIR *pwd_dir;
2184         int nr_options = 0, choice = -1, ret = -1;
2185         struct dirent *dent;
2186 
2187         pwd = getenv("PWD");
2188         if (!pwd)
2189                 return ret;
2190 
2191         pwd_dir = opendir(pwd);
2192         if (!pwd_dir)
2193                 return ret;
2194 
2195         memset(options, 0, sizeof(options));
2196         memset(options, 0, sizeof(abs_path));
2197 
2198         while ((dent = readdir(pwd_dir))) {
2199                 char path[PATH_MAX];
2200                 u64 magic;
2201                 char *name = dent->d_name;
2202                 FILE *file;
2203 
2204                 if (!(dent->d_type == DT_REG))
2205                         continue;
2206 
2207                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2208 
2209                 file = fopen(path, "r");
2210                 if (!file)
2211                         continue;
2212 
2213                 if (fread(&magic, 1, 8, file) < 8)
2214                         goto close_file_and_continue;
2215 
2216                 if (is_perf_magic(magic)) {
2217                         options[nr_options] = strdup(name);
2218                         if (!options[nr_options])
2219                                 goto close_file_and_continue;
2220 
2221                         abs_path[nr_options] = strdup(path);
2222                         if (!abs_path[nr_options]) {
2223                                 zfree(&options[nr_options]);
2224                                 ui__warning("Can't search all data files due to memory shortage.\n");
2225                                 fclose(file);
2226                                 break;
2227                         }
2228 
2229                         nr_options++;
2230                 }
2231 
2232 close_file_and_continue:
2233                 fclose(file);
2234                 if (nr_options >= 32) {
2235                         ui__warning("Too many perf data files in PWD!\n"
2236                                     "Only the first 32 files will be listed.\n");
2237                         break;
2238                 }
2239         }
2240         closedir(pwd_dir);
2241 
2242         if (nr_options) {
2243                 choice = ui__popup_menu(nr_options, options);
2244                 if (choice < nr_options && choice >= 0) {
2245                         tmp = strdup(abs_path[choice]);
2246                         if (tmp) {
2247                                 if (is_input_name_malloced)
2248                                         free((void *)input_name);
2249                                 input_name = tmp;
2250                                 is_input_name_malloced = true;
2251                                 ret = 0;
2252                         } else
2253                                 ui__warning("Data switch failed due to memory shortage!\n");
2254                 }
2255         }
2256 
2257         free_popup_options(options, nr_options);
2258         free_popup_options(abs_path, nr_options);
2259         return ret;
2260 }
2261 
2262 struct popup_action {
2263         struct thread           *thread;
2264         struct map_symbol       ms;
2265         int                     socket;
2266 
2267         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2268 };
2269 
2270 static int
2271 do_annotate(struct hist_browser *browser, struct popup_action *act)
2272 {
2273         struct perf_evsel *evsel;
2274         struct annotation *notes;
2275         struct hist_entry *he;
2276         int err;
2277 
2278         if (!objdump_path && perf_env__lookup_objdump(browser->env))
2279                 return 0;
2280 
2281         notes = symbol__annotation(act->ms.sym);
2282         if (!notes->src)
2283                 return 0;
2284 
2285         evsel = hists_to_evsel(browser->hists);
2286         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2287         he = hist_browser__selected_entry(browser);
2288         /*
2289          * offer option to annotate the other branch source or target
2290          * (if they exists) when returning from annotate
2291          */
2292         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2293                 return 1;
2294 
2295         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2296         if (err)
2297                 ui_browser__handle_resize(&browser->b);
2298         return 0;
2299 }
2300 
2301 static int
2302 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2303                  struct popup_action *act, char **optstr,
2304                  struct map *map, struct symbol *sym)
2305 {
2306         if (sym == NULL || map->dso->annotate_warned)
2307                 return 0;
2308 
2309         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2310                 return 0;
2311 
2312         act->ms.map = map;
2313         act->ms.sym = sym;
2314         act->fn = do_annotate;
2315         return 1;
2316 }
2317 
2318 static int
2319 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320 {
2321         struct thread *thread = act->thread;
2322 
2323         if ((!hists__has(browser->hists, thread) &&
2324              !hists__has(browser->hists, comm)) || thread == NULL)
2325                 return 0;
2326 
2327         if (browser->hists->thread_filter) {
2328                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2329                 perf_hpp__set_elide(HISTC_THREAD, false);
2330                 thread__zput(browser->hists->thread_filter);
2331                 ui_helpline__pop();
2332         } else {
2333                 if (hists__has(browser->hists, thread)) {
2334                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2335                                            thread->comm_set ? thread__comm_str(thread) : "",
2336                                            thread->tid);
2337                 } else {
2338                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2339                                            thread->comm_set ? thread__comm_str(thread) : "");
2340                 }
2341 
2342                 browser->hists->thread_filter = thread__get(thread);
2343                 perf_hpp__set_elide(HISTC_THREAD, false);
2344                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2345         }
2346 
2347         hists__filter_by_thread(browser->hists);
2348         hist_browser__reset(browser);
2349         return 0;
2350 }
2351 
2352 static int
2353 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2354                char **optstr, struct thread *thread)
2355 {
2356         int ret;
2357 
2358         if ((!hists__has(browser->hists, thread) &&
2359              !hists__has(browser->hists, comm)) || thread == NULL)
2360                 return 0;
2361 
2362         if (hists__has(browser->hists, 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 (!hists__has(browser->hists, 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 (!hists__has(browser->hists, 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,
2435             struct popup_action *act, char **optstr, struct map *map)
2436 {
2437         if (!hists__has(browser->hists, 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 (!hists__has(browser->hists, 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 (!hists__has(browser->hists, 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 (!hists__has(hists, 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 (!hists__has(hists, 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 (hists__has(hists, 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 (hists__has(hists, 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