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