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