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