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