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