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