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