Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git...
[deliverable/linux.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.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
21 struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
26 int print_seq;
27 bool show_dso;
28 float min_pcnt;
29 u64 nr_non_filtered_entries;
30 u64 nr_callchain_rows;
31 };
32
33 extern void hist_browser__init_hpp(void);
34
35 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
36 const char *ev_name);
37 static void hist_browser__update_nr_entries(struct hist_browser *hb);
38
39 static struct rb_node *hists__filter_entries(struct rb_node *nd,
40 struct hists *hists,
41 float min_pcnt);
42
43 static bool hist_browser__has_filter(struct hist_browser *hb)
44 {
45 return hists__has_filter(hb->hists) || hb->min_pcnt;
46 }
47
48 static u32 hist_browser__nr_entries(struct hist_browser *hb)
49 {
50 u32 nr_entries;
51
52 if (hist_browser__has_filter(hb))
53 nr_entries = hb->nr_non_filtered_entries;
54 else
55 nr_entries = hb->hists->nr_entries;
56
57 return nr_entries + hb->nr_callchain_rows;
58 }
59
60 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
61 {
62 /* 3 == +/- toggle symbol before actual hist_entry rendering */
63 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
64 sizeof("[k]"));
65 }
66
67 static void hist_browser__reset(struct hist_browser *browser)
68 {
69 /*
70 * The hists__remove_entry_filter() already folds non-filtered
71 * entries so we can assume it has 0 callchain rows.
72 */
73 browser->nr_callchain_rows = 0;
74
75 hist_browser__update_nr_entries(browser);
76 browser->b.nr_entries = hist_browser__nr_entries(browser);
77 hist_browser__refresh_dimensions(browser);
78 ui_browser__reset_index(&browser->b);
79 }
80
81 static char tree__folded_sign(bool unfolded)
82 {
83 return unfolded ? '-' : '+';
84 }
85
86 static char map_symbol__folded(const struct map_symbol *ms)
87 {
88 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
89 }
90
91 static char hist_entry__folded(const struct hist_entry *he)
92 {
93 return map_symbol__folded(&he->ms);
94 }
95
96 static char callchain_list__folded(const struct callchain_list *cl)
97 {
98 return map_symbol__folded(&cl->ms);
99 }
100
101 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
102 {
103 ms->unfolded = unfold ? ms->has_children : false;
104 }
105
106 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
107 {
108 int n = 0;
109 struct rb_node *nd;
110
111 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
112 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
113 struct callchain_list *chain;
114 char folded_sign = ' '; /* No children */
115
116 list_for_each_entry(chain, &child->val, list) {
117 ++n;
118 /* We need this because we may not have children */
119 folded_sign = callchain_list__folded(chain);
120 if (folded_sign == '+')
121 break;
122 }
123
124 if (folded_sign == '-') /* Have children and they're unfolded */
125 n += callchain_node__count_rows_rb_tree(child);
126 }
127
128 return n;
129 }
130
131 static int callchain_node__count_rows(struct callchain_node *node)
132 {
133 struct callchain_list *chain;
134 bool unfolded = false;
135 int n = 0;
136
137 list_for_each_entry(chain, &node->val, list) {
138 ++n;
139 unfolded = chain->ms.unfolded;
140 }
141
142 if (unfolded)
143 n += callchain_node__count_rows_rb_tree(node);
144
145 return n;
146 }
147
148 static int callchain__count_rows(struct rb_root *chain)
149 {
150 struct rb_node *nd;
151 int n = 0;
152
153 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
154 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
155 n += callchain_node__count_rows(node);
156 }
157
158 return n;
159 }
160
161 static bool map_symbol__toggle_fold(struct map_symbol *ms)
162 {
163 if (!ms)
164 return false;
165
166 if (!ms->has_children)
167 return false;
168
169 ms->unfolded = !ms->unfolded;
170 return true;
171 }
172
173 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
174 {
175 struct rb_node *nd = rb_first(&node->rb_root);
176
177 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
178 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
179 struct callchain_list *chain;
180 bool first = true;
181
182 list_for_each_entry(chain, &child->val, list) {
183 if (first) {
184 first = false;
185 chain->ms.has_children = chain->list.next != &child->val ||
186 !RB_EMPTY_ROOT(&child->rb_root);
187 } else
188 chain->ms.has_children = chain->list.next == &child->val &&
189 !RB_EMPTY_ROOT(&child->rb_root);
190 }
191
192 callchain_node__init_have_children_rb_tree(child);
193 }
194 }
195
196 static void callchain_node__init_have_children(struct callchain_node *node)
197 {
198 struct callchain_list *chain;
199
200 list_for_each_entry(chain, &node->val, list)
201 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
202
203 callchain_node__init_have_children_rb_tree(node);
204 }
205
206 static void callchain__init_have_children(struct rb_root *root)
207 {
208 struct rb_node *nd;
209
210 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
211 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
212 callchain_node__init_have_children(node);
213 }
214 }
215
216 static void hist_entry__init_have_children(struct hist_entry *he)
217 {
218 if (!he->init_have_children) {
219 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
220 callchain__init_have_children(&he->sorted_chain);
221 he->init_have_children = true;
222 }
223 }
224
225 static bool hist_browser__toggle_fold(struct hist_browser *browser)
226 {
227 if (map_symbol__toggle_fold(browser->selection)) {
228 struct hist_entry *he = browser->he_selection;
229
230 hist_entry__init_have_children(he);
231 browser->b.nr_entries -= he->nr_rows;
232 browser->nr_callchain_rows -= he->nr_rows;
233
234 if (he->ms.unfolded)
235 he->nr_rows = callchain__count_rows(&he->sorted_chain);
236 else
237 he->nr_rows = 0;
238
239 browser->b.nr_entries += he->nr_rows;
240 browser->nr_callchain_rows += he->nr_rows;
241
242 return true;
243 }
244
245 /* If it doesn't have children, no toggling performed */
246 return false;
247 }
248
249 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
250 {
251 int n = 0;
252 struct rb_node *nd;
253
254 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
255 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
256 struct callchain_list *chain;
257 bool has_children = false;
258
259 list_for_each_entry(chain, &child->val, list) {
260 ++n;
261 map_symbol__set_folding(&chain->ms, unfold);
262 has_children = chain->ms.has_children;
263 }
264
265 if (has_children)
266 n += callchain_node__set_folding_rb_tree(child, unfold);
267 }
268
269 return n;
270 }
271
272 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
273 {
274 struct callchain_list *chain;
275 bool has_children = false;
276 int n = 0;
277
278 list_for_each_entry(chain, &node->val, list) {
279 ++n;
280 map_symbol__set_folding(&chain->ms, unfold);
281 has_children = chain->ms.has_children;
282 }
283
284 if (has_children)
285 n += callchain_node__set_folding_rb_tree(node, unfold);
286
287 return n;
288 }
289
290 static int callchain__set_folding(struct rb_root *chain, bool unfold)
291 {
292 struct rb_node *nd;
293 int n = 0;
294
295 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
296 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
297 n += callchain_node__set_folding(node, unfold);
298 }
299
300 return n;
301 }
302
303 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
304 {
305 hist_entry__init_have_children(he);
306 map_symbol__set_folding(&he->ms, unfold);
307
308 if (he->ms.has_children) {
309 int n = callchain__set_folding(&he->sorted_chain, unfold);
310 he->nr_rows = unfold ? n : 0;
311 } else
312 he->nr_rows = 0;
313 }
314
315 static void
316 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
317 {
318 struct rb_node *nd;
319 struct hists *hists = browser->hists;
320
321 for (nd = rb_first(&hists->entries);
322 (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL;
323 nd = rb_next(nd)) {
324 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
325 hist_entry__set_folding(he, unfold);
326 browser->nr_callchain_rows += he->nr_rows;
327 }
328 }
329
330 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
331 {
332 browser->nr_callchain_rows = 0;
333 __hist_browser__set_folding(browser, unfold);
334
335 browser->b.nr_entries = hist_browser__nr_entries(browser);
336 /* Go to the start, we may be way after valid entries after a collapse */
337 ui_browser__reset_index(&browser->b);
338 }
339
340 static void ui_browser__warn_lost_events(struct ui_browser *browser)
341 {
342 ui_browser__warning(browser, 4,
343 "Events are being lost, check IO/CPU overload!\n\n"
344 "You may want to run 'perf' using a RT scheduler policy:\n\n"
345 " perf top -r 80\n\n"
346 "Or reduce the sampling frequency.");
347 }
348
349 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
350 struct hist_browser_timer *hbt)
351 {
352 int key;
353 char title[160];
354 int delay_secs = hbt ? hbt->refresh : 0;
355
356 browser->b.entries = &browser->hists->entries;
357 browser->b.nr_entries = hist_browser__nr_entries(browser);
358
359 hist_browser__refresh_dimensions(browser);
360 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
361
362 if (ui_browser__show(&browser->b, title,
363 "Press '?' for help on key bindings") < 0)
364 return -1;
365
366 while (1) {
367 key = ui_browser__run(&browser->b, delay_secs);
368
369 switch (key) {
370 case K_TIMER: {
371 u64 nr_entries;
372 hbt->timer(hbt->arg);
373
374 if (hist_browser__has_filter(browser))
375 hist_browser__update_nr_entries(browser);
376
377 nr_entries = hist_browser__nr_entries(browser);
378 ui_browser__update_nr_entries(&browser->b, nr_entries);
379
380 if (browser->hists->stats.nr_lost_warned !=
381 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
382 browser->hists->stats.nr_lost_warned =
383 browser->hists->stats.nr_events[PERF_RECORD_LOST];
384 ui_browser__warn_lost_events(&browser->b);
385 }
386
387 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
388 ui_browser__show_title(&browser->b, title);
389 continue;
390 }
391 case 'D': { /* Debug */
392 static int seq;
393 struct hist_entry *h = rb_entry(browser->b.top,
394 struct hist_entry, rb_node);
395 ui_helpline__pop();
396 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
397 seq++, browser->b.nr_entries,
398 browser->hists->nr_entries,
399 browser->b.height,
400 browser->b.index,
401 browser->b.top_idx,
402 h->row_offset, h->nr_rows);
403 }
404 break;
405 case 'C':
406 /* Collapse the whole world. */
407 hist_browser__set_folding(browser, false);
408 break;
409 case 'E':
410 /* Expand the whole world. */
411 hist_browser__set_folding(browser, true);
412 break;
413 case K_ENTER:
414 if (hist_browser__toggle_fold(browser))
415 break;
416 /* fall thru */
417 default:
418 goto out;
419 }
420 }
421 out:
422 ui_browser__hide(&browser->b);
423 return key;
424 }
425
426 static char *callchain_list__sym_name(struct callchain_list *cl,
427 char *bf, size_t bfsize, bool show_dso)
428 {
429 int printed;
430
431 if (cl->ms.sym)
432 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
433 else
434 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
435
436 if (show_dso)
437 scnprintf(bf + printed, bfsize - printed, " %s",
438 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
439
440 return bf;
441 }
442
443 #define LEVEL_OFFSET_STEP 3
444
445 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
446 struct callchain_node *chain_node,
447 u64 total, int level,
448 unsigned short row,
449 off_t *row_offset,
450 bool *is_current_entry)
451 {
452 struct rb_node *node;
453 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
454 u64 new_total, remaining;
455
456 if (callchain_param.mode == CHAIN_GRAPH_REL)
457 new_total = chain_node->children_hit;
458 else
459 new_total = total;
460
461 remaining = new_total;
462 node = rb_first(&chain_node->rb_root);
463 while (node) {
464 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
465 struct rb_node *next = rb_next(node);
466 u64 cumul = callchain_cumul_hits(child);
467 struct callchain_list *chain;
468 char folded_sign = ' ';
469 int first = true;
470 int extra_offset = 0;
471
472 remaining -= cumul;
473
474 list_for_each_entry(chain, &child->val, list) {
475 char bf[1024], *alloc_str;
476 const char *str;
477 int color;
478 bool was_first = first;
479
480 if (first)
481 first = false;
482 else
483 extra_offset = LEVEL_OFFSET_STEP;
484
485 folded_sign = callchain_list__folded(chain);
486 if (*row_offset != 0) {
487 --*row_offset;
488 goto do_next;
489 }
490
491 alloc_str = NULL;
492 str = callchain_list__sym_name(chain, bf, sizeof(bf),
493 browser->show_dso);
494 if (was_first) {
495 double percent = cumul * 100.0 / new_total;
496
497 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
498 str = "Not enough memory!";
499 else
500 str = alloc_str;
501 }
502
503 color = HE_COLORSET_NORMAL;
504 width = browser->b.width - (offset + extra_offset + 2);
505 if (ui_browser__is_current_entry(&browser->b, row)) {
506 browser->selection = &chain->ms;
507 color = HE_COLORSET_SELECTED;
508 *is_current_entry = true;
509 }
510
511 ui_browser__set_color(&browser->b, color);
512 ui_browser__gotorc(&browser->b, row, 0);
513 slsmg_write_nstring(" ", offset + extra_offset);
514 slsmg_printf("%c ", folded_sign);
515 slsmg_write_nstring(str, width);
516 free(alloc_str);
517
518 if (++row == browser->b.height)
519 goto out;
520 do_next:
521 if (folded_sign == '+')
522 break;
523 }
524
525 if (folded_sign == '-') {
526 const int new_level = level + (extra_offset ? 2 : 1);
527 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
528 new_level, row, row_offset,
529 is_current_entry);
530 }
531 if (row == browser->b.height)
532 goto out;
533 node = next;
534 }
535 out:
536 return row - first_row;
537 }
538
539 static int hist_browser__show_callchain_node(struct hist_browser *browser,
540 struct callchain_node *node,
541 int level, unsigned short row,
542 off_t *row_offset,
543 bool *is_current_entry)
544 {
545 struct callchain_list *chain;
546 int first_row = row,
547 offset = level * LEVEL_OFFSET_STEP,
548 width = browser->b.width - offset;
549 char folded_sign = ' ';
550
551 list_for_each_entry(chain, &node->val, list) {
552 char bf[1024], *s;
553 int color;
554
555 folded_sign = callchain_list__folded(chain);
556
557 if (*row_offset != 0) {
558 --*row_offset;
559 continue;
560 }
561
562 color = HE_COLORSET_NORMAL;
563 if (ui_browser__is_current_entry(&browser->b, row)) {
564 browser->selection = &chain->ms;
565 color = HE_COLORSET_SELECTED;
566 *is_current_entry = true;
567 }
568
569 s = callchain_list__sym_name(chain, bf, sizeof(bf),
570 browser->show_dso);
571 ui_browser__gotorc(&browser->b, row, 0);
572 ui_browser__set_color(&browser->b, color);
573 slsmg_write_nstring(" ", offset);
574 slsmg_printf("%c ", folded_sign);
575 slsmg_write_nstring(s, width - 2);
576
577 if (++row == browser->b.height)
578 goto out;
579 }
580
581 if (folded_sign == '-')
582 row += hist_browser__show_callchain_node_rb_tree(browser, node,
583 browser->hists->stats.total_period,
584 level + 1, row,
585 row_offset,
586 is_current_entry);
587 out:
588 return row - first_row;
589 }
590
591 static int hist_browser__show_callchain(struct hist_browser *browser,
592 struct rb_root *chain,
593 int level, unsigned short row,
594 off_t *row_offset,
595 bool *is_current_entry)
596 {
597 struct rb_node *nd;
598 int first_row = row;
599
600 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
601 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
602
603 row += hist_browser__show_callchain_node(browser, node, level,
604 row, row_offset,
605 is_current_entry);
606 if (row == browser->b.height)
607 break;
608 }
609
610 return row - first_row;
611 }
612
613 struct hpp_arg {
614 struct ui_browser *b;
615 char folded_sign;
616 bool current_entry;
617 };
618
619 static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
620 {
621 struct hpp_arg *arg = hpp->ptr;
622
623 if (arg->current_entry && arg->b->navkeypressed)
624 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
625 else
626 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
627
628 if (front) {
629 if (!symbol_conf.use_callchain)
630 return 0;
631
632 slsmg_printf("%c ", arg->folded_sign);
633 return 2;
634 }
635
636 return 0;
637 }
638
639 static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
640 {
641 struct hpp_arg *arg = hpp->ptr;
642
643 if (!arg->current_entry || !arg->b->navkeypressed)
644 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
645 return 0;
646 }
647
648 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
649 {
650 struct hpp_arg *arg = hpp->ptr;
651 int ret;
652 va_list args;
653 double percent;
654
655 va_start(args, fmt);
656 percent = va_arg(args, double);
657 va_end(args);
658
659 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
660
661 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
662 slsmg_printf("%s", hpp->buf);
663
664 advance_hpp(hpp, ret);
665 return ret;
666 }
667
668 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
669 static u64 __hpp_get_##_field(struct hist_entry *he) \
670 { \
671 return he->stat._field; \
672 } \
673 \
674 static int \
675 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
676 struct perf_hpp *hpp, \
677 struct hist_entry *he) \
678 { \
679 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
680 __hpp__slsmg_color_printf, true); \
681 }
682
683 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
684 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
685 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
686 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
687 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
688
689 #undef __HPP_COLOR_PERCENT_FN
690
691 void hist_browser__init_hpp(void)
692 {
693 perf_hpp__init();
694
695 perf_hpp__format[PERF_HPP__OVERHEAD].color =
696 hist_browser__hpp_color_overhead;
697 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
698 hist_browser__hpp_color_overhead_sys;
699 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
700 hist_browser__hpp_color_overhead_us;
701 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
702 hist_browser__hpp_color_overhead_guest_sys;
703 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
704 hist_browser__hpp_color_overhead_guest_us;
705 }
706
707 static int hist_browser__show_entry(struct hist_browser *browser,
708 struct hist_entry *entry,
709 unsigned short row)
710 {
711 char s[256];
712 int printed = 0;
713 int width = browser->b.width;
714 char folded_sign = ' ';
715 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
716 off_t row_offset = entry->row_offset;
717 bool first = true;
718 struct perf_hpp_fmt *fmt;
719
720 if (current_entry) {
721 browser->he_selection = entry;
722 browser->selection = &entry->ms;
723 }
724
725 if (symbol_conf.use_callchain) {
726 hist_entry__init_have_children(entry);
727 folded_sign = hist_entry__folded(entry);
728 }
729
730 if (row_offset == 0) {
731 struct hpp_arg arg = {
732 .b = &browser->b,
733 .folded_sign = folded_sign,
734 .current_entry = current_entry,
735 };
736 struct perf_hpp hpp = {
737 .buf = s,
738 .size = sizeof(s),
739 .ptr = &arg,
740 };
741
742 ui_browser__gotorc(&browser->b, row, 0);
743
744 perf_hpp__for_each_format(fmt) {
745 if (!first) {
746 slsmg_printf(" ");
747 width -= 2;
748 }
749 first = false;
750
751 if (fmt->color) {
752 width -= fmt->color(fmt, &hpp, entry);
753 } else {
754 width -= fmt->entry(fmt, &hpp, entry);
755 slsmg_printf("%s", s);
756 }
757 }
758
759 /* The scroll bar isn't being used */
760 if (!browser->b.navkeypressed)
761 width += 1;
762
763 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
764 slsmg_write_nstring(s, width);
765 ++row;
766 ++printed;
767 } else
768 --row_offset;
769
770 if (folded_sign == '-' && row != browser->b.height) {
771 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
772 1, row, &row_offset,
773 &current_entry);
774 if (current_entry)
775 browser->he_selection = entry;
776 }
777
778 return printed;
779 }
780
781 static void ui_browser__hists_init_top(struct ui_browser *browser)
782 {
783 if (browser->top == NULL) {
784 struct hist_browser *hb;
785
786 hb = container_of(browser, struct hist_browser, b);
787 browser->top = rb_first(&hb->hists->entries);
788 }
789 }
790
791 static unsigned int hist_browser__refresh(struct ui_browser *browser)
792 {
793 unsigned row = 0;
794 struct rb_node *nd;
795 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
796
797 ui_browser__hists_init_top(browser);
798
799 for (nd = browser->top; nd; nd = rb_next(nd)) {
800 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
801 u64 total = hists__total_period(h->hists);
802 float percent = 0.0;
803
804 if (h->filtered)
805 continue;
806
807 if (total)
808 percent = h->stat.period * 100.0 / total;
809
810 if (percent < hb->min_pcnt)
811 continue;
812
813 row += hist_browser__show_entry(hb, h, row);
814 if (row == browser->height)
815 break;
816 }
817
818 return row;
819 }
820
821 static struct rb_node *hists__filter_entries(struct rb_node *nd,
822 struct hists *hists,
823 float min_pcnt)
824 {
825 while (nd != NULL) {
826 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
827 u64 total = hists__total_period(hists);
828 float percent = 0.0;
829
830 if (total)
831 percent = h->stat.period * 100.0 / total;
832
833 if (percent < min_pcnt)
834 return NULL;
835
836 if (!h->filtered)
837 return nd;
838
839 nd = rb_next(nd);
840 }
841
842 return NULL;
843 }
844
845 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
846 struct hists *hists,
847 float min_pcnt)
848 {
849 while (nd != NULL) {
850 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
851 u64 total = hists__total_period(hists);
852 float percent = 0.0;
853
854 if (total)
855 percent = h->stat.period * 100.0 / total;
856
857 if (!h->filtered && percent >= min_pcnt)
858 return nd;
859
860 nd = rb_prev(nd);
861 }
862
863 return NULL;
864 }
865
866 static void ui_browser__hists_seek(struct ui_browser *browser,
867 off_t offset, int whence)
868 {
869 struct hist_entry *h;
870 struct rb_node *nd;
871 bool first = true;
872 struct hist_browser *hb;
873
874 hb = container_of(browser, struct hist_browser, b);
875
876 if (browser->nr_entries == 0)
877 return;
878
879 ui_browser__hists_init_top(browser);
880
881 switch (whence) {
882 case SEEK_SET:
883 nd = hists__filter_entries(rb_first(browser->entries),
884 hb->hists, hb->min_pcnt);
885 break;
886 case SEEK_CUR:
887 nd = browser->top;
888 goto do_offset;
889 case SEEK_END:
890 nd = hists__filter_prev_entries(rb_last(browser->entries),
891 hb->hists, hb->min_pcnt);
892 first = false;
893 break;
894 default:
895 return;
896 }
897
898 /*
899 * Moves not relative to the first visible entry invalidates its
900 * row_offset:
901 */
902 h = rb_entry(browser->top, struct hist_entry, rb_node);
903 h->row_offset = 0;
904
905 /*
906 * Here we have to check if nd is expanded (+), if it is we can't go
907 * the next top level hist_entry, instead we must compute an offset of
908 * what _not_ to show and not change the first visible entry.
909 *
910 * This offset increments when we are going from top to bottom and
911 * decreases when we're going from bottom to top.
912 *
913 * As we don't have backpointers to the top level in the callchains
914 * structure, we need to always print the whole hist_entry callchain,
915 * skipping the first ones that are before the first visible entry
916 * and stop when we printed enough lines to fill the screen.
917 */
918 do_offset:
919 if (offset > 0) {
920 do {
921 h = rb_entry(nd, struct hist_entry, rb_node);
922 if (h->ms.unfolded) {
923 u16 remaining = h->nr_rows - h->row_offset;
924 if (offset > remaining) {
925 offset -= remaining;
926 h->row_offset = 0;
927 } else {
928 h->row_offset += offset;
929 offset = 0;
930 browser->top = nd;
931 break;
932 }
933 }
934 nd = hists__filter_entries(rb_next(nd), hb->hists,
935 hb->min_pcnt);
936 if (nd == NULL)
937 break;
938 --offset;
939 browser->top = nd;
940 } while (offset != 0);
941 } else if (offset < 0) {
942 while (1) {
943 h = rb_entry(nd, struct hist_entry, rb_node);
944 if (h->ms.unfolded) {
945 if (first) {
946 if (-offset > h->row_offset) {
947 offset += h->row_offset;
948 h->row_offset = 0;
949 } else {
950 h->row_offset += offset;
951 offset = 0;
952 browser->top = nd;
953 break;
954 }
955 } else {
956 if (-offset > h->nr_rows) {
957 offset += h->nr_rows;
958 h->row_offset = 0;
959 } else {
960 h->row_offset = h->nr_rows + offset;
961 offset = 0;
962 browser->top = nd;
963 break;
964 }
965 }
966 }
967
968 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
969 hb->min_pcnt);
970 if (nd == NULL)
971 break;
972 ++offset;
973 browser->top = nd;
974 if (offset == 0) {
975 /*
976 * Last unfiltered hist_entry, check if it is
977 * unfolded, if it is then we should have
978 * row_offset at its last entry.
979 */
980 h = rb_entry(nd, struct hist_entry, rb_node);
981 if (h->ms.unfolded)
982 h->row_offset = h->nr_rows;
983 break;
984 }
985 first = false;
986 }
987 } else {
988 browser->top = nd;
989 h = rb_entry(nd, struct hist_entry, rb_node);
990 h->row_offset = 0;
991 }
992 }
993
994 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
995 struct callchain_node *chain_node,
996 u64 total, int level,
997 FILE *fp)
998 {
999 struct rb_node *node;
1000 int offset = level * LEVEL_OFFSET_STEP;
1001 u64 new_total, remaining;
1002 int printed = 0;
1003
1004 if (callchain_param.mode == CHAIN_GRAPH_REL)
1005 new_total = chain_node->children_hit;
1006 else
1007 new_total = total;
1008
1009 remaining = new_total;
1010 node = rb_first(&chain_node->rb_root);
1011 while (node) {
1012 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1013 struct rb_node *next = rb_next(node);
1014 u64 cumul = callchain_cumul_hits(child);
1015 struct callchain_list *chain;
1016 char folded_sign = ' ';
1017 int first = true;
1018 int extra_offset = 0;
1019
1020 remaining -= cumul;
1021
1022 list_for_each_entry(chain, &child->val, list) {
1023 char bf[1024], *alloc_str;
1024 const char *str;
1025 bool was_first = first;
1026
1027 if (first)
1028 first = false;
1029 else
1030 extra_offset = LEVEL_OFFSET_STEP;
1031
1032 folded_sign = callchain_list__folded(chain);
1033
1034 alloc_str = NULL;
1035 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1036 browser->show_dso);
1037 if (was_first) {
1038 double percent = cumul * 100.0 / new_total;
1039
1040 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1041 str = "Not enough memory!";
1042 else
1043 str = alloc_str;
1044 }
1045
1046 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1047 free(alloc_str);
1048 if (folded_sign == '+')
1049 break;
1050 }
1051
1052 if (folded_sign == '-') {
1053 const int new_level = level + (extra_offset ? 2 : 1);
1054 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1055 new_level, fp);
1056 }
1057
1058 node = next;
1059 }
1060
1061 return printed;
1062 }
1063
1064 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1065 struct callchain_node *node,
1066 int level, FILE *fp)
1067 {
1068 struct callchain_list *chain;
1069 int offset = level * LEVEL_OFFSET_STEP;
1070 char folded_sign = ' ';
1071 int printed = 0;
1072
1073 list_for_each_entry(chain, &node->val, list) {
1074 char bf[1024], *s;
1075
1076 folded_sign = callchain_list__folded(chain);
1077 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1078 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1079 }
1080
1081 if (folded_sign == '-')
1082 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1083 browser->hists->stats.total_period,
1084 level + 1, fp);
1085 return printed;
1086 }
1087
1088 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1089 struct rb_root *chain, int level, FILE *fp)
1090 {
1091 struct rb_node *nd;
1092 int printed = 0;
1093
1094 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1095 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1096
1097 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1098 }
1099
1100 return printed;
1101 }
1102
1103 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1104 struct hist_entry *he, FILE *fp)
1105 {
1106 char s[8192];
1107 double percent;
1108 int printed = 0;
1109 char folded_sign = ' ';
1110
1111 if (symbol_conf.use_callchain)
1112 folded_sign = hist_entry__folded(he);
1113
1114 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1115 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1116
1117 if (symbol_conf.use_callchain)
1118 printed += fprintf(fp, "%c ", folded_sign);
1119
1120 printed += fprintf(fp, " %5.2f%%", percent);
1121
1122 if (symbol_conf.show_nr_samples)
1123 printed += fprintf(fp, " %11u", he->stat.nr_events);
1124
1125 if (symbol_conf.show_total_period)
1126 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1127
1128 printed += fprintf(fp, "%s\n", rtrim(s));
1129
1130 if (folded_sign == '-')
1131 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1132
1133 return printed;
1134 }
1135
1136 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1137 {
1138 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1139 browser->hists,
1140 browser->min_pcnt);
1141 int printed = 0;
1142
1143 while (nd) {
1144 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1145
1146 printed += hist_browser__fprintf_entry(browser, h, fp);
1147 nd = hists__filter_entries(rb_next(nd), browser->hists,
1148 browser->min_pcnt);
1149 }
1150
1151 return printed;
1152 }
1153
1154 static int hist_browser__dump(struct hist_browser *browser)
1155 {
1156 char filename[64];
1157 FILE *fp;
1158
1159 while (1) {
1160 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1161 if (access(filename, F_OK))
1162 break;
1163 /*
1164 * XXX: Just an arbitrary lazy upper limit
1165 */
1166 if (++browser->print_seq == 8192) {
1167 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1168 return -1;
1169 }
1170 }
1171
1172 fp = fopen(filename, "w");
1173 if (fp == NULL) {
1174 char bf[64];
1175 const char *err = strerror_r(errno, bf, sizeof(bf));
1176 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1177 return -1;
1178 }
1179
1180 ++browser->print_seq;
1181 hist_browser__fprintf(browser, fp);
1182 fclose(fp);
1183 ui_helpline__fpush("%s written!", filename);
1184
1185 return 0;
1186 }
1187
1188 static struct hist_browser *hist_browser__new(struct hists *hists)
1189 {
1190 struct hist_browser *browser = zalloc(sizeof(*browser));
1191
1192 if (browser) {
1193 browser->hists = hists;
1194 browser->b.refresh = hist_browser__refresh;
1195 browser->b.seek = ui_browser__hists_seek;
1196 browser->b.use_navkeypressed = true;
1197 }
1198
1199 return browser;
1200 }
1201
1202 static void hist_browser__delete(struct hist_browser *browser)
1203 {
1204 free(browser);
1205 }
1206
1207 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1208 {
1209 return browser->he_selection;
1210 }
1211
1212 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1213 {
1214 return browser->he_selection->thread;
1215 }
1216
1217 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1218 const char *ev_name)
1219 {
1220 char unit;
1221 int printed;
1222 const struct dso *dso = hists->dso_filter;
1223 const struct thread *thread = hists->thread_filter;
1224 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1225 u64 nr_events = hists->stats.total_period;
1226 struct perf_evsel *evsel = hists_to_evsel(hists);
1227 char buf[512];
1228 size_t buflen = sizeof(buf);
1229
1230 if (symbol_conf.filter_relative) {
1231 nr_samples = hists->stats.nr_non_filtered_samples;
1232 nr_events = hists->stats.total_non_filtered_period;
1233 }
1234
1235 if (perf_evsel__is_group_event(evsel)) {
1236 struct perf_evsel *pos;
1237
1238 perf_evsel__group_desc(evsel, buf, buflen);
1239 ev_name = buf;
1240
1241 for_each_group_member(pos, evsel) {
1242 if (symbol_conf.filter_relative) {
1243 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1244 nr_events += pos->hists.stats.total_non_filtered_period;
1245 } else {
1246 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1247 nr_events += pos->hists.stats.total_period;
1248 }
1249 }
1250 }
1251
1252 nr_samples = convert_unit(nr_samples, &unit);
1253 printed = scnprintf(bf, size,
1254 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1255 nr_samples, unit, ev_name, nr_events);
1256
1257
1258 if (hists->uid_filter_str)
1259 printed += snprintf(bf + printed, size - printed,
1260 ", UID: %s", hists->uid_filter_str);
1261 if (thread)
1262 printed += scnprintf(bf + printed, size - printed,
1263 ", Thread: %s(%d)",
1264 (thread->comm_set ? thread__comm_str(thread) : ""),
1265 thread->tid);
1266 if (dso)
1267 printed += scnprintf(bf + printed, size - printed,
1268 ", DSO: %s", dso->short_name);
1269 return printed;
1270 }
1271
1272 static inline void free_popup_options(char **options, int n)
1273 {
1274 int i;
1275
1276 for (i = 0; i < n; ++i)
1277 zfree(&options[i]);
1278 }
1279
1280 /* Check whether the browser is for 'top' or 'report' */
1281 static inline bool is_report_browser(void *timer)
1282 {
1283 return timer == NULL;
1284 }
1285
1286 /*
1287 * Only runtime switching of perf data file will make "input_name" point
1288 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1289 * whether we need to call free() for current "input_name" during the switch.
1290 */
1291 static bool is_input_name_malloced = false;
1292
1293 static int switch_data_file(void)
1294 {
1295 char *pwd, *options[32], *abs_path[32], *tmp;
1296 DIR *pwd_dir;
1297 int nr_options = 0, choice = -1, ret = -1;
1298 struct dirent *dent;
1299
1300 pwd = getenv("PWD");
1301 if (!pwd)
1302 return ret;
1303
1304 pwd_dir = opendir(pwd);
1305 if (!pwd_dir)
1306 return ret;
1307
1308 memset(options, 0, sizeof(options));
1309 memset(options, 0, sizeof(abs_path));
1310
1311 while ((dent = readdir(pwd_dir))) {
1312 char path[PATH_MAX];
1313 u64 magic;
1314 char *name = dent->d_name;
1315 FILE *file;
1316
1317 if (!(dent->d_type == DT_REG))
1318 continue;
1319
1320 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1321
1322 file = fopen(path, "r");
1323 if (!file)
1324 continue;
1325
1326 if (fread(&magic, 1, 8, file) < 8)
1327 goto close_file_and_continue;
1328
1329 if (is_perf_magic(magic)) {
1330 options[nr_options] = strdup(name);
1331 if (!options[nr_options])
1332 goto close_file_and_continue;
1333
1334 abs_path[nr_options] = strdup(path);
1335 if (!abs_path[nr_options]) {
1336 zfree(&options[nr_options]);
1337 ui__warning("Can't search all data files due to memory shortage.\n");
1338 fclose(file);
1339 break;
1340 }
1341
1342 nr_options++;
1343 }
1344
1345 close_file_and_continue:
1346 fclose(file);
1347 if (nr_options >= 32) {
1348 ui__warning("Too many perf data files in PWD!\n"
1349 "Only the first 32 files will be listed.\n");
1350 break;
1351 }
1352 }
1353 closedir(pwd_dir);
1354
1355 if (nr_options) {
1356 choice = ui__popup_menu(nr_options, options);
1357 if (choice < nr_options && choice >= 0) {
1358 tmp = strdup(abs_path[choice]);
1359 if (tmp) {
1360 if (is_input_name_malloced)
1361 free((void *)input_name);
1362 input_name = tmp;
1363 is_input_name_malloced = true;
1364 ret = 0;
1365 } else
1366 ui__warning("Data switch failed due to memory shortage!\n");
1367 }
1368 }
1369
1370 free_popup_options(options, nr_options);
1371 free_popup_options(abs_path, nr_options);
1372 return ret;
1373 }
1374
1375 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1376 {
1377 u64 nr_entries = 0;
1378 struct rb_node *nd = rb_first(&hb->hists->entries);
1379
1380 if (hb->min_pcnt == 0) {
1381 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1382 return;
1383 }
1384
1385 while ((nd = hists__filter_entries(nd, hb->hists,
1386 hb->min_pcnt)) != NULL) {
1387 nr_entries++;
1388 nd = rb_next(nd);
1389 }
1390
1391 hb->nr_non_filtered_entries = nr_entries;
1392 }
1393
1394 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1395 const char *helpline, const char *ev_name,
1396 bool left_exits,
1397 struct hist_browser_timer *hbt,
1398 float min_pcnt,
1399 struct perf_session_env *env)
1400 {
1401 struct hists *hists = &evsel->hists;
1402 struct hist_browser *browser = hist_browser__new(hists);
1403 struct branch_info *bi;
1404 struct pstack *fstack;
1405 char *options[16];
1406 int nr_options = 0;
1407 int key = -1;
1408 char buf[64];
1409 char script_opt[64];
1410 int delay_secs = hbt ? hbt->refresh : 0;
1411
1412 #define HIST_BROWSER_HELP_COMMON \
1413 "h/?/F1 Show this window\n" \
1414 "UP/DOWN/PGUP\n" \
1415 "PGDN/SPACE Navigate\n" \
1416 "q/ESC/CTRL+C Exit browser\n\n" \
1417 "For multiple event sessions:\n\n" \
1418 "TAB/UNTAB Switch events\n\n" \
1419 "For symbolic views (--sort has sym):\n\n" \
1420 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1421 "<- Zoom out\n" \
1422 "a Annotate current symbol\n" \
1423 "C Collapse all callchains\n" \
1424 "d Zoom into current DSO\n" \
1425 "E Expand all callchains\n" \
1426 "F Toggle percentage of filtered entries\n" \
1427
1428 /* help messages are sorted by lexical order of the hotkey */
1429 const char report_help[] = HIST_BROWSER_HELP_COMMON
1430 "i Show header information\n"
1431 "P Print histograms to perf.hist.N\n"
1432 "r Run available scripts\n"
1433 "s Switch to another data file in PWD\n"
1434 "t Zoom into current Thread\n"
1435 "V Verbose (DSO names in callchains, etc)\n"
1436 "/ Filter symbol by name";
1437 const char top_help[] = HIST_BROWSER_HELP_COMMON
1438 "P Print histograms to perf.hist.N\n"
1439 "t Zoom into current Thread\n"
1440 "V Verbose (DSO names in callchains, etc)\n"
1441 "/ Filter symbol by name";
1442
1443 if (browser == NULL)
1444 return -1;
1445
1446 if (min_pcnt) {
1447 browser->min_pcnt = min_pcnt;
1448 hist_browser__update_nr_entries(browser);
1449 }
1450
1451 fstack = pstack__new(2);
1452 if (fstack == NULL)
1453 goto out;
1454
1455 ui_helpline__push(helpline);
1456
1457 memset(options, 0, sizeof(options));
1458
1459 while (1) {
1460 const struct thread *thread = NULL;
1461 const struct dso *dso = NULL;
1462 int choice = 0,
1463 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1464 annotate_f = -2, annotate_t = -2, browse_map = -2;
1465 int scripts_comm = -2, scripts_symbol = -2,
1466 scripts_all = -2, switch_data = -2;
1467
1468 nr_options = 0;
1469
1470 key = hist_browser__run(browser, ev_name, hbt);
1471
1472 if (browser->he_selection != NULL) {
1473 thread = hist_browser__selected_thread(browser);
1474 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1475 }
1476 switch (key) {
1477 case K_TAB:
1478 case K_UNTAB:
1479 if (nr_events == 1)
1480 continue;
1481 /*
1482 * Exit the browser, let hists__browser_tree
1483 * go to the next or previous
1484 */
1485 goto out_free_stack;
1486 case 'a':
1487 if (!sort__has_sym) {
1488 ui_browser__warning(&browser->b, delay_secs * 2,
1489 "Annotation is only available for symbolic views, "
1490 "include \"sym*\" in --sort to use it.");
1491 continue;
1492 }
1493
1494 if (browser->selection == NULL ||
1495 browser->selection->sym == NULL ||
1496 browser->selection->map->dso->annotate_warned)
1497 continue;
1498 goto do_annotate;
1499 case 'P':
1500 hist_browser__dump(browser);
1501 continue;
1502 case 'd':
1503 goto zoom_dso;
1504 case 'V':
1505 browser->show_dso = !browser->show_dso;
1506 continue;
1507 case 't':
1508 goto zoom_thread;
1509 case '/':
1510 if (ui_browser__input_window("Symbol to show",
1511 "Please enter the name of symbol you want to see",
1512 buf, "ENTER: OK, ESC: Cancel",
1513 delay_secs * 2) == K_ENTER) {
1514 hists->symbol_filter_str = *buf ? buf : NULL;
1515 hists__filter_by_symbol(hists);
1516 hist_browser__reset(browser);
1517 }
1518 continue;
1519 case 'r':
1520 if (is_report_browser(hbt))
1521 goto do_scripts;
1522 continue;
1523 case 's':
1524 if (is_report_browser(hbt))
1525 goto do_data_switch;
1526 continue;
1527 case 'i':
1528 /* env->arch is NULL for live-mode (i.e. perf top) */
1529 if (env->arch)
1530 tui__header_window(env);
1531 continue;
1532 case 'F':
1533 symbol_conf.filter_relative ^= 1;
1534 continue;
1535 case K_F1:
1536 case 'h':
1537 case '?':
1538 ui_browser__help_window(&browser->b,
1539 is_report_browser(hbt) ? report_help : top_help);
1540 continue;
1541 case K_ENTER:
1542 case K_RIGHT:
1543 /* menu */
1544 break;
1545 case K_LEFT: {
1546 const void *top;
1547
1548 if (pstack__empty(fstack)) {
1549 /*
1550 * Go back to the perf_evsel_menu__run or other user
1551 */
1552 if (left_exits)
1553 goto out_free_stack;
1554 continue;
1555 }
1556 top = pstack__pop(fstack);
1557 if (top == &browser->hists->dso_filter)
1558 goto zoom_out_dso;
1559 if (top == &browser->hists->thread_filter)
1560 goto zoom_out_thread;
1561 continue;
1562 }
1563 case K_ESC:
1564 if (!left_exits &&
1565 !ui_browser__dialog_yesno(&browser->b,
1566 "Do you really want to exit?"))
1567 continue;
1568 /* Fall thru */
1569 case 'q':
1570 case CTRL('c'):
1571 goto out_free_stack;
1572 default:
1573 continue;
1574 }
1575
1576 if (!sort__has_sym)
1577 goto add_exit_option;
1578
1579 if (sort__mode == SORT_MODE__BRANCH) {
1580 bi = browser->he_selection->branch_info;
1581 if (browser->selection != NULL &&
1582 bi &&
1583 bi->from.sym != NULL &&
1584 !bi->from.map->dso->annotate_warned &&
1585 asprintf(&options[nr_options], "Annotate %s",
1586 bi->from.sym->name) > 0)
1587 annotate_f = nr_options++;
1588
1589 if (browser->selection != NULL &&
1590 bi &&
1591 bi->to.sym != NULL &&
1592 !bi->to.map->dso->annotate_warned &&
1593 (bi->to.sym != bi->from.sym ||
1594 bi->to.map->dso != bi->from.map->dso) &&
1595 asprintf(&options[nr_options], "Annotate %s",
1596 bi->to.sym->name) > 0)
1597 annotate_t = nr_options++;
1598 } else {
1599
1600 if (browser->selection != NULL &&
1601 browser->selection->sym != NULL &&
1602 !browser->selection->map->dso->annotate_warned &&
1603 asprintf(&options[nr_options], "Annotate %s",
1604 browser->selection->sym->name) > 0)
1605 annotate = nr_options++;
1606 }
1607
1608 if (thread != NULL &&
1609 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1610 (browser->hists->thread_filter ? "out of" : "into"),
1611 (thread->comm_set ? thread__comm_str(thread) : ""),
1612 thread->tid) > 0)
1613 zoom_thread = nr_options++;
1614
1615 if (dso != NULL &&
1616 asprintf(&options[nr_options], "Zoom %s %s DSO",
1617 (browser->hists->dso_filter ? "out of" : "into"),
1618 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1619 zoom_dso = nr_options++;
1620
1621 if (browser->selection != NULL &&
1622 browser->selection->map != NULL &&
1623 asprintf(&options[nr_options], "Browse map details") > 0)
1624 browse_map = nr_options++;
1625
1626 /* perf script support */
1627 if (browser->he_selection) {
1628 struct symbol *sym;
1629
1630 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1631 thread__comm_str(browser->he_selection->thread)) > 0)
1632 scripts_comm = nr_options++;
1633
1634 sym = browser->he_selection->ms.sym;
1635 if (sym && sym->namelen &&
1636 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1637 sym->name) > 0)
1638 scripts_symbol = nr_options++;
1639 }
1640
1641 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1642 scripts_all = nr_options++;
1643
1644 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1645 "Switch to another data file in PWD") > 0)
1646 switch_data = nr_options++;
1647 add_exit_option:
1648 options[nr_options++] = (char *)"Exit";
1649 retry_popup_menu:
1650 choice = ui__popup_menu(nr_options, options);
1651
1652 if (choice == nr_options - 1)
1653 break;
1654
1655 if (choice == -1) {
1656 free_popup_options(options, nr_options - 1);
1657 continue;
1658 }
1659
1660 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1661 struct hist_entry *he;
1662 int err;
1663 do_annotate:
1664 if (!objdump_path && perf_session_env__lookup_objdump(env))
1665 continue;
1666
1667 he = hist_browser__selected_entry(browser);
1668 if (he == NULL)
1669 continue;
1670
1671 /*
1672 * we stash the branch_info symbol + map into the
1673 * the ms so we don't have to rewrite all the annotation
1674 * code to use branch_info.
1675 * in branch mode, the ms struct is not used
1676 */
1677 if (choice == annotate_f) {
1678 he->ms.sym = he->branch_info->from.sym;
1679 he->ms.map = he->branch_info->from.map;
1680 } else if (choice == annotate_t) {
1681 he->ms.sym = he->branch_info->to.sym;
1682 he->ms.map = he->branch_info->to.map;
1683 }
1684
1685 /*
1686 * Don't let this be freed, say, by hists__decay_entry.
1687 */
1688 he->used = true;
1689 err = hist_entry__tui_annotate(he, evsel, hbt);
1690 he->used = false;
1691 /*
1692 * offer option to annotate the other branch source or target
1693 * (if they exists) when returning from annotate
1694 */
1695 if ((err == 'q' || err == CTRL('c'))
1696 && annotate_t != -2 && annotate_f != -2)
1697 goto retry_popup_menu;
1698
1699 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1700 if (err)
1701 ui_browser__handle_resize(&browser->b);
1702
1703 } else if (choice == browse_map)
1704 map__browse(browser->selection->map);
1705 else if (choice == zoom_dso) {
1706 zoom_dso:
1707 if (browser->hists->dso_filter) {
1708 pstack__remove(fstack, &browser->hists->dso_filter);
1709 zoom_out_dso:
1710 ui_helpline__pop();
1711 browser->hists->dso_filter = NULL;
1712 sort_dso.elide = false;
1713 } else {
1714 if (dso == NULL)
1715 continue;
1716 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1717 dso->kernel ? "the Kernel" : dso->short_name);
1718 browser->hists->dso_filter = dso;
1719 sort_dso.elide = true;
1720 pstack__push(fstack, &browser->hists->dso_filter);
1721 }
1722 hists__filter_by_dso(hists);
1723 hist_browser__reset(browser);
1724 } else if (choice == zoom_thread) {
1725 zoom_thread:
1726 if (browser->hists->thread_filter) {
1727 pstack__remove(fstack, &browser->hists->thread_filter);
1728 zoom_out_thread:
1729 ui_helpline__pop();
1730 browser->hists->thread_filter = NULL;
1731 sort_thread.elide = false;
1732 } else {
1733 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1734 thread->comm_set ? thread__comm_str(thread) : "",
1735 thread->tid);
1736 browser->hists->thread_filter = thread;
1737 sort_thread.elide = true;
1738 pstack__push(fstack, &browser->hists->thread_filter);
1739 }
1740 hists__filter_by_thread(hists);
1741 hist_browser__reset(browser);
1742 }
1743 /* perf scripts support */
1744 else if (choice == scripts_all || choice == scripts_comm ||
1745 choice == scripts_symbol) {
1746 do_scripts:
1747 memset(script_opt, 0, 64);
1748
1749 if (choice == scripts_comm)
1750 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1751
1752 if (choice == scripts_symbol)
1753 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1754
1755 script_browse(script_opt);
1756 }
1757 /* Switch to another data file */
1758 else if (choice == switch_data) {
1759 do_data_switch:
1760 if (!switch_data_file()) {
1761 key = K_SWITCH_INPUT_DATA;
1762 break;
1763 } else
1764 ui__warning("Won't switch the data files due to\n"
1765 "no valid data file get selected!\n");
1766 }
1767 }
1768 out_free_stack:
1769 pstack__delete(fstack);
1770 out:
1771 hist_browser__delete(browser);
1772 free_popup_options(options, nr_options - 1);
1773 return key;
1774 }
1775
1776 struct perf_evsel_menu {
1777 struct ui_browser b;
1778 struct perf_evsel *selection;
1779 bool lost_events, lost_events_warned;
1780 float min_pcnt;
1781 struct perf_session_env *env;
1782 };
1783
1784 static void perf_evsel_menu__write(struct ui_browser *browser,
1785 void *entry, int row)
1786 {
1787 struct perf_evsel_menu *menu = container_of(browser,
1788 struct perf_evsel_menu, b);
1789 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1790 bool current_entry = ui_browser__is_current_entry(browser, row);
1791 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1792 const char *ev_name = perf_evsel__name(evsel);
1793 char bf[256], unit;
1794 const char *warn = " ";
1795 size_t printed;
1796
1797 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1798 HE_COLORSET_NORMAL);
1799
1800 if (perf_evsel__is_group_event(evsel)) {
1801 struct perf_evsel *pos;
1802
1803 ev_name = perf_evsel__group_name(evsel);
1804
1805 for_each_group_member(pos, evsel) {
1806 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1807 }
1808 }
1809
1810 nr_events = convert_unit(nr_events, &unit);
1811 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1812 unit, unit == ' ' ? "" : " ", ev_name);
1813 slsmg_printf("%s", bf);
1814
1815 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1816 if (nr_events != 0) {
1817 menu->lost_events = true;
1818 if (!current_entry)
1819 ui_browser__set_color(browser, HE_COLORSET_TOP);
1820 nr_events = convert_unit(nr_events, &unit);
1821 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1822 nr_events, unit, unit == ' ' ? "" : " ");
1823 warn = bf;
1824 }
1825
1826 slsmg_write_nstring(warn, browser->width - printed);
1827
1828 if (current_entry)
1829 menu->selection = evsel;
1830 }
1831
1832 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1833 int nr_events, const char *help,
1834 struct hist_browser_timer *hbt)
1835 {
1836 struct perf_evlist *evlist = menu->b.priv;
1837 struct perf_evsel *pos;
1838 const char *ev_name, *title = "Available samples";
1839 int delay_secs = hbt ? hbt->refresh : 0;
1840 int key;
1841
1842 if (ui_browser__show(&menu->b, title,
1843 "ESC: exit, ENTER|->: Browse histograms") < 0)
1844 return -1;
1845
1846 while (1) {
1847 key = ui_browser__run(&menu->b, delay_secs);
1848
1849 switch (key) {
1850 case K_TIMER:
1851 hbt->timer(hbt->arg);
1852
1853 if (!menu->lost_events_warned && menu->lost_events) {
1854 ui_browser__warn_lost_events(&menu->b);
1855 menu->lost_events_warned = true;
1856 }
1857 continue;
1858 case K_RIGHT:
1859 case K_ENTER:
1860 if (!menu->selection)
1861 continue;
1862 pos = menu->selection;
1863 browse_hists:
1864 perf_evlist__set_selected(evlist, pos);
1865 /*
1866 * Give the calling tool a chance to populate the non
1867 * default evsel resorted hists tree.
1868 */
1869 if (hbt)
1870 hbt->timer(hbt->arg);
1871 ev_name = perf_evsel__name(pos);
1872 key = perf_evsel__hists_browse(pos, nr_events, help,
1873 ev_name, true, hbt,
1874 menu->min_pcnt,
1875 menu->env);
1876 ui_browser__show_title(&menu->b, title);
1877 switch (key) {
1878 case K_TAB:
1879 if (pos->node.next == &evlist->entries)
1880 pos = perf_evlist__first(evlist);
1881 else
1882 pos = perf_evsel__next(pos);
1883 goto browse_hists;
1884 case K_UNTAB:
1885 if (pos->node.prev == &evlist->entries)
1886 pos = perf_evlist__last(evlist);
1887 else
1888 pos = perf_evsel__prev(pos);
1889 goto browse_hists;
1890 case K_ESC:
1891 if (!ui_browser__dialog_yesno(&menu->b,
1892 "Do you really want to exit?"))
1893 continue;
1894 /* Fall thru */
1895 case K_SWITCH_INPUT_DATA:
1896 case 'q':
1897 case CTRL('c'):
1898 goto out;
1899 default:
1900 continue;
1901 }
1902 case K_LEFT:
1903 continue;
1904 case K_ESC:
1905 if (!ui_browser__dialog_yesno(&menu->b,
1906 "Do you really want to exit?"))
1907 continue;
1908 /* Fall thru */
1909 case 'q':
1910 case CTRL('c'):
1911 goto out;
1912 default:
1913 continue;
1914 }
1915 }
1916
1917 out:
1918 ui_browser__hide(&menu->b);
1919 return key;
1920 }
1921
1922 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1923 void *entry)
1924 {
1925 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1926
1927 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1928 return true;
1929
1930 return false;
1931 }
1932
1933 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1934 int nr_entries, const char *help,
1935 struct hist_browser_timer *hbt,
1936 float min_pcnt,
1937 struct perf_session_env *env)
1938 {
1939 struct perf_evsel *pos;
1940 struct perf_evsel_menu menu = {
1941 .b = {
1942 .entries = &evlist->entries,
1943 .refresh = ui_browser__list_head_refresh,
1944 .seek = ui_browser__list_head_seek,
1945 .write = perf_evsel_menu__write,
1946 .filter = filter_group_entries,
1947 .nr_entries = nr_entries,
1948 .priv = evlist,
1949 },
1950 .min_pcnt = min_pcnt,
1951 .env = env,
1952 };
1953
1954 ui_helpline__push("Press ESC to exit");
1955
1956 evlist__for_each(evlist, pos) {
1957 const char *ev_name = perf_evsel__name(pos);
1958 size_t line_len = strlen(ev_name) + 7;
1959
1960 if (menu.b.width < line_len)
1961 menu.b.width = line_len;
1962 }
1963
1964 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1965 }
1966
1967 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1968 struct hist_browser_timer *hbt,
1969 float min_pcnt,
1970 struct perf_session_env *env)
1971 {
1972 int nr_entries = evlist->nr_entries;
1973
1974 single_entry:
1975 if (nr_entries == 1) {
1976 struct perf_evsel *first = perf_evlist__first(evlist);
1977 const char *ev_name = perf_evsel__name(first);
1978
1979 return perf_evsel__hists_browse(first, nr_entries, help,
1980 ev_name, false, hbt, min_pcnt,
1981 env);
1982 }
1983
1984 if (symbol_conf.event_group) {
1985 struct perf_evsel *pos;
1986
1987 nr_entries = 0;
1988 evlist__for_each(evlist, pos) {
1989 if (perf_evsel__is_group_leader(pos))
1990 nr_entries++;
1991 }
1992
1993 if (nr_entries == 1)
1994 goto single_entry;
1995 }
1996
1997 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1998 hbt, min_pcnt, env);
1999 }
This page took 0.070558 seconds and 6 git commands to generate.