]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/perf/ui/browsers/hists.c
Merge remote-tracking branches 'asoc/topic/inntel', 'asoc/topic/input', 'asoc/topic...
[mirror_ubuntu-bionic-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;
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 -= 2;
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 if (!first) {
1365 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1366 width -= hierarchy_indent;
1367 }
1368
1369 if (column >= browser->b.horiz_scroll) {
1370 char s[2048];
1371 struct perf_hpp hpp = {
1372 .buf = s,
1373 .size = sizeof(s),
1374 .ptr = &arg,
1375 };
1376
1377 if (current_entry && browser->b.navkeypressed) {
1378 ui_browser__set_color(&browser->b,
1379 HE_COLORSET_SELECTED);
1380 } else {
1381 ui_browser__set_color(&browser->b,
1382 HE_COLORSET_NORMAL);
1383 }
1384
1385 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1386 if (first) {
1387 ui_browser__printf(&browser->b, "%c ", folded_sign);
1388 first = false;
1389 } else {
1390 ui_browser__write_nstring(&browser->b, "", 2);
1391 }
1392
1393 width -= 2;
1394
1395 /*
1396 * No need to call hist_entry__snprintf_alignment()
1397 * since this fmt is always the last column in the
1398 * hierarchy mode.
1399 */
1400 if (fmt->color) {
1401 width -= fmt->color(fmt, &hpp, entry);
1402 } else {
1403 int i = 0;
1404
1405 width -= fmt->entry(fmt, &hpp, entry);
1406 ui_browser__printf(&browser->b, "%s", ltrim(s));
1407
1408 while (isspace(s[i++]))
1409 width++;
1410 }
1411 }
1412 }
1413
1414 /* The scroll bar isn't being used */
1415 if (!browser->b.navkeypressed)
1416 width += 1;
1417
1418 ui_browser__write_nstring(&browser->b, "", width);
1419
1420 ++row;
1421 ++printed;
1422
1423 show_callchain:
1424 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1425 struct callchain_print_arg carg = {
1426 .row_offset = row_offset,
1427 };
1428
1429 printed += hist_browser__show_callchain(browser, entry,
1430 level + 1, row,
1431 hist_browser__show_callchain_entry, &carg,
1432 hist_browser__check_output_full);
1433 }
1434
1435 return printed;
1436 }
1437
1438 static int hist_browser__show_no_entry(struct hist_browser *browser,
1439 unsigned short row, int level)
1440 {
1441 int width = browser->b.width;
1442 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1443 bool first = true;
1444 int column = 0;
1445 int ret;
1446 struct perf_hpp_fmt *fmt;
1447 struct perf_hpp_list_node *fmt_node;
1448 int indent = browser->hists->nr_hpp_node - 2;
1449
1450 if (current_entry) {
1451 browser->he_selection = NULL;
1452 browser->selection = NULL;
1453 }
1454
1455 hist_browser__gotorc(browser, row, 0);
1456
1457 if (current_entry && browser->b.navkeypressed)
1458 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1459 else
1460 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1461
1462 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1463 width -= level * HIERARCHY_INDENT;
1464
1465 /* the first hpp_list_node is for overhead columns */
1466 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1467 struct perf_hpp_list_node, list);
1468 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1469 if (perf_hpp__should_skip(fmt, browser->hists) ||
1470 column++ < browser->b.horiz_scroll)
1471 continue;
1472
1473 ret = fmt->width(fmt, NULL, browser->hists);
1474
1475 if (first) {
1476 /* for folded sign */
1477 first = false;
1478 ret++;
1479 } else {
1480 /* space between columns */
1481 ret += 2;
1482 }
1483
1484 ui_browser__write_nstring(&browser->b, "", ret);
1485 width -= ret;
1486 }
1487
1488 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1489 width -= indent * HIERARCHY_INDENT;
1490
1491 if (column >= browser->b.horiz_scroll) {
1492 char buf[32];
1493
1494 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1495 ui_browser__printf(&browser->b, " %s", buf);
1496 width -= ret + 2;
1497 }
1498
1499 /* The scroll bar isn't being used */
1500 if (!browser->b.navkeypressed)
1501 width += 1;
1502
1503 ui_browser__write_nstring(&browser->b, "", width);
1504 return 1;
1505 }
1506
1507 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1508 {
1509 advance_hpp(hpp, inc);
1510 return hpp->size <= 0;
1511 }
1512
1513 static int
1514 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1515 size_t size, int line)
1516 {
1517 struct hists *hists = browser->hists;
1518 struct perf_hpp dummy_hpp = {
1519 .buf = buf,
1520 .size = size,
1521 };
1522 struct perf_hpp_fmt *fmt;
1523 size_t ret = 0;
1524 int column = 0;
1525 int span = 0;
1526
1527 if (symbol_conf.use_callchain) {
1528 ret = scnprintf(buf, size, " ");
1529 if (advance_hpp_check(&dummy_hpp, ret))
1530 return ret;
1531 }
1532
1533 hists__for_each_format(browser->hists, fmt) {
1534 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1535 continue;
1536
1537 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1538 if (advance_hpp_check(&dummy_hpp, ret))
1539 break;
1540
1541 if (span)
1542 continue;
1543
1544 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1545 if (advance_hpp_check(&dummy_hpp, ret))
1546 break;
1547 }
1548
1549 return ret;
1550 }
1551
1552 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1553 {
1554 struct hists *hists = browser->hists;
1555 struct perf_hpp dummy_hpp = {
1556 .buf = buf,
1557 .size = size,
1558 };
1559 struct perf_hpp_fmt *fmt;
1560 struct perf_hpp_list_node *fmt_node;
1561 size_t ret = 0;
1562 int column = 0;
1563 int indent = hists->nr_hpp_node - 2;
1564 bool first_node, first_col;
1565
1566 ret = scnprintf(buf, size, " ");
1567 if (advance_hpp_check(&dummy_hpp, ret))
1568 return ret;
1569
1570 first_node = true;
1571 /* the first hpp_list_node is for overhead columns */
1572 fmt_node = list_first_entry(&hists->hpp_formats,
1573 struct perf_hpp_list_node, list);
1574 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1575 if (column++ < browser->b.horiz_scroll)
1576 continue;
1577
1578 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1579 if (advance_hpp_check(&dummy_hpp, ret))
1580 break;
1581
1582 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1583 if (advance_hpp_check(&dummy_hpp, ret))
1584 break;
1585
1586 first_node = false;
1587 }
1588
1589 if (!first_node) {
1590 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1591 indent * HIERARCHY_INDENT, "");
1592 if (advance_hpp_check(&dummy_hpp, ret))
1593 return ret;
1594 }
1595
1596 first_node = true;
1597 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1598 if (!first_node) {
1599 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1600 if (advance_hpp_check(&dummy_hpp, ret))
1601 break;
1602 }
1603 first_node = false;
1604
1605 first_col = true;
1606 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1607 char *start;
1608
1609 if (perf_hpp__should_skip(fmt, hists))
1610 continue;
1611
1612 if (!first_col) {
1613 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1614 if (advance_hpp_check(&dummy_hpp, ret))
1615 break;
1616 }
1617 first_col = false;
1618
1619 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1620 dummy_hpp.buf[ret] = '\0';
1621
1622 start = trim(dummy_hpp.buf);
1623 ret = strlen(start);
1624
1625 if (start != dummy_hpp.buf)
1626 memmove(dummy_hpp.buf, start, ret + 1);
1627
1628 if (advance_hpp_check(&dummy_hpp, ret))
1629 break;
1630 }
1631 }
1632
1633 return ret;
1634 }
1635
1636 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1637 {
1638 char headers[1024];
1639
1640 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1641 sizeof(headers));
1642
1643 ui_browser__gotorc(&browser->b, 0, 0);
1644 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1645 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1646 }
1647
1648 static void hists_browser__headers(struct hist_browser *browser)
1649 {
1650 struct hists *hists = browser->hists;
1651 struct perf_hpp_list *hpp_list = hists->hpp_list;
1652
1653 int line;
1654
1655 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1656 char headers[1024];
1657
1658 hists_browser__scnprintf_headers(browser, headers,
1659 sizeof(headers), line);
1660
1661 ui_browser__gotorc(&browser->b, line, 0);
1662 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1663 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1664 }
1665 }
1666
1667 static void hist_browser__show_headers(struct hist_browser *browser)
1668 {
1669 if (symbol_conf.report_hierarchy)
1670 hists_browser__hierarchy_headers(browser);
1671 else
1672 hists_browser__headers(browser);
1673 }
1674
1675 static void ui_browser__hists_init_top(struct ui_browser *browser)
1676 {
1677 if (browser->top == NULL) {
1678 struct hist_browser *hb;
1679
1680 hb = container_of(browser, struct hist_browser, b);
1681 browser->top = rb_first(&hb->hists->entries);
1682 }
1683 }
1684
1685 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1686 {
1687 unsigned row = 0;
1688 u16 header_offset = 0;
1689 struct rb_node *nd;
1690 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1691 struct hists *hists = hb->hists;
1692
1693 if (hb->show_headers) {
1694 struct perf_hpp_list *hpp_list = hists->hpp_list;
1695
1696 hist_browser__show_headers(hb);
1697 header_offset = hpp_list->nr_header_lines;
1698 }
1699
1700 ui_browser__hists_init_top(browser);
1701 hb->he_selection = NULL;
1702 hb->selection = NULL;
1703
1704 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1705 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1706 float percent;
1707
1708 if (h->filtered) {
1709 /* let it move to sibling */
1710 h->unfolded = false;
1711 continue;
1712 }
1713
1714 percent = hist_entry__get_percent_limit(h);
1715 if (percent < hb->min_pcnt)
1716 continue;
1717
1718 if (symbol_conf.report_hierarchy) {
1719 row += hist_browser__show_hierarchy_entry(hb, h, row,
1720 h->depth);
1721 if (row == browser->rows)
1722 break;
1723
1724 if (h->has_no_entry) {
1725 hist_browser__show_no_entry(hb, row, h->depth + 1);
1726 row++;
1727 }
1728 } else {
1729 row += hist_browser__show_entry(hb, h, row);
1730 }
1731
1732 if (row == browser->rows)
1733 break;
1734 }
1735
1736 return row + header_offset;
1737 }
1738
1739 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1740 float min_pcnt)
1741 {
1742 while (nd != NULL) {
1743 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1744 float percent = hist_entry__get_percent_limit(h);
1745
1746 if (!h->filtered && percent >= min_pcnt)
1747 return nd;
1748
1749 /*
1750 * If it's filtered, its all children also were filtered.
1751 * So move to sibling node.
1752 */
1753 if (rb_next(nd))
1754 nd = rb_next(nd);
1755 else
1756 nd = rb_hierarchy_next(nd);
1757 }
1758
1759 return NULL;
1760 }
1761
1762 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1763 float min_pcnt)
1764 {
1765 while (nd != NULL) {
1766 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1767 float percent = hist_entry__get_percent_limit(h);
1768
1769 if (!h->filtered && percent >= min_pcnt)
1770 return nd;
1771
1772 nd = rb_hierarchy_prev(nd);
1773 }
1774
1775 return NULL;
1776 }
1777
1778 static void ui_browser__hists_seek(struct ui_browser *browser,
1779 off_t offset, int whence)
1780 {
1781 struct hist_entry *h;
1782 struct rb_node *nd;
1783 bool first = true;
1784 struct hist_browser *hb;
1785
1786 hb = container_of(browser, struct hist_browser, b);
1787
1788 if (browser->nr_entries == 0)
1789 return;
1790
1791 ui_browser__hists_init_top(browser);
1792
1793 switch (whence) {
1794 case SEEK_SET:
1795 nd = hists__filter_entries(rb_first(browser->entries),
1796 hb->min_pcnt);
1797 break;
1798 case SEEK_CUR:
1799 nd = browser->top;
1800 goto do_offset;
1801 case SEEK_END:
1802 nd = rb_hierarchy_last(rb_last(browser->entries));
1803 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1804 first = false;
1805 break;
1806 default:
1807 return;
1808 }
1809
1810 /*
1811 * Moves not relative to the first visible entry invalidates its
1812 * row_offset:
1813 */
1814 h = rb_entry(browser->top, struct hist_entry, rb_node);
1815 h->row_offset = 0;
1816
1817 /*
1818 * Here we have to check if nd is expanded (+), if it is we can't go
1819 * the next top level hist_entry, instead we must compute an offset of
1820 * what _not_ to show and not change the first visible entry.
1821 *
1822 * This offset increments when we are going from top to bottom and
1823 * decreases when we're going from bottom to top.
1824 *
1825 * As we don't have backpointers to the top level in the callchains
1826 * structure, we need to always print the whole hist_entry callchain,
1827 * skipping the first ones that are before the first visible entry
1828 * and stop when we printed enough lines to fill the screen.
1829 */
1830 do_offset:
1831 if (!nd)
1832 return;
1833
1834 if (offset > 0) {
1835 do {
1836 h = rb_entry(nd, struct hist_entry, rb_node);
1837 if (h->unfolded && h->leaf) {
1838 u16 remaining = h->nr_rows - h->row_offset;
1839 if (offset > remaining) {
1840 offset -= remaining;
1841 h->row_offset = 0;
1842 } else {
1843 h->row_offset += offset;
1844 offset = 0;
1845 browser->top = nd;
1846 break;
1847 }
1848 }
1849 nd = hists__filter_entries(rb_hierarchy_next(nd),
1850 hb->min_pcnt);
1851 if (nd == NULL)
1852 break;
1853 --offset;
1854 browser->top = nd;
1855 } while (offset != 0);
1856 } else if (offset < 0) {
1857 while (1) {
1858 h = rb_entry(nd, struct hist_entry, rb_node);
1859 if (h->unfolded && h->leaf) {
1860 if (first) {
1861 if (-offset > h->row_offset) {
1862 offset += h->row_offset;
1863 h->row_offset = 0;
1864 } else {
1865 h->row_offset += offset;
1866 offset = 0;
1867 browser->top = nd;
1868 break;
1869 }
1870 } else {
1871 if (-offset > h->nr_rows) {
1872 offset += h->nr_rows;
1873 h->row_offset = 0;
1874 } else {
1875 h->row_offset = h->nr_rows + offset;
1876 offset = 0;
1877 browser->top = nd;
1878 break;
1879 }
1880 }
1881 }
1882
1883 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1884 hb->min_pcnt);
1885 if (nd == NULL)
1886 break;
1887 ++offset;
1888 browser->top = nd;
1889 if (offset == 0) {
1890 /*
1891 * Last unfiltered hist_entry, check if it is
1892 * unfolded, if it is then we should have
1893 * row_offset at its last entry.
1894 */
1895 h = rb_entry(nd, struct hist_entry, rb_node);
1896 if (h->unfolded && h->leaf)
1897 h->row_offset = h->nr_rows;
1898 break;
1899 }
1900 first = false;
1901 }
1902 } else {
1903 browser->top = nd;
1904 h = rb_entry(nd, struct hist_entry, rb_node);
1905 h->row_offset = 0;
1906 }
1907 }
1908
1909 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1910 struct hist_entry *he, FILE *fp,
1911 int level)
1912 {
1913 struct callchain_print_arg arg = {
1914 .fp = fp,
1915 };
1916
1917 hist_browser__show_callchain(browser, he, level, 0,
1918 hist_browser__fprintf_callchain_entry, &arg,
1919 hist_browser__check_dump_full);
1920 return arg.printed;
1921 }
1922
1923 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1924 struct hist_entry *he, FILE *fp)
1925 {
1926 char s[8192];
1927 int printed = 0;
1928 char folded_sign = ' ';
1929 struct perf_hpp hpp = {
1930 .buf = s,
1931 .size = sizeof(s),
1932 };
1933 struct perf_hpp_fmt *fmt;
1934 bool first = true;
1935 int ret;
1936
1937 if (symbol_conf.use_callchain) {
1938 folded_sign = hist_entry__folded(he);
1939 printed += fprintf(fp, "%c ", folded_sign);
1940 }
1941
1942 hists__for_each_format(browser->hists, fmt) {
1943 if (perf_hpp__should_skip(fmt, he->hists))
1944 continue;
1945
1946 if (!first) {
1947 ret = scnprintf(hpp.buf, hpp.size, " ");
1948 advance_hpp(&hpp, ret);
1949 } else
1950 first = false;
1951
1952 ret = fmt->entry(fmt, &hpp, he);
1953 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1954 advance_hpp(&hpp, ret);
1955 }
1956 printed += fprintf(fp, "%s\n", s);
1957
1958 if (folded_sign == '-')
1959 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1960
1961 return printed;
1962 }
1963
1964
1965 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1966 struct hist_entry *he,
1967 FILE *fp, int level)
1968 {
1969 char s[8192];
1970 int printed = 0;
1971 char folded_sign = ' ';
1972 struct perf_hpp hpp = {
1973 .buf = s,
1974 .size = sizeof(s),
1975 };
1976 struct perf_hpp_fmt *fmt;
1977 struct perf_hpp_list_node *fmt_node;
1978 bool first = true;
1979 int ret;
1980 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1981
1982 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1983
1984 folded_sign = hist_entry__folded(he);
1985 printed += fprintf(fp, "%c", folded_sign);
1986
1987 /* the first hpp_list_node is for overhead columns */
1988 fmt_node = list_first_entry(&he->hists->hpp_formats,
1989 struct perf_hpp_list_node, list);
1990 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1991 if (!first) {
1992 ret = scnprintf(hpp.buf, hpp.size, " ");
1993 advance_hpp(&hpp, ret);
1994 } else
1995 first = false;
1996
1997 ret = fmt->entry(fmt, &hpp, he);
1998 advance_hpp(&hpp, ret);
1999 }
2000
2001 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2002 advance_hpp(&hpp, ret);
2003
2004 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2005 ret = scnprintf(hpp.buf, hpp.size, " ");
2006 advance_hpp(&hpp, ret);
2007
2008 ret = fmt->entry(fmt, &hpp, he);
2009 advance_hpp(&hpp, ret);
2010 }
2011
2012 printed += fprintf(fp, "%s\n", rtrim(s));
2013
2014 if (he->leaf && folded_sign == '-') {
2015 printed += hist_browser__fprintf_callchain(browser, he, fp,
2016 he->depth + 1);
2017 }
2018
2019 return printed;
2020 }
2021
2022 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2023 {
2024 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2025 browser->min_pcnt);
2026 int printed = 0;
2027
2028 while (nd) {
2029 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2030
2031 if (symbol_conf.report_hierarchy) {
2032 printed += hist_browser__fprintf_hierarchy_entry(browser,
2033 h, fp,
2034 h->depth);
2035 } else {
2036 printed += hist_browser__fprintf_entry(browser, h, fp);
2037 }
2038
2039 nd = hists__filter_entries(rb_hierarchy_next(nd),
2040 browser->min_pcnt);
2041 }
2042
2043 return printed;
2044 }
2045
2046 static int hist_browser__dump(struct hist_browser *browser)
2047 {
2048 char filename[64];
2049 FILE *fp;
2050
2051 while (1) {
2052 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2053 if (access(filename, F_OK))
2054 break;
2055 /*
2056 * XXX: Just an arbitrary lazy upper limit
2057 */
2058 if (++browser->print_seq == 8192) {
2059 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2060 return -1;
2061 }
2062 }
2063
2064 fp = fopen(filename, "w");
2065 if (fp == NULL) {
2066 char bf[64];
2067 const char *err = str_error_r(errno, bf, sizeof(bf));
2068 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2069 return -1;
2070 }
2071
2072 ++browser->print_seq;
2073 hist_browser__fprintf(browser, fp);
2074 fclose(fp);
2075 ui_helpline__fpush("%s written!", filename);
2076
2077 return 0;
2078 }
2079
2080 void hist_browser__init(struct hist_browser *browser,
2081 struct hists *hists)
2082 {
2083 struct perf_hpp_fmt *fmt;
2084
2085 browser->hists = hists;
2086 browser->b.refresh = hist_browser__refresh;
2087 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2088 browser->b.seek = ui_browser__hists_seek;
2089 browser->b.use_navkeypressed = true;
2090 browser->show_headers = symbol_conf.show_hist_headers;
2091
2092 if (symbol_conf.report_hierarchy) {
2093 struct perf_hpp_list_node *fmt_node;
2094
2095 /* count overhead columns (in the first node) */
2096 fmt_node = list_first_entry(&hists->hpp_formats,
2097 struct perf_hpp_list_node, list);
2098 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2099 ++browser->b.columns;
2100
2101 /* add a single column for whole hierarchy sort keys*/
2102 ++browser->b.columns;
2103 } else {
2104 hists__for_each_format(hists, fmt)
2105 ++browser->b.columns;
2106 }
2107
2108 hists__reset_column_width(hists);
2109 }
2110
2111 struct hist_browser *hist_browser__new(struct hists *hists)
2112 {
2113 struct hist_browser *browser = zalloc(sizeof(*browser));
2114
2115 if (browser)
2116 hist_browser__init(browser, hists);
2117
2118 return browser;
2119 }
2120
2121 static struct hist_browser *
2122 perf_evsel_browser__new(struct perf_evsel *evsel,
2123 struct hist_browser_timer *hbt,
2124 struct perf_env *env)
2125 {
2126 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2127
2128 if (browser) {
2129 browser->hbt = hbt;
2130 browser->env = env;
2131 browser->title = perf_evsel_browser_title;
2132 }
2133 return browser;
2134 }
2135
2136 void hist_browser__delete(struct hist_browser *browser)
2137 {
2138 free(browser);
2139 }
2140
2141 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2142 {
2143 return browser->he_selection;
2144 }
2145
2146 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2147 {
2148 return browser->he_selection->thread;
2149 }
2150
2151 /* Check whether the browser is for 'top' or 'report' */
2152 static inline bool is_report_browser(void *timer)
2153 {
2154 return timer == NULL;
2155 }
2156
2157 static int perf_evsel_browser_title(struct hist_browser *browser,
2158 char *bf, size_t size)
2159 {
2160 struct hist_browser_timer *hbt = browser->hbt;
2161 struct hists *hists = browser->hists;
2162 char unit;
2163 int printed;
2164 const struct dso *dso = hists->dso_filter;
2165 const struct thread *thread = hists->thread_filter;
2166 int socket_id = hists->socket_filter;
2167 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2168 u64 nr_events = hists->stats.total_period;
2169 struct perf_evsel *evsel = hists_to_evsel(hists);
2170 const char *ev_name = perf_evsel__name(evsel);
2171 char buf[512];
2172 size_t buflen = sizeof(buf);
2173 char ref[30] = " show reference callgraph, ";
2174 bool enable_ref = false;
2175
2176 if (symbol_conf.filter_relative) {
2177 nr_samples = hists->stats.nr_non_filtered_samples;
2178 nr_events = hists->stats.total_non_filtered_period;
2179 }
2180
2181 if (perf_evsel__is_group_event(evsel)) {
2182 struct perf_evsel *pos;
2183
2184 perf_evsel__group_desc(evsel, buf, buflen);
2185 ev_name = buf;
2186
2187 for_each_group_member(pos, evsel) {
2188 struct hists *pos_hists = evsel__hists(pos);
2189
2190 if (symbol_conf.filter_relative) {
2191 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2192 nr_events += pos_hists->stats.total_non_filtered_period;
2193 } else {
2194 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2195 nr_events += pos_hists->stats.total_period;
2196 }
2197 }
2198 }
2199
2200 if (symbol_conf.show_ref_callgraph &&
2201 strstr(ev_name, "call-graph=no"))
2202 enable_ref = true;
2203 nr_samples = convert_unit(nr_samples, &unit);
2204 printed = scnprintf(bf, size,
2205 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2206 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2207
2208
2209 if (hists->uid_filter_str)
2210 printed += snprintf(bf + printed, size - printed,
2211 ", UID: %s", hists->uid_filter_str);
2212 if (thread) {
2213 if (hists__has(hists, thread)) {
2214 printed += scnprintf(bf + printed, size - printed,
2215 ", Thread: %s(%d)",
2216 (thread->comm_set ? thread__comm_str(thread) : ""),
2217 thread->tid);
2218 } else {
2219 printed += scnprintf(bf + printed, size - printed,
2220 ", Thread: %s",
2221 (thread->comm_set ? thread__comm_str(thread) : ""));
2222 }
2223 }
2224 if (dso)
2225 printed += scnprintf(bf + printed, size - printed,
2226 ", DSO: %s", dso->short_name);
2227 if (socket_id > -1)
2228 printed += scnprintf(bf + printed, size - printed,
2229 ", Processor Socket: %d", socket_id);
2230 if (!is_report_browser(hbt)) {
2231 struct perf_top *top = hbt->arg;
2232
2233 if (top->zero)
2234 printed += scnprintf(bf + printed, size - printed, " [z]");
2235 }
2236
2237 return printed;
2238 }
2239
2240 static inline void free_popup_options(char **options, int n)
2241 {
2242 int i;
2243
2244 for (i = 0; i < n; ++i)
2245 zfree(&options[i]);
2246 }
2247
2248 /*
2249 * Only runtime switching of perf data file will make "input_name" point
2250 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2251 * whether we need to call free() for current "input_name" during the switch.
2252 */
2253 static bool is_input_name_malloced = false;
2254
2255 static int switch_data_file(void)
2256 {
2257 char *pwd, *options[32], *abs_path[32], *tmp;
2258 DIR *pwd_dir;
2259 int nr_options = 0, choice = -1, ret = -1;
2260 struct dirent *dent;
2261
2262 pwd = getenv("PWD");
2263 if (!pwd)
2264 return ret;
2265
2266 pwd_dir = opendir(pwd);
2267 if (!pwd_dir)
2268 return ret;
2269
2270 memset(options, 0, sizeof(options));
2271 memset(options, 0, sizeof(abs_path));
2272
2273 while ((dent = readdir(pwd_dir))) {
2274 char path[PATH_MAX];
2275 u64 magic;
2276 char *name = dent->d_name;
2277 FILE *file;
2278
2279 if (!(dent->d_type == DT_REG))
2280 continue;
2281
2282 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2283
2284 file = fopen(path, "r");
2285 if (!file)
2286 continue;
2287
2288 if (fread(&magic, 1, 8, file) < 8)
2289 goto close_file_and_continue;
2290
2291 if (is_perf_magic(magic)) {
2292 options[nr_options] = strdup(name);
2293 if (!options[nr_options])
2294 goto close_file_and_continue;
2295
2296 abs_path[nr_options] = strdup(path);
2297 if (!abs_path[nr_options]) {
2298 zfree(&options[nr_options]);
2299 ui__warning("Can't search all data files due to memory shortage.\n");
2300 fclose(file);
2301 break;
2302 }
2303
2304 nr_options++;
2305 }
2306
2307 close_file_and_continue:
2308 fclose(file);
2309 if (nr_options >= 32) {
2310 ui__warning("Too many perf data files in PWD!\n"
2311 "Only the first 32 files will be listed.\n");
2312 break;
2313 }
2314 }
2315 closedir(pwd_dir);
2316
2317 if (nr_options) {
2318 choice = ui__popup_menu(nr_options, options);
2319 if (choice < nr_options && choice >= 0) {
2320 tmp = strdup(abs_path[choice]);
2321 if (tmp) {
2322 if (is_input_name_malloced)
2323 free((void *)input_name);
2324 input_name = tmp;
2325 is_input_name_malloced = true;
2326 ret = 0;
2327 } else
2328 ui__warning("Data switch failed due to memory shortage!\n");
2329 }
2330 }
2331
2332 free_popup_options(options, nr_options);
2333 free_popup_options(abs_path, nr_options);
2334 return ret;
2335 }
2336
2337 struct popup_action {
2338 struct thread *thread;
2339 struct map_symbol ms;
2340 int socket;
2341
2342 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2343 };
2344
2345 static int
2346 do_annotate(struct hist_browser *browser, struct popup_action *act)
2347 {
2348 struct perf_evsel *evsel;
2349 struct annotation *notes;
2350 struct hist_entry *he;
2351 int err;
2352
2353 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2354 return 0;
2355
2356 notes = symbol__annotation(act->ms.sym);
2357 if (!notes->src)
2358 return 0;
2359
2360 evsel = hists_to_evsel(browser->hists);
2361 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2362 he = hist_browser__selected_entry(browser);
2363 /*
2364 * offer option to annotate the other branch source or target
2365 * (if they exists) when returning from annotate
2366 */
2367 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2368 return 1;
2369
2370 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2371 if (err)
2372 ui_browser__handle_resize(&browser->b);
2373 return 0;
2374 }
2375
2376 static int
2377 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2378 struct popup_action *act, char **optstr,
2379 struct map *map, struct symbol *sym)
2380 {
2381 if (sym == NULL || map->dso->annotate_warned)
2382 return 0;
2383
2384 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2385 return 0;
2386
2387 act->ms.map = map;
2388 act->ms.sym = sym;
2389 act->fn = do_annotate;
2390 return 1;
2391 }
2392
2393 static int
2394 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2395 {
2396 struct thread *thread = act->thread;
2397
2398 if ((!hists__has(browser->hists, thread) &&
2399 !hists__has(browser->hists, comm)) || thread == NULL)
2400 return 0;
2401
2402 if (browser->hists->thread_filter) {
2403 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2404 perf_hpp__set_elide(HISTC_THREAD, false);
2405 thread__zput(browser->hists->thread_filter);
2406 ui_helpline__pop();
2407 } else {
2408 if (hists__has(browser->hists, thread)) {
2409 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2410 thread->comm_set ? thread__comm_str(thread) : "",
2411 thread->tid);
2412 } else {
2413 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2414 thread->comm_set ? thread__comm_str(thread) : "");
2415 }
2416
2417 browser->hists->thread_filter = thread__get(thread);
2418 perf_hpp__set_elide(HISTC_THREAD, false);
2419 pstack__push(browser->pstack, &browser->hists->thread_filter);
2420 }
2421
2422 hists__filter_by_thread(browser->hists);
2423 hist_browser__reset(browser);
2424 return 0;
2425 }
2426
2427 static int
2428 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2429 char **optstr, struct thread *thread)
2430 {
2431 int ret;
2432
2433 if ((!hists__has(browser->hists, thread) &&
2434 !hists__has(browser->hists, comm)) || thread == NULL)
2435 return 0;
2436
2437 if (hists__has(browser->hists, thread)) {
2438 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2439 browser->hists->thread_filter ? "out of" : "into",
2440 thread->comm_set ? thread__comm_str(thread) : "",
2441 thread->tid);
2442 } else {
2443 ret = asprintf(optstr, "Zoom %s %s thread",
2444 browser->hists->thread_filter ? "out of" : "into",
2445 thread->comm_set ? thread__comm_str(thread) : "");
2446 }
2447 if (ret < 0)
2448 return 0;
2449
2450 act->thread = thread;
2451 act->fn = do_zoom_thread;
2452 return 1;
2453 }
2454
2455 static int
2456 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2457 {
2458 struct map *map = act->ms.map;
2459
2460 if (!hists__has(browser->hists, dso) || map == NULL)
2461 return 0;
2462
2463 if (browser->hists->dso_filter) {
2464 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2465 perf_hpp__set_elide(HISTC_DSO, false);
2466 browser->hists->dso_filter = NULL;
2467 ui_helpline__pop();
2468 } else {
2469 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2470 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2471 browser->hists->dso_filter = map->dso;
2472 perf_hpp__set_elide(HISTC_DSO, true);
2473 pstack__push(browser->pstack, &browser->hists->dso_filter);
2474 }
2475
2476 hists__filter_by_dso(browser->hists);
2477 hist_browser__reset(browser);
2478 return 0;
2479 }
2480
2481 static int
2482 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2483 char **optstr, struct map *map)
2484 {
2485 if (!hists__has(browser->hists, dso) || map == NULL)
2486 return 0;
2487
2488 if (asprintf(optstr, "Zoom %s %s DSO",
2489 browser->hists->dso_filter ? "out of" : "into",
2490 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2491 return 0;
2492
2493 act->ms.map = map;
2494 act->fn = do_zoom_dso;
2495 return 1;
2496 }
2497
2498 static int
2499 do_browse_map(struct hist_browser *browser __maybe_unused,
2500 struct popup_action *act)
2501 {
2502 map__browse(act->ms.map);
2503 return 0;
2504 }
2505
2506 static int
2507 add_map_opt(struct hist_browser *browser,
2508 struct popup_action *act, char **optstr, struct map *map)
2509 {
2510 if (!hists__has(browser->hists, dso) || map == NULL)
2511 return 0;
2512
2513 if (asprintf(optstr, "Browse map details") < 0)
2514 return 0;
2515
2516 act->ms.map = map;
2517 act->fn = do_browse_map;
2518 return 1;
2519 }
2520
2521 static int
2522 do_run_script(struct hist_browser *browser __maybe_unused,
2523 struct popup_action *act)
2524 {
2525 char script_opt[64];
2526 memset(script_opt, 0, sizeof(script_opt));
2527
2528 if (act->thread) {
2529 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2530 thread__comm_str(act->thread));
2531 } else if (act->ms.sym) {
2532 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2533 act->ms.sym->name);
2534 }
2535
2536 script_browse(script_opt);
2537 return 0;
2538 }
2539
2540 static int
2541 add_script_opt(struct hist_browser *browser __maybe_unused,
2542 struct popup_action *act, char **optstr,
2543 struct thread *thread, struct symbol *sym)
2544 {
2545 if (thread) {
2546 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2547 thread__comm_str(thread)) < 0)
2548 return 0;
2549 } else if (sym) {
2550 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2551 sym->name) < 0)
2552 return 0;
2553 } else {
2554 if (asprintf(optstr, "Run scripts for all samples") < 0)
2555 return 0;
2556 }
2557
2558 act->thread = thread;
2559 act->ms.sym = sym;
2560 act->fn = do_run_script;
2561 return 1;
2562 }
2563
2564 static int
2565 do_switch_data(struct hist_browser *browser __maybe_unused,
2566 struct popup_action *act __maybe_unused)
2567 {
2568 if (switch_data_file()) {
2569 ui__warning("Won't switch the data files due to\n"
2570 "no valid data file get selected!\n");
2571 return 0;
2572 }
2573
2574 return K_SWITCH_INPUT_DATA;
2575 }
2576
2577 static int
2578 add_switch_opt(struct hist_browser *browser,
2579 struct popup_action *act, char **optstr)
2580 {
2581 if (!is_report_browser(browser->hbt))
2582 return 0;
2583
2584 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2585 return 0;
2586
2587 act->fn = do_switch_data;
2588 return 1;
2589 }
2590
2591 static int
2592 do_exit_browser(struct hist_browser *browser __maybe_unused,
2593 struct popup_action *act __maybe_unused)
2594 {
2595 return 0;
2596 }
2597
2598 static int
2599 add_exit_opt(struct hist_browser *browser __maybe_unused,
2600 struct popup_action *act, char **optstr)
2601 {
2602 if (asprintf(optstr, "Exit") < 0)
2603 return 0;
2604
2605 act->fn = do_exit_browser;
2606 return 1;
2607 }
2608
2609 static int
2610 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2611 {
2612 if (!hists__has(browser->hists, socket) || act->socket < 0)
2613 return 0;
2614
2615 if (browser->hists->socket_filter > -1) {
2616 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2617 browser->hists->socket_filter = -1;
2618 perf_hpp__set_elide(HISTC_SOCKET, false);
2619 } else {
2620 browser->hists->socket_filter = act->socket;
2621 perf_hpp__set_elide(HISTC_SOCKET, true);
2622 pstack__push(browser->pstack, &browser->hists->socket_filter);
2623 }
2624
2625 hists__filter_by_socket(browser->hists);
2626 hist_browser__reset(browser);
2627 return 0;
2628 }
2629
2630 static int
2631 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2632 char **optstr, int socket_id)
2633 {
2634 if (!hists__has(browser->hists, socket) || socket_id < 0)
2635 return 0;
2636
2637 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2638 (browser->hists->socket_filter > -1) ? "out of" : "into",
2639 socket_id) < 0)
2640 return 0;
2641
2642 act->socket = socket_id;
2643 act->fn = do_zoom_socket;
2644 return 1;
2645 }
2646
2647 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2648 {
2649 u64 nr_entries = 0;
2650 struct rb_node *nd = rb_first(&hb->hists->entries);
2651
2652 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2653 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2654 return;
2655 }
2656
2657 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2658 nr_entries++;
2659 nd = rb_hierarchy_next(nd);
2660 }
2661
2662 hb->nr_non_filtered_entries = nr_entries;
2663 hb->nr_hierarchy_entries = nr_entries;
2664 }
2665
2666 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2667 double percent)
2668 {
2669 struct hist_entry *he;
2670 struct rb_node *nd = rb_first(&hb->hists->entries);
2671 u64 total = hists__total_period(hb->hists);
2672 u64 min_callchain_hits = total * (percent / 100);
2673
2674 hb->min_pcnt = callchain_param.min_percent = percent;
2675
2676 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2677 he = rb_entry(nd, struct hist_entry, rb_node);
2678
2679 if (he->has_no_entry) {
2680 he->has_no_entry = false;
2681 he->nr_rows = 0;
2682 }
2683
2684 if (!he->leaf || !symbol_conf.use_callchain)
2685 goto next;
2686
2687 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2688 total = he->stat.period;
2689
2690 if (symbol_conf.cumulate_callchain)
2691 total = he->stat_acc->period;
2692
2693 min_callchain_hits = total * (percent / 100);
2694 }
2695
2696 callchain_param.sort(&he->sorted_chain, he->callchain,
2697 min_callchain_hits, &callchain_param);
2698
2699 next:
2700 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2701
2702 /* force to re-evaluate folding state of callchains */
2703 he->init_have_children = false;
2704 hist_entry__set_folding(he, hb, false);
2705 }
2706 }
2707
2708 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2709 const char *helpline,
2710 bool left_exits,
2711 struct hist_browser_timer *hbt,
2712 float min_pcnt,
2713 struct perf_env *env)
2714 {
2715 struct hists *hists = evsel__hists(evsel);
2716 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2717 struct branch_info *bi;
2718 #define MAX_OPTIONS 16
2719 char *options[MAX_OPTIONS];
2720 struct popup_action actions[MAX_OPTIONS];
2721 int nr_options = 0;
2722 int key = -1;
2723 char buf[64];
2724 int delay_secs = hbt ? hbt->refresh : 0;
2725
2726 #define HIST_BROWSER_HELP_COMMON \
2727 "h/?/F1 Show this window\n" \
2728 "UP/DOWN/PGUP\n" \
2729 "PGDN/SPACE Navigate\n" \
2730 "q/ESC/CTRL+C Exit browser\n\n" \
2731 "For multiple event sessions:\n\n" \
2732 "TAB/UNTAB Switch events\n\n" \
2733 "For symbolic views (--sort has sym):\n\n" \
2734 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2735 "ESC Zoom out\n" \
2736 "a Annotate current symbol\n" \
2737 "C Collapse all callchains\n" \
2738 "d Zoom into current DSO\n" \
2739 "E Expand all callchains\n" \
2740 "F Toggle percentage of filtered entries\n" \
2741 "H Display column headers\n" \
2742 "L Change percent limit\n" \
2743 "m Display context menu\n" \
2744 "S Zoom into current Processor Socket\n" \
2745
2746 /* help messages are sorted by lexical order of the hotkey */
2747 const char report_help[] = HIST_BROWSER_HELP_COMMON
2748 "i Show header information\n"
2749 "P Print histograms to perf.hist.N\n"
2750 "r Run available scripts\n"
2751 "s Switch to another data file in PWD\n"
2752 "t Zoom into current Thread\n"
2753 "V Verbose (DSO names in callchains, etc)\n"
2754 "/ Filter symbol by name";
2755 const char top_help[] = HIST_BROWSER_HELP_COMMON
2756 "P Print histograms to perf.hist.N\n"
2757 "t Zoom into current Thread\n"
2758 "V Verbose (DSO names in callchains, etc)\n"
2759 "z Toggle zeroing of samples\n"
2760 "f Enable/Disable events\n"
2761 "/ Filter symbol by name";
2762
2763 if (browser == NULL)
2764 return -1;
2765
2766 /* reset abort key so that it can get Ctrl-C as a key */
2767 SLang_reset_tty();
2768 SLang_init_tty(0, 0, 0);
2769
2770 if (min_pcnt)
2771 browser->min_pcnt = min_pcnt;
2772 hist_browser__update_nr_entries(browser);
2773
2774 browser->pstack = pstack__new(3);
2775 if (browser->pstack == NULL)
2776 goto out;
2777
2778 ui_helpline__push(helpline);
2779
2780 memset(options, 0, sizeof(options));
2781 memset(actions, 0, sizeof(actions));
2782
2783 if (symbol_conf.col_width_list_str)
2784 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2785
2786 while (1) {
2787 struct thread *thread = NULL;
2788 struct map *map = NULL;
2789 int choice = 0;
2790 int socked_id = -1;
2791
2792 nr_options = 0;
2793
2794 key = hist_browser__run(browser, helpline);
2795
2796 if (browser->he_selection != NULL) {
2797 thread = hist_browser__selected_thread(browser);
2798 map = browser->selection->map;
2799 socked_id = browser->he_selection->socket;
2800 }
2801 switch (key) {
2802 case K_TAB:
2803 case K_UNTAB:
2804 if (nr_events == 1)
2805 continue;
2806 /*
2807 * Exit the browser, let hists__browser_tree
2808 * go to the next or previous
2809 */
2810 goto out_free_stack;
2811 case 'a':
2812 if (!hists__has(hists, sym)) {
2813 ui_browser__warning(&browser->b, delay_secs * 2,
2814 "Annotation is only available for symbolic views, "
2815 "include \"sym*\" in --sort to use it.");
2816 continue;
2817 }
2818
2819 if (browser->selection == NULL ||
2820 browser->selection->sym == NULL ||
2821 browser->selection->map->dso->annotate_warned)
2822 continue;
2823
2824 actions->ms.map = browser->selection->map;
2825 actions->ms.sym = browser->selection->sym;
2826 do_annotate(browser, actions);
2827 continue;
2828 case 'P':
2829 hist_browser__dump(browser);
2830 continue;
2831 case 'd':
2832 actions->ms.map = map;
2833 do_zoom_dso(browser, actions);
2834 continue;
2835 case 'V':
2836 browser->show_dso = !browser->show_dso;
2837 continue;
2838 case 't':
2839 actions->thread = thread;
2840 do_zoom_thread(browser, actions);
2841 continue;
2842 case 'S':
2843 actions->socket = socked_id;
2844 do_zoom_socket(browser, actions);
2845 continue;
2846 case '/':
2847 if (ui_browser__input_window("Symbol to show",
2848 "Please enter the name of symbol you want to see.\n"
2849 "To remove the filter later, press / + ENTER.",
2850 buf, "ENTER: OK, ESC: Cancel",
2851 delay_secs * 2) == K_ENTER) {
2852 hists->symbol_filter_str = *buf ? buf : NULL;
2853 hists__filter_by_symbol(hists);
2854 hist_browser__reset(browser);
2855 }
2856 continue;
2857 case 'r':
2858 if (is_report_browser(hbt)) {
2859 actions->thread = NULL;
2860 actions->ms.sym = NULL;
2861 do_run_script(browser, actions);
2862 }
2863 continue;
2864 case 's':
2865 if (is_report_browser(hbt)) {
2866 key = do_switch_data(browser, actions);
2867 if (key == K_SWITCH_INPUT_DATA)
2868 goto out_free_stack;
2869 }
2870 continue;
2871 case 'i':
2872 /* env->arch is NULL for live-mode (i.e. perf top) */
2873 if (env->arch)
2874 tui__header_window(env);
2875 continue;
2876 case 'F':
2877 symbol_conf.filter_relative ^= 1;
2878 continue;
2879 case 'z':
2880 if (!is_report_browser(hbt)) {
2881 struct perf_top *top = hbt->arg;
2882
2883 top->zero = !top->zero;
2884 }
2885 continue;
2886 case 'L':
2887 if (ui_browser__input_window("Percent Limit",
2888 "Please enter the value you want to hide entries under that percent.",
2889 buf, "ENTER: OK, ESC: Cancel",
2890 delay_secs * 2) == K_ENTER) {
2891 char *end;
2892 double new_percent = strtod(buf, &end);
2893
2894 if (new_percent < 0 || new_percent > 100) {
2895 ui_browser__warning(&browser->b, delay_secs * 2,
2896 "Invalid percent: %.2f", new_percent);
2897 continue;
2898 }
2899
2900 hist_browser__update_percent_limit(browser, new_percent);
2901 hist_browser__reset(browser);
2902 }
2903 continue;
2904 case K_F1:
2905 case 'h':
2906 case '?':
2907 ui_browser__help_window(&browser->b,
2908 is_report_browser(hbt) ? report_help : top_help);
2909 continue;
2910 case K_ENTER:
2911 case K_RIGHT:
2912 case 'm':
2913 /* menu */
2914 break;
2915 case K_ESC:
2916 case K_LEFT: {
2917 const void *top;
2918
2919 if (pstack__empty(browser->pstack)) {
2920 /*
2921 * Go back to the perf_evsel_menu__run or other user
2922 */
2923 if (left_exits)
2924 goto out_free_stack;
2925
2926 if (key == K_ESC &&
2927 ui_browser__dialog_yesno(&browser->b,
2928 "Do you really want to exit?"))
2929 goto out_free_stack;
2930
2931 continue;
2932 }
2933 top = pstack__peek(browser->pstack);
2934 if (top == &browser->hists->dso_filter) {
2935 /*
2936 * No need to set actions->dso here since
2937 * it's just to remove the current filter.
2938 * Ditto for thread below.
2939 */
2940 do_zoom_dso(browser, actions);
2941 } else if (top == &browser->hists->thread_filter) {
2942 do_zoom_thread(browser, actions);
2943 } else if (top == &browser->hists->socket_filter) {
2944 do_zoom_socket(browser, actions);
2945 }
2946 continue;
2947 }
2948 case 'q':
2949 case CTRL('c'):
2950 goto out_free_stack;
2951 case 'f':
2952 if (!is_report_browser(hbt)) {
2953 struct perf_top *top = hbt->arg;
2954
2955 perf_evlist__toggle_enable(top->evlist);
2956 /*
2957 * No need to refresh, resort/decay histogram
2958 * entries if we are not collecting samples:
2959 */
2960 if (top->evlist->enabled) {
2961 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2962 hbt->refresh = delay_secs;
2963 } else {
2964 helpline = "Press 'f' again to re-enable the events";
2965 hbt->refresh = 0;
2966 }
2967 continue;
2968 }
2969 /* Fall thru */
2970 default:
2971 helpline = "Press '?' for help on key bindings";
2972 continue;
2973 }
2974
2975 if (!hists__has(hists, sym) || browser->selection == NULL)
2976 goto skip_annotation;
2977
2978 if (sort__mode == SORT_MODE__BRANCH) {
2979 bi = browser->he_selection->branch_info;
2980
2981 if (bi == NULL)
2982 goto skip_annotation;
2983
2984 nr_options += add_annotate_opt(browser,
2985 &actions[nr_options],
2986 &options[nr_options],
2987 bi->from.map,
2988 bi->from.sym);
2989 if (bi->to.sym != bi->from.sym)
2990 nr_options += add_annotate_opt(browser,
2991 &actions[nr_options],
2992 &options[nr_options],
2993 bi->to.map,
2994 bi->to.sym);
2995 } else {
2996 nr_options += add_annotate_opt(browser,
2997 &actions[nr_options],
2998 &options[nr_options],
2999 browser->selection->map,
3000 browser->selection->sym);
3001 }
3002 skip_annotation:
3003 nr_options += add_thread_opt(browser, &actions[nr_options],
3004 &options[nr_options], thread);
3005 nr_options += add_dso_opt(browser, &actions[nr_options],
3006 &options[nr_options], map);
3007 nr_options += add_map_opt(browser, &actions[nr_options],
3008 &options[nr_options],
3009 browser->selection ?
3010 browser->selection->map : NULL);
3011 nr_options += add_socket_opt(browser, &actions[nr_options],
3012 &options[nr_options],
3013 socked_id);
3014 /* perf script support */
3015 if (!is_report_browser(hbt))
3016 goto skip_scripting;
3017
3018 if (browser->he_selection) {
3019 if (hists__has(hists, thread) && thread) {
3020 nr_options += add_script_opt(browser,
3021 &actions[nr_options],
3022 &options[nr_options],
3023 thread, NULL);
3024 }
3025 /*
3026 * Note that browser->selection != NULL
3027 * when browser->he_selection is not NULL,
3028 * so we don't need to check browser->selection
3029 * before fetching browser->selection->sym like what
3030 * we do before fetching browser->selection->map.
3031 *
3032 * See hist_browser__show_entry.
3033 */
3034 if (hists__has(hists, sym) && browser->selection->sym) {
3035 nr_options += add_script_opt(browser,
3036 &actions[nr_options],
3037 &options[nr_options],
3038 NULL, browser->selection->sym);
3039 }
3040 }
3041 nr_options += add_script_opt(browser, &actions[nr_options],
3042 &options[nr_options], NULL, NULL);
3043 nr_options += add_switch_opt(browser, &actions[nr_options],
3044 &options[nr_options]);
3045 skip_scripting:
3046 nr_options += add_exit_opt(browser, &actions[nr_options],
3047 &options[nr_options]);
3048
3049 do {
3050 struct popup_action *act;
3051
3052 choice = ui__popup_menu(nr_options, options);
3053 if (choice == -1 || choice >= nr_options)
3054 break;
3055
3056 act = &actions[choice];
3057 key = act->fn(browser, act);
3058 } while (key == 1);
3059
3060 if (key == K_SWITCH_INPUT_DATA)
3061 break;
3062 }
3063 out_free_stack:
3064 pstack__delete(browser->pstack);
3065 out:
3066 hist_browser__delete(browser);
3067 free_popup_options(options, MAX_OPTIONS);
3068 return key;
3069 }
3070
3071 struct perf_evsel_menu {
3072 struct ui_browser b;
3073 struct perf_evsel *selection;
3074 bool lost_events, lost_events_warned;
3075 float min_pcnt;
3076 struct perf_env *env;
3077 };
3078
3079 static void perf_evsel_menu__write(struct ui_browser *browser,
3080 void *entry, int row)
3081 {
3082 struct perf_evsel_menu *menu = container_of(browser,
3083 struct perf_evsel_menu, b);
3084 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3085 struct hists *hists = evsel__hists(evsel);
3086 bool current_entry = ui_browser__is_current_entry(browser, row);
3087 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3088 const char *ev_name = perf_evsel__name(evsel);
3089 char bf[256], unit;
3090 const char *warn = " ";
3091 size_t printed;
3092
3093 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3094 HE_COLORSET_NORMAL);
3095
3096 if (perf_evsel__is_group_event(evsel)) {
3097 struct perf_evsel *pos;
3098
3099 ev_name = perf_evsel__group_name(evsel);
3100
3101 for_each_group_member(pos, evsel) {
3102 struct hists *pos_hists = evsel__hists(pos);
3103 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3104 }
3105 }
3106
3107 nr_events = convert_unit(nr_events, &unit);
3108 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3109 unit, unit == ' ' ? "" : " ", ev_name);
3110 ui_browser__printf(browser, "%s", bf);
3111
3112 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3113 if (nr_events != 0) {
3114 menu->lost_events = true;
3115 if (!current_entry)
3116 ui_browser__set_color(browser, HE_COLORSET_TOP);
3117 nr_events = convert_unit(nr_events, &unit);
3118 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3119 nr_events, unit, unit == ' ' ? "" : " ");
3120 warn = bf;
3121 }
3122
3123 ui_browser__write_nstring(browser, warn, browser->width - printed);
3124
3125 if (current_entry)
3126 menu->selection = evsel;
3127 }
3128
3129 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3130 int nr_events, const char *help,
3131 struct hist_browser_timer *hbt)
3132 {
3133 struct perf_evlist *evlist = menu->b.priv;
3134 struct perf_evsel *pos;
3135 const char *title = "Available samples";
3136 int delay_secs = hbt ? hbt->refresh : 0;
3137 int key;
3138
3139 if (ui_browser__show(&menu->b, title,
3140 "ESC: exit, ENTER|->: Browse histograms") < 0)
3141 return -1;
3142
3143 while (1) {
3144 key = ui_browser__run(&menu->b, delay_secs);
3145
3146 switch (key) {
3147 case K_TIMER:
3148 hbt->timer(hbt->arg);
3149
3150 if (!menu->lost_events_warned && menu->lost_events) {
3151 ui_browser__warn_lost_events(&menu->b);
3152 menu->lost_events_warned = true;
3153 }
3154 continue;
3155 case K_RIGHT:
3156 case K_ENTER:
3157 if (!menu->selection)
3158 continue;
3159 pos = menu->selection;
3160 browse_hists:
3161 perf_evlist__set_selected(evlist, pos);
3162 /*
3163 * Give the calling tool a chance to populate the non
3164 * default evsel resorted hists tree.
3165 */
3166 if (hbt)
3167 hbt->timer(hbt->arg);
3168 key = perf_evsel__hists_browse(pos, nr_events, help,
3169 true, hbt,
3170 menu->min_pcnt,
3171 menu->env);
3172 ui_browser__show_title(&menu->b, title);
3173 switch (key) {
3174 case K_TAB:
3175 if (pos->node.next == &evlist->entries)
3176 pos = perf_evlist__first(evlist);
3177 else
3178 pos = perf_evsel__next(pos);
3179 goto browse_hists;
3180 case K_UNTAB:
3181 if (pos->node.prev == &evlist->entries)
3182 pos = perf_evlist__last(evlist);
3183 else
3184 pos = perf_evsel__prev(pos);
3185 goto browse_hists;
3186 case K_SWITCH_INPUT_DATA:
3187 case 'q':
3188 case CTRL('c'):
3189 goto out;
3190 case K_ESC:
3191 default:
3192 continue;
3193 }
3194 case K_LEFT:
3195 continue;
3196 case K_ESC:
3197 if (!ui_browser__dialog_yesno(&menu->b,
3198 "Do you really want to exit?"))
3199 continue;
3200 /* Fall thru */
3201 case 'q':
3202 case CTRL('c'):
3203 goto out;
3204 default:
3205 continue;
3206 }
3207 }
3208
3209 out:
3210 ui_browser__hide(&menu->b);
3211 return key;
3212 }
3213
3214 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3215 void *entry)
3216 {
3217 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3218
3219 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3220 return true;
3221
3222 return false;
3223 }
3224
3225 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3226 int nr_entries, const char *help,
3227 struct hist_browser_timer *hbt,
3228 float min_pcnt,
3229 struct perf_env *env)
3230 {
3231 struct perf_evsel *pos;
3232 struct perf_evsel_menu menu = {
3233 .b = {
3234 .entries = &evlist->entries,
3235 .refresh = ui_browser__list_head_refresh,
3236 .seek = ui_browser__list_head_seek,
3237 .write = perf_evsel_menu__write,
3238 .filter = filter_group_entries,
3239 .nr_entries = nr_entries,
3240 .priv = evlist,
3241 },
3242 .min_pcnt = min_pcnt,
3243 .env = env,
3244 };
3245
3246 ui_helpline__push("Press ESC to exit");
3247
3248 evlist__for_each_entry(evlist, pos) {
3249 const char *ev_name = perf_evsel__name(pos);
3250 size_t line_len = strlen(ev_name) + 7;
3251
3252 if (menu.b.width < line_len)
3253 menu.b.width = line_len;
3254 }
3255
3256 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3257 }
3258
3259 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3260 struct hist_browser_timer *hbt,
3261 float min_pcnt,
3262 struct perf_env *env)
3263 {
3264 int nr_entries = evlist->nr_entries;
3265
3266 single_entry:
3267 if (nr_entries == 1) {
3268 struct perf_evsel *first = perf_evlist__first(evlist);
3269
3270 return perf_evsel__hists_browse(first, nr_entries, help,
3271 false, hbt, min_pcnt,
3272 env);
3273 }
3274
3275 if (symbol_conf.event_group) {
3276 struct perf_evsel *pos;
3277
3278 nr_entries = 0;
3279 evlist__for_each_entry(evlist, pos) {
3280 if (perf_evsel__is_group_leader(pos))
3281 nr_entries++;
3282 }
3283
3284 if (nr_entries == 1)
3285 goto single_entry;
3286 }
3287
3288 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3289 hbt, min_pcnt, env);
3290 }