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