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