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