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