]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/perf/ui/browsers/hists.c
84f5dd2fb59cb17f2c97b9c62a72133abdd1f308
[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 if (symbol_conf.report_hierarchy) {
2080 struct perf_hpp_list_node *fmt_node;
2081
2082 /* count overhead columns (in the first node) */
2083 fmt_node = list_first_entry(&hists->hpp_formats,
2084 struct perf_hpp_list_node, list);
2085 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2086 ++browser->b.columns;
2087
2088 /* add a single column for whole hierarchy sort keys*/
2089 ++browser->b.columns;
2090 } else {
2091 hists__for_each_format(hists, fmt)
2092 ++browser->b.columns;
2093 }
2094
2095 hists__reset_column_width(hists);
2096 }
2097
2098 struct hist_browser *hist_browser__new(struct hists *hists)
2099 {
2100 struct hist_browser *browser = zalloc(sizeof(*browser));
2101
2102 if (browser)
2103 hist_browser__init(browser, hists);
2104
2105 return browser;
2106 }
2107
2108 static struct hist_browser *
2109 perf_evsel_browser__new(struct perf_evsel *evsel,
2110 struct hist_browser_timer *hbt,
2111 struct perf_env *env)
2112 {
2113 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2114
2115 if (browser) {
2116 browser->hbt = hbt;
2117 browser->env = env;
2118 browser->title = perf_evsel_browser_title;
2119 }
2120 return browser;
2121 }
2122
2123 void hist_browser__delete(struct hist_browser *browser)
2124 {
2125 free(browser);
2126 }
2127
2128 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2129 {
2130 return browser->he_selection;
2131 }
2132
2133 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2134 {
2135 return browser->he_selection->thread;
2136 }
2137
2138 /* Check whether the browser is for 'top' or 'report' */
2139 static inline bool is_report_browser(void *timer)
2140 {
2141 return timer == NULL;
2142 }
2143
2144 static int perf_evsel_browser_title(struct hist_browser *browser,
2145 char *bf, size_t size)
2146 {
2147 struct hist_browser_timer *hbt = browser->hbt;
2148 struct hists *hists = browser->hists;
2149 char unit;
2150 int printed;
2151 const struct dso *dso = hists->dso_filter;
2152 const struct thread *thread = hists->thread_filter;
2153 int socket_id = hists->socket_filter;
2154 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2155 u64 nr_events = hists->stats.total_period;
2156 struct perf_evsel *evsel = hists_to_evsel(hists);
2157 const char *ev_name = perf_evsel__name(evsel);
2158 char buf[512];
2159 size_t buflen = sizeof(buf);
2160 char ref[30] = " show reference callgraph, ";
2161 bool enable_ref = false;
2162
2163 if (symbol_conf.filter_relative) {
2164 nr_samples = hists->stats.nr_non_filtered_samples;
2165 nr_events = hists->stats.total_non_filtered_period;
2166 }
2167
2168 if (perf_evsel__is_group_event(evsel)) {
2169 struct perf_evsel *pos;
2170
2171 perf_evsel__group_desc(evsel, buf, buflen);
2172 ev_name = buf;
2173
2174 for_each_group_member(pos, evsel) {
2175 struct hists *pos_hists = evsel__hists(pos);
2176
2177 if (symbol_conf.filter_relative) {
2178 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2179 nr_events += pos_hists->stats.total_non_filtered_period;
2180 } else {
2181 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2182 nr_events += pos_hists->stats.total_period;
2183 }
2184 }
2185 }
2186
2187 if (symbol_conf.show_ref_callgraph &&
2188 strstr(ev_name, "call-graph=no"))
2189 enable_ref = true;
2190 nr_samples = convert_unit(nr_samples, &unit);
2191 printed = scnprintf(bf, size,
2192 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2193 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2194
2195
2196 if (hists->uid_filter_str)
2197 printed += snprintf(bf + printed, size - printed,
2198 ", UID: %s", hists->uid_filter_str);
2199 if (thread) {
2200 if (hists__has(hists, thread)) {
2201 printed += scnprintf(bf + printed, size - printed,
2202 ", Thread: %s(%d)",
2203 (thread->comm_set ? thread__comm_str(thread) : ""),
2204 thread->tid);
2205 } else {
2206 printed += scnprintf(bf + printed, size - printed,
2207 ", Thread: %s",
2208 (thread->comm_set ? thread__comm_str(thread) : ""));
2209 }
2210 }
2211 if (dso)
2212 printed += scnprintf(bf + printed, size - printed,
2213 ", DSO: %s", dso->short_name);
2214 if (socket_id > -1)
2215 printed += scnprintf(bf + printed, size - printed,
2216 ", Processor Socket: %d", socket_id);
2217 if (!is_report_browser(hbt)) {
2218 struct perf_top *top = hbt->arg;
2219
2220 if (top->zero)
2221 printed += scnprintf(bf + printed, size - printed, " [z]");
2222 }
2223
2224 return printed;
2225 }
2226
2227 static inline void free_popup_options(char **options, int n)
2228 {
2229 int i;
2230
2231 for (i = 0; i < n; ++i)
2232 zfree(&options[i]);
2233 }
2234
2235 /*
2236 * Only runtime switching of perf data file will make "input_name" point
2237 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2238 * whether we need to call free() for current "input_name" during the switch.
2239 */
2240 static bool is_input_name_malloced = false;
2241
2242 static int switch_data_file(void)
2243 {
2244 char *pwd, *options[32], *abs_path[32], *tmp;
2245 DIR *pwd_dir;
2246 int nr_options = 0, choice = -1, ret = -1;
2247 struct dirent *dent;
2248
2249 pwd = getenv("PWD");
2250 if (!pwd)
2251 return ret;
2252
2253 pwd_dir = opendir(pwd);
2254 if (!pwd_dir)
2255 return ret;
2256
2257 memset(options, 0, sizeof(options));
2258 memset(options, 0, sizeof(abs_path));
2259
2260 while ((dent = readdir(pwd_dir))) {
2261 char path[PATH_MAX];
2262 u64 magic;
2263 char *name = dent->d_name;
2264 FILE *file;
2265
2266 if (!(dent->d_type == DT_REG))
2267 continue;
2268
2269 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2270
2271 file = fopen(path, "r");
2272 if (!file)
2273 continue;
2274
2275 if (fread(&magic, 1, 8, file) < 8)
2276 goto close_file_and_continue;
2277
2278 if (is_perf_magic(magic)) {
2279 options[nr_options] = strdup(name);
2280 if (!options[nr_options])
2281 goto close_file_and_continue;
2282
2283 abs_path[nr_options] = strdup(path);
2284 if (!abs_path[nr_options]) {
2285 zfree(&options[nr_options]);
2286 ui__warning("Can't search all data files due to memory shortage.\n");
2287 fclose(file);
2288 break;
2289 }
2290
2291 nr_options++;
2292 }
2293
2294 close_file_and_continue:
2295 fclose(file);
2296 if (nr_options >= 32) {
2297 ui__warning("Too many perf data files in PWD!\n"
2298 "Only the first 32 files will be listed.\n");
2299 break;
2300 }
2301 }
2302 closedir(pwd_dir);
2303
2304 if (nr_options) {
2305 choice = ui__popup_menu(nr_options, options);
2306 if (choice < nr_options && choice >= 0) {
2307 tmp = strdup(abs_path[choice]);
2308 if (tmp) {
2309 if (is_input_name_malloced)
2310 free((void *)input_name);
2311 input_name = tmp;
2312 is_input_name_malloced = true;
2313 ret = 0;
2314 } else
2315 ui__warning("Data switch failed due to memory shortage!\n");
2316 }
2317 }
2318
2319 free_popup_options(options, nr_options);
2320 free_popup_options(abs_path, nr_options);
2321 return ret;
2322 }
2323
2324 struct popup_action {
2325 struct thread *thread;
2326 struct map_symbol ms;
2327 int socket;
2328
2329 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2330 };
2331
2332 static int
2333 do_annotate(struct hist_browser *browser, struct popup_action *act)
2334 {
2335 struct perf_evsel *evsel;
2336 struct annotation *notes;
2337 struct hist_entry *he;
2338 int err;
2339
2340 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2341 return 0;
2342
2343 notes = symbol__annotation(act->ms.sym);
2344 if (!notes->src)
2345 return 0;
2346
2347 evsel = hists_to_evsel(browser->hists);
2348 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2349 he = hist_browser__selected_entry(browser);
2350 /*
2351 * offer option to annotate the other branch source or target
2352 * (if they exists) when returning from annotate
2353 */
2354 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2355 return 1;
2356
2357 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2358 if (err)
2359 ui_browser__handle_resize(&browser->b);
2360 return 0;
2361 }
2362
2363 static int
2364 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2365 struct popup_action *act, char **optstr,
2366 struct map *map, struct symbol *sym)
2367 {
2368 if (sym == NULL || map->dso->annotate_warned)
2369 return 0;
2370
2371 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2372 return 0;
2373
2374 act->ms.map = map;
2375 act->ms.sym = sym;
2376 act->fn = do_annotate;
2377 return 1;
2378 }
2379
2380 static int
2381 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2382 {
2383 struct thread *thread = act->thread;
2384
2385 if ((!hists__has(browser->hists, thread) &&
2386 !hists__has(browser->hists, comm)) || thread == NULL)
2387 return 0;
2388
2389 if (browser->hists->thread_filter) {
2390 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2391 perf_hpp__set_elide(HISTC_THREAD, false);
2392 thread__zput(browser->hists->thread_filter);
2393 ui_helpline__pop();
2394 } else {
2395 if (hists__has(browser->hists, thread)) {
2396 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2397 thread->comm_set ? thread__comm_str(thread) : "",
2398 thread->tid);
2399 } else {
2400 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2401 thread->comm_set ? thread__comm_str(thread) : "");
2402 }
2403
2404 browser->hists->thread_filter = thread__get(thread);
2405 perf_hpp__set_elide(HISTC_THREAD, false);
2406 pstack__push(browser->pstack, &browser->hists->thread_filter);
2407 }
2408
2409 hists__filter_by_thread(browser->hists);
2410 hist_browser__reset(browser);
2411 return 0;
2412 }
2413
2414 static int
2415 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2416 char **optstr, struct thread *thread)
2417 {
2418 int ret;
2419
2420 if ((!hists__has(browser->hists, thread) &&
2421 !hists__has(browser->hists, comm)) || thread == NULL)
2422 return 0;
2423
2424 if (hists__has(browser->hists, thread)) {
2425 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2426 browser->hists->thread_filter ? "out of" : "into",
2427 thread->comm_set ? thread__comm_str(thread) : "",
2428 thread->tid);
2429 } else {
2430 ret = asprintf(optstr, "Zoom %s %s thread",
2431 browser->hists->thread_filter ? "out of" : "into",
2432 thread->comm_set ? thread__comm_str(thread) : "");
2433 }
2434 if (ret < 0)
2435 return 0;
2436
2437 act->thread = thread;
2438 act->fn = do_zoom_thread;
2439 return 1;
2440 }
2441
2442 static int
2443 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2444 {
2445 struct map *map = act->ms.map;
2446
2447 if (!hists__has(browser->hists, dso) || map == NULL)
2448 return 0;
2449
2450 if (browser->hists->dso_filter) {
2451 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2452 perf_hpp__set_elide(HISTC_DSO, false);
2453 browser->hists->dso_filter = NULL;
2454 ui_helpline__pop();
2455 } else {
2456 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2457 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2458 browser->hists->dso_filter = map->dso;
2459 perf_hpp__set_elide(HISTC_DSO, true);
2460 pstack__push(browser->pstack, &browser->hists->dso_filter);
2461 }
2462
2463 hists__filter_by_dso(browser->hists);
2464 hist_browser__reset(browser);
2465 return 0;
2466 }
2467
2468 static int
2469 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2470 char **optstr, struct map *map)
2471 {
2472 if (!hists__has(browser->hists, dso) || map == NULL)
2473 return 0;
2474
2475 if (asprintf(optstr, "Zoom %s %s DSO",
2476 browser->hists->dso_filter ? "out of" : "into",
2477 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2478 return 0;
2479
2480 act->ms.map = map;
2481 act->fn = do_zoom_dso;
2482 return 1;
2483 }
2484
2485 static int
2486 do_browse_map(struct hist_browser *browser __maybe_unused,
2487 struct popup_action *act)
2488 {
2489 map__browse(act->ms.map);
2490 return 0;
2491 }
2492
2493 static int
2494 add_map_opt(struct hist_browser *browser,
2495 struct popup_action *act, char **optstr, struct map *map)
2496 {
2497 if (!hists__has(browser->hists, dso) || map == NULL)
2498 return 0;
2499
2500 if (asprintf(optstr, "Browse map details") < 0)
2501 return 0;
2502
2503 act->ms.map = map;
2504 act->fn = do_browse_map;
2505 return 1;
2506 }
2507
2508 static int
2509 do_run_script(struct hist_browser *browser __maybe_unused,
2510 struct popup_action *act)
2511 {
2512 char script_opt[64];
2513 memset(script_opt, 0, sizeof(script_opt));
2514
2515 if (act->thread) {
2516 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2517 thread__comm_str(act->thread));
2518 } else if (act->ms.sym) {
2519 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2520 act->ms.sym->name);
2521 }
2522
2523 script_browse(script_opt);
2524 return 0;
2525 }
2526
2527 static int
2528 add_script_opt(struct hist_browser *browser __maybe_unused,
2529 struct popup_action *act, char **optstr,
2530 struct thread *thread, struct symbol *sym)
2531 {
2532 if (thread) {
2533 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2534 thread__comm_str(thread)) < 0)
2535 return 0;
2536 } else if (sym) {
2537 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2538 sym->name) < 0)
2539 return 0;
2540 } else {
2541 if (asprintf(optstr, "Run scripts for all samples") < 0)
2542 return 0;
2543 }
2544
2545 act->thread = thread;
2546 act->ms.sym = sym;
2547 act->fn = do_run_script;
2548 return 1;
2549 }
2550
2551 static int
2552 do_switch_data(struct hist_browser *browser __maybe_unused,
2553 struct popup_action *act __maybe_unused)
2554 {
2555 if (switch_data_file()) {
2556 ui__warning("Won't switch the data files due to\n"
2557 "no valid data file get selected!\n");
2558 return 0;
2559 }
2560
2561 return K_SWITCH_INPUT_DATA;
2562 }
2563
2564 static int
2565 add_switch_opt(struct hist_browser *browser,
2566 struct popup_action *act, char **optstr)
2567 {
2568 if (!is_report_browser(browser->hbt))
2569 return 0;
2570
2571 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2572 return 0;
2573
2574 act->fn = do_switch_data;
2575 return 1;
2576 }
2577
2578 static int
2579 do_exit_browser(struct hist_browser *browser __maybe_unused,
2580 struct popup_action *act __maybe_unused)
2581 {
2582 return 0;
2583 }
2584
2585 static int
2586 add_exit_opt(struct hist_browser *browser __maybe_unused,
2587 struct popup_action *act, char **optstr)
2588 {
2589 if (asprintf(optstr, "Exit") < 0)
2590 return 0;
2591
2592 act->fn = do_exit_browser;
2593 return 1;
2594 }
2595
2596 static int
2597 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2598 {
2599 if (!hists__has(browser->hists, socket) || act->socket < 0)
2600 return 0;
2601
2602 if (browser->hists->socket_filter > -1) {
2603 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2604 browser->hists->socket_filter = -1;
2605 perf_hpp__set_elide(HISTC_SOCKET, false);
2606 } else {
2607 browser->hists->socket_filter = act->socket;
2608 perf_hpp__set_elide(HISTC_SOCKET, true);
2609 pstack__push(browser->pstack, &browser->hists->socket_filter);
2610 }
2611
2612 hists__filter_by_socket(browser->hists);
2613 hist_browser__reset(browser);
2614 return 0;
2615 }
2616
2617 static int
2618 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2619 char **optstr, int socket_id)
2620 {
2621 if (!hists__has(browser->hists, socket) || socket_id < 0)
2622 return 0;
2623
2624 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2625 (browser->hists->socket_filter > -1) ? "out of" : "into",
2626 socket_id) < 0)
2627 return 0;
2628
2629 act->socket = socket_id;
2630 act->fn = do_zoom_socket;
2631 return 1;
2632 }
2633
2634 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2635 {
2636 u64 nr_entries = 0;
2637 struct rb_node *nd = rb_first(&hb->hists->entries);
2638
2639 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2640 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2641 return;
2642 }
2643
2644 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2645 nr_entries++;
2646 nd = rb_hierarchy_next(nd);
2647 }
2648
2649 hb->nr_non_filtered_entries = nr_entries;
2650 hb->nr_hierarchy_entries = nr_entries;
2651 }
2652
2653 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2654 double percent)
2655 {
2656 struct hist_entry *he;
2657 struct rb_node *nd = rb_first(&hb->hists->entries);
2658 u64 total = hists__total_period(hb->hists);
2659 u64 min_callchain_hits = total * (percent / 100);
2660
2661 hb->min_pcnt = callchain_param.min_percent = percent;
2662
2663 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2664 he = rb_entry(nd, struct hist_entry, rb_node);
2665
2666 if (he->has_no_entry) {
2667 he->has_no_entry = false;
2668 he->nr_rows = 0;
2669 }
2670
2671 if (!he->leaf || !symbol_conf.use_callchain)
2672 goto next;
2673
2674 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2675 total = he->stat.period;
2676
2677 if (symbol_conf.cumulate_callchain)
2678 total = he->stat_acc->period;
2679
2680 min_callchain_hits = total * (percent / 100);
2681 }
2682
2683 callchain_param.sort(&he->sorted_chain, he->callchain,
2684 min_callchain_hits, &callchain_param);
2685
2686 next:
2687 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2688
2689 /* force to re-evaluate folding state of callchains */
2690 he->init_have_children = false;
2691 hist_entry__set_folding(he, hb, false);
2692 }
2693 }
2694
2695 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2696 const char *helpline,
2697 bool left_exits,
2698 struct hist_browser_timer *hbt,
2699 float min_pcnt,
2700 struct perf_env *env)
2701 {
2702 struct hists *hists = evsel__hists(evsel);
2703 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2704 struct branch_info *bi;
2705 #define MAX_OPTIONS 16
2706 char *options[MAX_OPTIONS];
2707 struct popup_action actions[MAX_OPTIONS];
2708 int nr_options = 0;
2709 int key = -1;
2710 char buf[64];
2711 int delay_secs = hbt ? hbt->refresh : 0;
2712
2713 #define HIST_BROWSER_HELP_COMMON \
2714 "h/?/F1 Show this window\n" \
2715 "UP/DOWN/PGUP\n" \
2716 "PGDN/SPACE Navigate\n" \
2717 "q/ESC/CTRL+C Exit browser\n\n" \
2718 "For multiple event sessions:\n\n" \
2719 "TAB/UNTAB Switch events\n\n" \
2720 "For symbolic views (--sort has sym):\n\n" \
2721 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2722 "ESC Zoom out\n" \
2723 "a Annotate current symbol\n" \
2724 "C Collapse all callchains\n" \
2725 "d Zoom into current DSO\n" \
2726 "E Expand all callchains\n" \
2727 "F Toggle percentage of filtered entries\n" \
2728 "H Display column headers\n" \
2729 "L Change percent limit\n" \
2730 "m Display context menu\n" \
2731 "S Zoom into current Processor Socket\n" \
2732
2733 /* help messages are sorted by lexical order of the hotkey */
2734 const char report_help[] = HIST_BROWSER_HELP_COMMON
2735 "i Show header information\n"
2736 "P Print histograms to perf.hist.N\n"
2737 "r Run available scripts\n"
2738 "s Switch to another data file in PWD\n"
2739 "t Zoom into current Thread\n"
2740 "V Verbose (DSO names in callchains, etc)\n"
2741 "/ Filter symbol by name";
2742 const char top_help[] = HIST_BROWSER_HELP_COMMON
2743 "P Print histograms to perf.hist.N\n"
2744 "t Zoom into current Thread\n"
2745 "V Verbose (DSO names in callchains, etc)\n"
2746 "z Toggle zeroing of samples\n"
2747 "f Enable/Disable events\n"
2748 "/ Filter symbol by name";
2749
2750 if (browser == NULL)
2751 return -1;
2752
2753 /* reset abort key so that it can get Ctrl-C as a key */
2754 SLang_reset_tty();
2755 SLang_init_tty(0, 0, 0);
2756
2757 if (min_pcnt)
2758 browser->min_pcnt = min_pcnt;
2759 hist_browser__update_nr_entries(browser);
2760
2761 browser->pstack = pstack__new(3);
2762 if (browser->pstack == NULL)
2763 goto out;
2764
2765 ui_helpline__push(helpline);
2766
2767 memset(options, 0, sizeof(options));
2768 memset(actions, 0, sizeof(actions));
2769
2770 if (symbol_conf.col_width_list_str)
2771 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2772
2773 while (1) {
2774 struct thread *thread = NULL;
2775 struct map *map = NULL;
2776 int choice = 0;
2777 int socked_id = -1;
2778
2779 nr_options = 0;
2780
2781 key = hist_browser__run(browser, helpline);
2782
2783 if (browser->he_selection != NULL) {
2784 thread = hist_browser__selected_thread(browser);
2785 map = browser->selection->map;
2786 socked_id = browser->he_selection->socket;
2787 }
2788 switch (key) {
2789 case K_TAB:
2790 case K_UNTAB:
2791 if (nr_events == 1)
2792 continue;
2793 /*
2794 * Exit the browser, let hists__browser_tree
2795 * go to the next or previous
2796 */
2797 goto out_free_stack;
2798 case 'a':
2799 if (!hists__has(hists, sym)) {
2800 ui_browser__warning(&browser->b, delay_secs * 2,
2801 "Annotation is only available for symbolic views, "
2802 "include \"sym*\" in --sort to use it.");
2803 continue;
2804 }
2805
2806 if (browser->selection == NULL ||
2807 browser->selection->sym == NULL ||
2808 browser->selection->map->dso->annotate_warned)
2809 continue;
2810
2811 actions->ms.map = browser->selection->map;
2812 actions->ms.sym = browser->selection->sym;
2813 do_annotate(browser, actions);
2814 continue;
2815 case 'P':
2816 hist_browser__dump(browser);
2817 continue;
2818 case 'd':
2819 actions->ms.map = map;
2820 do_zoom_dso(browser, actions);
2821 continue;
2822 case 'V':
2823 verbose = (verbose + 1) % 4;
2824 browser->show_dso = verbose > 0;
2825 ui_helpline__fpush("Verbosity level set to %d\n",
2826 verbose);
2827 continue;
2828 case 't':
2829 actions->thread = thread;
2830 do_zoom_thread(browser, actions);
2831 continue;
2832 case 'S':
2833 actions->socket = socked_id;
2834 do_zoom_socket(browser, actions);
2835 continue;
2836 case '/':
2837 if (ui_browser__input_window("Symbol to show",
2838 "Please enter the name of symbol you want to see.\n"
2839 "To remove the filter later, press / + ENTER.",
2840 buf, "ENTER: OK, ESC: Cancel",
2841 delay_secs * 2) == K_ENTER) {
2842 hists->symbol_filter_str = *buf ? buf : NULL;
2843 hists__filter_by_symbol(hists);
2844 hist_browser__reset(browser);
2845 }
2846 continue;
2847 case 'r':
2848 if (is_report_browser(hbt)) {
2849 actions->thread = NULL;
2850 actions->ms.sym = NULL;
2851 do_run_script(browser, actions);
2852 }
2853 continue;
2854 case 's':
2855 if (is_report_browser(hbt)) {
2856 key = do_switch_data(browser, actions);
2857 if (key == K_SWITCH_INPUT_DATA)
2858 goto out_free_stack;
2859 }
2860 continue;
2861 case 'i':
2862 /* env->arch is NULL for live-mode (i.e. perf top) */
2863 if (env->arch)
2864 tui__header_window(env);
2865 continue;
2866 case 'F':
2867 symbol_conf.filter_relative ^= 1;
2868 continue;
2869 case 'z':
2870 if (!is_report_browser(hbt)) {
2871 struct perf_top *top = hbt->arg;
2872
2873 top->zero = !top->zero;
2874 }
2875 continue;
2876 case 'L':
2877 if (ui_browser__input_window("Percent Limit",
2878 "Please enter the value you want to hide entries under that percent.",
2879 buf, "ENTER: OK, ESC: Cancel",
2880 delay_secs * 2) == K_ENTER) {
2881 char *end;
2882 double new_percent = strtod(buf, &end);
2883
2884 if (new_percent < 0 || new_percent > 100) {
2885 ui_browser__warning(&browser->b, delay_secs * 2,
2886 "Invalid percent: %.2f", new_percent);
2887 continue;
2888 }
2889
2890 hist_browser__update_percent_limit(browser, new_percent);
2891 hist_browser__reset(browser);
2892 }
2893 continue;
2894 case K_F1:
2895 case 'h':
2896 case '?':
2897 ui_browser__help_window(&browser->b,
2898 is_report_browser(hbt) ? report_help : top_help);
2899 continue;
2900 case K_ENTER:
2901 case K_RIGHT:
2902 case 'm':
2903 /* menu */
2904 break;
2905 case K_ESC:
2906 case K_LEFT: {
2907 const void *top;
2908
2909 if (pstack__empty(browser->pstack)) {
2910 /*
2911 * Go back to the perf_evsel_menu__run or other user
2912 */
2913 if (left_exits)
2914 goto out_free_stack;
2915
2916 if (key == K_ESC &&
2917 ui_browser__dialog_yesno(&browser->b,
2918 "Do you really want to exit?"))
2919 goto out_free_stack;
2920
2921 continue;
2922 }
2923 top = pstack__peek(browser->pstack);
2924 if (top == &browser->hists->dso_filter) {
2925 /*
2926 * No need to set actions->dso here since
2927 * it's just to remove the current filter.
2928 * Ditto for thread below.
2929 */
2930 do_zoom_dso(browser, actions);
2931 } else if (top == &browser->hists->thread_filter) {
2932 do_zoom_thread(browser, actions);
2933 } else if (top == &browser->hists->socket_filter) {
2934 do_zoom_socket(browser, actions);
2935 }
2936 continue;
2937 }
2938 case 'q':
2939 case CTRL('c'):
2940 goto out_free_stack;
2941 case 'f':
2942 if (!is_report_browser(hbt)) {
2943 struct perf_top *top = hbt->arg;
2944
2945 perf_evlist__toggle_enable(top->evlist);
2946 /*
2947 * No need to refresh, resort/decay histogram
2948 * entries if we are not collecting samples:
2949 */
2950 if (top->evlist->enabled) {
2951 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2952 hbt->refresh = delay_secs;
2953 } else {
2954 helpline = "Press 'f' again to re-enable the events";
2955 hbt->refresh = 0;
2956 }
2957 continue;
2958 }
2959 /* Fall thru */
2960 default:
2961 helpline = "Press '?' for help on key bindings";
2962 continue;
2963 }
2964
2965 if (!hists__has(hists, sym) || browser->selection == NULL)
2966 goto skip_annotation;
2967
2968 if (sort__mode == SORT_MODE__BRANCH) {
2969 bi = browser->he_selection->branch_info;
2970
2971 if (bi == NULL)
2972 goto skip_annotation;
2973
2974 nr_options += add_annotate_opt(browser,
2975 &actions[nr_options],
2976 &options[nr_options],
2977 bi->from.map,
2978 bi->from.sym);
2979 if (bi->to.sym != bi->from.sym)
2980 nr_options += add_annotate_opt(browser,
2981 &actions[nr_options],
2982 &options[nr_options],
2983 bi->to.map,
2984 bi->to.sym);
2985 } else {
2986 nr_options += add_annotate_opt(browser,
2987 &actions[nr_options],
2988 &options[nr_options],
2989 browser->selection->map,
2990 browser->selection->sym);
2991 }
2992 skip_annotation:
2993 nr_options += add_thread_opt(browser, &actions[nr_options],
2994 &options[nr_options], thread);
2995 nr_options += add_dso_opt(browser, &actions[nr_options],
2996 &options[nr_options], map);
2997 nr_options += add_map_opt(browser, &actions[nr_options],
2998 &options[nr_options],
2999 browser->selection ?
3000 browser->selection->map : NULL);
3001 nr_options += add_socket_opt(browser, &actions[nr_options],
3002 &options[nr_options],
3003 socked_id);
3004 /* perf script support */
3005 if (!is_report_browser(hbt))
3006 goto skip_scripting;
3007
3008 if (browser->he_selection) {
3009 if (hists__has(hists, thread) && thread) {
3010 nr_options += add_script_opt(browser,
3011 &actions[nr_options],
3012 &options[nr_options],
3013 thread, NULL);
3014 }
3015 /*
3016 * Note that browser->selection != NULL
3017 * when browser->he_selection is not NULL,
3018 * so we don't need to check browser->selection
3019 * before fetching browser->selection->sym like what
3020 * we do before fetching browser->selection->map.
3021 *
3022 * See hist_browser__show_entry.
3023 */
3024 if (hists__has(hists, sym) && browser->selection->sym) {
3025 nr_options += add_script_opt(browser,
3026 &actions[nr_options],
3027 &options[nr_options],
3028 NULL, browser->selection->sym);
3029 }
3030 }
3031 nr_options += add_script_opt(browser, &actions[nr_options],
3032 &options[nr_options], NULL, NULL);
3033 nr_options += add_switch_opt(browser, &actions[nr_options],
3034 &options[nr_options]);
3035 skip_scripting:
3036 nr_options += add_exit_opt(browser, &actions[nr_options],
3037 &options[nr_options]);
3038
3039 do {
3040 struct popup_action *act;
3041
3042 choice = ui__popup_menu(nr_options, options);
3043 if (choice == -1 || choice >= nr_options)
3044 break;
3045
3046 act = &actions[choice];
3047 key = act->fn(browser, act);
3048 } while (key == 1);
3049
3050 if (key == K_SWITCH_INPUT_DATA)
3051 break;
3052 }
3053 out_free_stack:
3054 pstack__delete(browser->pstack);
3055 out:
3056 hist_browser__delete(browser);
3057 free_popup_options(options, MAX_OPTIONS);
3058 return key;
3059 }
3060
3061 struct perf_evsel_menu {
3062 struct ui_browser b;
3063 struct perf_evsel *selection;
3064 bool lost_events, lost_events_warned;
3065 float min_pcnt;
3066 struct perf_env *env;
3067 };
3068
3069 static void perf_evsel_menu__write(struct ui_browser *browser,
3070 void *entry, int row)
3071 {
3072 struct perf_evsel_menu *menu = container_of(browser,
3073 struct perf_evsel_menu, b);
3074 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3075 struct hists *hists = evsel__hists(evsel);
3076 bool current_entry = ui_browser__is_current_entry(browser, row);
3077 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3078 const char *ev_name = perf_evsel__name(evsel);
3079 char bf[256], unit;
3080 const char *warn = " ";
3081 size_t printed;
3082
3083 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3084 HE_COLORSET_NORMAL);
3085
3086 if (perf_evsel__is_group_event(evsel)) {
3087 struct perf_evsel *pos;
3088
3089 ev_name = perf_evsel__group_name(evsel);
3090
3091 for_each_group_member(pos, evsel) {
3092 struct hists *pos_hists = evsel__hists(pos);
3093 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3094 }
3095 }
3096
3097 nr_events = convert_unit(nr_events, &unit);
3098 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3099 unit, unit == ' ' ? "" : " ", ev_name);
3100 ui_browser__printf(browser, "%s", bf);
3101
3102 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3103 if (nr_events != 0) {
3104 menu->lost_events = true;
3105 if (!current_entry)
3106 ui_browser__set_color(browser, HE_COLORSET_TOP);
3107 nr_events = convert_unit(nr_events, &unit);
3108 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3109 nr_events, unit, unit == ' ' ? "" : " ");
3110 warn = bf;
3111 }
3112
3113 ui_browser__write_nstring(browser, warn, browser->width - printed);
3114
3115 if (current_entry)
3116 menu->selection = evsel;
3117 }
3118
3119 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3120 int nr_events, const char *help,
3121 struct hist_browser_timer *hbt)
3122 {
3123 struct perf_evlist *evlist = menu->b.priv;
3124 struct perf_evsel *pos;
3125 const char *title = "Available samples";
3126 int delay_secs = hbt ? hbt->refresh : 0;
3127 int key;
3128
3129 if (ui_browser__show(&menu->b, title,
3130 "ESC: exit, ENTER|->: Browse histograms") < 0)
3131 return -1;
3132
3133 while (1) {
3134 key = ui_browser__run(&menu->b, delay_secs);
3135
3136 switch (key) {
3137 case K_TIMER:
3138 hbt->timer(hbt->arg);
3139
3140 if (!menu->lost_events_warned && menu->lost_events) {
3141 ui_browser__warn_lost_events(&menu->b);
3142 menu->lost_events_warned = true;
3143 }
3144 continue;
3145 case K_RIGHT:
3146 case K_ENTER:
3147 if (!menu->selection)
3148 continue;
3149 pos = menu->selection;
3150 browse_hists:
3151 perf_evlist__set_selected(evlist, pos);
3152 /*
3153 * Give the calling tool a chance to populate the non
3154 * default evsel resorted hists tree.
3155 */
3156 if (hbt)
3157 hbt->timer(hbt->arg);
3158 key = perf_evsel__hists_browse(pos, nr_events, help,
3159 true, hbt,
3160 menu->min_pcnt,
3161 menu->env);
3162 ui_browser__show_title(&menu->b, title);
3163 switch (key) {
3164 case K_TAB:
3165 if (pos->node.next == &evlist->entries)
3166 pos = perf_evlist__first(evlist);
3167 else
3168 pos = perf_evsel__next(pos);
3169 goto browse_hists;
3170 case K_UNTAB:
3171 if (pos->node.prev == &evlist->entries)
3172 pos = perf_evlist__last(evlist);
3173 else
3174 pos = perf_evsel__prev(pos);
3175 goto browse_hists;
3176 case K_SWITCH_INPUT_DATA:
3177 case 'q':
3178 case CTRL('c'):
3179 goto out;
3180 case K_ESC:
3181 default:
3182 continue;
3183 }
3184 case K_LEFT:
3185 continue;
3186 case K_ESC:
3187 if (!ui_browser__dialog_yesno(&menu->b,
3188 "Do you really want to exit?"))
3189 continue;
3190 /* Fall thru */
3191 case 'q':
3192 case CTRL('c'):
3193 goto out;
3194 default:
3195 continue;
3196 }
3197 }
3198
3199 out:
3200 ui_browser__hide(&menu->b);
3201 return key;
3202 }
3203
3204 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3205 void *entry)
3206 {
3207 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3208
3209 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3210 return true;
3211
3212 return false;
3213 }
3214
3215 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3216 int nr_entries, const char *help,
3217 struct hist_browser_timer *hbt,
3218 float min_pcnt,
3219 struct perf_env *env)
3220 {
3221 struct perf_evsel *pos;
3222 struct perf_evsel_menu menu = {
3223 .b = {
3224 .entries = &evlist->entries,
3225 .refresh = ui_browser__list_head_refresh,
3226 .seek = ui_browser__list_head_seek,
3227 .write = perf_evsel_menu__write,
3228 .filter = filter_group_entries,
3229 .nr_entries = nr_entries,
3230 .priv = evlist,
3231 },
3232 .min_pcnt = min_pcnt,
3233 .env = env,
3234 };
3235
3236 ui_helpline__push("Press ESC to exit");
3237
3238 evlist__for_each_entry(evlist, pos) {
3239 const char *ev_name = perf_evsel__name(pos);
3240 size_t line_len = strlen(ev_name) + 7;
3241
3242 if (menu.b.width < line_len)
3243 menu.b.width = line_len;
3244 }
3245
3246 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3247 }
3248
3249 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3250 struct hist_browser_timer *hbt,
3251 float min_pcnt,
3252 struct perf_env *env)
3253 {
3254 int nr_entries = evlist->nr_entries;
3255
3256 single_entry:
3257 if (nr_entries == 1) {
3258 struct perf_evsel *first = perf_evlist__first(evlist);
3259
3260 return perf_evsel__hists_browse(first, nr_entries, help,
3261 false, hbt, min_pcnt,
3262 env);
3263 }
3264
3265 if (symbol_conf.event_group) {
3266 struct perf_evsel *pos;
3267
3268 nr_entries = 0;
3269 evlist__for_each_entry(evlist, pos) {
3270 if (perf_evsel__is_group_leader(pos))
3271 nr_entries++;
3272 }
3273
3274 if (nr_entries == 1)
3275 goto single_entry;
3276 }
3277
3278 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3279 hbt, min_pcnt, env);
3280 }