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