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