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