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