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