Merge branch 'enable-devices' into omap-for-v4.5/fixes
[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(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(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 struct hpp_arg {
914 struct ui_browser *b;
915 char folded_sign;
916 bool current_entry;
917 };
918
919 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
920 {
921 struct hpp_arg *arg = hpp->ptr;
922 int ret, len;
923 va_list args;
924 double percent;
925
926 va_start(args, fmt);
927 len = va_arg(args, int);
928 percent = va_arg(args, double);
929 va_end(args);
930
931 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
932
933 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
934 ui_browser__printf(arg->b, "%s", hpp->buf);
935
936 advance_hpp(hpp, ret);
937 return ret;
938 }
939
940 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
941 static u64 __hpp_get_##_field(struct hist_entry *he) \
942 { \
943 return he->stat._field; \
944 } \
945 \
946 static int \
947 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
948 struct perf_hpp *hpp, \
949 struct hist_entry *he) \
950 { \
951 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
952 __hpp__slsmg_color_printf, true); \
953 }
954
955 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
956 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
957 { \
958 return he->stat_acc->_field; \
959 } \
960 \
961 static int \
962 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
963 struct perf_hpp *hpp, \
964 struct hist_entry *he) \
965 { \
966 if (!symbol_conf.cumulate_callchain) { \
967 struct hpp_arg *arg = hpp->ptr; \
968 int len = fmt->user_len ?: fmt->len; \
969 int ret = scnprintf(hpp->buf, hpp->size, \
970 "%*s", len, "N/A"); \
971 ui_browser__printf(arg->b, "%s", hpp->buf); \
972 \
973 return ret; \
974 } \
975 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
976 " %*.2f%%", __hpp__slsmg_color_printf, true); \
977 }
978
979 __HPP_COLOR_PERCENT_FN(overhead, period)
980 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
981 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
982 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
983 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
984 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
985
986 #undef __HPP_COLOR_PERCENT_FN
987 #undef __HPP_COLOR_ACC_PERCENT_FN
988
989 void hist_browser__init_hpp(void)
990 {
991 perf_hpp__format[PERF_HPP__OVERHEAD].color =
992 hist_browser__hpp_color_overhead;
993 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
994 hist_browser__hpp_color_overhead_sys;
995 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
996 hist_browser__hpp_color_overhead_us;
997 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
998 hist_browser__hpp_color_overhead_guest_sys;
999 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1000 hist_browser__hpp_color_overhead_guest_us;
1001 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1002 hist_browser__hpp_color_overhead_acc;
1003 }
1004
1005 static int hist_browser__show_entry(struct hist_browser *browser,
1006 struct hist_entry *entry,
1007 unsigned short row)
1008 {
1009 char s[256];
1010 int printed = 0;
1011 int width = browser->b.width;
1012 char folded_sign = ' ';
1013 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1014 off_t row_offset = entry->row_offset;
1015 bool first = true;
1016 struct perf_hpp_fmt *fmt;
1017
1018 if (current_entry) {
1019 browser->he_selection = entry;
1020 browser->selection = &entry->ms;
1021 }
1022
1023 if (symbol_conf.use_callchain) {
1024 hist_entry__init_have_children(entry);
1025 folded_sign = hist_entry__folded(entry);
1026 }
1027
1028 if (row_offset == 0) {
1029 struct hpp_arg arg = {
1030 .b = &browser->b,
1031 .folded_sign = folded_sign,
1032 .current_entry = current_entry,
1033 };
1034 struct perf_hpp hpp = {
1035 .buf = s,
1036 .size = sizeof(s),
1037 .ptr = &arg,
1038 };
1039 int column = 0;
1040
1041 hist_browser__gotorc(browser, row, 0);
1042
1043 perf_hpp__for_each_format(fmt) {
1044 if (perf_hpp__should_skip(fmt, entry->hists) ||
1045 column++ < browser->b.horiz_scroll)
1046 continue;
1047
1048 if (current_entry && browser->b.navkeypressed) {
1049 ui_browser__set_color(&browser->b,
1050 HE_COLORSET_SELECTED);
1051 } else {
1052 ui_browser__set_color(&browser->b,
1053 HE_COLORSET_NORMAL);
1054 }
1055
1056 if (first) {
1057 if (symbol_conf.use_callchain) {
1058 ui_browser__printf(&browser->b, "%c ", folded_sign);
1059 width -= 2;
1060 }
1061 first = false;
1062 } else {
1063 ui_browser__printf(&browser->b, " ");
1064 width -= 2;
1065 }
1066
1067 if (fmt->color) {
1068 width -= fmt->color(fmt, &hpp, entry);
1069 } else {
1070 width -= fmt->entry(fmt, &hpp, entry);
1071 ui_browser__printf(&browser->b, "%s", s);
1072 }
1073 }
1074
1075 /* The scroll bar isn't being used */
1076 if (!browser->b.navkeypressed)
1077 width += 1;
1078
1079 ui_browser__write_nstring(&browser->b, "", width);
1080
1081 ++row;
1082 ++printed;
1083 } else
1084 --row_offset;
1085
1086 if (folded_sign == '-' && row != browser->b.rows) {
1087 u64 total = hists__total_period(entry->hists);
1088 struct callchain_print_arg arg = {
1089 .row_offset = row_offset,
1090 .is_current_entry = current_entry,
1091 };
1092
1093 if (callchain_param.mode == CHAIN_GRAPH_REL) {
1094 if (symbol_conf.cumulate_callchain)
1095 total = entry->stat_acc->period;
1096 else
1097 total = entry->stat.period;
1098 }
1099
1100 if (callchain_param.mode == CHAIN_FLAT) {
1101 printed += hist_browser__show_callchain_flat(browser,
1102 &entry->sorted_chain, row, total,
1103 hist_browser__show_callchain_entry, &arg,
1104 hist_browser__check_output_full);
1105 } else if (callchain_param.mode == CHAIN_FOLDED) {
1106 printed += hist_browser__show_callchain_folded(browser,
1107 &entry->sorted_chain, row, total,
1108 hist_browser__show_callchain_entry, &arg,
1109 hist_browser__check_output_full);
1110 } else {
1111 printed += hist_browser__show_callchain(browser,
1112 &entry->sorted_chain, 1, row, total,
1113 hist_browser__show_callchain_entry, &arg,
1114 hist_browser__check_output_full);
1115 }
1116
1117 if (arg.is_current_entry)
1118 browser->he_selection = entry;
1119 }
1120
1121 return printed;
1122 }
1123
1124 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1125 {
1126 advance_hpp(hpp, inc);
1127 return hpp->size <= 0;
1128 }
1129
1130 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1131 {
1132 struct hists *hists = browser->hists;
1133 struct perf_hpp dummy_hpp = {
1134 .buf = buf,
1135 .size = size,
1136 };
1137 struct perf_hpp_fmt *fmt;
1138 size_t ret = 0;
1139 int column = 0;
1140
1141 if (symbol_conf.use_callchain) {
1142 ret = scnprintf(buf, size, " ");
1143 if (advance_hpp_check(&dummy_hpp, ret))
1144 return ret;
1145 }
1146
1147 perf_hpp__for_each_format(fmt) {
1148 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1149 continue;
1150
1151 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1152 if (advance_hpp_check(&dummy_hpp, ret))
1153 break;
1154
1155 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1156 if (advance_hpp_check(&dummy_hpp, ret))
1157 break;
1158 }
1159
1160 return ret;
1161 }
1162
1163 static void hist_browser__show_headers(struct hist_browser *browser)
1164 {
1165 char headers[1024];
1166
1167 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
1168 ui_browser__gotorc(&browser->b, 0, 0);
1169 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1170 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1171 }
1172
1173 static void ui_browser__hists_init_top(struct ui_browser *browser)
1174 {
1175 if (browser->top == NULL) {
1176 struct hist_browser *hb;
1177
1178 hb = container_of(browser, struct hist_browser, b);
1179 browser->top = rb_first(&hb->hists->entries);
1180 }
1181 }
1182
1183 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1184 {
1185 unsigned row = 0;
1186 u16 header_offset = 0;
1187 struct rb_node *nd;
1188 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1189
1190 if (hb->show_headers) {
1191 hist_browser__show_headers(hb);
1192 header_offset = 1;
1193 }
1194
1195 ui_browser__hists_init_top(browser);
1196 hb->he_selection = NULL;
1197 hb->selection = NULL;
1198
1199 for (nd = browser->top; nd; nd = rb_next(nd)) {
1200 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1201 float percent;
1202
1203 if (h->filtered)
1204 continue;
1205
1206 percent = hist_entry__get_percent_limit(h);
1207 if (percent < hb->min_pcnt)
1208 continue;
1209
1210 row += hist_browser__show_entry(hb, h, row);
1211 if (row == browser->rows)
1212 break;
1213 }
1214
1215 return row + header_offset;
1216 }
1217
1218 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1219 float min_pcnt)
1220 {
1221 while (nd != NULL) {
1222 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1223 float percent = hist_entry__get_percent_limit(h);
1224
1225 if (!h->filtered && percent >= min_pcnt)
1226 return nd;
1227
1228 nd = rb_next(nd);
1229 }
1230
1231 return NULL;
1232 }
1233
1234 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1235 float min_pcnt)
1236 {
1237 while (nd != NULL) {
1238 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1239 float percent = hist_entry__get_percent_limit(h);
1240
1241 if (!h->filtered && percent >= min_pcnt)
1242 return nd;
1243
1244 nd = rb_prev(nd);
1245 }
1246
1247 return NULL;
1248 }
1249
1250 static void ui_browser__hists_seek(struct ui_browser *browser,
1251 off_t offset, int whence)
1252 {
1253 struct hist_entry *h;
1254 struct rb_node *nd;
1255 bool first = true;
1256 struct hist_browser *hb;
1257
1258 hb = container_of(browser, struct hist_browser, b);
1259
1260 if (browser->nr_entries == 0)
1261 return;
1262
1263 ui_browser__hists_init_top(browser);
1264
1265 switch (whence) {
1266 case SEEK_SET:
1267 nd = hists__filter_entries(rb_first(browser->entries),
1268 hb->min_pcnt);
1269 break;
1270 case SEEK_CUR:
1271 nd = browser->top;
1272 goto do_offset;
1273 case SEEK_END:
1274 nd = hists__filter_prev_entries(rb_last(browser->entries),
1275 hb->min_pcnt);
1276 first = false;
1277 break;
1278 default:
1279 return;
1280 }
1281
1282 /*
1283 * Moves not relative to the first visible entry invalidates its
1284 * row_offset:
1285 */
1286 h = rb_entry(browser->top, struct hist_entry, rb_node);
1287 h->row_offset = 0;
1288
1289 /*
1290 * Here we have to check if nd is expanded (+), if it is we can't go
1291 * the next top level hist_entry, instead we must compute an offset of
1292 * what _not_ to show and not change the first visible entry.
1293 *
1294 * This offset increments when we are going from top to bottom and
1295 * decreases when we're going from bottom to top.
1296 *
1297 * As we don't have backpointers to the top level in the callchains
1298 * structure, we need to always print the whole hist_entry callchain,
1299 * skipping the first ones that are before the first visible entry
1300 * and stop when we printed enough lines to fill the screen.
1301 */
1302 do_offset:
1303 if (!nd)
1304 return;
1305
1306 if (offset > 0) {
1307 do {
1308 h = rb_entry(nd, struct hist_entry, rb_node);
1309 if (h->unfolded) {
1310 u16 remaining = h->nr_rows - h->row_offset;
1311 if (offset > remaining) {
1312 offset -= remaining;
1313 h->row_offset = 0;
1314 } else {
1315 h->row_offset += offset;
1316 offset = 0;
1317 browser->top = nd;
1318 break;
1319 }
1320 }
1321 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1322 if (nd == NULL)
1323 break;
1324 --offset;
1325 browser->top = nd;
1326 } while (offset != 0);
1327 } else if (offset < 0) {
1328 while (1) {
1329 h = rb_entry(nd, struct hist_entry, rb_node);
1330 if (h->unfolded) {
1331 if (first) {
1332 if (-offset > h->row_offset) {
1333 offset += h->row_offset;
1334 h->row_offset = 0;
1335 } else {
1336 h->row_offset += offset;
1337 offset = 0;
1338 browser->top = nd;
1339 break;
1340 }
1341 } else {
1342 if (-offset > h->nr_rows) {
1343 offset += h->nr_rows;
1344 h->row_offset = 0;
1345 } else {
1346 h->row_offset = h->nr_rows + offset;
1347 offset = 0;
1348 browser->top = nd;
1349 break;
1350 }
1351 }
1352 }
1353
1354 nd = hists__filter_prev_entries(rb_prev(nd),
1355 hb->min_pcnt);
1356 if (nd == NULL)
1357 break;
1358 ++offset;
1359 browser->top = nd;
1360 if (offset == 0) {
1361 /*
1362 * Last unfiltered hist_entry, check if it is
1363 * unfolded, if it is then we should have
1364 * row_offset at its last entry.
1365 */
1366 h = rb_entry(nd, struct hist_entry, rb_node);
1367 if (h->unfolded)
1368 h->row_offset = h->nr_rows;
1369 break;
1370 }
1371 first = false;
1372 }
1373 } else {
1374 browser->top = nd;
1375 h = rb_entry(nd, struct hist_entry, rb_node);
1376 h->row_offset = 0;
1377 }
1378 }
1379
1380 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1381 struct hist_entry *he, FILE *fp)
1382 {
1383 u64 total = hists__total_period(he->hists);
1384 struct callchain_print_arg arg = {
1385 .fp = fp,
1386 };
1387
1388 if (symbol_conf.cumulate_callchain)
1389 total = he->stat_acc->period;
1390
1391 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1392 hist_browser__fprintf_callchain_entry, &arg,
1393 hist_browser__check_dump_full);
1394 return arg.printed;
1395 }
1396
1397 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1398 struct hist_entry *he, FILE *fp)
1399 {
1400 char s[8192];
1401 int printed = 0;
1402 char folded_sign = ' ';
1403 struct perf_hpp hpp = {
1404 .buf = s,
1405 .size = sizeof(s),
1406 };
1407 struct perf_hpp_fmt *fmt;
1408 bool first = true;
1409 int ret;
1410
1411 if (symbol_conf.use_callchain)
1412 folded_sign = hist_entry__folded(he);
1413
1414 if (symbol_conf.use_callchain)
1415 printed += fprintf(fp, "%c ", folded_sign);
1416
1417 perf_hpp__for_each_format(fmt) {
1418 if (perf_hpp__should_skip(fmt, he->hists))
1419 continue;
1420
1421 if (!first) {
1422 ret = scnprintf(hpp.buf, hpp.size, " ");
1423 advance_hpp(&hpp, ret);
1424 } else
1425 first = false;
1426
1427 ret = fmt->entry(fmt, &hpp, he);
1428 advance_hpp(&hpp, ret);
1429 }
1430 printed += fprintf(fp, "%s\n", rtrim(s));
1431
1432 if (folded_sign == '-')
1433 printed += hist_browser__fprintf_callchain(browser, he, fp);
1434
1435 return printed;
1436 }
1437
1438 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1439 {
1440 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1441 browser->min_pcnt);
1442 int printed = 0;
1443
1444 while (nd) {
1445 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1446
1447 printed += hist_browser__fprintf_entry(browser, h, fp);
1448 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1449 }
1450
1451 return printed;
1452 }
1453
1454 static int hist_browser__dump(struct hist_browser *browser)
1455 {
1456 char filename[64];
1457 FILE *fp;
1458
1459 while (1) {
1460 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1461 if (access(filename, F_OK))
1462 break;
1463 /*
1464 * XXX: Just an arbitrary lazy upper limit
1465 */
1466 if (++browser->print_seq == 8192) {
1467 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1468 return -1;
1469 }
1470 }
1471
1472 fp = fopen(filename, "w");
1473 if (fp == NULL) {
1474 char bf[64];
1475 const char *err = strerror_r(errno, bf, sizeof(bf));
1476 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1477 return -1;
1478 }
1479
1480 ++browser->print_seq;
1481 hist_browser__fprintf(browser, fp);
1482 fclose(fp);
1483 ui_helpline__fpush("%s written!", filename);
1484
1485 return 0;
1486 }
1487
1488 static struct hist_browser *hist_browser__new(struct hists *hists,
1489 struct hist_browser_timer *hbt,
1490 struct perf_env *env)
1491 {
1492 struct hist_browser *browser = zalloc(sizeof(*browser));
1493
1494 if (browser) {
1495 browser->hists = hists;
1496 browser->b.refresh = hist_browser__refresh;
1497 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1498 browser->b.seek = ui_browser__hists_seek;
1499 browser->b.use_navkeypressed = true;
1500 browser->show_headers = symbol_conf.show_hist_headers;
1501 browser->hbt = hbt;
1502 browser->env = env;
1503 }
1504
1505 return browser;
1506 }
1507
1508 static void hist_browser__delete(struct hist_browser *browser)
1509 {
1510 free(browser);
1511 }
1512
1513 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1514 {
1515 return browser->he_selection;
1516 }
1517
1518 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1519 {
1520 return browser->he_selection->thread;
1521 }
1522
1523 /* Check whether the browser is for 'top' or 'report' */
1524 static inline bool is_report_browser(void *timer)
1525 {
1526 return timer == NULL;
1527 }
1528
1529 static int hists__browser_title(struct hists *hists,
1530 struct hist_browser_timer *hbt,
1531 char *bf, size_t size)
1532 {
1533 char unit;
1534 int printed;
1535 const struct dso *dso = hists->dso_filter;
1536 const struct thread *thread = hists->thread_filter;
1537 int socket_id = hists->socket_filter;
1538 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1539 u64 nr_events = hists->stats.total_period;
1540 struct perf_evsel *evsel = hists_to_evsel(hists);
1541 const char *ev_name = perf_evsel__name(evsel);
1542 char buf[512];
1543 size_t buflen = sizeof(buf);
1544 char ref[30] = " show reference callgraph, ";
1545 bool enable_ref = false;
1546
1547 if (symbol_conf.filter_relative) {
1548 nr_samples = hists->stats.nr_non_filtered_samples;
1549 nr_events = hists->stats.total_non_filtered_period;
1550 }
1551
1552 if (perf_evsel__is_group_event(evsel)) {
1553 struct perf_evsel *pos;
1554
1555 perf_evsel__group_desc(evsel, buf, buflen);
1556 ev_name = buf;
1557
1558 for_each_group_member(pos, evsel) {
1559 struct hists *pos_hists = evsel__hists(pos);
1560
1561 if (symbol_conf.filter_relative) {
1562 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1563 nr_events += pos_hists->stats.total_non_filtered_period;
1564 } else {
1565 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1566 nr_events += pos_hists->stats.total_period;
1567 }
1568 }
1569 }
1570
1571 if (symbol_conf.show_ref_callgraph &&
1572 strstr(ev_name, "call-graph=no"))
1573 enable_ref = true;
1574 nr_samples = convert_unit(nr_samples, &unit);
1575 printed = scnprintf(bf, size,
1576 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1577 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1578
1579
1580 if (hists->uid_filter_str)
1581 printed += snprintf(bf + printed, size - printed,
1582 ", UID: %s", hists->uid_filter_str);
1583 if (thread)
1584 printed += scnprintf(bf + printed, size - printed,
1585 ", Thread: %s(%d)",
1586 (thread->comm_set ? thread__comm_str(thread) : ""),
1587 thread->tid);
1588 if (dso)
1589 printed += scnprintf(bf + printed, size - printed,
1590 ", DSO: %s", dso->short_name);
1591 if (socket_id > -1)
1592 printed += scnprintf(bf + printed, size - printed,
1593 ", Processor Socket: %d", socket_id);
1594 if (!is_report_browser(hbt)) {
1595 struct perf_top *top = hbt->arg;
1596
1597 if (top->zero)
1598 printed += scnprintf(bf + printed, size - printed, " [z]");
1599 }
1600
1601 return printed;
1602 }
1603
1604 static inline void free_popup_options(char **options, int n)
1605 {
1606 int i;
1607
1608 for (i = 0; i < n; ++i)
1609 zfree(&options[i]);
1610 }
1611
1612 /*
1613 * Only runtime switching of perf data file will make "input_name" point
1614 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1615 * whether we need to call free() for current "input_name" during the switch.
1616 */
1617 static bool is_input_name_malloced = false;
1618
1619 static int switch_data_file(void)
1620 {
1621 char *pwd, *options[32], *abs_path[32], *tmp;
1622 DIR *pwd_dir;
1623 int nr_options = 0, choice = -1, ret = -1;
1624 struct dirent *dent;
1625
1626 pwd = getenv("PWD");
1627 if (!pwd)
1628 return ret;
1629
1630 pwd_dir = opendir(pwd);
1631 if (!pwd_dir)
1632 return ret;
1633
1634 memset(options, 0, sizeof(options));
1635 memset(options, 0, sizeof(abs_path));
1636
1637 while ((dent = readdir(pwd_dir))) {
1638 char path[PATH_MAX];
1639 u64 magic;
1640 char *name = dent->d_name;
1641 FILE *file;
1642
1643 if (!(dent->d_type == DT_REG))
1644 continue;
1645
1646 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1647
1648 file = fopen(path, "r");
1649 if (!file)
1650 continue;
1651
1652 if (fread(&magic, 1, 8, file) < 8)
1653 goto close_file_and_continue;
1654
1655 if (is_perf_magic(magic)) {
1656 options[nr_options] = strdup(name);
1657 if (!options[nr_options])
1658 goto close_file_and_continue;
1659
1660 abs_path[nr_options] = strdup(path);
1661 if (!abs_path[nr_options]) {
1662 zfree(&options[nr_options]);
1663 ui__warning("Can't search all data files due to memory shortage.\n");
1664 fclose(file);
1665 break;
1666 }
1667
1668 nr_options++;
1669 }
1670
1671 close_file_and_continue:
1672 fclose(file);
1673 if (nr_options >= 32) {
1674 ui__warning("Too many perf data files in PWD!\n"
1675 "Only the first 32 files will be listed.\n");
1676 break;
1677 }
1678 }
1679 closedir(pwd_dir);
1680
1681 if (nr_options) {
1682 choice = ui__popup_menu(nr_options, options);
1683 if (choice < nr_options && choice >= 0) {
1684 tmp = strdup(abs_path[choice]);
1685 if (tmp) {
1686 if (is_input_name_malloced)
1687 free((void *)input_name);
1688 input_name = tmp;
1689 is_input_name_malloced = true;
1690 ret = 0;
1691 } else
1692 ui__warning("Data switch failed due to memory shortage!\n");
1693 }
1694 }
1695
1696 free_popup_options(options, nr_options);
1697 free_popup_options(abs_path, nr_options);
1698 return ret;
1699 }
1700
1701 struct popup_action {
1702 struct thread *thread;
1703 struct map_symbol ms;
1704 int socket;
1705
1706 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1707 };
1708
1709 static int
1710 do_annotate(struct hist_browser *browser, struct popup_action *act)
1711 {
1712 struct perf_evsel *evsel;
1713 struct annotation *notes;
1714 struct hist_entry *he;
1715 int err;
1716
1717 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1718 return 0;
1719
1720 notes = symbol__annotation(act->ms.sym);
1721 if (!notes->src)
1722 return 0;
1723
1724 evsel = hists_to_evsel(browser->hists);
1725 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1726 he = hist_browser__selected_entry(browser);
1727 /*
1728 * offer option to annotate the other branch source or target
1729 * (if they exists) when returning from annotate
1730 */
1731 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1732 return 1;
1733
1734 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1735 if (err)
1736 ui_browser__handle_resize(&browser->b);
1737 return 0;
1738 }
1739
1740 static int
1741 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1742 struct popup_action *act, char **optstr,
1743 struct map *map, struct symbol *sym)
1744 {
1745 if (sym == NULL || map->dso->annotate_warned)
1746 return 0;
1747
1748 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1749 return 0;
1750
1751 act->ms.map = map;
1752 act->ms.sym = sym;
1753 act->fn = do_annotate;
1754 return 1;
1755 }
1756
1757 static int
1758 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1759 {
1760 struct thread *thread = act->thread;
1761
1762 if (browser->hists->thread_filter) {
1763 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1764 perf_hpp__set_elide(HISTC_THREAD, false);
1765 thread__zput(browser->hists->thread_filter);
1766 ui_helpline__pop();
1767 } else {
1768 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1769 thread->comm_set ? thread__comm_str(thread) : "",
1770 thread->tid);
1771 browser->hists->thread_filter = thread__get(thread);
1772 perf_hpp__set_elide(HISTC_THREAD, false);
1773 pstack__push(browser->pstack, &browser->hists->thread_filter);
1774 }
1775
1776 hists__filter_by_thread(browser->hists);
1777 hist_browser__reset(browser);
1778 return 0;
1779 }
1780
1781 static int
1782 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1783 char **optstr, struct thread *thread)
1784 {
1785 if (thread == NULL)
1786 return 0;
1787
1788 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1789 browser->hists->thread_filter ? "out of" : "into",
1790 thread->comm_set ? thread__comm_str(thread) : "",
1791 thread->tid) < 0)
1792 return 0;
1793
1794 act->thread = thread;
1795 act->fn = do_zoom_thread;
1796 return 1;
1797 }
1798
1799 static int
1800 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1801 {
1802 struct map *map = act->ms.map;
1803
1804 if (browser->hists->dso_filter) {
1805 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1806 perf_hpp__set_elide(HISTC_DSO, false);
1807 browser->hists->dso_filter = NULL;
1808 ui_helpline__pop();
1809 } else {
1810 if (map == NULL)
1811 return 0;
1812 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1813 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1814 browser->hists->dso_filter = map->dso;
1815 perf_hpp__set_elide(HISTC_DSO, true);
1816 pstack__push(browser->pstack, &browser->hists->dso_filter);
1817 }
1818
1819 hists__filter_by_dso(browser->hists);
1820 hist_browser__reset(browser);
1821 return 0;
1822 }
1823
1824 static int
1825 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1826 char **optstr, struct map *map)
1827 {
1828 if (map == NULL)
1829 return 0;
1830
1831 if (asprintf(optstr, "Zoom %s %s DSO",
1832 browser->hists->dso_filter ? "out of" : "into",
1833 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1834 return 0;
1835
1836 act->ms.map = map;
1837 act->fn = do_zoom_dso;
1838 return 1;
1839 }
1840
1841 static int
1842 do_browse_map(struct hist_browser *browser __maybe_unused,
1843 struct popup_action *act)
1844 {
1845 map__browse(act->ms.map);
1846 return 0;
1847 }
1848
1849 static int
1850 add_map_opt(struct hist_browser *browser __maybe_unused,
1851 struct popup_action *act, char **optstr, struct map *map)
1852 {
1853 if (map == NULL)
1854 return 0;
1855
1856 if (asprintf(optstr, "Browse map details") < 0)
1857 return 0;
1858
1859 act->ms.map = map;
1860 act->fn = do_browse_map;
1861 return 1;
1862 }
1863
1864 static int
1865 do_run_script(struct hist_browser *browser __maybe_unused,
1866 struct popup_action *act)
1867 {
1868 char script_opt[64];
1869 memset(script_opt, 0, sizeof(script_opt));
1870
1871 if (act->thread) {
1872 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1873 thread__comm_str(act->thread));
1874 } else if (act->ms.sym) {
1875 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1876 act->ms.sym->name);
1877 }
1878
1879 script_browse(script_opt);
1880 return 0;
1881 }
1882
1883 static int
1884 add_script_opt(struct hist_browser *browser __maybe_unused,
1885 struct popup_action *act, char **optstr,
1886 struct thread *thread, struct symbol *sym)
1887 {
1888 if (thread) {
1889 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1890 thread__comm_str(thread)) < 0)
1891 return 0;
1892 } else if (sym) {
1893 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1894 sym->name) < 0)
1895 return 0;
1896 } else {
1897 if (asprintf(optstr, "Run scripts for all samples") < 0)
1898 return 0;
1899 }
1900
1901 act->thread = thread;
1902 act->ms.sym = sym;
1903 act->fn = do_run_script;
1904 return 1;
1905 }
1906
1907 static int
1908 do_switch_data(struct hist_browser *browser __maybe_unused,
1909 struct popup_action *act __maybe_unused)
1910 {
1911 if (switch_data_file()) {
1912 ui__warning("Won't switch the data files due to\n"
1913 "no valid data file get selected!\n");
1914 return 0;
1915 }
1916
1917 return K_SWITCH_INPUT_DATA;
1918 }
1919
1920 static int
1921 add_switch_opt(struct hist_browser *browser,
1922 struct popup_action *act, char **optstr)
1923 {
1924 if (!is_report_browser(browser->hbt))
1925 return 0;
1926
1927 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1928 return 0;
1929
1930 act->fn = do_switch_data;
1931 return 1;
1932 }
1933
1934 static int
1935 do_exit_browser(struct hist_browser *browser __maybe_unused,
1936 struct popup_action *act __maybe_unused)
1937 {
1938 return 0;
1939 }
1940
1941 static int
1942 add_exit_opt(struct hist_browser *browser __maybe_unused,
1943 struct popup_action *act, char **optstr)
1944 {
1945 if (asprintf(optstr, "Exit") < 0)
1946 return 0;
1947
1948 act->fn = do_exit_browser;
1949 return 1;
1950 }
1951
1952 static int
1953 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1954 {
1955 if (browser->hists->socket_filter > -1) {
1956 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1957 browser->hists->socket_filter = -1;
1958 perf_hpp__set_elide(HISTC_SOCKET, false);
1959 } else {
1960 browser->hists->socket_filter = act->socket;
1961 perf_hpp__set_elide(HISTC_SOCKET, true);
1962 pstack__push(browser->pstack, &browser->hists->socket_filter);
1963 }
1964
1965 hists__filter_by_socket(browser->hists);
1966 hist_browser__reset(browser);
1967 return 0;
1968 }
1969
1970 static int
1971 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1972 char **optstr, int socket_id)
1973 {
1974 if (socket_id < 0)
1975 return 0;
1976
1977 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1978 (browser->hists->socket_filter > -1) ? "out of" : "into",
1979 socket_id) < 0)
1980 return 0;
1981
1982 act->socket = socket_id;
1983 act->fn = do_zoom_socket;
1984 return 1;
1985 }
1986
1987 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1988 {
1989 u64 nr_entries = 0;
1990 struct rb_node *nd = rb_first(&hb->hists->entries);
1991
1992 if (hb->min_pcnt == 0) {
1993 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1994 return;
1995 }
1996
1997 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1998 nr_entries++;
1999 nd = rb_next(nd);
2000 }
2001
2002 hb->nr_non_filtered_entries = nr_entries;
2003 }
2004
2005 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2006 const char *helpline,
2007 bool left_exits,
2008 struct hist_browser_timer *hbt,
2009 float min_pcnt,
2010 struct perf_env *env)
2011 {
2012 struct hists *hists = evsel__hists(evsel);
2013 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2014 struct branch_info *bi;
2015 #define MAX_OPTIONS 16
2016 char *options[MAX_OPTIONS];
2017 struct popup_action actions[MAX_OPTIONS];
2018 int nr_options = 0;
2019 int key = -1;
2020 char buf[64];
2021 int delay_secs = hbt ? hbt->refresh : 0;
2022 struct perf_hpp_fmt *fmt;
2023
2024 #define HIST_BROWSER_HELP_COMMON \
2025 "h/?/F1 Show this window\n" \
2026 "UP/DOWN/PGUP\n" \
2027 "PGDN/SPACE Navigate\n" \
2028 "q/ESC/CTRL+C Exit browser\n\n" \
2029 "For multiple event sessions:\n\n" \
2030 "TAB/UNTAB Switch events\n\n" \
2031 "For symbolic views (--sort has sym):\n\n" \
2032 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2033 "ESC Zoom out\n" \
2034 "a Annotate current symbol\n" \
2035 "C Collapse all callchains\n" \
2036 "d Zoom into current DSO\n" \
2037 "E Expand all callchains\n" \
2038 "F Toggle percentage of filtered entries\n" \
2039 "H Display column headers\n" \
2040 "m Display context menu\n" \
2041 "S Zoom into current Processor Socket\n" \
2042
2043 /* help messages are sorted by lexical order of the hotkey */
2044 const char report_help[] = HIST_BROWSER_HELP_COMMON
2045 "i Show header information\n"
2046 "P Print histograms to perf.hist.N\n"
2047 "r Run available scripts\n"
2048 "s Switch to another data file in PWD\n"
2049 "t Zoom into current Thread\n"
2050 "V Verbose (DSO names in callchains, etc)\n"
2051 "/ Filter symbol by name";
2052 const char top_help[] = HIST_BROWSER_HELP_COMMON
2053 "P Print histograms to perf.hist.N\n"
2054 "t Zoom into current Thread\n"
2055 "V Verbose (DSO names in callchains, etc)\n"
2056 "z Toggle zeroing of samples\n"
2057 "f Enable/Disable events\n"
2058 "/ Filter symbol by name";
2059
2060 if (browser == NULL)
2061 return -1;
2062
2063 /* reset abort key so that it can get Ctrl-C as a key */
2064 SLang_reset_tty();
2065 SLang_init_tty(0, 0, 0);
2066
2067 if (min_pcnt)
2068 browser->min_pcnt = min_pcnt;
2069 hist_browser__update_nr_entries(browser);
2070
2071 browser->pstack = pstack__new(3);
2072 if (browser->pstack == NULL)
2073 goto out;
2074
2075 ui_helpline__push(helpline);
2076
2077 memset(options, 0, sizeof(options));
2078 memset(actions, 0, sizeof(actions));
2079
2080 perf_hpp__for_each_format(fmt) {
2081 perf_hpp__reset_width(fmt, hists);
2082 /*
2083 * This is done just once, and activates the horizontal scrolling
2084 * code in the ui_browser code, it would be better to have a the
2085 * counter in the perf_hpp code, but I couldn't find doing it here
2086 * works, FIXME by setting this in hist_browser__new, for now, be
2087 * clever 8-)
2088 */
2089 ++browser->b.columns;
2090 }
2091
2092 if (symbol_conf.col_width_list_str)
2093 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2094
2095 while (1) {
2096 struct thread *thread = NULL;
2097 struct map *map = NULL;
2098 int choice = 0;
2099 int socked_id = -1;
2100
2101 nr_options = 0;
2102
2103 key = hist_browser__run(browser, helpline);
2104
2105 if (browser->he_selection != NULL) {
2106 thread = hist_browser__selected_thread(browser);
2107 map = browser->selection->map;
2108 socked_id = browser->he_selection->socket;
2109 }
2110 switch (key) {
2111 case K_TAB:
2112 case K_UNTAB:
2113 if (nr_events == 1)
2114 continue;
2115 /*
2116 * Exit the browser, let hists__browser_tree
2117 * go to the next or previous
2118 */
2119 goto out_free_stack;
2120 case 'a':
2121 if (!sort__has_sym) {
2122 ui_browser__warning(&browser->b, delay_secs * 2,
2123 "Annotation is only available for symbolic views, "
2124 "include \"sym*\" in --sort to use it.");
2125 continue;
2126 }
2127
2128 if (browser->selection == NULL ||
2129 browser->selection->sym == NULL ||
2130 browser->selection->map->dso->annotate_warned)
2131 continue;
2132
2133 actions->ms.map = browser->selection->map;
2134 actions->ms.sym = browser->selection->sym;
2135 do_annotate(browser, actions);
2136 continue;
2137 case 'P':
2138 hist_browser__dump(browser);
2139 continue;
2140 case 'd':
2141 actions->ms.map = map;
2142 do_zoom_dso(browser, actions);
2143 continue;
2144 case 'V':
2145 browser->show_dso = !browser->show_dso;
2146 continue;
2147 case 't':
2148 actions->thread = thread;
2149 do_zoom_thread(browser, actions);
2150 continue;
2151 case 'S':
2152 actions->socket = socked_id;
2153 do_zoom_socket(browser, actions);
2154 continue;
2155 case '/':
2156 if (ui_browser__input_window("Symbol to show",
2157 "Please enter the name of symbol you want to see.\n"
2158 "To remove the filter later, press / + ENTER.",
2159 buf, "ENTER: OK, ESC: Cancel",
2160 delay_secs * 2) == K_ENTER) {
2161 hists->symbol_filter_str = *buf ? buf : NULL;
2162 hists__filter_by_symbol(hists);
2163 hist_browser__reset(browser);
2164 }
2165 continue;
2166 case 'r':
2167 if (is_report_browser(hbt)) {
2168 actions->thread = NULL;
2169 actions->ms.sym = NULL;
2170 do_run_script(browser, actions);
2171 }
2172 continue;
2173 case 's':
2174 if (is_report_browser(hbt)) {
2175 key = do_switch_data(browser, actions);
2176 if (key == K_SWITCH_INPUT_DATA)
2177 goto out_free_stack;
2178 }
2179 continue;
2180 case 'i':
2181 /* env->arch is NULL for live-mode (i.e. perf top) */
2182 if (env->arch)
2183 tui__header_window(env);
2184 continue;
2185 case 'F':
2186 symbol_conf.filter_relative ^= 1;
2187 continue;
2188 case 'z':
2189 if (!is_report_browser(hbt)) {
2190 struct perf_top *top = hbt->arg;
2191
2192 top->zero = !top->zero;
2193 }
2194 continue;
2195 case K_F1:
2196 case 'h':
2197 case '?':
2198 ui_browser__help_window(&browser->b,
2199 is_report_browser(hbt) ? report_help : top_help);
2200 continue;
2201 case K_ENTER:
2202 case K_RIGHT:
2203 case 'm':
2204 /* menu */
2205 break;
2206 case K_ESC:
2207 case K_LEFT: {
2208 const void *top;
2209
2210 if (pstack__empty(browser->pstack)) {
2211 /*
2212 * Go back to the perf_evsel_menu__run or other user
2213 */
2214 if (left_exits)
2215 goto out_free_stack;
2216
2217 if (key == K_ESC &&
2218 ui_browser__dialog_yesno(&browser->b,
2219 "Do you really want to exit?"))
2220 goto out_free_stack;
2221
2222 continue;
2223 }
2224 top = pstack__peek(browser->pstack);
2225 if (top == &browser->hists->dso_filter) {
2226 /*
2227 * No need to set actions->dso here since
2228 * it's just to remove the current filter.
2229 * Ditto for thread below.
2230 */
2231 do_zoom_dso(browser, actions);
2232 } else if (top == &browser->hists->thread_filter) {
2233 do_zoom_thread(browser, actions);
2234 } else if (top == &browser->hists->socket_filter) {
2235 do_zoom_socket(browser, actions);
2236 }
2237 continue;
2238 }
2239 case 'q':
2240 case CTRL('c'):
2241 goto out_free_stack;
2242 case 'f':
2243 if (!is_report_browser(hbt)) {
2244 struct perf_top *top = hbt->arg;
2245
2246 perf_evlist__toggle_enable(top->evlist);
2247 /*
2248 * No need to refresh, resort/decay histogram
2249 * entries if we are not collecting samples:
2250 */
2251 if (top->evlist->enabled) {
2252 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2253 hbt->refresh = delay_secs;
2254 } else {
2255 helpline = "Press 'f' again to re-enable the events";
2256 hbt->refresh = 0;
2257 }
2258 continue;
2259 }
2260 /* Fall thru */
2261 default:
2262 helpline = "Press '?' for help on key bindings";
2263 continue;
2264 }
2265
2266 if (!sort__has_sym)
2267 goto add_exit_option;
2268
2269 if (browser->selection == NULL)
2270 goto skip_annotation;
2271
2272 if (sort__mode == SORT_MODE__BRANCH) {
2273 bi = browser->he_selection->branch_info;
2274
2275 if (bi == NULL)
2276 goto skip_annotation;
2277
2278 nr_options += add_annotate_opt(browser,
2279 &actions[nr_options],
2280 &options[nr_options],
2281 bi->from.map,
2282 bi->from.sym);
2283 if (bi->to.sym != bi->from.sym)
2284 nr_options += add_annotate_opt(browser,
2285 &actions[nr_options],
2286 &options[nr_options],
2287 bi->to.map,
2288 bi->to.sym);
2289 } else {
2290 nr_options += add_annotate_opt(browser,
2291 &actions[nr_options],
2292 &options[nr_options],
2293 browser->selection->map,
2294 browser->selection->sym);
2295 }
2296 skip_annotation:
2297 nr_options += add_thread_opt(browser, &actions[nr_options],
2298 &options[nr_options], thread);
2299 nr_options += add_dso_opt(browser, &actions[nr_options],
2300 &options[nr_options], map);
2301 nr_options += add_map_opt(browser, &actions[nr_options],
2302 &options[nr_options],
2303 browser->selection ?
2304 browser->selection->map : NULL);
2305 nr_options += add_socket_opt(browser, &actions[nr_options],
2306 &options[nr_options],
2307 socked_id);
2308 /* perf script support */
2309 if (browser->he_selection) {
2310 nr_options += add_script_opt(browser,
2311 &actions[nr_options],
2312 &options[nr_options],
2313 thread, NULL);
2314 /*
2315 * Note that browser->selection != NULL
2316 * when browser->he_selection is not NULL,
2317 * so we don't need to check browser->selection
2318 * before fetching browser->selection->sym like what
2319 * we do before fetching browser->selection->map.
2320 *
2321 * See hist_browser__show_entry.
2322 */
2323 nr_options += add_script_opt(browser,
2324 &actions[nr_options],
2325 &options[nr_options],
2326 NULL, browser->selection->sym);
2327 }
2328 nr_options += add_script_opt(browser, &actions[nr_options],
2329 &options[nr_options], NULL, NULL);
2330 nr_options += add_switch_opt(browser, &actions[nr_options],
2331 &options[nr_options]);
2332 add_exit_option:
2333 nr_options += add_exit_opt(browser, &actions[nr_options],
2334 &options[nr_options]);
2335
2336 do {
2337 struct popup_action *act;
2338
2339 choice = ui__popup_menu(nr_options, options);
2340 if (choice == -1 || choice >= nr_options)
2341 break;
2342
2343 act = &actions[choice];
2344 key = act->fn(browser, act);
2345 } while (key == 1);
2346
2347 if (key == K_SWITCH_INPUT_DATA)
2348 break;
2349 }
2350 out_free_stack:
2351 pstack__delete(browser->pstack);
2352 out:
2353 hist_browser__delete(browser);
2354 free_popup_options(options, MAX_OPTIONS);
2355 return key;
2356 }
2357
2358 struct perf_evsel_menu {
2359 struct ui_browser b;
2360 struct perf_evsel *selection;
2361 bool lost_events, lost_events_warned;
2362 float min_pcnt;
2363 struct perf_env *env;
2364 };
2365
2366 static void perf_evsel_menu__write(struct ui_browser *browser,
2367 void *entry, int row)
2368 {
2369 struct perf_evsel_menu *menu = container_of(browser,
2370 struct perf_evsel_menu, b);
2371 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2372 struct hists *hists = evsel__hists(evsel);
2373 bool current_entry = ui_browser__is_current_entry(browser, row);
2374 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2375 const char *ev_name = perf_evsel__name(evsel);
2376 char bf[256], unit;
2377 const char *warn = " ";
2378 size_t printed;
2379
2380 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2381 HE_COLORSET_NORMAL);
2382
2383 if (perf_evsel__is_group_event(evsel)) {
2384 struct perf_evsel *pos;
2385
2386 ev_name = perf_evsel__group_name(evsel);
2387
2388 for_each_group_member(pos, evsel) {
2389 struct hists *pos_hists = evsel__hists(pos);
2390 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2391 }
2392 }
2393
2394 nr_events = convert_unit(nr_events, &unit);
2395 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2396 unit, unit == ' ' ? "" : " ", ev_name);
2397 ui_browser__printf(browser, "%s", bf);
2398
2399 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2400 if (nr_events != 0) {
2401 menu->lost_events = true;
2402 if (!current_entry)
2403 ui_browser__set_color(browser, HE_COLORSET_TOP);
2404 nr_events = convert_unit(nr_events, &unit);
2405 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2406 nr_events, unit, unit == ' ' ? "" : " ");
2407 warn = bf;
2408 }
2409
2410 ui_browser__write_nstring(browser, warn, browser->width - printed);
2411
2412 if (current_entry)
2413 menu->selection = evsel;
2414 }
2415
2416 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2417 int nr_events, const char *help,
2418 struct hist_browser_timer *hbt)
2419 {
2420 struct perf_evlist *evlist = menu->b.priv;
2421 struct perf_evsel *pos;
2422 const char *title = "Available samples";
2423 int delay_secs = hbt ? hbt->refresh : 0;
2424 int key;
2425
2426 if (ui_browser__show(&menu->b, title,
2427 "ESC: exit, ENTER|->: Browse histograms") < 0)
2428 return -1;
2429
2430 while (1) {
2431 key = ui_browser__run(&menu->b, delay_secs);
2432
2433 switch (key) {
2434 case K_TIMER:
2435 hbt->timer(hbt->arg);
2436
2437 if (!menu->lost_events_warned && menu->lost_events) {
2438 ui_browser__warn_lost_events(&menu->b);
2439 menu->lost_events_warned = true;
2440 }
2441 continue;
2442 case K_RIGHT:
2443 case K_ENTER:
2444 if (!menu->selection)
2445 continue;
2446 pos = menu->selection;
2447 browse_hists:
2448 perf_evlist__set_selected(evlist, pos);
2449 /*
2450 * Give the calling tool a chance to populate the non
2451 * default evsel resorted hists tree.
2452 */
2453 if (hbt)
2454 hbt->timer(hbt->arg);
2455 key = perf_evsel__hists_browse(pos, nr_events, help,
2456 true, hbt,
2457 menu->min_pcnt,
2458 menu->env);
2459 ui_browser__show_title(&menu->b, title);
2460 switch (key) {
2461 case K_TAB:
2462 if (pos->node.next == &evlist->entries)
2463 pos = perf_evlist__first(evlist);
2464 else
2465 pos = perf_evsel__next(pos);
2466 goto browse_hists;
2467 case K_UNTAB:
2468 if (pos->node.prev == &evlist->entries)
2469 pos = perf_evlist__last(evlist);
2470 else
2471 pos = perf_evsel__prev(pos);
2472 goto browse_hists;
2473 case K_SWITCH_INPUT_DATA:
2474 case 'q':
2475 case CTRL('c'):
2476 goto out;
2477 case K_ESC:
2478 default:
2479 continue;
2480 }
2481 case K_LEFT:
2482 continue;
2483 case K_ESC:
2484 if (!ui_browser__dialog_yesno(&menu->b,
2485 "Do you really want to exit?"))
2486 continue;
2487 /* Fall thru */
2488 case 'q':
2489 case CTRL('c'):
2490 goto out;
2491 default:
2492 continue;
2493 }
2494 }
2495
2496 out:
2497 ui_browser__hide(&menu->b);
2498 return key;
2499 }
2500
2501 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2502 void *entry)
2503 {
2504 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2505
2506 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2507 return true;
2508
2509 return false;
2510 }
2511
2512 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2513 int nr_entries, const char *help,
2514 struct hist_browser_timer *hbt,
2515 float min_pcnt,
2516 struct perf_env *env)
2517 {
2518 struct perf_evsel *pos;
2519 struct perf_evsel_menu menu = {
2520 .b = {
2521 .entries = &evlist->entries,
2522 .refresh = ui_browser__list_head_refresh,
2523 .seek = ui_browser__list_head_seek,
2524 .write = perf_evsel_menu__write,
2525 .filter = filter_group_entries,
2526 .nr_entries = nr_entries,
2527 .priv = evlist,
2528 },
2529 .min_pcnt = min_pcnt,
2530 .env = env,
2531 };
2532
2533 ui_helpline__push("Press ESC to exit");
2534
2535 evlist__for_each(evlist, pos) {
2536 const char *ev_name = perf_evsel__name(pos);
2537 size_t line_len = strlen(ev_name) + 7;
2538
2539 if (menu.b.width < line_len)
2540 menu.b.width = line_len;
2541 }
2542
2543 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2544 }
2545
2546 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2547 struct hist_browser_timer *hbt,
2548 float min_pcnt,
2549 struct perf_env *env)
2550 {
2551 int nr_entries = evlist->nr_entries;
2552
2553 single_entry:
2554 if (nr_entries == 1) {
2555 struct perf_evsel *first = perf_evlist__first(evlist);
2556
2557 return perf_evsel__hists_browse(first, nr_entries, help,
2558 false, hbt, min_pcnt,
2559 env);
2560 }
2561
2562 if (symbol_conf.event_group) {
2563 struct perf_evsel *pos;
2564
2565 nr_entries = 0;
2566 evlist__for_each(evlist, pos) {
2567 if (perf_evsel__is_group_leader(pos))
2568 nr_entries++;
2569 }
2570
2571 if (nr_entries == 1)
2572 goto single_entry;
2573 }
2574
2575 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2576 hbt, min_pcnt, env);
2577 }
This page took 0.098233 seconds and 6 git commands to generate.