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