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