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