1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
13
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../arch/common.h"
33 #include "../../perf.h"
34
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
37 #include "../util.h"
38 #include "../ui.h"
39 #include "map.h"
40 #include "annotate.h"
41 #include "srcline.h"
42 #include "string2.h"
43 #include "units.h"
44 #include "time-utils.h"
45
46 #include <linux/ctype.h>
47
48 extern void hist_browser__init_hpp(void);
49
50 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51 static void hist_browser__update_nr_entries(struct hist_browser *hb);
52
53 static struct rb_node *hists__filter_entries(struct rb_node *nd,
54 float min_pcnt);
55
hist_browser__has_filter(struct hist_browser *hb)56 static bool hist_browser__has_filter(struct hist_browser *hb)
57 {
58 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
59 }
60
hist_browser__get_folding(struct hist_browser *browser)61 static int hist_browser__get_folding(struct hist_browser *browser)
62 {
63 struct rb_node *nd;
64 struct hists *hists = browser->hists;
65 int unfolded_rows = 0;
66
67 for (nd = rb_first_cached(&hists->entries);
68 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69 nd = rb_hierarchy_next(nd)) {
70 struct hist_entry *he =
71 rb_entry(nd, struct hist_entry, rb_node);
72
73 if (he->leaf && he->unfolded)
74 unfolded_rows += he->nr_rows;
75 }
76 return unfolded_rows;
77 }
78
hist_browser__set_title_space(struct hist_browser *hb)79 static void hist_browser__set_title_space(struct hist_browser *hb)
80 {
81 struct ui_browser *browser = &hb->b;
82 struct hists *hists = hb->hists;
83 struct perf_hpp_list *hpp_list = hists->hpp_list;
84
85 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
86 }
87
hist_browser__nr_entries(struct hist_browser *hb)88 static u32 hist_browser__nr_entries(struct hist_browser *hb)
89 {
90 u32 nr_entries;
91
92 if (symbol_conf.report_hierarchy)
93 nr_entries = hb->nr_hierarchy_entries;
94 else if (hist_browser__has_filter(hb))
95 nr_entries = hb->nr_non_filtered_entries;
96 else
97 nr_entries = hb->hists->nr_entries;
98
99 hb->nr_callchain_rows = hist_browser__get_folding(hb);
100 return nr_entries + hb->nr_callchain_rows;
101 }
102
hist_browser__update_rows(struct hist_browser *hb)103 static void hist_browser__update_rows(struct hist_browser *hb)
104 {
105 struct ui_browser *browser = &hb->b;
106 struct hists *hists = hb->hists;
107 struct perf_hpp_list *hpp_list = hists->hpp_list;
108 u16 index_row;
109
110 if (!hb->show_headers) {
111 browser->rows += browser->extra_title_lines;
112 browser->extra_title_lines = 0;
113 return;
114 }
115
116 browser->extra_title_lines = hpp_list->nr_header_lines;
117 browser->rows -= browser->extra_title_lines;
118 /*
119 * Verify if we were at the last line and that line isn't
120 * visibe because we now show the header line(s).
121 */
122 index_row = browser->index - browser->top_idx;
123 if (index_row >= browser->rows)
124 browser->index -= index_row - browser->rows + 1;
125 }
126
hist_browser__refresh_dimensions(struct ui_browser *browser)127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
128 {
129 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
130
131 /* 3 == +/- toggle symbol before actual hist_entry rendering */
132 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
133 /*
134 * FIXME: Just keeping existing behaviour, but this really should be
135 * before updating browser->width, as it will invalidate the
136 * calculation above. Fix this and the fallout in another
137 * changeset.
138 */
139 ui_browser__refresh_dimensions(browser);
140 }
141
hist_browser__reset(struct hist_browser *browser)142 static void hist_browser__reset(struct hist_browser *browser)
143 {
144 /*
145 * The hists__remove_entry_filter() already folds non-filtered
146 * entries so we can assume it has 0 callchain rows.
147 */
148 browser->nr_callchain_rows = 0;
149
150 hist_browser__update_nr_entries(browser);
151 browser->b.nr_entries = hist_browser__nr_entries(browser);
152 hist_browser__refresh_dimensions(&browser->b);
153 ui_browser__reset_index(&browser->b);
154 }
155
tree__folded_sign(bool unfolded)156 static char tree__folded_sign(bool unfolded)
157 {
158 return unfolded ? '-' : '+';
159 }
160
hist_entry__folded(const struct hist_entry *he)161 static char hist_entry__folded(const struct hist_entry *he)
162 {
163 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164 }
165
callchain_list__folded(const struct callchain_list *cl)166 static char callchain_list__folded(const struct callchain_list *cl)
167 {
168 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169 }
170
callchain_list__set_folding(struct callchain_list *cl, bool unfold)171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
172 {
173 cl->unfolded = unfold ? cl->has_children : false;
174 }
175
callchain_node__count_rows_rb_tree(struct callchain_node *node)176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
177 {
178 int n = 0;
179 struct rb_node *nd;
180
181 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183 struct callchain_list *chain;
184 char folded_sign = ' '; /* No children */
185
186 list_for_each_entry(chain, &child->val, list) {
187 ++n;
188
189 /* We need this because we may not have children */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
192 break;
193 }
194
195 if (folded_sign == '-') /* Have children and they're unfolded */
196 n += callchain_node__count_rows_rb_tree(child);
197 }
198
199 return n;
200 }
201
callchain_node__count_flat_rows(struct callchain_node *node)202 static int callchain_node__count_flat_rows(struct callchain_node *node)
203 {
204 struct callchain_list *chain;
205 char folded_sign = 0;
206 int n = 0;
207
208 list_for_each_entry(chain, &node->parent_val, list) {
209 if (!folded_sign) {
210 /* only check first chain list entry */
211 folded_sign = callchain_list__folded(chain);
212 if (folded_sign == '+')
213 return 1;
214 }
215 n++;
216 }
217
218 list_for_each_entry(chain, &node->val, list) {
219 if (!folded_sign) {
220 /* node->parent_val list might be empty */
221 folded_sign = callchain_list__folded(chain);
222 if (folded_sign == '+')
223 return 1;
224 }
225 n++;
226 }
227
228 return n;
229 }
230
callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
232 {
233 return 1;
234 }
235
callchain_node__count_rows(struct callchain_node *node)236 static int callchain_node__count_rows(struct callchain_node *node)
237 {
238 struct callchain_list *chain;
239 bool unfolded = false;
240 int n = 0;
241
242 if (callchain_param.mode == CHAIN_FLAT)
243 return callchain_node__count_flat_rows(node);
244 else if (callchain_param.mode == CHAIN_FOLDED)
245 return callchain_node__count_folded_rows(node);
246
247 list_for_each_entry(chain, &node->val, list) {
248 ++n;
249
250 unfolded = chain->unfolded;
251 }
252
253 if (unfolded)
254 n += callchain_node__count_rows_rb_tree(node);
255
256 return n;
257 }
258
callchain__count_rows(struct rb_root *chain)259 static int callchain__count_rows(struct rb_root *chain)
260 {
261 struct rb_node *nd;
262 int n = 0;
263
264 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266 n += callchain_node__count_rows(node);
267 }
268
269 return n;
270 }
271
hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he, bool include_children)272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273 bool include_children)
274 {
275 int count = 0;
276 struct rb_node *node;
277 struct hist_entry *child;
278
279 if (he->leaf)
280 return callchain__count_rows(&he->sorted_chain);
281
282 if (he->has_no_entry)
283 return 1;
284
285 node = rb_first_cached(&he->hroot_out);
286 while (node) {
287 float percent;
288
289 child = rb_entry(node, struct hist_entry, rb_node);
290 percent = hist_entry__get_percent_limit(child);
291
292 if (!child->filtered && percent >= hb->min_pcnt) {
293 count++;
294
295 if (include_children && child->unfolded)
296 count += hierarchy_count_rows(hb, child, true);
297 }
298
299 node = rb_next(node);
300 }
301 return count;
302 }
303
hist_entry__toggle_fold(struct hist_entry *he)304 static bool hist_entry__toggle_fold(struct hist_entry *he)
305 {
306 if (!he)
307 return false;
308
309 if (!he->has_children)
310 return false;
311
312 he->unfolded = !he->unfolded;
313 return true;
314 }
315
callchain_list__toggle_fold(struct callchain_list *cl)316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
317 {
318 if (!cl)
319 return false;
320
321 if (!cl->has_children)
322 return false;
323
324 cl->unfolded = !cl->unfolded;
325 return true;
326 }
327
callchain_node__init_have_children_rb_tree(struct callchain_node *node)328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
329 {
330 struct rb_node *nd = rb_first(&node->rb_root);
331
332 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334 struct callchain_list *chain;
335 bool first = true;
336
337 list_for_each_entry(chain, &child->val, list) {
338 if (first) {
339 first = false;
340 chain->has_children = chain->list.next != &child->val ||
341 !RB_EMPTY_ROOT(&child->rb_root);
342 } else
343 chain->has_children = chain->list.next == &child->val &&
344 !RB_EMPTY_ROOT(&child->rb_root);
345 }
346
347 callchain_node__init_have_children_rb_tree(child);
348 }
349 }
350
callchain_node__init_have_children(struct callchain_node *node, bool has_sibling)351 static void callchain_node__init_have_children(struct callchain_node *node,
352 bool has_sibling)
353 {
354 struct callchain_list *chain;
355
356 chain = list_entry(node->val.next, struct callchain_list, list);
357 chain->has_children = has_sibling;
358
359 if (!list_empty(&node->val)) {
360 chain = list_entry(node->val.prev, struct callchain_list, list);
361 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
362 }
363
364 callchain_node__init_have_children_rb_tree(node);
365 }
366
callchain__init_have_children(struct rb_root *root)367 static void callchain__init_have_children(struct rb_root *root)
368 {
369 struct rb_node *nd = rb_first(root);
370 bool has_sibling = nd && rb_next(nd);
371
372 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374 callchain_node__init_have_children(node, has_sibling);
375 if (callchain_param.mode == CHAIN_FLAT ||
376 callchain_param.mode == CHAIN_FOLDED)
377 callchain_node__make_parent_list(node);
378 }
379 }
380
hist_entry__init_have_children(struct hist_entry *he)381 static void hist_entry__init_have_children(struct hist_entry *he)
382 {
383 if (he->init_have_children)
384 return;
385
386 if (he->leaf) {
387 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
388 callchain__init_have_children(&he->sorted_chain);
389 } else {
390 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391 }
392
393 he->init_have_children = true;
394 }
395
hist_browser__selection_has_children(struct hist_browser *browser)396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
397 {
398 struct hist_entry *he = browser->he_selection;
399 struct map_symbol *ms = browser->selection;
400
401 if (!he || !ms)
402 return false;
403
404 if (ms == &he->ms)
405 return he->has_children;
406
407 return container_of(ms, struct callchain_list, ms)->has_children;
408 }
409
hist_browser__selection_unfolded(struct hist_browser *browser)410 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
411 {
412 struct hist_entry *he = browser->he_selection;
413 struct map_symbol *ms = browser->selection;
414
415 if (!he || !ms)
416 return false;
417
418 if (ms == &he->ms)
419 return he->unfolded;
420
421 return container_of(ms, struct callchain_list, ms)->unfolded;
422 }
423
hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)424 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
425 {
426 struct hist_entry *he = browser->he_selection;
427 struct map_symbol *ms = browser->selection;
428 struct callchain_list *callchain_entry;
429
430 if (!he || !ms)
431 return NULL;
432
433 if (ms == &he->ms) {
434 hist_entry__sym_snprintf(he, bf, size, 0);
435 return bf + 4; // skip the level, e.g. '[k] '
436 }
437
438 callchain_entry = container_of(ms, struct callchain_list, ms);
439 return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
440 }
441
hist_browser__toggle_fold(struct hist_browser *browser)442 static bool hist_browser__toggle_fold(struct hist_browser *browser)
443 {
444 struct hist_entry *he = browser->he_selection;
445 struct map_symbol *ms = browser->selection;
446 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
447 bool has_children;
448
449 if (!he || !ms)
450 return false;
451
452 if (ms == &he->ms)
453 has_children = hist_entry__toggle_fold(he);
454 else
455 has_children = callchain_list__toggle_fold(cl);
456
457 if (has_children) {
458 int child_rows = 0;
459
460 hist_entry__init_have_children(he);
461 browser->b.nr_entries -= he->nr_rows;
462
463 if (he->leaf)
464 browser->nr_callchain_rows -= he->nr_rows;
465 else
466 browser->nr_hierarchy_entries -= he->nr_rows;
467
468 if (symbol_conf.report_hierarchy)
469 child_rows = hierarchy_count_rows(browser, he, true);
470
471 if (he->unfolded) {
472 if (he->leaf)
473 he->nr_rows = callchain__count_rows(
474 &he->sorted_chain);
475 else
476 he->nr_rows = hierarchy_count_rows(browser, he, false);
477
478 /* account grand children */
479 if (symbol_conf.report_hierarchy)
480 browser->b.nr_entries += child_rows - he->nr_rows;
481
482 if (!he->leaf && he->nr_rows == 0) {
483 he->has_no_entry = true;
484 he->nr_rows = 1;
485 }
486 } else {
487 if (symbol_conf.report_hierarchy)
488 browser->b.nr_entries -= child_rows - he->nr_rows;
489
490 if (he->has_no_entry)
491 he->has_no_entry = false;
492
493 he->nr_rows = 0;
494 }
495
496 browser->b.nr_entries += he->nr_rows;
497
498 if (he->leaf)
499 browser->nr_callchain_rows += he->nr_rows;
500 else
501 browser->nr_hierarchy_entries += he->nr_rows;
502
503 return true;
504 }
505
506 /* If it doesn't have children, no toggling performed */
507 return false;
508 }
509
callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)510 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
511 {
512 int n = 0;
513 struct rb_node *nd;
514
515 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
516 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
517 struct callchain_list *chain;
518 bool has_children = false;
519
520 list_for_each_entry(chain, &child->val, list) {
521 ++n;
522 callchain_list__set_folding(chain, unfold);
523 has_children = chain->has_children;
524 }
525
526 if (has_children)
527 n += callchain_node__set_folding_rb_tree(child, unfold);
528 }
529
530 return n;
531 }
532
callchain_node__set_folding(struct callchain_node *node, bool unfold)533 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
534 {
535 struct callchain_list *chain;
536 bool has_children = false;
537 int n = 0;
538
539 list_for_each_entry(chain, &node->val, list) {
540 ++n;
541 callchain_list__set_folding(chain, unfold);
542 has_children = chain->has_children;
543 }
544
545 if (has_children)
546 n += callchain_node__set_folding_rb_tree(node, unfold);
547
548 return n;
549 }
550
callchain__set_folding(struct rb_root *chain, bool unfold)551 static int callchain__set_folding(struct rb_root *chain, bool unfold)
552 {
553 struct rb_node *nd;
554 int n = 0;
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 n += callchain_node__set_folding(node, unfold);
559 }
560
561 return n;
562 }
563
hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, bool unfold __maybe_unused)564 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
565 bool unfold __maybe_unused)
566 {
567 float percent;
568 struct rb_node *nd;
569 struct hist_entry *child;
570 int n = 0;
571
572 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
573 child = rb_entry(nd, struct hist_entry, rb_node);
574 percent = hist_entry__get_percent_limit(child);
575 if (!child->filtered && percent >= hb->min_pcnt)
576 n++;
577 }
578
579 return n;
580 }
581
hist_entry__set_folding(struct hist_entry *he, struct hist_browser *hb, bool unfold)582 static void hist_entry__set_folding(struct hist_entry *he,
583 struct hist_browser *hb, bool unfold)
584 {
585 hist_entry__init_have_children(he);
586 he->unfolded = unfold ? he->has_children : false;
587
588 if (he->has_children) {
589 int n;
590
591 if (he->leaf)
592 n = callchain__set_folding(&he->sorted_chain, unfold);
593 else
594 n = hierarchy_set_folding(hb, he, unfold);
595
596 he->nr_rows = unfold ? n : 0;
597 } else
598 he->nr_rows = 0;
599 }
600
601 static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)602 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
603 {
604 struct rb_node *nd;
605 struct hist_entry *he;
606 double percent;
607
608 nd = rb_first_cached(&browser->hists->entries);
609 while (nd) {
610 he = rb_entry(nd, struct hist_entry, rb_node);
611
612 /* set folding state even if it's currently folded */
613 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
614
615 hist_entry__set_folding(he, browser, unfold);
616
617 percent = hist_entry__get_percent_limit(he);
618 if (he->filtered || percent < browser->min_pcnt)
619 continue;
620
621 if (!he->depth || unfold)
622 browser->nr_hierarchy_entries++;
623 if (he->leaf)
624 browser->nr_callchain_rows += he->nr_rows;
625 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
626 browser->nr_hierarchy_entries++;
627 he->has_no_entry = true;
628 he->nr_rows = 1;
629 } else
630 he->has_no_entry = false;
631 }
632 }
633
hist_browser__set_folding(struct hist_browser *browser, bool unfold)634 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
635 {
636 browser->nr_hierarchy_entries = 0;
637 browser->nr_callchain_rows = 0;
638 __hist_browser__set_folding(browser, unfold);
639
640 browser->b.nr_entries = hist_browser__nr_entries(browser);
641 /* Go to the start, we may be way after valid entries after a collapse */
642 ui_browser__reset_index(&browser->b);
643 }
644
hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)645 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
646 {
647 if (!browser->he_selection)
648 return;
649
650 if (unfold == browser->he_selection->unfolded)
651 return;
652
653 hist_browser__toggle_fold(browser);
654 }
655
ui_browser__warn_lost_events(struct ui_browser *browser)656 static void ui_browser__warn_lost_events(struct ui_browser *browser)
657 {
658 ui_browser__warning(browser, 4,
659 "Events are being lost, check IO/CPU overload!\n\n"
660 "You may want to run 'perf' using a RT scheduler policy:\n\n"
661 " perf top -r 80\n\n"
662 "Or reduce the sampling frequency.");
663 }
664
hist_browser__title(struct hist_browser *browser, char *bf, size_t size)665 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
666 {
667 return browser->title ? browser->title(browser, bf, size) : 0;
668 }
669
hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)670 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
671 {
672 switch (key) {
673 case K_TIMER: {
674 struct hist_browser_timer *hbt = browser->hbt;
675 u64 nr_entries;
676
677 WARN_ON_ONCE(!hbt);
678
679 if (hbt)
680 hbt->timer(hbt->arg);
681
682 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
683 hist_browser__update_nr_entries(browser);
684
685 nr_entries = hist_browser__nr_entries(browser);
686 ui_browser__update_nr_entries(&browser->b, nr_entries);
687
688 if (warn_lost_event &&
689 (browser->hists->stats.nr_lost_warned !=
690 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
691 browser->hists->stats.nr_lost_warned =
692 browser->hists->stats.nr_events[PERF_RECORD_LOST];
693 ui_browser__warn_lost_events(&browser->b);
694 }
695
696 hist_browser__title(browser, title, size);
697 ui_browser__show_title(&browser->b, title);
698 break;
699 }
700 case 'D': { /* Debug */
701 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
702 static int seq;
703
704 ui_helpline__pop();
705 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
706 seq++, browser->b.nr_entries, browser->hists->nr_entries,
707 browser->b.extra_title_lines, browser->b.rows,
708 browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
709 }
710 break;
711 case 'C':
712 /* Collapse the whole world. */
713 hist_browser__set_folding(browser, false);
714 break;
715 case 'c':
716 /* Collapse the selected entry. */
717 hist_browser__set_folding_selected(browser, false);
718 break;
719 case 'E':
720 /* Expand the whole world. */
721 hist_browser__set_folding(browser, true);
722 break;
723 case 'e':
724 /* Toggle expand/collapse the selected entry. */
725 hist_browser__toggle_fold(browser);
726 break;
727 case 'H':
728 browser->show_headers = !browser->show_headers;
729 hist_browser__update_rows(browser);
730 break;
731 case '+':
732 if (hist_browser__toggle_fold(browser))
733 break;
734 /* fall thru */
735 default:
736 return -1;
737 }
738
739 return 0;
740 }
741
hist_browser__run(struct hist_browser *browser, const char *help, bool warn_lost_event, int key)742 int hist_browser__run(struct hist_browser *browser, const char *help,
743 bool warn_lost_event, int key)
744 {
745 char title[160];
746 struct hist_browser_timer *hbt = browser->hbt;
747 int delay_secs = hbt ? hbt->refresh : 0;
748
749 browser->b.entries = &browser->hists->entries;
750 browser->b.nr_entries = hist_browser__nr_entries(browser);
751
752 hist_browser__title(browser, title, sizeof(title));
753
754 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
755 return -1;
756
757 if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
758 goto out;
759
760 while (1) {
761 key = ui_browser__run(&browser->b, delay_secs);
762
763 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
764 break;
765 }
766 out:
767 ui_browser__hide(&browser->b);
768 return key;
769 }
770
771 struct callchain_print_arg {
772 /* for hists browser */
773 off_t row_offset;
774 bool is_current_entry;
775
776 /* for file dump */
777 FILE *fp;
778 int printed;
779 };
780
781 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
782 struct callchain_list *chain,
783 const char *str, int offset,
784 unsigned short row,
785 struct callchain_print_arg *arg);
786
hist_browser__show_callchain_entry(struct hist_browser *browser, struct callchain_list *chain, const char *str, int offset, unsigned short row, struct callchain_print_arg *arg)787 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
788 struct callchain_list *chain,
789 const char *str, int offset,
790 unsigned short row,
791 struct callchain_print_arg *arg)
792 {
793 int color, width;
794 char folded_sign = callchain_list__folded(chain);
795 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
796
797 color = HE_COLORSET_NORMAL;
798 width = browser->b.width - (offset + 2);
799 if (ui_browser__is_current_entry(&browser->b, row)) {
800 browser->selection = &chain->ms;
801 color = HE_COLORSET_SELECTED;
802 arg->is_current_entry = true;
803 }
804
805 ui_browser__set_color(&browser->b, color);
806 ui_browser__gotorc(&browser->b, row, 0);
807 ui_browser__write_nstring(&browser->b, " ", offset);
808 ui_browser__printf(&browser->b, "%c", folded_sign);
809 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
810 ui_browser__write_nstring(&browser->b, str, width);
811 }
812
hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, struct callchain_list *chain, const char *str, int offset, unsigned short row __maybe_unused, struct callchain_print_arg *arg)813 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
814 struct callchain_list *chain,
815 const char *str, int offset,
816 unsigned short row __maybe_unused,
817 struct callchain_print_arg *arg)
818 {
819 char folded_sign = callchain_list__folded(chain);
820
821 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
822 folded_sign, str);
823 }
824
825 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
826 unsigned short row);
827
hist_browser__check_output_full(struct hist_browser *browser, unsigned short row)828 static bool hist_browser__check_output_full(struct hist_browser *browser,
829 unsigned short row)
830 {
831 return browser->b.rows == row;
832 }
833
hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, unsigned short row __maybe_unused)834 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
835 unsigned short row __maybe_unused)
836 {
837 return false;
838 }
839
840 #define LEVEL_OFFSET_STEP 3
841
hist_browser__show_callchain_list(struct hist_browser *browser, struct callchain_node *node, struct callchain_list *chain, unsigned short row, u64 total, bool need_percent, int offset, print_callchain_entry_fn print, struct callchain_print_arg *arg)842 static int hist_browser__show_callchain_list(struct hist_browser *browser,
843 struct callchain_node *node,
844 struct callchain_list *chain,
845 unsigned short row, u64 total,
846 bool need_percent, int offset,
847 print_callchain_entry_fn print,
848 struct callchain_print_arg *arg)
849 {
850 char bf[1024], *alloc_str;
851 char buf[64], *alloc_str2;
852 const char *str;
853 int ret = 1;
854
855 if (arg->row_offset != 0) {
856 arg->row_offset--;
857 return 0;
858 }
859
860 alloc_str = NULL;
861 alloc_str2 = NULL;
862
863 str = callchain_list__sym_name(chain, bf, sizeof(bf),
864 browser->show_dso);
865
866 if (symbol_conf.show_branchflag_count) {
867 callchain_list_counts__printf_value(chain, NULL,
868 buf, sizeof(buf));
869
870 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
871 str = "Not enough memory!";
872 else
873 str = alloc_str2;
874 }
875
876 if (need_percent) {
877 callchain_node__scnprintf_value(node, buf, sizeof(buf),
878 total);
879
880 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
881 str = "Not enough memory!";
882 else
883 str = alloc_str;
884 }
885
886 print(browser, chain, str, offset, row, arg);
887 free(alloc_str);
888 free(alloc_str2);
889
890 return ret;
891 }
892
check_percent_display(struct rb_node *node, u64 parent_total)893 static bool check_percent_display(struct rb_node *node, u64 parent_total)
894 {
895 struct callchain_node *child;
896
897 if (node == NULL)
898 return false;
899
900 if (rb_next(node))
901 return true;
902
903 child = rb_entry(node, struct callchain_node, rb_node);
904 return callchain_cumul_hits(child) != parent_total;
905 }
906
hist_browser__show_callchain_flat(struct hist_browser *browser, struct rb_root *root, unsigned short row, u64 total, u64 parent_total, print_callchain_entry_fn print, struct callchain_print_arg *arg, check_output_full_fn is_output_full)907 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
908 struct rb_root *root,
909 unsigned short row, u64 total,
910 u64 parent_total,
911 print_callchain_entry_fn print,
912 struct callchain_print_arg *arg,
913 check_output_full_fn is_output_full)
914 {
915 struct rb_node *node;
916 int first_row = row, offset = LEVEL_OFFSET_STEP;
917 bool need_percent;
918
919 node = rb_first(root);
920 need_percent = check_percent_display(node, parent_total);
921
922 while (node) {
923 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
924 struct rb_node *next = rb_next(node);
925 struct callchain_list *chain;
926 char folded_sign = ' ';
927 int first = true;
928 int extra_offset = 0;
929
930 list_for_each_entry(chain, &child->parent_val, list) {
931 bool was_first = first;
932
933 if (first)
934 first = false;
935 else if (need_percent)
936 extra_offset = LEVEL_OFFSET_STEP;
937
938 folded_sign = callchain_list__folded(chain);
939
940 row += hist_browser__show_callchain_list(browser, child,
941 chain, row, total,
942 was_first && need_percent,
943 offset + extra_offset,
944 print, arg);
945
946 if (is_output_full(browser, row))
947 goto out;
948
949 if (folded_sign == '+')
950 goto next;
951 }
952
953 list_for_each_entry(chain, &child->val, list) {
954 bool was_first = first;
955
956 if (first)
957 first = false;
958 else if (need_percent)
959 extra_offset = LEVEL_OFFSET_STEP;
960
961 folded_sign = callchain_list__folded(chain);
962
963 row += hist_browser__show_callchain_list(browser, child,
964 chain, row, total,
965 was_first && need_percent,
966 offset + extra_offset,
967 print, arg);
968
969 if (is_output_full(browser, row))
970 goto out;
971
972 if (folded_sign == '+')
973 break;
974 }
975
976 next:
977 if (is_output_full(browser, row))
978 break;
979 node = next;
980 }
981 out:
982 return row - first_row;
983 }
984
hist_browser__folded_callchain_str(struct hist_browser *browser, struct callchain_list *chain, char *value_str, char *old_str)985 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
986 struct callchain_list *chain,
987 char *value_str, char *old_str)
988 {
989 char bf[1024];
990 const char *str;
991 char *new;
992
993 str = callchain_list__sym_name(chain, bf, sizeof(bf),
994 browser->show_dso);
995 if (old_str) {
996 if (asprintf(&new, "%s%s%s", old_str,
997 symbol_conf.field_sep ?: ";", str) < 0)
998 new = NULL;
999 } else {
1000 if (value_str) {
1001 if (asprintf(&new, "%s %s", value_str, str) < 0)
1002 new = NULL;
1003 } else {
1004 if (asprintf(&new, "%s", str) < 0)
1005 new = NULL;
1006 }
1007 }
1008 return new;
1009 }
1010
hist_browser__show_callchain_folded(struct hist_browser *browser, struct rb_root *root, unsigned short row, u64 total, u64 parent_total, print_callchain_entry_fn print, struct callchain_print_arg *arg, check_output_full_fn is_output_full)1011 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1012 struct rb_root *root,
1013 unsigned short row, u64 total,
1014 u64 parent_total,
1015 print_callchain_entry_fn print,
1016 struct callchain_print_arg *arg,
1017 check_output_full_fn is_output_full)
1018 {
1019 struct rb_node *node;
1020 int first_row = row, offset = LEVEL_OFFSET_STEP;
1021 bool need_percent;
1022
1023 node = rb_first(root);
1024 need_percent = check_percent_display(node, parent_total);
1025
1026 while (node) {
1027 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1028 struct rb_node *next = rb_next(node);
1029 struct callchain_list *chain, *first_chain = NULL;
1030 int first = true;
1031 char *value_str = NULL, *value_str_alloc = NULL;
1032 char *chain_str = NULL, *chain_str_alloc = NULL;
1033
1034 if (arg->row_offset != 0) {
1035 arg->row_offset--;
1036 goto next;
1037 }
1038
1039 if (need_percent) {
1040 char buf[64];
1041
1042 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1043 if (asprintf(&value_str, "%s", buf) < 0) {
1044 value_str = (char *)"<...>";
1045 goto do_print;
1046 }
1047 value_str_alloc = value_str;
1048 }
1049
1050 list_for_each_entry(chain, &child->parent_val, list) {
1051 chain_str = hist_browser__folded_callchain_str(browser,
1052 chain, value_str, chain_str);
1053 if (first) {
1054 first = false;
1055 first_chain = chain;
1056 }
1057
1058 if (chain_str == NULL) {
1059 chain_str = (char *)"Not enough memory!";
1060 goto do_print;
1061 }
1062
1063 chain_str_alloc = chain_str;
1064 }
1065
1066 list_for_each_entry(chain, &child->val, list) {
1067 chain_str = hist_browser__folded_callchain_str(browser,
1068 chain, value_str, chain_str);
1069 if (first) {
1070 first = false;
1071 first_chain = chain;
1072 }
1073
1074 if (chain_str == NULL) {
1075 chain_str = (char *)"Not enough memory!";
1076 goto do_print;
1077 }
1078
1079 chain_str_alloc = chain_str;
1080 }
1081
1082 do_print:
1083 print(browser, first_chain, chain_str, offset, row++, arg);
1084 free(value_str_alloc);
1085 free(chain_str_alloc);
1086
1087 next:
1088 if (is_output_full(browser, row))
1089 break;
1090 node = next;
1091 }
1092
1093 return row - first_row;
1094 }
1095
hist_browser__show_callchain_graph(struct hist_browser *browser, struct rb_root *root, int level, unsigned short row, u64 total, u64 parent_total, print_callchain_entry_fn print, struct callchain_print_arg *arg, check_output_full_fn is_output_full)1096 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1097 struct rb_root *root, int level,
1098 unsigned short row, u64 total,
1099 u64 parent_total,
1100 print_callchain_entry_fn print,
1101 struct callchain_print_arg *arg,
1102 check_output_full_fn is_output_full)
1103 {
1104 struct rb_node *node;
1105 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1106 bool need_percent;
1107 u64 percent_total = total;
1108
1109 if (callchain_param.mode == CHAIN_GRAPH_REL)
1110 percent_total = parent_total;
1111
1112 node = rb_first(root);
1113 need_percent = check_percent_display(node, parent_total);
1114
1115 while (node) {
1116 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1117 struct rb_node *next = rb_next(node);
1118 struct callchain_list *chain;
1119 char folded_sign = ' ';
1120 int first = true;
1121 int extra_offset = 0;
1122
1123 list_for_each_entry(chain, &child->val, list) {
1124 bool was_first = first;
1125
1126 if (first)
1127 first = false;
1128 else if (need_percent)
1129 extra_offset = LEVEL_OFFSET_STEP;
1130
1131 folded_sign = callchain_list__folded(chain);
1132
1133 row += hist_browser__show_callchain_list(browser, child,
1134 chain, row, percent_total,
1135 was_first && need_percent,
1136 offset + extra_offset,
1137 print, arg);
1138
1139 if (is_output_full(browser, row))
1140 goto out;
1141
1142 if (folded_sign == '+')
1143 break;
1144 }
1145
1146 if (folded_sign == '-') {
1147 const int new_level = level + (extra_offset ? 2 : 1);
1148
1149 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1150 new_level, row, total,
1151 child->children_hit,
1152 print, arg, is_output_full);
1153 }
1154 if (is_output_full(browser, row))
1155 break;
1156 node = next;
1157 }
1158 out:
1159 return row - first_row;
1160 }
1161
hist_browser__show_callchain(struct hist_browser *browser, struct hist_entry *entry, int level, unsigned short row, print_callchain_entry_fn print, struct callchain_print_arg *arg, check_output_full_fn is_output_full)1162 static int hist_browser__show_callchain(struct hist_browser *browser,
1163 struct hist_entry *entry, int level,
1164 unsigned short row,
1165 print_callchain_entry_fn print,
1166 struct callchain_print_arg *arg,
1167 check_output_full_fn is_output_full)
1168 {
1169 u64 total = hists__total_period(entry->hists);
1170 u64 parent_total;
1171 int printed;
1172
1173 if (symbol_conf.cumulate_callchain)
1174 parent_total = entry->stat_acc->period;
1175 else
1176 parent_total = entry->stat.period;
1177
1178 if (callchain_param.mode == CHAIN_FLAT) {
1179 printed = hist_browser__show_callchain_flat(browser,
1180 &entry->sorted_chain, row,
1181 total, parent_total, print, arg,
1182 is_output_full);
1183 } else if (callchain_param.mode == CHAIN_FOLDED) {
1184 printed = hist_browser__show_callchain_folded(browser,
1185 &entry->sorted_chain, row,
1186 total, parent_total, print, arg,
1187 is_output_full);
1188 } else {
1189 printed = hist_browser__show_callchain_graph(browser,
1190 &entry->sorted_chain, level, row,
1191 total, parent_total, print, arg,
1192 is_output_full);
1193 }
1194
1195 if (arg->is_current_entry)
1196 browser->he_selection = entry;
1197
1198 return printed;
1199 }
1200
1201 struct hpp_arg {
1202 struct ui_browser *b;
1203 char folded_sign;
1204 bool current_entry;
1205 };
1206
__hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)1207 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1208 {
1209 struct hpp_arg *arg = hpp->ptr;
1210 int ret, len;
1211 va_list args;
1212 double percent;
1213
1214 va_start(args, fmt);
1215 len = va_arg(args, int);
1216 percent = va_arg(args, double);
1217 va_end(args);
1218
1219 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1220
1221 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1222 ui_browser__printf(arg->b, "%s", hpp->buf);
1223
1224 return ret;
1225 }
1226
1227 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1228 static u64 __hpp_get_##_field(struct hist_entry *he) \
1229 { \
1230 return he->stat._field; \
1231 } \
1232 \
1233 static int \
1234 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1235 struct perf_hpp *hpp, \
1236 struct hist_entry *he) \
1237 { \
1238 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1239 __hpp__slsmg_color_printf, true); \
1240 }
1241
1242 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1243 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1244 { \
1245 return he->stat_acc->_field; \
1246 } \
1247 \
1248 static int \
1249 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1250 struct perf_hpp *hpp, \
1251 struct hist_entry *he) \
1252 { \
1253 if (!symbol_conf.cumulate_callchain) { \
1254 struct hpp_arg *arg = hpp->ptr; \
1255 int len = fmt->user_len ?: fmt->len; \
1256 int ret = scnprintf(hpp->buf, hpp->size, \
1257 "%*s", len, "N/A"); \
1258 ui_browser__printf(arg->b, "%s", hpp->buf); \
1259 \
1260 return ret; \
1261 } \
1262 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1263 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1264 }
1265
__HPP_COLOR_PERCENT_FNnull1266 __HPP_COLOR_PERCENT_FN(overhead, period)
1267 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1268 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1269 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1270 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1271 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1272
1273 #undef __HPP_COLOR_PERCENT_FN
1274 #undef __HPP_COLOR_ACC_PERCENT_FN
1275
1276 void hist_browser__init_hpp(void)
1277 {
1278 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1279 hist_browser__hpp_color_overhead;
1280 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1281 hist_browser__hpp_color_overhead_sys;
1282 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1283 hist_browser__hpp_color_overhead_us;
1284 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1285 hist_browser__hpp_color_overhead_guest_sys;
1286 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1287 hist_browser__hpp_color_overhead_guest_us;
1288 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1289 hist_browser__hpp_color_overhead_acc;
1290
1291 res_sample_init();
1292 }
1293
hist_browser__show_entry(struct hist_browser *browser, struct hist_entry *entry, unsigned short row)1294 static int hist_browser__show_entry(struct hist_browser *browser,
1295 struct hist_entry *entry,
1296 unsigned short row)
1297 {
1298 int printed = 0;
1299 int width = browser->b.width;
1300 char folded_sign = ' ';
1301 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1302 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1303 off_t row_offset = entry->row_offset;
1304 bool first = true;
1305 struct perf_hpp_fmt *fmt;
1306
1307 if (current_entry) {
1308 browser->he_selection = entry;
1309 browser->selection = &entry->ms;
1310 }
1311
1312 if (use_callchain) {
1313 hist_entry__init_have_children(entry);
1314 folded_sign = hist_entry__folded(entry);
1315 }
1316
1317 if (row_offset == 0) {
1318 struct hpp_arg arg = {
1319 .b = &browser->b,
1320 .folded_sign = folded_sign,
1321 .current_entry = current_entry,
1322 };
1323 int column = 0;
1324
1325 ui_browser__gotorc(&browser->b, row, 0);
1326
1327 hists__for_each_format(browser->hists, fmt) {
1328 char s[2048];
1329 struct perf_hpp hpp = {
1330 .buf = s,
1331 .size = sizeof(s),
1332 .ptr = &arg,
1333 };
1334
1335 if (perf_hpp__should_skip(fmt, entry->hists) ||
1336 column++ < browser->b.horiz_scroll)
1337 continue;
1338
1339 if (current_entry && browser->b.navkeypressed) {
1340 ui_browser__set_color(&browser->b,
1341 HE_COLORSET_SELECTED);
1342 } else {
1343 ui_browser__set_color(&browser->b,
1344 HE_COLORSET_NORMAL);
1345 }
1346
1347 if (first) {
1348 if (use_callchain) {
1349 ui_browser__printf(&browser->b, "%c ", folded_sign);
1350 width -= 2;
1351 }
1352 first = false;
1353 } else {
1354 ui_browser__printf(&browser->b, " ");
1355 width -= 2;
1356 }
1357
1358 if (fmt->color) {
1359 int ret = fmt->color(fmt, &hpp, entry);
1360 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1361 /*
1362 * fmt->color() already used ui_browser to
1363 * print the non alignment bits, skip it (+ret):
1364 */
1365 ui_browser__printf(&browser->b, "%s", s + ret);
1366 } else {
1367 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1368 ui_browser__printf(&browser->b, "%s", s);
1369 }
1370 width -= hpp.buf - s;
1371 }
1372
1373 /* The scroll bar isn't being used */
1374 if (!browser->b.navkeypressed)
1375 width += 1;
1376
1377 ui_browser__write_nstring(&browser->b, "", width);
1378
1379 ++row;
1380 ++printed;
1381 } else
1382 --row_offset;
1383
1384 if (folded_sign == '-' && row != browser->b.rows) {
1385 struct callchain_print_arg arg = {
1386 .row_offset = row_offset,
1387 .is_current_entry = current_entry,
1388 };
1389
1390 printed += hist_browser__show_callchain(browser,
1391 entry, 1, row,
1392 hist_browser__show_callchain_entry,
1393 &arg,
1394 hist_browser__check_output_full);
1395 }
1396
1397 return printed;
1398 }
1399
hist_browser__show_hierarchy_entry(struct hist_browser *browser, struct hist_entry *entry, unsigned short row, int level)1400 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1401 struct hist_entry *entry,
1402 unsigned short row,
1403 int level)
1404 {
1405 int printed = 0;
1406 int width = browser->b.width;
1407 char folded_sign = ' ';
1408 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1409 off_t row_offset = entry->row_offset;
1410 bool first = true;
1411 struct perf_hpp_fmt *fmt;
1412 struct perf_hpp_list_node *fmt_node;
1413 struct hpp_arg arg = {
1414 .b = &browser->b,
1415 .current_entry = current_entry,
1416 };
1417 int column = 0;
1418 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1419
1420 if (current_entry) {
1421 browser->he_selection = entry;
1422 browser->selection = &entry->ms;
1423 }
1424
1425 hist_entry__init_have_children(entry);
1426 folded_sign = hist_entry__folded(entry);
1427 arg.folded_sign = folded_sign;
1428
1429 if (entry->leaf && row_offset) {
1430 row_offset--;
1431 goto show_callchain;
1432 }
1433
1434 ui_browser__gotorc(&browser->b, row, 0);
1435
1436 if (current_entry && browser->b.navkeypressed)
1437 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1438 else
1439 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1440
1441 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1442 width -= level * HIERARCHY_INDENT;
1443
1444 /* the first hpp_list_node is for overhead columns */
1445 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1446 struct perf_hpp_list_node, list);
1447 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1448 char s[2048];
1449 struct perf_hpp hpp = {
1450 .buf = s,
1451 .size = sizeof(s),
1452 .ptr = &arg,
1453 };
1454
1455 if (perf_hpp__should_skip(fmt, entry->hists) ||
1456 column++ < browser->b.horiz_scroll)
1457 continue;
1458
1459 if (current_entry && browser->b.navkeypressed) {
1460 ui_browser__set_color(&browser->b,
1461 HE_COLORSET_SELECTED);
1462 } else {
1463 ui_browser__set_color(&browser->b,
1464 HE_COLORSET_NORMAL);
1465 }
1466
1467 if (first) {
1468 ui_browser__printf(&browser->b, "%c ", folded_sign);
1469 width -= 2;
1470 first = false;
1471 } else {
1472 ui_browser__printf(&browser->b, " ");
1473 width -= 2;
1474 }
1475
1476 if (fmt->color) {
1477 int ret = fmt->color(fmt, &hpp, entry);
1478 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1479 /*
1480 * fmt->color() already used ui_browser to
1481 * print the non alignment bits, skip it (+ret):
1482 */
1483 ui_browser__printf(&browser->b, "%s", s + ret);
1484 } else {
1485 int ret = fmt->entry(fmt, &hpp, entry);
1486 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1487 ui_browser__printf(&browser->b, "%s", s);
1488 }
1489 width -= hpp.buf - s;
1490 }
1491
1492 if (!first) {
1493 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1494 width -= hierarchy_indent;
1495 }
1496
1497 if (column >= browser->b.horiz_scroll) {
1498 char s[2048];
1499 struct perf_hpp hpp = {
1500 .buf = s,
1501 .size = sizeof(s),
1502 .ptr = &arg,
1503 };
1504
1505 if (current_entry && browser->b.navkeypressed) {
1506 ui_browser__set_color(&browser->b,
1507 HE_COLORSET_SELECTED);
1508 } else {
1509 ui_browser__set_color(&browser->b,
1510 HE_COLORSET_NORMAL);
1511 }
1512
1513 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1514 if (first) {
1515 ui_browser__printf(&browser->b, "%c ", folded_sign);
1516 first = false;
1517 } else {
1518 ui_browser__write_nstring(&browser->b, "", 2);
1519 }
1520
1521 width -= 2;
1522
1523 /*
1524 * No need to call hist_entry__snprintf_alignment()
1525 * since this fmt is always the last column in the
1526 * hierarchy mode.
1527 */
1528 if (fmt->color) {
1529 width -= fmt->color(fmt, &hpp, entry);
1530 } else {
1531 int i = 0;
1532
1533 width -= fmt->entry(fmt, &hpp, entry);
1534 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1535
1536 while (isspace(s[i++]))
1537 width++;
1538 }
1539 }
1540 }
1541
1542 /* The scroll bar isn't being used */
1543 if (!browser->b.navkeypressed)
1544 width += 1;
1545
1546 ui_browser__write_nstring(&browser->b, "", width);
1547
1548 ++row;
1549 ++printed;
1550
1551 show_callchain:
1552 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1553 struct callchain_print_arg carg = {
1554 .row_offset = row_offset,
1555 };
1556
1557 printed += hist_browser__show_callchain(browser, entry,
1558 level + 1, row,
1559 hist_browser__show_callchain_entry, &carg,
1560 hist_browser__check_output_full);
1561 }
1562
1563 return printed;
1564 }
1565
hist_browser__show_no_entry(struct hist_browser *browser, unsigned short row, int level)1566 static int hist_browser__show_no_entry(struct hist_browser *browser,
1567 unsigned short row, int level)
1568 {
1569 int width = browser->b.width;
1570 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1571 bool first = true;
1572 int column = 0;
1573 int ret;
1574 struct perf_hpp_fmt *fmt;
1575 struct perf_hpp_list_node *fmt_node;
1576 int indent = browser->hists->nr_hpp_node - 2;
1577
1578 if (current_entry) {
1579 browser->he_selection = NULL;
1580 browser->selection = NULL;
1581 }
1582
1583 ui_browser__gotorc(&browser->b, row, 0);
1584
1585 if (current_entry && browser->b.navkeypressed)
1586 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1587 else
1588 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1589
1590 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1591 width -= level * HIERARCHY_INDENT;
1592
1593 /* the first hpp_list_node is for overhead columns */
1594 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1595 struct perf_hpp_list_node, list);
1596 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1597 if (perf_hpp__should_skip(fmt, browser->hists) ||
1598 column++ < browser->b.horiz_scroll)
1599 continue;
1600
1601 ret = fmt->width(fmt, NULL, browser->hists);
1602
1603 if (first) {
1604 /* for folded sign */
1605 first = false;
1606 ret++;
1607 } else {
1608 /* space between columns */
1609 ret += 2;
1610 }
1611
1612 ui_browser__write_nstring(&browser->b, "", ret);
1613 width -= ret;
1614 }
1615
1616 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1617 width -= indent * HIERARCHY_INDENT;
1618
1619 if (column >= browser->b.horiz_scroll) {
1620 char buf[32];
1621
1622 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1623 ui_browser__printf(&browser->b, " %s", buf);
1624 width -= ret + 2;
1625 }
1626
1627 /* The scroll bar isn't being used */
1628 if (!browser->b.navkeypressed)
1629 width += 1;
1630
1631 ui_browser__write_nstring(&browser->b, "", width);
1632 return 1;
1633 }
1634
advance_hpp_check(struct perf_hpp *hpp, int inc)1635 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1636 {
1637 advance_hpp(hpp, inc);
1638 return hpp->size <= 0;
1639 }
1640
1641 static int
hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size, int line)1642 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1643 size_t size, int line)
1644 {
1645 struct hists *hists = browser->hists;
1646 struct perf_hpp dummy_hpp = {
1647 .buf = buf,
1648 .size = size,
1649 };
1650 struct perf_hpp_fmt *fmt;
1651 size_t ret = 0;
1652 int column = 0;
1653 int span = 0;
1654
1655 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1656 ret = scnprintf(buf, size, " ");
1657 if (advance_hpp_check(&dummy_hpp, ret))
1658 return ret;
1659 }
1660
1661 hists__for_each_format(browser->hists, fmt) {
1662 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1663 continue;
1664
1665 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1666 if (advance_hpp_check(&dummy_hpp, ret))
1667 break;
1668
1669 if (span)
1670 continue;
1671
1672 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1673 if (advance_hpp_check(&dummy_hpp, ret))
1674 break;
1675 }
1676
1677 return ret;
1678 }
1679
hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)1680 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1681 {
1682 struct hists *hists = browser->hists;
1683 struct perf_hpp dummy_hpp = {
1684 .buf = buf,
1685 .size = size,
1686 };
1687 struct perf_hpp_fmt *fmt;
1688 struct perf_hpp_list_node *fmt_node;
1689 size_t ret = 0;
1690 int column = 0;
1691 int indent = hists->nr_hpp_node - 2;
1692 bool first_node, first_col;
1693
1694 ret = scnprintf(buf, size, " ");
1695 if (advance_hpp_check(&dummy_hpp, ret))
1696 return ret;
1697
1698 first_node = true;
1699 /* the first hpp_list_node is for overhead columns */
1700 fmt_node = list_first_entry(&hists->hpp_formats,
1701 struct perf_hpp_list_node, list);
1702 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1703 if (column++ < browser->b.horiz_scroll)
1704 continue;
1705
1706 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1707 if (advance_hpp_check(&dummy_hpp, ret))
1708 break;
1709
1710 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1711 if (advance_hpp_check(&dummy_hpp, ret))
1712 break;
1713
1714 first_node = false;
1715 }
1716
1717 if (!first_node) {
1718 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1719 indent * HIERARCHY_INDENT, "");
1720 if (advance_hpp_check(&dummy_hpp, ret))
1721 return ret;
1722 }
1723
1724 first_node = true;
1725 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1726 if (!first_node) {
1727 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1728 if (advance_hpp_check(&dummy_hpp, ret))
1729 break;
1730 }
1731 first_node = false;
1732
1733 first_col = true;
1734 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1735 char *start;
1736
1737 if (perf_hpp__should_skip(fmt, hists))
1738 continue;
1739
1740 if (!first_col) {
1741 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1742 if (advance_hpp_check(&dummy_hpp, ret))
1743 break;
1744 }
1745 first_col = false;
1746
1747 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1748 dummy_hpp.buf[ret] = '\0';
1749
1750 start = strim(dummy_hpp.buf);
1751 ret = strlen(start);
1752
1753 if (start != dummy_hpp.buf)
1754 memmove(dummy_hpp.buf, start, ret + 1);
1755
1756 if (advance_hpp_check(&dummy_hpp, ret))
1757 break;
1758 }
1759 }
1760
1761 return ret;
1762 }
1763
hists_browser__hierarchy_headers(struct hist_browser *browser)1764 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1765 {
1766 char headers[1024];
1767
1768 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1769 sizeof(headers));
1770
1771 ui_browser__gotorc_title(&browser->b, 0, 0);
1772 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1773 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1774 }
1775
hists_browser__headers(struct hist_browser *browser)1776 static void hists_browser__headers(struct hist_browser *browser)
1777 {
1778 struct hists *hists = browser->hists;
1779 struct perf_hpp_list *hpp_list = hists->hpp_list;
1780
1781 int line;
1782
1783 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1784 char headers[1024];
1785
1786 hists_browser__scnprintf_headers(browser, headers,
1787 sizeof(headers), line);
1788
1789 ui_browser__gotorc_title(&browser->b, line, 0);
1790 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1791 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1792 }
1793 }
1794
hist_browser__show_headers(struct hist_browser *browser)1795 static void hist_browser__show_headers(struct hist_browser *browser)
1796 {
1797 if (symbol_conf.report_hierarchy)
1798 hists_browser__hierarchy_headers(browser);
1799 else
1800 hists_browser__headers(browser);
1801 }
1802
ui_browser__hists_init_top(struct ui_browser *browser)1803 static void ui_browser__hists_init_top(struct ui_browser *browser)
1804 {
1805 if (browser->top == NULL) {
1806 struct hist_browser *hb;
1807
1808 hb = container_of(browser, struct hist_browser, b);
1809 browser->top = rb_first_cached(&hb->hists->entries);
1810 }
1811 }
1812
hist_browser__refresh(struct ui_browser *browser)1813 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1814 {
1815 unsigned row = 0;
1816 struct rb_node *nd;
1817 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1818
1819 if (hb->show_headers)
1820 hist_browser__show_headers(hb);
1821
1822 ui_browser__hists_init_top(browser);
1823 hb->he_selection = NULL;
1824 hb->selection = NULL;
1825
1826 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1827 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1828 float percent;
1829
1830 if (h->filtered) {
1831 /* let it move to sibling */
1832 h->unfolded = false;
1833 continue;
1834 }
1835
1836 if (symbol_conf.report_individual_block)
1837 percent = block_info__total_cycles_percent(h);
1838 else
1839 percent = hist_entry__get_percent_limit(h);
1840
1841 if (percent < hb->min_pcnt)
1842 continue;
1843
1844 if (symbol_conf.report_hierarchy) {
1845 row += hist_browser__show_hierarchy_entry(hb, h, row,
1846 h->depth);
1847 if (row == browser->rows)
1848 break;
1849
1850 if (h->has_no_entry) {
1851 hist_browser__show_no_entry(hb, row, h->depth + 1);
1852 row++;
1853 }
1854 } else {
1855 row += hist_browser__show_entry(hb, h, row);
1856 }
1857
1858 if (row == browser->rows)
1859 break;
1860 }
1861
1862 return row;
1863 }
1864
hists__filter_entries(struct rb_node *nd, float min_pcnt)1865 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1866 float min_pcnt)
1867 {
1868 while (nd != NULL) {
1869 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1870 float percent = hist_entry__get_percent_limit(h);
1871
1872 if (!h->filtered && percent >= min_pcnt)
1873 return nd;
1874
1875 /*
1876 * If it's filtered, its all children also were filtered.
1877 * So move to sibling node.
1878 */
1879 if (rb_next(nd))
1880 nd = rb_next(nd);
1881 else
1882 nd = rb_hierarchy_next(nd);
1883 }
1884
1885 return NULL;
1886 }
1887
hists__filter_prev_entries(struct rb_node *nd, float min_pcnt)1888 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1889 float min_pcnt)
1890 {
1891 while (nd != NULL) {
1892 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1893 float percent = hist_entry__get_percent_limit(h);
1894
1895 if (!h->filtered && percent >= min_pcnt)
1896 return nd;
1897
1898 nd = rb_hierarchy_prev(nd);
1899 }
1900
1901 return NULL;
1902 }
1903
ui_browser__hists_seek(struct ui_browser *browser, off_t offset, int whence)1904 static void ui_browser__hists_seek(struct ui_browser *browser,
1905 off_t offset, int whence)
1906 {
1907 struct hist_entry *h;
1908 struct rb_node *nd;
1909 bool first = true;
1910 struct hist_browser *hb;
1911
1912 hb = container_of(browser, struct hist_browser, b);
1913
1914 if (browser->nr_entries == 0)
1915 return;
1916
1917 ui_browser__hists_init_top(browser);
1918
1919 switch (whence) {
1920 case SEEK_SET:
1921 nd = hists__filter_entries(rb_first(browser->entries),
1922 hb->min_pcnt);
1923 break;
1924 case SEEK_CUR:
1925 nd = browser->top;
1926 goto do_offset;
1927 case SEEK_END:
1928 nd = rb_hierarchy_last(rb_last(browser->entries));
1929 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1930 first = false;
1931 break;
1932 default:
1933 return;
1934 }
1935
1936 /*
1937 * Moves not relative to the first visible entry invalidates its
1938 * row_offset:
1939 */
1940 h = rb_entry(browser->top, struct hist_entry, rb_node);
1941 h->row_offset = 0;
1942
1943 /*
1944 * Here we have to check if nd is expanded (+), if it is we can't go
1945 * the next top level hist_entry, instead we must compute an offset of
1946 * what _not_ to show and not change the first visible entry.
1947 *
1948 * This offset increments when we are going from top to bottom and
1949 * decreases when we're going from bottom to top.
1950 *
1951 * As we don't have backpointers to the top level in the callchains
1952 * structure, we need to always print the whole hist_entry callchain,
1953 * skipping the first ones that are before the first visible entry
1954 * and stop when we printed enough lines to fill the screen.
1955 */
1956 do_offset:
1957 if (!nd)
1958 return;
1959
1960 if (offset > 0) {
1961 do {
1962 h = rb_entry(nd, struct hist_entry, rb_node);
1963 if (h->unfolded && h->leaf) {
1964 u16 remaining = h->nr_rows - h->row_offset;
1965 if (offset > remaining) {
1966 offset -= remaining;
1967 h->row_offset = 0;
1968 } else {
1969 h->row_offset += offset;
1970 offset = 0;
1971 browser->top = nd;
1972 break;
1973 }
1974 }
1975 nd = hists__filter_entries(rb_hierarchy_next(nd),
1976 hb->min_pcnt);
1977 if (nd == NULL)
1978 break;
1979 --offset;
1980 browser->top = nd;
1981 } while (offset != 0);
1982 } else if (offset < 0) {
1983 while (1) {
1984 h = rb_entry(nd, struct hist_entry, rb_node);
1985 if (h->unfolded && h->leaf) {
1986 if (first) {
1987 if (-offset > h->row_offset) {
1988 offset += h->row_offset;
1989 h->row_offset = 0;
1990 } else {
1991 h->row_offset += offset;
1992 offset = 0;
1993 browser->top = nd;
1994 break;
1995 }
1996 } else {
1997 if (-offset > h->nr_rows) {
1998 offset += h->nr_rows;
1999 h->row_offset = 0;
2000 } else {
2001 h->row_offset = h->nr_rows + offset;
2002 offset = 0;
2003 browser->top = nd;
2004 break;
2005 }
2006 }
2007 }
2008
2009 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2010 hb->min_pcnt);
2011 if (nd == NULL)
2012 break;
2013 ++offset;
2014 browser->top = nd;
2015 if (offset == 0) {
2016 /*
2017 * Last unfiltered hist_entry, check if it is
2018 * unfolded, if it is then we should have
2019 * row_offset at its last entry.
2020 */
2021 h = rb_entry(nd, struct hist_entry, rb_node);
2022 if (h->unfolded && h->leaf)
2023 h->row_offset = h->nr_rows;
2024 break;
2025 }
2026 first = false;
2027 }
2028 } else {
2029 browser->top = nd;
2030 h = rb_entry(nd, struct hist_entry, rb_node);
2031 h->row_offset = 0;
2032 }
2033 }
2034
hist_browser__fprintf_callchain(struct hist_browser *browser, struct hist_entry *he, FILE *fp, int level)2035 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2036 struct hist_entry *he, FILE *fp,
2037 int level)
2038 {
2039 struct callchain_print_arg arg = {
2040 .fp = fp,
2041 };
2042
2043 hist_browser__show_callchain(browser, he, level, 0,
2044 hist_browser__fprintf_callchain_entry, &arg,
2045 hist_browser__check_dump_full);
2046 return arg.printed;
2047 }
2048
hist_browser__fprintf_entry(struct hist_browser *browser, struct hist_entry *he, FILE *fp)2049 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2050 struct hist_entry *he, FILE *fp)
2051 {
2052 char s[8192];
2053 int printed = 0;
2054 char folded_sign = ' ';
2055 struct perf_hpp hpp = {
2056 .buf = s,
2057 .size = sizeof(s),
2058 };
2059 struct perf_hpp_fmt *fmt;
2060 bool first = true;
2061 int ret;
2062
2063 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2064 folded_sign = hist_entry__folded(he);
2065 printed += fprintf(fp, "%c ", folded_sign);
2066 }
2067
2068 hists__for_each_format(browser->hists, fmt) {
2069 if (perf_hpp__should_skip(fmt, he->hists))
2070 continue;
2071
2072 if (!first) {
2073 ret = scnprintf(hpp.buf, hpp.size, " ");
2074 advance_hpp(&hpp, ret);
2075 } else
2076 first = false;
2077
2078 ret = fmt->entry(fmt, &hpp, he);
2079 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2080 advance_hpp(&hpp, ret);
2081 }
2082 printed += fprintf(fp, "%s\n", s);
2083
2084 if (folded_sign == '-')
2085 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2086
2087 return printed;
2088 }
2089
2090
hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, struct hist_entry *he, FILE *fp, int level)2091 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2092 struct hist_entry *he,
2093 FILE *fp, int level)
2094 {
2095 char s[8192];
2096 int printed = 0;
2097 char folded_sign = ' ';
2098 struct perf_hpp hpp = {
2099 .buf = s,
2100 .size = sizeof(s),
2101 };
2102 struct perf_hpp_fmt *fmt;
2103 struct perf_hpp_list_node *fmt_node;
2104 bool first = true;
2105 int ret;
2106 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2107
2108 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2109
2110 folded_sign = hist_entry__folded(he);
2111 printed += fprintf(fp, "%c", folded_sign);
2112
2113 /* the first hpp_list_node is for overhead columns */
2114 fmt_node = list_first_entry(&he->hists->hpp_formats,
2115 struct perf_hpp_list_node, list);
2116 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2117 if (!first) {
2118 ret = scnprintf(hpp.buf, hpp.size, " ");
2119 advance_hpp(&hpp, ret);
2120 } else
2121 first = false;
2122
2123 ret = fmt->entry(fmt, &hpp, he);
2124 advance_hpp(&hpp, ret);
2125 }
2126
2127 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2128 advance_hpp(&hpp, ret);
2129
2130 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2131 ret = scnprintf(hpp.buf, hpp.size, " ");
2132 advance_hpp(&hpp, ret);
2133
2134 ret = fmt->entry(fmt, &hpp, he);
2135 advance_hpp(&hpp, ret);
2136 }
2137
2138 strim(s);
2139 printed += fprintf(fp, "%s\n", s);
2140
2141 if (he->leaf && folded_sign == '-') {
2142 printed += hist_browser__fprintf_callchain(browser, he, fp,
2143 he->depth + 1);
2144 }
2145
2146 return printed;
2147 }
2148
hist_browser__fprintf(struct hist_browser *browser, FILE *fp)2149 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2150 {
2151 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2152 browser->min_pcnt);
2153 int printed = 0;
2154
2155 while (nd) {
2156 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2157
2158 if (symbol_conf.report_hierarchy) {
2159 printed += hist_browser__fprintf_hierarchy_entry(browser,
2160 h, fp,
2161 h->depth);
2162 } else {
2163 printed += hist_browser__fprintf_entry(browser, h, fp);
2164 }
2165
2166 nd = hists__filter_entries(rb_hierarchy_next(nd),
2167 browser->min_pcnt);
2168 }
2169
2170 return printed;
2171 }
2172
hist_browser__dump(struct hist_browser *browser)2173 static int hist_browser__dump(struct hist_browser *browser)
2174 {
2175 char filename[64];
2176 FILE *fp;
2177
2178 while (1) {
2179 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2180 if (access(filename, F_OK))
2181 break;
2182 /*
2183 * XXX: Just an arbitrary lazy upper limit
2184 */
2185 if (++browser->print_seq == 8192) {
2186 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2187 return -1;
2188 }
2189 }
2190
2191 fp = fopen(filename, "w");
2192 if (fp == NULL) {
2193 char bf[64];
2194 const char *err = str_error_r(errno, bf, sizeof(bf));
2195 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2196 return -1;
2197 }
2198
2199 ++browser->print_seq;
2200 hist_browser__fprintf(browser, fp);
2201 fclose(fp);
2202 ui_helpline__fpush("%s written!", filename);
2203
2204 return 0;
2205 }
2206
hist_browser__init(struct hist_browser *browser, struct hists *hists)2207 void hist_browser__init(struct hist_browser *browser,
2208 struct hists *hists)
2209 {
2210 struct perf_hpp_fmt *fmt;
2211
2212 browser->hists = hists;
2213 browser->b.refresh = hist_browser__refresh;
2214 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2215 browser->b.seek = ui_browser__hists_seek;
2216 browser->b.use_navkeypressed = true;
2217 browser->show_headers = symbol_conf.show_hist_headers;
2218 hist_browser__set_title_space(browser);
2219
2220 if (symbol_conf.report_hierarchy) {
2221 struct perf_hpp_list_node *fmt_node;
2222
2223 /* count overhead columns (in the first node) */
2224 fmt_node = list_first_entry(&hists->hpp_formats,
2225 struct perf_hpp_list_node, list);
2226 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2227 ++browser->b.columns;
2228
2229 /* add a single column for whole hierarchy sort keys*/
2230 ++browser->b.columns;
2231 } else {
2232 hists__for_each_format(hists, fmt)
2233 ++browser->b.columns;
2234 }
2235
2236 hists__reset_column_width(hists);
2237 }
2238
hist_browser__new(struct hists *hists)2239 struct hist_browser *hist_browser__new(struct hists *hists)
2240 {
2241 struct hist_browser *browser = zalloc(sizeof(*browser));
2242
2243 if (browser)
2244 hist_browser__init(browser, hists);
2245
2246 return browser;
2247 }
2248
2249 static struct hist_browser *
perf_evsel_browser__new(struct evsel *evsel, struct hist_browser_timer *hbt, struct perf_env *env, struct annotation_options *annotation_opts)2250 perf_evsel_browser__new(struct evsel *evsel,
2251 struct hist_browser_timer *hbt,
2252 struct perf_env *env,
2253 struct annotation_options *annotation_opts)
2254 {
2255 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2256
2257 if (browser) {
2258 browser->hbt = hbt;
2259 browser->env = env;
2260 browser->title = hists_browser__scnprintf_title;
2261 browser->annotation_opts = annotation_opts;
2262 }
2263 return browser;
2264 }
2265
hist_browser__delete(struct hist_browser *browser)2266 void hist_browser__delete(struct hist_browser *browser)
2267 {
2268 free(browser);
2269 }
2270
hist_browser__selected_entry(struct hist_browser *browser)2271 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2272 {
2273 return browser->he_selection;
2274 }
2275
hist_browser__selected_thread(struct hist_browser *browser)2276 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2277 {
2278 return browser->he_selection->thread;
2279 }
2280
hist_browser__selected_res_sample(struct hist_browser *browser)2281 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2282 {
2283 return browser->he_selection ? browser->he_selection->res_samples : NULL;
2284 }
2285
2286 /* Check whether the browser is for 'top' or 'report' */
is_report_browser(void *timer)2287 static inline bool is_report_browser(void *timer)
2288 {
2289 return timer == NULL;
2290 }
2291
hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)2292 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2293 {
2294 struct hist_browser_timer *hbt = browser->hbt;
2295 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2296
2297 if (!is_report_browser(hbt)) {
2298 struct perf_top *top = hbt->arg;
2299
2300 printed += scnprintf(bf + printed, size - printed,
2301 " lost: %" PRIu64 "/%" PRIu64,
2302 top->lost, top->lost_total);
2303
2304 printed += scnprintf(bf + printed, size - printed,
2305 " drop: %" PRIu64 "/%" PRIu64,
2306 top->drop, top->drop_total);
2307
2308 if (top->zero)
2309 printed += scnprintf(bf + printed, size - printed, " [z]");
2310
2311 perf_top__reset_sample_counters(top);
2312 }
2313
2314
2315 return printed;
2316 }
2317
free_popup_options(char **options, int n)2318 static inline void free_popup_options(char **options, int n)
2319 {
2320 int i;
2321
2322 for (i = 0; i < n; ++i)
2323 zfree(&options[i]);
2324 }
2325
2326 /*
2327 * Only runtime switching of perf data file will make "input_name" point
2328 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2329 * whether we need to call free() for current "input_name" during the switch.
2330 */
2331 static bool is_input_name_malloced = false;
2332
switch_data_file(void)2333 static int switch_data_file(void)
2334 {
2335 char *pwd, *options[32], *abs_path[32], *tmp;
2336 DIR *pwd_dir;
2337 int nr_options = 0, choice = -1, ret = -1;
2338 struct dirent *dent;
2339
2340 pwd = getenv("PWD");
2341 if (!pwd)
2342 return ret;
2343
2344 pwd_dir = opendir(pwd);
2345 if (!pwd_dir)
2346 return ret;
2347
2348 memset(options, 0, sizeof(options));
2349 memset(abs_path, 0, sizeof(abs_path));
2350
2351 while ((dent = readdir(pwd_dir))) {
2352 char path[PATH_MAX];
2353 u64 magic;
2354 char *name = dent->d_name;
2355 FILE *file;
2356
2357 if (!(dent->d_type == DT_REG))
2358 continue;
2359
2360 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2361
2362 file = fopen(path, "r");
2363 if (!file)
2364 continue;
2365
2366 if (fread(&magic, 1, 8, file) < 8)
2367 goto close_file_and_continue;
2368
2369 if (is_perf_magic(magic)) {
2370 options[nr_options] = strdup(name);
2371 if (!options[nr_options])
2372 goto close_file_and_continue;
2373
2374 abs_path[nr_options] = strdup(path);
2375 if (!abs_path[nr_options]) {
2376 zfree(&options[nr_options]);
2377 ui__warning("Can't search all data files due to memory shortage.\n");
2378 fclose(file);
2379 break;
2380 }
2381
2382 nr_options++;
2383 }
2384
2385 close_file_and_continue:
2386 fclose(file);
2387 if (nr_options >= 32) {
2388 ui__warning("Too many perf data files in PWD!\n"
2389 "Only the first 32 files will be listed.\n");
2390 break;
2391 }
2392 }
2393 closedir(pwd_dir);
2394
2395 if (nr_options) {
2396 choice = ui__popup_menu(nr_options, options, NULL);
2397 if (choice < nr_options && choice >= 0) {
2398 tmp = strdup(abs_path[choice]);
2399 if (tmp) {
2400 if (is_input_name_malloced)
2401 free((void *)input_name);
2402 input_name = tmp;
2403 is_input_name_malloced = true;
2404 ret = 0;
2405 } else
2406 ui__warning("Data switch failed due to memory shortage!\n");
2407 }
2408 }
2409
2410 free_popup_options(options, nr_options);
2411 free_popup_options(abs_path, nr_options);
2412 return ret;
2413 }
2414
2415 struct popup_action {
2416 unsigned long time;
2417 struct thread *thread;
2418 struct map_symbol ms;
2419 int socket;
2420 struct evsel *evsel;
2421 enum rstype rstype;
2422
2423 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2424 };
2425
2426 static int
do_annotate(struct hist_browser *browser, struct popup_action *act)2427 do_annotate(struct hist_browser *browser, struct popup_action *act)
2428 {
2429 struct evsel *evsel;
2430 struct annotation *notes;
2431 struct hist_entry *he;
2432 int err;
2433
2434 if (!browser->annotation_opts->objdump_path &&
2435 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2436 return 0;
2437
2438 notes = symbol__annotation(act->ms.sym);
2439 if (!notes->src)
2440 return 0;
2441
2442 if (browser->block_evsel)
2443 evsel = browser->block_evsel;
2444 else
2445 evsel = hists_to_evsel(browser->hists);
2446
2447 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2448 browser->annotation_opts);
2449 he = hist_browser__selected_entry(browser);
2450 /*
2451 * offer option to annotate the other branch source or target
2452 * (if they exists) when returning from annotate
2453 */
2454 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2455 return 1;
2456
2457 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2458 if (err)
2459 ui_browser__handle_resize(&browser->b);
2460 return 0;
2461 }
2462
symbol__new_unresolved(u64 addr, struct map *map)2463 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2464 {
2465 struct annotated_source *src;
2466 struct symbol *sym;
2467 char name[64];
2468
2469 snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2470
2471 sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2472 if (sym) {
2473 src = symbol__hists(sym, 1);
2474 if (!src) {
2475 symbol__delete(sym);
2476 return NULL;
2477 }
2478
2479 dso__insert_symbol(map->dso, sym);
2480 }
2481
2482 return sym;
2483 }
2484
2485 static int
add_annotate_opt(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr, struct map_symbol *ms, u64 addr)2486 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2487 struct popup_action *act, char **optstr,
2488 struct map_symbol *ms,
2489 u64 addr)
2490 {
2491 if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2492 return 0;
2493
2494 if (!ms->sym)
2495 ms->sym = symbol__new_unresolved(addr, ms->map);
2496
2497 if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2498 return 0;
2499
2500 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2501 return 0;
2502
2503 act->ms = *ms;
2504 act->fn = do_annotate;
2505 return 1;
2506 }
2507
2508 static int
do_zoom_thread(struct hist_browser *browser, struct popup_action *act)2509 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2510 {
2511 struct thread *thread = act->thread;
2512
2513 if ((!hists__has(browser->hists, thread) &&
2514 !hists__has(browser->hists, comm)) || thread == NULL)
2515 return 0;
2516
2517 if (browser->hists->thread_filter) {
2518 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2519 perf_hpp__set_elide(HISTC_THREAD, false);
2520 thread__zput(browser->hists->thread_filter);
2521 ui_helpline__pop();
2522 } else {
2523 if (hists__has(browser->hists, thread)) {
2524 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2525 thread->comm_set ? thread__comm_str(thread) : "",
2526 thread->tid);
2527 } else {
2528 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2529 thread->comm_set ? thread__comm_str(thread) : "");
2530 }
2531
2532 browser->hists->thread_filter = thread__get(thread);
2533 perf_hpp__set_elide(HISTC_THREAD, false);
2534 pstack__push(browser->pstack, &browser->hists->thread_filter);
2535 }
2536
2537 hists__filter_by_thread(browser->hists);
2538 hist_browser__reset(browser);
2539 return 0;
2540 }
2541
2542 static int
add_thread_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct thread *thread)2543 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2544 char **optstr, struct thread *thread)
2545 {
2546 int ret;
2547
2548 if ((!hists__has(browser->hists, thread) &&
2549 !hists__has(browser->hists, comm)) || thread == NULL)
2550 return 0;
2551
2552 if (hists__has(browser->hists, thread)) {
2553 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2554 browser->hists->thread_filter ? "out of" : "into",
2555 thread->comm_set ? thread__comm_str(thread) : "",
2556 thread->tid);
2557 } else {
2558 ret = asprintf(optstr, "Zoom %s %s thread",
2559 browser->hists->thread_filter ? "out of" : "into",
2560 thread->comm_set ? thread__comm_str(thread) : "");
2561 }
2562 if (ret < 0)
2563 return 0;
2564
2565 act->thread = thread;
2566 act->fn = do_zoom_thread;
2567 return 1;
2568 }
2569
hists_browser__zoom_map(struct hist_browser *browser, struct map *map)2570 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2571 {
2572 if (!hists__has(browser->hists, dso) || map == NULL)
2573 return 0;
2574
2575 if (browser->hists->dso_filter) {
2576 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2577 perf_hpp__set_elide(HISTC_DSO, false);
2578 browser->hists->dso_filter = NULL;
2579 ui_helpline__pop();
2580 } else {
2581 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2582 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2583 browser->hists->dso_filter = map->dso;
2584 perf_hpp__set_elide(HISTC_DSO, true);
2585 pstack__push(browser->pstack, &browser->hists->dso_filter);
2586 }
2587
2588 hists__filter_by_dso(browser->hists);
2589 hist_browser__reset(browser);
2590 return 0;
2591 }
2592
2593 static int
do_zoom_dso(struct hist_browser *browser, struct popup_action *act)2594 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2595 {
2596 return hists_browser__zoom_map(browser, act->ms.map);
2597 }
2598
2599 static int
add_dso_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct map *map)2600 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2601 char **optstr, struct map *map)
2602 {
2603 if (!hists__has(browser->hists, dso) || map == NULL)
2604 return 0;
2605
2606 if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2607 browser->hists->dso_filter ? "out of" : "into",
2608 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2609 return 0;
2610
2611 act->ms.map = map;
2612 act->fn = do_zoom_dso;
2613 return 1;
2614 }
2615
do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)2616 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2617 {
2618 hist_browser__toggle_fold(browser);
2619 return 0;
2620 }
2621
add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)2622 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2623 {
2624 char sym_name[512];
2625
2626 if (!hist_browser__selection_has_children(browser))
2627 return 0;
2628
2629 if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2630 hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2631 hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2632 return 0;
2633
2634 act->fn = do_toggle_callchain;
2635 return 1;
2636 }
2637
2638 static int
do_browse_map(struct hist_browser *browser __maybe_unused, struct popup_action *act)2639 do_browse_map(struct hist_browser *browser __maybe_unused,
2640 struct popup_action *act)
2641 {
2642 map__browse(act->ms.map);
2643 return 0;
2644 }
2645
2646 static int
add_map_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct map *map)2647 add_map_opt(struct hist_browser *browser,
2648 struct popup_action *act, char **optstr, struct map *map)
2649 {
2650 if (!hists__has(browser->hists, dso) || map == NULL)
2651 return 0;
2652
2653 if (asprintf(optstr, "Browse map details") < 0)
2654 return 0;
2655
2656 act->ms.map = map;
2657 act->fn = do_browse_map;
2658 return 1;
2659 }
2660
2661 static int
do_run_script(struct hist_browser *browser __maybe_unused, struct popup_action *act)2662 do_run_script(struct hist_browser *browser __maybe_unused,
2663 struct popup_action *act)
2664 {
2665 char *script_opt;
2666 int len;
2667 int n = 0;
2668
2669 len = 100;
2670 if (act->thread)
2671 len += strlen(thread__comm_str(act->thread));
2672 else if (act->ms.sym)
2673 len += strlen(act->ms.sym->name);
2674 script_opt = malloc(len);
2675 if (!script_opt)
2676 return -1;
2677
2678 script_opt[0] = 0;
2679 if (act->thread) {
2680 n = scnprintf(script_opt, len, " -c %s ",
2681 thread__comm_str(act->thread));
2682 } else if (act->ms.sym) {
2683 n = scnprintf(script_opt, len, " -S %s ",
2684 act->ms.sym->name);
2685 }
2686
2687 if (act->time) {
2688 char start[32], end[32];
2689 unsigned long starttime = act->time;
2690 unsigned long endtime = act->time + symbol_conf.time_quantum;
2691
2692 if (starttime == endtime) { /* Display 1ms as fallback */
2693 starttime -= 1*NSEC_PER_MSEC;
2694 endtime += 1*NSEC_PER_MSEC;
2695 }
2696 timestamp__scnprintf_usec(starttime, start, sizeof start);
2697 timestamp__scnprintf_usec(endtime, end, sizeof end);
2698 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2699 }
2700
2701 script_browse(script_opt, act->evsel);
2702 free(script_opt);
2703 return 0;
2704 }
2705
2706 static int
do_res_sample_script(struct hist_browser *browser __maybe_unused, struct popup_action *act)2707 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2708 struct popup_action *act)
2709 {
2710 struct hist_entry *he;
2711
2712 he = hist_browser__selected_entry(browser);
2713 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2714 return 0;
2715 }
2716
2717 static int
add_script_opt_2(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr, struct thread *thread, struct symbol *sym, struct evsel *evsel, const char *tstr)2718 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2719 struct popup_action *act, char **optstr,
2720 struct thread *thread, struct symbol *sym,
2721 struct evsel *evsel, const char *tstr)
2722 {
2723
2724 if (thread) {
2725 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2726 thread__comm_str(thread), tstr) < 0)
2727 return 0;
2728 } else if (sym) {
2729 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2730 sym->name, tstr) < 0)
2731 return 0;
2732 } else {
2733 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2734 return 0;
2735 }
2736
2737 act->thread = thread;
2738 act->ms.sym = sym;
2739 act->evsel = evsel;
2740 act->fn = do_run_script;
2741 return 1;
2742 }
2743
2744 static int
add_script_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct thread *thread, struct symbol *sym, struct evsel *evsel)2745 add_script_opt(struct hist_browser *browser,
2746 struct popup_action *act, char **optstr,
2747 struct thread *thread, struct symbol *sym,
2748 struct evsel *evsel)
2749 {
2750 int n, j;
2751 struct hist_entry *he;
2752
2753 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2754
2755 he = hist_browser__selected_entry(browser);
2756 if (sort_order && strstr(sort_order, "time")) {
2757 char tstr[128];
2758
2759 optstr++;
2760 act++;
2761 j = sprintf(tstr, " in ");
2762 j += timestamp__scnprintf_usec(he->time, tstr + j,
2763 sizeof tstr - j);
2764 j += sprintf(tstr + j, "-");
2765 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2766 tstr + j, sizeof tstr - j);
2767 n += add_script_opt_2(browser, act, optstr, thread, sym,
2768 evsel, tstr);
2769 act->time = he->time;
2770 }
2771 return n;
2772 }
2773
2774 static int
add_res_sample_opt(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr, struct res_sample *res_sample, struct evsel *evsel, enum rstype type)2775 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2776 struct popup_action *act, char **optstr,
2777 struct res_sample *res_sample,
2778 struct evsel *evsel,
2779 enum rstype type)
2780 {
2781 if (!res_sample)
2782 return 0;
2783
2784 if (asprintf(optstr, "Show context for individual samples %s",
2785 type == A_ASM ? "with assembler" :
2786 type == A_SOURCE ? "with source" : "") < 0)
2787 return 0;
2788
2789 act->fn = do_res_sample_script;
2790 act->evsel = evsel;
2791 act->rstype = type;
2792 return 1;
2793 }
2794
2795 static int
do_switch_data(struct hist_browser *browser __maybe_unused, struct popup_action *act __maybe_unused)2796 do_switch_data(struct hist_browser *browser __maybe_unused,
2797 struct popup_action *act __maybe_unused)
2798 {
2799 if (switch_data_file()) {
2800 ui__warning("Won't switch the data files due to\n"
2801 "no valid data file get selected!\n");
2802 return 0;
2803 }
2804
2805 return K_SWITCH_INPUT_DATA;
2806 }
2807
2808 static int
add_switch_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)2809 add_switch_opt(struct hist_browser *browser,
2810 struct popup_action *act, char **optstr)
2811 {
2812 if (!is_report_browser(browser->hbt))
2813 return 0;
2814
2815 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2816 return 0;
2817
2818 act->fn = do_switch_data;
2819 return 1;
2820 }
2821
2822 static int
do_exit_browser(struct hist_browser *browser __maybe_unused, struct popup_action *act __maybe_unused)2823 do_exit_browser(struct hist_browser *browser __maybe_unused,
2824 struct popup_action *act __maybe_unused)
2825 {
2826 return 0;
2827 }
2828
2829 static int
add_exit_opt(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr)2830 add_exit_opt(struct hist_browser *browser __maybe_unused,
2831 struct popup_action *act, char **optstr)
2832 {
2833 if (asprintf(optstr, "Exit") < 0)
2834 return 0;
2835
2836 act->fn = do_exit_browser;
2837 return 1;
2838 }
2839
2840 static int
do_zoom_socket(struct hist_browser *browser, struct popup_action *act)2841 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2842 {
2843 if (!hists__has(browser->hists, socket) || act->socket < 0)
2844 return 0;
2845
2846 if (browser->hists->socket_filter > -1) {
2847 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2848 browser->hists->socket_filter = -1;
2849 perf_hpp__set_elide(HISTC_SOCKET, false);
2850 } else {
2851 browser->hists->socket_filter = act->socket;
2852 perf_hpp__set_elide(HISTC_SOCKET, true);
2853 pstack__push(browser->pstack, &browser->hists->socket_filter);
2854 }
2855
2856 hists__filter_by_socket(browser->hists);
2857 hist_browser__reset(browser);
2858 return 0;
2859 }
2860
2861 static int
add_socket_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, int socket_id)2862 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2863 char **optstr, int socket_id)
2864 {
2865 if (!hists__has(browser->hists, socket) || socket_id < 0)
2866 return 0;
2867
2868 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2869 (browser->hists->socket_filter > -1) ? "out of" : "into",
2870 socket_id) < 0)
2871 return 0;
2872
2873 act->socket = socket_id;
2874 act->fn = do_zoom_socket;
2875 return 1;
2876 }
2877
hist_browser__update_nr_entries(struct hist_browser *hb)2878 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2879 {
2880 u64 nr_entries = 0;
2881 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2882
2883 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2884 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2885 return;
2886 }
2887
2888 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2889 nr_entries++;
2890 nd = rb_hierarchy_next(nd);
2891 }
2892
2893 hb->nr_non_filtered_entries = nr_entries;
2894 hb->nr_hierarchy_entries = nr_entries;
2895 }
2896
hist_browser__update_percent_limit(struct hist_browser *hb, double percent)2897 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2898 double percent)
2899 {
2900 struct hist_entry *he;
2901 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2902 u64 total = hists__total_period(hb->hists);
2903 u64 min_callchain_hits = total * (percent / 100);
2904
2905 hb->min_pcnt = callchain_param.min_percent = percent;
2906
2907 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2908 he = rb_entry(nd, struct hist_entry, rb_node);
2909
2910 if (he->has_no_entry) {
2911 he->has_no_entry = false;
2912 he->nr_rows = 0;
2913 }
2914
2915 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2916 goto next;
2917
2918 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2919 total = he->stat.period;
2920
2921 if (symbol_conf.cumulate_callchain)
2922 total = he->stat_acc->period;
2923
2924 min_callchain_hits = total * (percent / 100);
2925 }
2926
2927 callchain_param.sort(&he->sorted_chain, he->callchain,
2928 min_callchain_hits, &callchain_param);
2929
2930 next:
2931 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2932
2933 /* force to re-evaluate folding state of callchains */
2934 he->init_have_children = false;
2935 hist_entry__set_folding(he, hb, false);
2936 }
2937 }
2938
perf_evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline, bool left_exits, struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env, bool warn_lost_event, struct annotation_options *annotation_opts)2939 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2940 const char *helpline,
2941 bool left_exits,
2942 struct hist_browser_timer *hbt,
2943 float min_pcnt,
2944 struct perf_env *env,
2945 bool warn_lost_event,
2946 struct annotation_options *annotation_opts)
2947 {
2948 struct hists *hists = evsel__hists(evsel);
2949 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2950 struct branch_info *bi = NULL;
2951 #define MAX_OPTIONS 16
2952 char *options[MAX_OPTIONS];
2953 struct popup_action actions[MAX_OPTIONS];
2954 int nr_options = 0;
2955 int key = -1;
2956 char buf[128];
2957 int delay_secs = hbt ? hbt->refresh : 0;
2958
2959 #define HIST_BROWSER_HELP_COMMON \
2960 "h/?/F1 Show this window\n" \
2961 "UP/DOWN/PGUP\n" \
2962 "PGDN/SPACE Navigate\n" \
2963 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2964 "For multiple event sessions:\n\n" \
2965 "TAB/UNTAB Switch events\n\n" \
2966 "For symbolic views (--sort has sym):\n\n" \
2967 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2968 "ESC Zoom out\n" \
2969 "+ Expand/Collapse one callchain level\n" \
2970 "a Annotate current symbol\n" \
2971 "C Collapse all callchains\n" \
2972 "d Zoom into current DSO\n" \
2973 "e Expand/Collapse main entry callchains\n" \
2974 "E Expand all callchains\n" \
2975 "F Toggle percentage of filtered entries\n" \
2976 "H Display column headers\n" \
2977 "k Zoom into the kernel map\n" \
2978 "L Change percent limit\n" \
2979 "m Display context menu\n" \
2980 "S Zoom into current Processor Socket\n" \
2981
2982 /* help messages are sorted by lexical order of the hotkey */
2983 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2984 "i Show header information\n"
2985 "P Print histograms to perf.hist.N\n"
2986 "r Run available scripts\n"
2987 "s Switch to another data file in PWD\n"
2988 "t Zoom into current Thread\n"
2989 "V Verbose (DSO names in callchains, etc)\n"
2990 "/ Filter symbol by name\n"
2991 "0-9 Sort by event n in group";
2992 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2993 "P Print histograms to perf.hist.N\n"
2994 "t Zoom into current Thread\n"
2995 "V Verbose (DSO names in callchains, etc)\n"
2996 "z Toggle zeroing of samples\n"
2997 "f Enable/Disable events\n"
2998 "/ Filter symbol by name";
2999
3000 if (browser == NULL)
3001 return -1;
3002
3003 /* reset abort key so that it can get Ctrl-C as a key */
3004 SLang_reset_tty();
3005 SLang_init_tty(0, 0, 0);
3006
3007 if (min_pcnt)
3008 browser->min_pcnt = min_pcnt;
3009 hist_browser__update_nr_entries(browser);
3010
3011 browser->pstack = pstack__new(3);
3012 if (browser->pstack == NULL)
3013 goto out;
3014
3015 ui_helpline__push(helpline);
3016
3017 memset(options, 0, sizeof(options));
3018 memset(actions, 0, sizeof(actions));
3019
3020 if (symbol_conf.col_width_list_str)
3021 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3022
3023 if (!is_report_browser(hbt))
3024 browser->b.no_samples_msg = "Collecting samples...";
3025
3026 while (1) {
3027 struct thread *thread = NULL;
3028 struct map *map = NULL;
3029 int choice;
3030 int socked_id = -1;
3031
3032 key = 0; // reset key
3033 do_hotkey: // key came straight from options ui__popup_menu()
3034 choice = nr_options = 0;
3035 key = hist_browser__run(browser, helpline, warn_lost_event, key);
3036
3037 if (browser->he_selection != NULL) {
3038 thread = hist_browser__selected_thread(browser);
3039 map = browser->selection->map;
3040 socked_id = browser->he_selection->socket;
3041 }
3042 switch (key) {
3043 case K_TAB:
3044 case K_UNTAB:
3045 if (nr_events == 1)
3046 continue;
3047 /*
3048 * Exit the browser, let hists__browser_tree
3049 * go to the next or previous
3050 */
3051 goto out_free_stack;
3052 case '0' ... '9':
3053 if (!symbol_conf.event_group ||
3054 evsel->core.nr_members < 2) {
3055 snprintf(buf, sizeof(buf),
3056 "Sort by index only available with group events!");
3057 helpline = buf;
3058 continue;
3059 }
3060
3061 if (key - '0' == symbol_conf.group_sort_idx)
3062 continue;
3063
3064 symbol_conf.group_sort_idx = key - '0';
3065
3066 if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3067 snprintf(buf, sizeof(buf),
3068 "Max event group index to sort is %d (index from 0 to %d)",
3069 evsel->core.nr_members - 1,
3070 evsel->core.nr_members - 1);
3071 helpline = buf;
3072 continue;
3073 }
3074
3075 key = K_RELOAD;
3076 goto out_free_stack;
3077 case 'a':
3078 if (!hists__has(hists, sym)) {
3079 ui_browser__warning(&browser->b, delay_secs * 2,
3080 "Annotation is only available for symbolic views, "
3081 "include \"sym*\" in --sort to use it.");
3082 continue;
3083 }
3084
3085 if (!browser->selection ||
3086 !browser->selection->map ||
3087 !browser->selection->map->dso ||
3088 browser->selection->map->dso->annotate_warned) {
3089 continue;
3090 }
3091
3092 if (!browser->selection->sym) {
3093 if (!browser->he_selection)
3094 continue;
3095
3096 if (sort__mode == SORT_MODE__BRANCH) {
3097 bi = browser->he_selection->branch_info;
3098 if (!bi || !bi->to.ms.map)
3099 continue;
3100
3101 actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3102 actions->ms.map = bi->to.ms.map;
3103 } else {
3104 actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3105 browser->selection->map);
3106 actions->ms.map = browser->selection->map;
3107 }
3108
3109 if (!actions->ms.sym)
3110 continue;
3111 } else {
3112 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3113 ui_browser__warning(&browser->b, delay_secs * 2,
3114 "No samples for the \"%s\" symbol.\n\n"
3115 "Probably appeared just in a callchain",
3116 browser->selection->sym->name);
3117 continue;
3118 }
3119
3120 actions->ms.map = browser->selection->map;
3121 actions->ms.sym = browser->selection->sym;
3122 }
3123
3124 do_annotate(browser, actions);
3125 continue;
3126 case 'P':
3127 hist_browser__dump(browser);
3128 continue;
3129 case 'd':
3130 actions->ms.map = map;
3131 do_zoom_dso(browser, actions);
3132 continue;
3133 case 'k':
3134 if (browser->selection != NULL)
3135 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3136 continue;
3137 case 'V':
3138 verbose = (verbose + 1) % 4;
3139 browser->show_dso = verbose > 0;
3140 ui_helpline__fpush("Verbosity level set to %d\n",
3141 verbose);
3142 continue;
3143 case 't':
3144 actions->thread = thread;
3145 do_zoom_thread(browser, actions);
3146 continue;
3147 case 'S':
3148 actions->socket = socked_id;
3149 do_zoom_socket(browser, actions);
3150 continue;
3151 case '/':
3152 if (ui_browser__input_window("Symbol to show",
3153 "Please enter the name of symbol you want to see.\n"
3154 "To remove the filter later, press / + ENTER.",
3155 buf, "ENTER: OK, ESC: Cancel",
3156 delay_secs * 2) == K_ENTER) {
3157 hists->symbol_filter_str = *buf ? buf : NULL;
3158 hists__filter_by_symbol(hists);
3159 hist_browser__reset(browser);
3160 }
3161 continue;
3162 case 'r':
3163 if (is_report_browser(hbt)) {
3164 actions->thread = NULL;
3165 actions->ms.sym = NULL;
3166 do_run_script(browser, actions);
3167 }
3168 continue;
3169 case 's':
3170 if (is_report_browser(hbt)) {
3171 key = do_switch_data(browser, actions);
3172 if (key == K_SWITCH_INPUT_DATA)
3173 goto out_free_stack;
3174 }
3175 continue;
3176 case 'i':
3177 /* env->arch is NULL for live-mode (i.e. perf top) */
3178 if (env->arch)
3179 tui__header_window(env);
3180 continue;
3181 case 'F':
3182 symbol_conf.filter_relative ^= 1;
3183 continue;
3184 case 'z':
3185 if (!is_report_browser(hbt)) {
3186 struct perf_top *top = hbt->arg;
3187
3188 top->zero = !top->zero;
3189 }
3190 continue;
3191 case 'L':
3192 if (ui_browser__input_window("Percent Limit",
3193 "Please enter the value you want to hide entries under that percent.",
3194 buf, "ENTER: OK, ESC: Cancel",
3195 delay_secs * 2) == K_ENTER) {
3196 char *end;
3197 double new_percent = strtod(buf, &end);
3198
3199 if (new_percent < 0 || new_percent > 100) {
3200 ui_browser__warning(&browser->b, delay_secs * 2,
3201 "Invalid percent: %.2f", new_percent);
3202 continue;
3203 }
3204
3205 hist_browser__update_percent_limit(browser, new_percent);
3206 hist_browser__reset(browser);
3207 }
3208 continue;
3209 case K_F1:
3210 case 'h':
3211 case '?':
3212 ui_browser__help_window(&browser->b,
3213 is_report_browser(hbt) ? report_help : top_help);
3214 continue;
3215 case K_ENTER:
3216 case K_RIGHT:
3217 case 'm':
3218 /* menu */
3219 break;
3220 case K_ESC:
3221 case K_LEFT: {
3222 const void *top;
3223
3224 if (pstack__empty(browser->pstack)) {
3225 /*
3226 * Go back to the perf_evsel_menu__run or other user
3227 */
3228 if (left_exits)
3229 goto out_free_stack;
3230
3231 if (key == K_ESC &&
3232 ui_browser__dialog_yesno(&browser->b,
3233 "Do you really want to exit?"))
3234 goto out_free_stack;
3235
3236 continue;
3237 }
3238 actions->ms.map = map;
3239 top = pstack__peek(browser->pstack);
3240 if (top == &browser->hists->dso_filter) {
3241 /*
3242 * No need to set actions->dso here since
3243 * it's just to remove the current filter.
3244 * Ditto for thread below.
3245 */
3246 do_zoom_dso(browser, actions);
3247 } else if (top == &browser->hists->thread_filter) {
3248 do_zoom_thread(browser, actions);
3249 } else if (top == &browser->hists->socket_filter) {
3250 do_zoom_socket(browser, actions);
3251 }
3252 continue;
3253 }
3254 case 'q':
3255 case CTRL('c'):
3256 goto out_free_stack;
3257 case 'f':
3258 if (!is_report_browser(hbt)) {
3259 struct perf_top *top = hbt->arg;
3260
3261 perf_evlist__toggle_enable(top->evlist);
3262 /*
3263 * No need to refresh, resort/decay histogram
3264 * entries if we are not collecting samples:
3265 */
3266 if (top->evlist->enabled) {
3267 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3268 hbt->refresh = delay_secs;
3269 } else {
3270 helpline = "Press 'f' again to re-enable the events";
3271 hbt->refresh = 0;
3272 }
3273 continue;
3274 }
3275 /* Fall thru */
3276 default:
3277 helpline = "Press '?' for help on key bindings";
3278 continue;
3279 }
3280
3281 if (!hists__has(hists, sym) || browser->selection == NULL)
3282 goto skip_annotation;
3283
3284 if (sort__mode == SORT_MODE__BRANCH) {
3285
3286 if (browser->he_selection)
3287 bi = browser->he_selection->branch_info;
3288
3289 if (bi == NULL)
3290 goto skip_annotation;
3291
3292 nr_options += add_annotate_opt(browser,
3293 &actions[nr_options],
3294 &options[nr_options],
3295 &bi->from.ms,
3296 bi->from.al_addr);
3297 if (bi->to.ms.sym != bi->from.ms.sym)
3298 nr_options += add_annotate_opt(browser,
3299 &actions[nr_options],
3300 &options[nr_options],
3301 &bi->to.ms,
3302 bi->to.al_addr);
3303 } else {
3304 nr_options += add_annotate_opt(browser,
3305 &actions[nr_options],
3306 &options[nr_options],
3307 browser->selection,
3308 browser->he_selection->ip);
3309 }
3310 skip_annotation:
3311 nr_options += add_thread_opt(browser, &actions[nr_options],
3312 &options[nr_options], thread);
3313 nr_options += add_dso_opt(browser, &actions[nr_options],
3314 &options[nr_options], map);
3315 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3316 nr_options += add_map_opt(browser, &actions[nr_options],
3317 &options[nr_options],
3318 browser->selection ?
3319 browser->selection->map : NULL);
3320 nr_options += add_socket_opt(browser, &actions[nr_options],
3321 &options[nr_options],
3322 socked_id);
3323 /* perf script support */
3324 if (!is_report_browser(hbt))
3325 goto skip_scripting;
3326
3327 if (browser->he_selection) {
3328 if (hists__has(hists, thread) && thread) {
3329 nr_options += add_script_opt(browser,
3330 &actions[nr_options],
3331 &options[nr_options],
3332 thread, NULL, evsel);
3333 }
3334 /*
3335 * Note that browser->selection != NULL
3336 * when browser->he_selection is not NULL,
3337 * so we don't need to check browser->selection
3338 * before fetching browser->selection->sym like what
3339 * we do before fetching browser->selection->map.
3340 *
3341 * See hist_browser__show_entry.
3342 */
3343 if (hists__has(hists, sym) && browser->selection->sym) {
3344 nr_options += add_script_opt(browser,
3345 &actions[nr_options],
3346 &options[nr_options],
3347 NULL, browser->selection->sym,
3348 evsel);
3349 }
3350 }
3351 nr_options += add_script_opt(browser, &actions[nr_options],
3352 &options[nr_options], NULL, NULL, evsel);
3353 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3354 &options[nr_options],
3355 hist_browser__selected_res_sample(browser),
3356 evsel, A_NORMAL);
3357 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3358 &options[nr_options],
3359 hist_browser__selected_res_sample(browser),
3360 evsel, A_ASM);
3361 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3362 &options[nr_options],
3363 hist_browser__selected_res_sample(browser),
3364 evsel, A_SOURCE);
3365 nr_options += add_switch_opt(browser, &actions[nr_options],
3366 &options[nr_options]);
3367 skip_scripting:
3368 nr_options += add_exit_opt(browser, &actions[nr_options],
3369 &options[nr_options]);
3370
3371 do {
3372 struct popup_action *act;
3373
3374 choice = ui__popup_menu(nr_options, options, &key);
3375 if (choice == -1)
3376 break;
3377
3378 if (choice == nr_options)
3379 goto do_hotkey;
3380
3381 act = &actions[choice];
3382 key = act->fn(browser, act);
3383 } while (key == 1);
3384
3385 if (key == K_SWITCH_INPUT_DATA)
3386 break;
3387 }
3388 out_free_stack:
3389 pstack__delete(browser->pstack);
3390 out:
3391 hist_browser__delete(browser);
3392 free_popup_options(options, MAX_OPTIONS);
3393 return key;
3394 }
3395
3396 struct evsel_menu {
3397 struct ui_browser b;
3398 struct evsel *selection;
3399 struct annotation_options *annotation_opts;
3400 bool lost_events, lost_events_warned;
3401 float min_pcnt;
3402 struct perf_env *env;
3403 };
3404
perf_evsel_menu__write(struct ui_browser *browser, void *entry, int row)3405 static void perf_evsel_menu__write(struct ui_browser *browser,
3406 void *entry, int row)
3407 {
3408 struct evsel_menu *menu = container_of(browser,
3409 struct evsel_menu, b);
3410 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3411 struct hists *hists = evsel__hists(evsel);
3412 bool current_entry = ui_browser__is_current_entry(browser, row);
3413 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3414 const char *ev_name = evsel__name(evsel);
3415 char bf[256], unit;
3416 const char *warn = " ";
3417 size_t printed;
3418
3419 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3420 HE_COLORSET_NORMAL);
3421
3422 if (evsel__is_group_event(evsel)) {
3423 struct evsel *pos;
3424
3425 ev_name = evsel__group_name(evsel);
3426
3427 for_each_group_member(pos, evsel) {
3428 struct hists *pos_hists = evsel__hists(pos);
3429 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3430 }
3431 }
3432
3433 nr_events = convert_unit(nr_events, &unit);
3434 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3435 unit, unit == ' ' ? "" : " ", ev_name);
3436 ui_browser__printf(browser, "%s", bf);
3437
3438 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3439 if (nr_events != 0) {
3440 menu->lost_events = true;
3441 if (!current_entry)
3442 ui_browser__set_color(browser, HE_COLORSET_TOP);
3443 nr_events = convert_unit(nr_events, &unit);
3444 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3445 nr_events, unit, unit == ' ' ? "" : " ");
3446 warn = bf;
3447 }
3448
3449 ui_browser__write_nstring(browser, warn, browser->width - printed);
3450
3451 if (current_entry)
3452 menu->selection = evsel;
3453 }
3454
perf_evsel_menu__run(struct evsel_menu *menu, int nr_events, const char *help, struct hist_browser_timer *hbt, bool warn_lost_event)3455 static int perf_evsel_menu__run(struct evsel_menu *menu,
3456 int nr_events, const char *help,
3457 struct hist_browser_timer *hbt,
3458 bool warn_lost_event)
3459 {
3460 struct evlist *evlist = menu->b.priv;
3461 struct evsel *pos;
3462 const char *title = "Available samples";
3463 int delay_secs = hbt ? hbt->refresh : 0;
3464 int key;
3465
3466 if (ui_browser__show(&menu->b, title,
3467 "ESC: exit, ENTER|->: Browse histograms") < 0)
3468 return -1;
3469
3470 while (1) {
3471 key = ui_browser__run(&menu->b, delay_secs);
3472
3473 switch (key) {
3474 case K_TIMER:
3475 if (hbt)
3476 hbt->timer(hbt->arg);
3477
3478 if (!menu->lost_events_warned &&
3479 menu->lost_events &&
3480 warn_lost_event) {
3481 ui_browser__warn_lost_events(&menu->b);
3482 menu->lost_events_warned = true;
3483 }
3484 continue;
3485 case K_RIGHT:
3486 case K_ENTER:
3487 if (!menu->selection)
3488 continue;
3489 pos = menu->selection;
3490 browse_hists:
3491 perf_evlist__set_selected(evlist, pos);
3492 /*
3493 * Give the calling tool a chance to populate the non
3494 * default evsel resorted hists tree.
3495 */
3496 if (hbt)
3497 hbt->timer(hbt->arg);
3498 key = perf_evsel__hists_browse(pos, nr_events, help,
3499 true, hbt,
3500 menu->min_pcnt,
3501 menu->env,
3502 warn_lost_event,
3503 menu->annotation_opts);
3504 ui_browser__show_title(&menu->b, title);
3505 switch (key) {
3506 case K_TAB:
3507 if (pos->core.node.next == &evlist->core.entries)
3508 pos = evlist__first(evlist);
3509 else
3510 pos = evsel__next(pos);
3511 goto browse_hists;
3512 case K_UNTAB:
3513 if (pos->core.node.prev == &evlist->core.entries)
3514 pos = evlist__last(evlist);
3515 else
3516 pos = evsel__prev(pos);
3517 goto browse_hists;
3518 case K_SWITCH_INPUT_DATA:
3519 case K_RELOAD:
3520 case 'q':
3521 case CTRL('c'):
3522 goto out;
3523 case K_ESC:
3524 default:
3525 continue;
3526 }
3527 case K_LEFT:
3528 continue;
3529 case K_ESC:
3530 if (!ui_browser__dialog_yesno(&menu->b,
3531 "Do you really want to exit?"))
3532 continue;
3533 /* Fall thru */
3534 case 'q':
3535 case CTRL('c'):
3536 goto out;
3537 default:
3538 continue;
3539 }
3540 }
3541
3542 out:
3543 ui_browser__hide(&menu->b);
3544 return key;
3545 }
3546
filter_group_entries(struct ui_browser *browser __maybe_unused, void *entry)3547 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3548 void *entry)
3549 {
3550 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3551
3552 if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3553 return true;
3554
3555 return false;
3556 }
3557
__perf_evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help, struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env, bool warn_lost_event, struct annotation_options *annotation_opts)3558 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3559 int nr_entries, const char *help,
3560 struct hist_browser_timer *hbt,
3561 float min_pcnt,
3562 struct perf_env *env,
3563 bool warn_lost_event,
3564 struct annotation_options *annotation_opts)
3565 {
3566 struct evsel *pos;
3567 struct evsel_menu menu = {
3568 .b = {
3569 .entries = &evlist->core.entries,
3570 .refresh = ui_browser__list_head_refresh,
3571 .seek = ui_browser__list_head_seek,
3572 .write = perf_evsel_menu__write,
3573 .filter = filter_group_entries,
3574 .nr_entries = nr_entries,
3575 .priv = evlist,
3576 },
3577 .min_pcnt = min_pcnt,
3578 .env = env,
3579 .annotation_opts = annotation_opts,
3580 };
3581
3582 ui_helpline__push("Press ESC to exit");
3583
3584 evlist__for_each_entry(evlist, pos) {
3585 const char *ev_name = evsel__name(pos);
3586 size_t line_len = strlen(ev_name) + 7;
3587
3588 if (menu.b.width < line_len)
3589 menu.b.width = line_len;
3590 }
3591
3592 return perf_evsel_menu__run(&menu, nr_entries, help,
3593 hbt, warn_lost_event);
3594 }
3595
perf_evlist__single_entry(struct evlist *evlist)3596 static bool perf_evlist__single_entry(struct evlist *evlist)
3597 {
3598 int nr_entries = evlist->core.nr_entries;
3599
3600 if (nr_entries == 1)
3601 return true;
3602
3603 if (nr_entries == 2) {
3604 struct evsel *last = evlist__last(evlist);
3605
3606 if (evsel__is_dummy_event(last))
3607 return true;
3608 }
3609
3610 return false;
3611 }
3612
perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env, bool warn_lost_event, struct annotation_options *annotation_opts)3613 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3614 struct hist_browser_timer *hbt,
3615 float min_pcnt,
3616 struct perf_env *env,
3617 bool warn_lost_event,
3618 struct annotation_options *annotation_opts)
3619 {
3620 int nr_entries = evlist->core.nr_entries;
3621
3622 if (perf_evlist__single_entry(evlist)) {
3623 single_entry: {
3624 struct evsel *first = evlist__first(evlist);
3625
3626 return perf_evsel__hists_browse(first, nr_entries, help,
3627 false, hbt, min_pcnt,
3628 env, warn_lost_event,
3629 annotation_opts);
3630 }
3631 }
3632
3633 if (symbol_conf.event_group) {
3634 struct evsel *pos;
3635
3636 nr_entries = 0;
3637 evlist__for_each_entry(evlist, pos) {
3638 if (evsel__is_group_leader(pos))
3639 nr_entries++;
3640 }
3641
3642 if (nr_entries == 1)
3643 goto single_entry;
3644 }
3645
3646 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3647 hbt, min_pcnt, env,
3648 warn_lost_event,
3649 annotation_opts);
3650 }
3651
block_hists_browser__title(struct hist_browser *browser, char *bf, size_t size)3652 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3653 size_t size)
3654 {
3655 struct hists *hists = evsel__hists(browser->block_evsel);
3656 const char *evname = evsel__name(browser->block_evsel);
3657 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3658 int ret;
3659
3660 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3661 if (evname)
3662 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3663
3664 return 0;
3665 }
3666
block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, float min_percent, struct perf_env *env, struct annotation_options *annotation_opts)3667 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3668 float min_percent, struct perf_env *env,
3669 struct annotation_options *annotation_opts)
3670 {
3671 struct hists *hists = &bh->block_hists;
3672 struct hist_browser *browser;
3673 int key = -1;
3674 struct popup_action action;
3675 static const char help[] =
3676 " q Quit \n";
3677
3678 browser = hist_browser__new(hists);
3679 if (!browser)
3680 return -1;
3681
3682 browser->block_evsel = evsel;
3683 browser->title = block_hists_browser__title;
3684 browser->min_pcnt = min_percent;
3685 browser->env = env;
3686 browser->annotation_opts = annotation_opts;
3687
3688 /* reset abort key so that it can get Ctrl-C as a key */
3689 SLang_reset_tty();
3690 SLang_init_tty(0, 0, 0);
3691
3692 memset(&action, 0, sizeof(action));
3693
3694 while (1) {
3695 key = hist_browser__run(browser, "? - help", true, 0);
3696
3697 switch (key) {
3698 case 'q':
3699 goto out;
3700 case '?':
3701 ui_browser__help_window(&browser->b, help);
3702 break;
3703 case 'a':
3704 case K_ENTER:
3705 if (!browser->selection ||
3706 !browser->selection->sym) {
3707 continue;
3708 }
3709
3710 action.ms.map = browser->selection->map;
3711 action.ms.sym = browser->selection->sym;
3712 do_annotate(browser, &action);
3713 continue;
3714 default:
3715 break;
3716 }
3717 }
3718
3719 out:
3720 hist_browser__delete(browser);
3721 return 0;
3722 }
3723