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