perf tools: Support '<event>.*' dynamic sort key
[deliverable/linux.git] / tools / perf / ui / browsers / hists.c
CommitLineData
d1b4f249 1#include <stdio.h>
d1b4f249
ACM
2#include <stdlib.h>
3#include <string.h>
d1b4f249
ACM
4#include <linux/rbtree.h>
5
aca7a94d
NK
6#include "../../util/evsel.h"
7#include "../../util/evlist.h"
8#include "../../util/hist.h"
9#include "../../util/pstack.h"
10#include "../../util/sort.h"
11#include "../../util/util.h"
42337a22 12#include "../../util/top.h"
68d80758 13#include "../../arch/common.h"
d1b4f249
ACM
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
4610e413 18#include "../ui.h"
d1b4f249 19#include "map.h"
d755330c 20#include "annotate.h"
d1b4f249 21
d1b4f249
ACM
22struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
c2a51ab8 27 struct hist_browser_timer *hbt;
01f00a1c 28 struct pstack *pstack;
ce80d3be 29 struct perf_env *env;
aff3f3f6 30 int print_seq;
a7cb8863 31 bool show_dso;
025bf7ea 32 bool show_headers;
064f1981 33 float min_pcnt;
112f761f 34 u64 nr_non_filtered_entries;
c3b78952 35 u64 nr_callchain_rows;
d1b4f249
ACM
36};
37
f5951d56
NK
38extern void hist_browser__init_hpp(void);
39
1e378ebd
TS
40static int hists__browser_title(struct hists *hists,
41 struct hist_browser_timer *hbt,
42 char *bf, size_t size);
112f761f 43static void hist_browser__update_nr_entries(struct hist_browser *hb);
81cce8de 44
c3b78952 45static struct rb_node *hists__filter_entries(struct rb_node *nd,
c3b78952
NK
46 float min_pcnt);
47
268397cb
NK
48static bool hist_browser__has_filter(struct hist_browser *hb)
49{
9c0fa8dd 50 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
268397cb
NK
51}
52
4fabf3d1
HK
53static int hist_browser__get_folding(struct hist_browser *browser)
54{
55 struct rb_node *nd;
56 struct hists *hists = browser->hists;
57 int unfolded_rows = 0;
58
59 for (nd = rb_first(&hists->entries);
60 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
61 nd = rb_next(nd)) {
62 struct hist_entry *he =
63 rb_entry(nd, struct hist_entry, rb_node);
64
3698dab1 65 if (he->unfolded)
4fabf3d1
HK
66 unfolded_rows += he->nr_rows;
67 }
68 return unfolded_rows;
69}
70
c3b78952
NK
71static u32 hist_browser__nr_entries(struct hist_browser *hb)
72{
73 u32 nr_entries;
74
75 if (hist_browser__has_filter(hb))
76 nr_entries = hb->nr_non_filtered_entries;
77 else
78 nr_entries = hb->hists->nr_entries;
79
4fabf3d1 80 hb->nr_callchain_rows = hist_browser__get_folding(hb);
c3b78952
NK
81 return nr_entries + hb->nr_callchain_rows;
82}
83
025bf7ea
ACM
84static void hist_browser__update_rows(struct hist_browser *hb)
85{
86 struct ui_browser *browser = &hb->b;
87 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
88
89 browser->rows = browser->height - header_offset;
90 /*
91 * Verify if we were at the last line and that line isn't
92 * visibe because we now show the header line(s).
93 */
94 index_row = browser->index - browser->top_idx;
95 if (index_row >= browser->rows)
96 browser->index -= index_row - browser->rows + 1;
97}
98
357cfff1 99static void hist_browser__refresh_dimensions(struct ui_browser *browser)
d1b4f249 100{
357cfff1
ACM
101 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
102
d1b4f249 103 /* 3 == +/- toggle symbol before actual hist_entry rendering */
357cfff1
ACM
104 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
105 /*
106 * FIXME: Just keeping existing behaviour, but this really should be
107 * before updating browser->width, as it will invalidate the
108 * calculation above. Fix this and the fallout in another
109 * changeset.
110 */
111 ui_browser__refresh_dimensions(browser);
025bf7ea 112 hist_browser__update_rows(hb);
d1b4f249
ACM
113}
114
ca3ff33b
ACM
115static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
116{
025bf7ea
ACM
117 u16 header_offset = browser->show_headers ? 1 : 0;
118
119 ui_browser__gotorc(&browser->b, row + header_offset, column);
ca3ff33b
ACM
120}
121
05e8b080 122static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 123{
c3b78952
NK
124 /*
125 * The hists__remove_entry_filter() already folds non-filtered
126 * entries so we can assume it has 0 callchain rows.
127 */
128 browser->nr_callchain_rows = 0;
129
268397cb 130 hist_browser__update_nr_entries(browser);
c3b78952 131 browser->b.nr_entries = hist_browser__nr_entries(browser);
357cfff1 132 hist_browser__refresh_dimensions(&browser->b);
05e8b080 133 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
134}
135
136static char tree__folded_sign(bool unfolded)
137{
138 return unfolded ? '-' : '+';
139}
140
05e8b080 141static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 142{
3698dab1 143 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
d1b4f249
ACM
144}
145
05e8b080 146static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 147{
3698dab1 148 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
d1b4f249
ACM
149}
150
3698dab1 151static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
3c916cc2 152{
3698dab1 153 cl->unfolded = unfold ? cl->has_children : false;
3c916cc2
ACM
154}
155
05e8b080 156static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249
ACM
157{
158 int n = 0;
159 struct rb_node *nd;
160
05e8b080 161 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
162 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
163 struct callchain_list *chain;
164 char folded_sign = ' '; /* No children */
165
166 list_for_each_entry(chain, &child->val, list) {
167 ++n;
168 /* We need this because we may not have children */
169 folded_sign = callchain_list__folded(chain);
170 if (folded_sign == '+')
171 break;
172 }
173
174 if (folded_sign == '-') /* Have children and they're unfolded */
175 n += callchain_node__count_rows_rb_tree(child);
176 }
177
178 return n;
179}
180
4b3a3212
NK
181static int callchain_node__count_flat_rows(struct callchain_node *node)
182{
183 struct callchain_list *chain;
184 char folded_sign = 0;
185 int n = 0;
186
187 list_for_each_entry(chain, &node->parent_val, list) {
188 if (!folded_sign) {
189 /* only check first chain list entry */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
192 return 1;
193 }
194 n++;
195 }
196
197 list_for_each_entry(chain, &node->val, list) {
198 if (!folded_sign) {
199 /* node->parent_val list might be empty */
200 folded_sign = callchain_list__folded(chain);
201 if (folded_sign == '+')
202 return 1;
203 }
204 n++;
205 }
206
207 return n;
208}
209
8c430a34
NK
210static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
211{
212 return 1;
213}
214
d1b4f249
ACM
215static int callchain_node__count_rows(struct callchain_node *node)
216{
217 struct callchain_list *chain;
218 bool unfolded = false;
219 int n = 0;
220
4b3a3212
NK
221 if (callchain_param.mode == CHAIN_FLAT)
222 return callchain_node__count_flat_rows(node);
8c430a34
NK
223 else if (callchain_param.mode == CHAIN_FOLDED)
224 return callchain_node__count_folded_rows(node);
4b3a3212 225
d1b4f249
ACM
226 list_for_each_entry(chain, &node->val, list) {
227 ++n;
3698dab1 228 unfolded = chain->unfolded;
d1b4f249
ACM
229 }
230
231 if (unfolded)
232 n += callchain_node__count_rows_rb_tree(node);
233
234 return n;
235}
236
237static int callchain__count_rows(struct rb_root *chain)
238{
239 struct rb_node *nd;
240 int n = 0;
241
242 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
243 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
244 n += callchain_node__count_rows(node);
245 }
246
247 return n;
248}
249
3698dab1 250static bool hist_entry__toggle_fold(struct hist_entry *he)
d1b4f249 251{
3698dab1 252 if (!he)
8493fe1d
JO
253 return false;
254
3698dab1 255 if (!he->has_children)
d1b4f249
ACM
256 return false;
257
3698dab1
NK
258 he->unfolded = !he->unfolded;
259 return true;
260}
261
262static bool callchain_list__toggle_fold(struct callchain_list *cl)
263{
264 if (!cl)
265 return false;
266
267 if (!cl->has_children)
268 return false;
269
270 cl->unfolded = !cl->unfolded;
d1b4f249
ACM
271 return true;
272}
273
05e8b080 274static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 275{
05e8b080 276 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 277
05e8b080 278 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
279 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
280 struct callchain_list *chain;
293db47f 281 bool first = true;
d1b4f249
ACM
282
283 list_for_each_entry(chain, &child->val, list) {
284 if (first) {
285 first = false;
3698dab1 286 chain->has_children = chain->list.next != &child->val ||
293db47f 287 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249 288 } else
3698dab1 289 chain->has_children = chain->list.next == &child->val &&
293db47f 290 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
291 }
292
293 callchain_node__init_have_children_rb_tree(child);
294 }
295}
296
a7444af6
NK
297static void callchain_node__init_have_children(struct callchain_node *node,
298 bool has_sibling)
d1b4f249
ACM
299{
300 struct callchain_list *chain;
301
a7444af6 302 chain = list_entry(node->val.next, struct callchain_list, list);
3698dab1 303 chain->has_children = has_sibling;
a7444af6 304
4b3a3212 305 if (node->val.next != node->val.prev) {
82162b5a 306 chain = list_entry(node->val.prev, struct callchain_list, list);
3698dab1 307 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
82162b5a 308 }
d1b4f249 309
05e8b080 310 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
311}
312
05e8b080 313static void callchain__init_have_children(struct rb_root *root)
d1b4f249 314{
a7444af6
NK
315 struct rb_node *nd = rb_first(root);
316 bool has_sibling = nd && rb_next(nd);
d1b4f249 317
05e8b080 318 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249 319 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
a7444af6 320 callchain_node__init_have_children(node, has_sibling);
8c430a34
NK
321 if (callchain_param.mode == CHAIN_FLAT ||
322 callchain_param.mode == CHAIN_FOLDED)
4b3a3212 323 callchain_node__make_parent_list(node);
d1b4f249
ACM
324 }
325}
326
05e8b080 327static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 328{
05e8b080 329 if (!he->init_have_children) {
3698dab1 330 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
05e8b080
ACM
331 callchain__init_have_children(&he->sorted_chain);
332 he->init_have_children = true;
d1b4f249
ACM
333 }
334}
335
05e8b080 336static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 337{
3698dab1
NK
338 struct hist_entry *he = browser->he_selection;
339 struct map_symbol *ms = browser->selection;
340 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
341 bool has_children;
342
4938cf0c
WN
343 if (!he || !ms)
344 return false;
345
3698dab1
NK
346 if (ms == &he->ms)
347 has_children = hist_entry__toggle_fold(he);
348 else
349 has_children = callchain_list__toggle_fold(cl);
d1b4f249 350
3698dab1 351 if (has_children) {
d1b4f249 352 hist_entry__init_have_children(he);
c3b78952
NK
353 browser->b.nr_entries -= he->nr_rows;
354 browser->nr_callchain_rows -= he->nr_rows;
d1b4f249 355
3698dab1 356 if (he->unfolded)
d1b4f249
ACM
357 he->nr_rows = callchain__count_rows(&he->sorted_chain);
358 else
359 he->nr_rows = 0;
c3b78952
NK
360
361 browser->b.nr_entries += he->nr_rows;
362 browser->nr_callchain_rows += he->nr_rows;
d1b4f249
ACM
363
364 return true;
365 }
366
367 /* If it doesn't have children, no toggling performed */
368 return false;
369}
370
05e8b080 371static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
372{
373 int n = 0;
374 struct rb_node *nd;
375
05e8b080 376 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
377 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
378 struct callchain_list *chain;
379 bool has_children = false;
380
381 list_for_each_entry(chain, &child->val, list) {
382 ++n;
3698dab1
NK
383 callchain_list__set_folding(chain, unfold);
384 has_children = chain->has_children;
3c916cc2
ACM
385 }
386
387 if (has_children)
388 n += callchain_node__set_folding_rb_tree(child, unfold);
389 }
390
391 return n;
392}
393
394static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
395{
396 struct callchain_list *chain;
397 bool has_children = false;
398 int n = 0;
399
400 list_for_each_entry(chain, &node->val, list) {
401 ++n;
3698dab1
NK
402 callchain_list__set_folding(chain, unfold);
403 has_children = chain->has_children;
3c916cc2
ACM
404 }
405
406 if (has_children)
407 n += callchain_node__set_folding_rb_tree(node, unfold);
408
409 return n;
410}
411
412static int callchain__set_folding(struct rb_root *chain, bool unfold)
413{
414 struct rb_node *nd;
415 int n = 0;
416
417 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
418 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
419 n += callchain_node__set_folding(node, unfold);
420 }
421
422 return n;
423}
424
05e8b080 425static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
3c916cc2 426{
05e8b080 427 hist_entry__init_have_children(he);
3698dab1 428 he->unfolded = unfold ? he->has_children : false;
3c916cc2 429
3698dab1 430 if (he->has_children) {
05e8b080
ACM
431 int n = callchain__set_folding(&he->sorted_chain, unfold);
432 he->nr_rows = unfold ? n : 0;
3c916cc2 433 } else
05e8b080 434 he->nr_rows = 0;
3c916cc2
ACM
435}
436
c3b78952
NK
437static void
438__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2
ACM
439{
440 struct rb_node *nd;
c3b78952 441 struct hists *hists = browser->hists;
3c916cc2 442
c3b78952 443 for (nd = rb_first(&hists->entries);
14135663 444 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
c3b78952 445 nd = rb_next(nd)) {
3c916cc2
ACM
446 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
447 hist_entry__set_folding(he, unfold);
c3b78952 448 browser->nr_callchain_rows += he->nr_rows;
3c916cc2
ACM
449 }
450}
451
05e8b080 452static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 453{
c3b78952
NK
454 browser->nr_callchain_rows = 0;
455 __hist_browser__set_folding(browser, unfold);
456
457 browser->b.nr_entries = hist_browser__nr_entries(browser);
3c916cc2 458 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 459 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
460}
461
7b27509f
ACM
462static void ui_browser__warn_lost_events(struct ui_browser *browser)
463{
464 ui_browser__warning(browser, 4,
465 "Events are being lost, check IO/CPU overload!\n\n"
466 "You may want to run 'perf' using a RT scheduler policy:\n\n"
467 " perf top -r 80\n\n"
468 "Or reduce the sampling frequency.");
469}
470
5f00b0f4 471static int hist_browser__run(struct hist_browser *browser, const char *help)
d1b4f249 472{
b50e003d 473 int key;
81cce8de 474 char title[160];
c2a51ab8 475 struct hist_browser_timer *hbt = browser->hbt;
9783adf7 476 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 477
05e8b080 478 browser->b.entries = &browser->hists->entries;
c3b78952 479 browser->b.nr_entries = hist_browser__nr_entries(browser);
d1b4f249 480
1e378ebd 481 hists__browser_title(browser->hists, hbt, title, sizeof(title));
d1b4f249 482
5f00b0f4 483 if (ui_browser__show(&browser->b, title, help) < 0)
d1b4f249
ACM
484 return -1;
485
d1b4f249 486 while (1) {
05e8b080 487 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 488
b50e003d 489 switch (key) {
fa5df943
NK
490 case K_TIMER: {
491 u64 nr_entries;
9783adf7 492 hbt->timer(hbt->arg);
fa5df943 493
c3b78952 494 if (hist_browser__has_filter(browser))
112f761f 495 hist_browser__update_nr_entries(browser);
fa5df943 496
c3b78952 497 nr_entries = hist_browser__nr_entries(browser);
fa5df943 498 ui_browser__update_nr_entries(&browser->b, nr_entries);
7b27509f 499
05e8b080
ACM
500 if (browser->hists->stats.nr_lost_warned !=
501 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
502 browser->hists->stats.nr_lost_warned =
503 browser->hists->stats.nr_events[PERF_RECORD_LOST];
504 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
505 }
506
1e378ebd
TS
507 hists__browser_title(browser->hists,
508 hbt, title, sizeof(title));
05e8b080 509 ui_browser__show_title(&browser->b, title);
81cce8de 510 continue;
fa5df943 511 }
4694153c 512 case 'D': { /* Debug */
d1b4f249 513 static int seq;
05e8b080 514 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
515 struct hist_entry, rb_node);
516 ui_helpline__pop();
62c95ae3 517 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
518 seq++, browser->b.nr_entries,
519 browser->hists->nr_entries,
62c95ae3 520 browser->b.rows,
05e8b080
ACM
521 browser->b.index,
522 browser->b.top_idx,
d1b4f249
ACM
523 h->row_offset, h->nr_rows);
524 }
3c916cc2
ACM
525 break;
526 case 'C':
527 /* Collapse the whole world. */
05e8b080 528 hist_browser__set_folding(browser, false);
3c916cc2
ACM
529 break;
530 case 'E':
531 /* Expand the whole world. */
05e8b080 532 hist_browser__set_folding(browser, true);
3c916cc2 533 break;
025bf7ea
ACM
534 case 'H':
535 browser->show_headers = !browser->show_headers;
536 hist_browser__update_rows(browser);
537 break;
cf958003 538 case K_ENTER:
05e8b080 539 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
540 break;
541 /* fall thru */
542 default:
b50e003d 543 goto out;
d1b4f249
ACM
544 }
545 }
b50e003d 546out:
05e8b080 547 ui_browser__hide(&browser->b);
b50e003d 548 return key;
d1b4f249
ACM
549}
550
39ee533f
NK
551struct callchain_print_arg {
552 /* for hists browser */
553 off_t row_offset;
554 bool is_current_entry;
555
556 /* for file dump */
557 FILE *fp;
558 int printed;
559};
560
561typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
562 struct callchain_list *chain,
563 const char *str, int offset,
564 unsigned short row,
565 struct callchain_print_arg *arg);
566
f4536ddd
NK
567static void hist_browser__show_callchain_entry(struct hist_browser *browser,
568 struct callchain_list *chain,
39ee533f
NK
569 const char *str, int offset,
570 unsigned short row,
571 struct callchain_print_arg *arg)
f4536ddd
NK
572{
573 int color, width;
39ee533f 574 char folded_sign = callchain_list__folded(chain);
70e97278 575 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
f4536ddd
NK
576
577 color = HE_COLORSET_NORMAL;
578 width = browser->b.width - (offset + 2);
579 if (ui_browser__is_current_entry(&browser->b, row)) {
580 browser->selection = &chain->ms;
581 color = HE_COLORSET_SELECTED;
39ee533f 582 arg->is_current_entry = true;
f4536ddd
NK
583 }
584
585 ui_browser__set_color(&browser->b, color);
586 hist_browser__gotorc(browser, row, 0);
26270a00 587 ui_browser__write_nstring(&browser->b, " ", offset);
517dfdb3 588 ui_browser__printf(&browser->b, "%c", folded_sign);
70e97278 589 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
26270a00 590 ui_browser__write_nstring(&browser->b, str, width);
f4536ddd
NK
591}
592
39ee533f
NK
593static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
594 struct callchain_list *chain,
595 const char *str, int offset,
596 unsigned short row __maybe_unused,
597 struct callchain_print_arg *arg)
598{
599 char folded_sign = callchain_list__folded(chain);
600
601 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
602 folded_sign, str);
603}
604
605typedef bool (*check_output_full_fn)(struct hist_browser *browser,
606 unsigned short row);
607
608static bool hist_browser__check_output_full(struct hist_browser *browser,
609 unsigned short row)
610{
611 return browser->b.rows == row;
612}
613
614static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
615 unsigned short row __maybe_unused)
616{
617 return false;
618}
619
d1b4f249
ACM
620#define LEVEL_OFFSET_STEP 3
621
18bb8381
NK
622static int hist_browser__show_callchain_list(struct hist_browser *browser,
623 struct callchain_node *node,
624 struct callchain_list *chain,
625 unsigned short row, u64 total,
626 bool need_percent, int offset,
627 print_callchain_entry_fn print,
628 struct callchain_print_arg *arg)
629{
630 char bf[1024], *alloc_str;
631 const char *str;
632
633 if (arg->row_offset != 0) {
634 arg->row_offset--;
635 return 0;
636 }
637
638 alloc_str = NULL;
639 str = callchain_list__sym_name(chain, bf, sizeof(bf),
640 browser->show_dso);
641
642 if (need_percent) {
643 char buf[64];
644
645 callchain_node__scnprintf_value(node, buf, sizeof(buf),
646 total);
647
648 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
649 str = "Not enough memory!";
650 else
651 str = alloc_str;
652 }
653
654 print(browser, chain, str, offset, row, arg);
655
656 free(alloc_str);
657 return 1;
658}
659
4b3a3212
NK
660static int hist_browser__show_callchain_flat(struct hist_browser *browser,
661 struct rb_root *root,
662 unsigned short row, u64 total,
663 print_callchain_entry_fn print,
664 struct callchain_print_arg *arg,
665 check_output_full_fn is_output_full)
666{
667 struct rb_node *node;
668 int first_row = row, offset = LEVEL_OFFSET_STEP;
669 bool need_percent;
670
671 node = rb_first(root);
672 need_percent = node && rb_next(node);
673
674 while (node) {
675 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
676 struct rb_node *next = rb_next(node);
677 struct callchain_list *chain;
678 char folded_sign = ' ';
679 int first = true;
680 int extra_offset = 0;
681
682 list_for_each_entry(chain, &child->parent_val, list) {
683 bool was_first = first;
684
685 if (first)
686 first = false;
687 else if (need_percent)
688 extra_offset = LEVEL_OFFSET_STEP;
689
690 folded_sign = callchain_list__folded(chain);
691
692 row += hist_browser__show_callchain_list(browser, child,
693 chain, row, total,
694 was_first && need_percent,
695 offset + extra_offset,
696 print, arg);
697
698 if (is_output_full(browser, row))
699 goto out;
700
701 if (folded_sign == '+')
702 goto next;
703 }
704
705 list_for_each_entry(chain, &child->val, list) {
706 bool was_first = first;
707
708 if (first)
709 first = false;
710 else if (need_percent)
711 extra_offset = LEVEL_OFFSET_STEP;
712
713 folded_sign = callchain_list__folded(chain);
714
715 row += hist_browser__show_callchain_list(browser, child,
716 chain, row, total,
717 was_first && need_percent,
718 offset + extra_offset,
719 print, arg);
720
721 if (is_output_full(browser, row))
722 goto out;
723
724 if (folded_sign == '+')
725 break;
726 }
727
728next:
729 if (is_output_full(browser, row))
730 break;
731 node = next;
732 }
733out:
734 return row - first_row;
735}
736
8c430a34
NK
737static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
738 struct callchain_list *chain,
739 char *value_str, char *old_str)
740{
741 char bf[1024];
742 const char *str;
743 char *new;
744
745 str = callchain_list__sym_name(chain, bf, sizeof(bf),
746 browser->show_dso);
747 if (old_str) {
748 if (asprintf(&new, "%s%s%s", old_str,
749 symbol_conf.field_sep ?: ";", str) < 0)
750 new = NULL;
751 } else {
752 if (value_str) {
753 if (asprintf(&new, "%s %s", value_str, str) < 0)
754 new = NULL;
755 } else {
756 if (asprintf(&new, "%s", str) < 0)
757 new = NULL;
758 }
759 }
760 return new;
761}
762
763static int hist_browser__show_callchain_folded(struct hist_browser *browser,
764 struct rb_root *root,
765 unsigned short row, u64 total,
766 print_callchain_entry_fn print,
767 struct callchain_print_arg *arg,
768 check_output_full_fn is_output_full)
769{
770 struct rb_node *node;
771 int first_row = row, offset = LEVEL_OFFSET_STEP;
772 bool need_percent;
773
774 node = rb_first(root);
775 need_percent = node && rb_next(node);
776
777 while (node) {
778 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
779 struct rb_node *next = rb_next(node);
780 struct callchain_list *chain, *first_chain = NULL;
781 int first = true;
782 char *value_str = NULL, *value_str_alloc = NULL;
783 char *chain_str = NULL, *chain_str_alloc = NULL;
784
785 if (arg->row_offset != 0) {
786 arg->row_offset--;
787 goto next;
788 }
789
790 if (need_percent) {
791 char buf[64];
792
793 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
794 if (asprintf(&value_str, "%s", buf) < 0) {
795 value_str = (char *)"<...>";
796 goto do_print;
797 }
798 value_str_alloc = value_str;
799 }
800
801 list_for_each_entry(chain, &child->parent_val, list) {
802 chain_str = hist_browser__folded_callchain_str(browser,
803 chain, value_str, chain_str);
804 if (first) {
805 first = false;
806 first_chain = chain;
807 }
808
809 if (chain_str == NULL) {
810 chain_str = (char *)"Not enough memory!";
811 goto do_print;
812 }
813
814 chain_str_alloc = chain_str;
815 }
816
817 list_for_each_entry(chain, &child->val, list) {
818 chain_str = hist_browser__folded_callchain_str(browser,
819 chain, value_str, chain_str);
820 if (first) {
821 first = false;
822 first_chain = chain;
823 }
824
825 if (chain_str == NULL) {
826 chain_str = (char *)"Not enough memory!";
827 goto do_print;
828 }
829
830 chain_str_alloc = chain_str;
831 }
832
833do_print:
834 print(browser, first_chain, chain_str, offset, row++, arg);
835 free(value_str_alloc);
836 free(chain_str_alloc);
837
838next:
839 if (is_output_full(browser, row))
840 break;
841 node = next;
842 }
843
844 return row - first_row;
845}
846
c09a7e75
NK
847static int hist_browser__show_callchain(struct hist_browser *browser,
848 struct rb_root *root, int level,
39ee533f
NK
849 unsigned short row, u64 total,
850 print_callchain_entry_fn print,
851 struct callchain_print_arg *arg,
852 check_output_full_fn is_output_full)
d1b4f249
ACM
853{
854 struct rb_node *node;
f4536ddd 855 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
36e15dd4 856 u64 new_total;
4087d11c 857 bool need_percent;
d1b4f249 858
c09a7e75 859 node = rb_first(root);
c09e31cc 860 need_percent = node && rb_next(node);
4087d11c 861
d1b4f249
ACM
862 while (node) {
863 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
864 struct rb_node *next = rb_next(node);
d1b4f249
ACM
865 struct callchain_list *chain;
866 char folded_sign = ' ';
867 int first = true;
868 int extra_offset = 0;
869
d1b4f249 870 list_for_each_entry(chain, &child->val, list) {
d1b4f249
ACM
871 bool was_first = first;
872
163caed9 873 if (first)
d1b4f249 874 first = false;
4087d11c 875 else if (need_percent)
d1b4f249 876 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
877
878 folded_sign = callchain_list__folded(chain);
c09a7e75 879
18bb8381
NK
880 row += hist_browser__show_callchain_list(browser, child,
881 chain, row, total,
882 was_first && need_percent,
883 offset + extra_offset,
884 print, arg);
d1b4f249 885
18bb8381 886 if (is_output_full(browser, row))
d1b4f249 887 goto out;
18bb8381 888
d1b4f249
ACM
889 if (folded_sign == '+')
890 break;
891 }
892
893 if (folded_sign == '-') {
894 const int new_level = level + (extra_offset ? 2 : 1);
d1b4f249 895
c09a7e75
NK
896 if (callchain_param.mode == CHAIN_GRAPH_REL)
897 new_total = child->children_hit;
898 else
899 new_total = total;
d1b4f249 900
c09a7e75 901 row += hist_browser__show_callchain(browser, &child->rb_root,
39ee533f
NK
902 new_level, row, new_total,
903 print, arg, is_output_full);
d1b4f249 904 }
39ee533f 905 if (is_output_full(browser, row))
d1b4f249 906 break;
c09a7e75 907 node = next;
d1b4f249 908 }
c09a7e75 909out:
d1b4f249
ACM
910 return row - first_row;
911}
912
89701460
NK
913struct hpp_arg {
914 struct ui_browser *b;
915 char folded_sign;
916 bool current_entry;
917};
918
2f6d9009
NK
919static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
920{
921 struct hpp_arg *arg = hpp->ptr;
d675107c 922 int ret, len;
2f6d9009
NK
923 va_list args;
924 double percent;
371d8c40 925
2f6d9009 926 va_start(args, fmt);
d675107c 927 len = va_arg(args, int);
2f6d9009
NK
928 percent = va_arg(args, double);
929 va_end(args);
371d8c40 930
2f6d9009 931 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 932
d675107c 933 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
517dfdb3 934 ui_browser__printf(arg->b, "%s", hpp->buf);
5aed9d24 935
2f6d9009 936 advance_hpp(hpp, ret);
5aed9d24
NK
937 return ret;
938}
939
fb821c9e 940#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
941static u64 __hpp_get_##_field(struct hist_entry *he) \
942{ \
943 return he->stat._field; \
944} \
945 \
2c5d4b4a 946static int \
5b591669 947hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
2c5d4b4a
JO
948 struct perf_hpp *hpp, \
949 struct hist_entry *he) \
f5951d56 950{ \
5b591669
NK
951 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
952 __hpp__slsmg_color_printf, true); \
f5951d56
NK
953}
954
0434ddd2
NK
955#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
956static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
957{ \
958 return he->stat_acc->_field; \
959} \
960 \
961static int \
5b591669 962hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
0434ddd2
NK
963 struct perf_hpp *hpp, \
964 struct hist_entry *he) \
965{ \
966 if (!symbol_conf.cumulate_callchain) { \
517dfdb3 967 struct hpp_arg *arg = hpp->ptr; \
5b591669 968 int len = fmt->user_len ?: fmt->len; \
d675107c 969 int ret = scnprintf(hpp->buf, hpp->size, \
5b591669 970 "%*s", len, "N/A"); \
517dfdb3 971 ui_browser__printf(arg->b, "%s", hpp->buf); \
0434ddd2
NK
972 \
973 return ret; \
974 } \
5b591669
NK
975 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
976 " %*.2f%%", __hpp__slsmg_color_printf, true); \
0434ddd2
NK
977}
978
fb821c9e
NK
979__HPP_COLOR_PERCENT_FN(overhead, period)
980__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
981__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
982__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
983__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 984__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 985
5aed9d24 986#undef __HPP_COLOR_PERCENT_FN
0434ddd2 987#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
988
989void hist_browser__init_hpp(void)
990{
f5951d56
NK
991 perf_hpp__format[PERF_HPP__OVERHEAD].color =
992 hist_browser__hpp_color_overhead;
993 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
994 hist_browser__hpp_color_overhead_sys;
995 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
996 hist_browser__hpp_color_overhead_us;
997 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
998 hist_browser__hpp_color_overhead_guest_sys;
999 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1000 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
1001 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1002 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
1003}
1004
05e8b080 1005static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
1006 struct hist_entry *entry,
1007 unsigned short row)
1008{
1009 char s[256];
1240005e 1010 int printed = 0;
67d25916 1011 int width = browser->b.width;
d1b4f249 1012 char folded_sign = ' ';
05e8b080 1013 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 1014 off_t row_offset = entry->row_offset;
63a1a3d8 1015 bool first = true;
1240005e 1016 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
1017
1018 if (current_entry) {
05e8b080
ACM
1019 browser->he_selection = entry;
1020 browser->selection = &entry->ms;
d1b4f249
ACM
1021 }
1022
1023 if (symbol_conf.use_callchain) {
163caed9 1024 hist_entry__init_have_children(entry);
d1b4f249
ACM
1025 folded_sign = hist_entry__folded(entry);
1026 }
1027
1028 if (row_offset == 0) {
89701460 1029 struct hpp_arg arg = {
fb821c9e 1030 .b = &browser->b,
89701460
NK
1031 .folded_sign = folded_sign,
1032 .current_entry = current_entry,
1033 };
f5951d56
NK
1034 struct perf_hpp hpp = {
1035 .buf = s,
1036 .size = sizeof(s),
89701460 1037 .ptr = &arg,
f5951d56 1038 };
c6c3c02d 1039 int column = 0;
d1b4f249 1040
ca3ff33b 1041 hist_browser__gotorc(browser, row, 0);
c172f742 1042
1240005e 1043 perf_hpp__for_each_format(fmt) {
c6c3c02d 1044 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
e67d49a7
NK
1045 continue;
1046
fb821c9e
NK
1047 if (current_entry && browser->b.navkeypressed) {
1048 ui_browser__set_color(&browser->b,
1049 HE_COLORSET_SELECTED);
1050 } else {
1051 ui_browser__set_color(&browser->b,
1052 HE_COLORSET_NORMAL);
1053 }
1054
1055 if (first) {
1056 if (symbol_conf.use_callchain) {
517dfdb3 1057 ui_browser__printf(&browser->b, "%c ", folded_sign);
fb821c9e
NK
1058 width -= 2;
1059 }
1060 first = false;
1061 } else {
517dfdb3 1062 ui_browser__printf(&browser->b, " ");
f5951d56
NK
1063 width -= 2;
1064 }
c172f742 1065
1240005e 1066 if (fmt->color) {
2c5d4b4a 1067 width -= fmt->color(fmt, &hpp, entry);
f5951d56 1068 } else {
2c5d4b4a 1069 width -= fmt->entry(fmt, &hpp, entry);
517dfdb3 1070 ui_browser__printf(&browser->b, "%s", s);
f5951d56 1071 }
2cf9cebf
ACM
1072 }
1073
f5951d56
NK
1074 /* The scroll bar isn't being used */
1075 if (!browser->b.navkeypressed)
1076 width += 1;
1077
26270a00 1078 ui_browser__write_nstring(&browser->b, "", width);
26d8b338 1079
d1b4f249
ACM
1080 ++row;
1081 ++printed;
1082 } else
1083 --row_offset;
1084
62c95ae3 1085 if (folded_sign == '-' && row != browser->b.rows) {
c09a7e75 1086 u64 total = hists__total_period(entry->hists);
39ee533f
NK
1087 struct callchain_print_arg arg = {
1088 .row_offset = row_offset,
1089 .is_current_entry = current_entry,
1090 };
c09a7e75 1091
4087d11c
NK
1092 if (callchain_param.mode == CHAIN_GRAPH_REL) {
1093 if (symbol_conf.cumulate_callchain)
1094 total = entry->stat_acc->period;
1095 else
1096 total = entry->stat.period;
1097 }
1098
4b3a3212
NK
1099 if (callchain_param.mode == CHAIN_FLAT) {
1100 printed += hist_browser__show_callchain_flat(browser,
1101 &entry->sorted_chain, row, total,
1102 hist_browser__show_callchain_entry, &arg,
1103 hist_browser__check_output_full);
8c430a34
NK
1104 } else if (callchain_param.mode == CHAIN_FOLDED) {
1105 printed += hist_browser__show_callchain_folded(browser,
1106 &entry->sorted_chain, row, total,
1107 hist_browser__show_callchain_entry, &arg,
1108 hist_browser__check_output_full);
4b3a3212
NK
1109 } else {
1110 printed += hist_browser__show_callchain(browser,
39ee533f
NK
1111 &entry->sorted_chain, 1, row, total,
1112 hist_browser__show_callchain_entry, &arg,
1113 hist_browser__check_output_full);
4b3a3212 1114 }
c09a7e75 1115
39ee533f 1116 if (arg.is_current_entry)
05e8b080 1117 browser->he_selection = entry;
d1b4f249
ACM
1118 }
1119
1120 return printed;
1121}
1122
81a888fe
JO
1123static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1124{
1125 advance_hpp(hpp, inc);
1126 return hpp->size <= 0;
1127}
1128
c6c3c02d 1129static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
81a888fe 1130{
c6c3c02d 1131 struct hists *hists = browser->hists;
81a888fe
JO
1132 struct perf_hpp dummy_hpp = {
1133 .buf = buf,
1134 .size = size,
1135 };
1136 struct perf_hpp_fmt *fmt;
1137 size_t ret = 0;
c6c3c02d 1138 int column = 0;
81a888fe
JO
1139
1140 if (symbol_conf.use_callchain) {
1141 ret = scnprintf(buf, size, " ");
1142 if (advance_hpp_check(&dummy_hpp, ret))
1143 return ret;
1144 }
1145
1146 perf_hpp__for_each_format(fmt) {
c6c3c02d 1147 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
81a888fe
JO
1148 continue;
1149
81a888fe
JO
1150 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1151 if (advance_hpp_check(&dummy_hpp, ret))
1152 break;
1153
1154 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1155 if (advance_hpp_check(&dummy_hpp, ret))
1156 break;
1157 }
1158
1159 return ret;
1160}
1161
025bf7ea
ACM
1162static void hist_browser__show_headers(struct hist_browser *browser)
1163{
81a888fe
JO
1164 char headers[1024];
1165
c6c3c02d 1166 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
025bf7ea
ACM
1167 ui_browser__gotorc(&browser->b, 0, 0);
1168 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
26270a00 1169 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
025bf7ea
ACM
1170}
1171
437cfe7a
ACM
1172static void ui_browser__hists_init_top(struct ui_browser *browser)
1173{
1174 if (browser->top == NULL) {
1175 struct hist_browser *hb;
1176
1177 hb = container_of(browser, struct hist_browser, b);
1178 browser->top = rb_first(&hb->hists->entries);
1179 }
1180}
1181
05e8b080 1182static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
1183{
1184 unsigned row = 0;
025bf7ea 1185 u16 header_offset = 0;
d1b4f249 1186 struct rb_node *nd;
05e8b080 1187 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 1188
025bf7ea
ACM
1189 if (hb->show_headers) {
1190 hist_browser__show_headers(hb);
1191 header_offset = 1;
1192 }
1193
05e8b080 1194 ui_browser__hists_init_top(browser);
979d2cac
WN
1195 hb->he_selection = NULL;
1196 hb->selection = NULL;
d1b4f249 1197
05e8b080 1198 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249 1199 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1200 float percent;
d1b4f249
ACM
1201
1202 if (h->filtered)
1203 continue;
1204
14135663 1205 percent = hist_entry__get_percent_limit(h);
064f1981
NK
1206 if (percent < hb->min_pcnt)
1207 continue;
1208
d1b4f249 1209 row += hist_browser__show_entry(hb, h, row);
62c95ae3 1210 if (row == browser->rows)
d1b4f249
ACM
1211 break;
1212 }
1213
025bf7ea 1214 return row + header_offset;
d1b4f249
ACM
1215}
1216
064f1981 1217static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 1218 float min_pcnt)
d1b4f249
ACM
1219{
1220 while (nd != NULL) {
1221 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1222 float percent = hist_entry__get_percent_limit(h);
064f1981 1223
c0f1527b 1224 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1225 return nd;
1226
1227 nd = rb_next(nd);
1228 }
1229
1230 return NULL;
1231}
1232
064f1981 1233static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 1234 float min_pcnt)
d1b4f249
ACM
1235{
1236 while (nd != NULL) {
1237 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1238 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
1239
1240 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1241 return nd;
1242
1243 nd = rb_prev(nd);
1244 }
1245
1246 return NULL;
1247}
1248
05e8b080 1249static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
1250 off_t offset, int whence)
1251{
1252 struct hist_entry *h;
1253 struct rb_node *nd;
1254 bool first = true;
064f1981
NK
1255 struct hist_browser *hb;
1256
1257 hb = container_of(browser, struct hist_browser, b);
d1b4f249 1258
05e8b080 1259 if (browser->nr_entries == 0)
60098917
ACM
1260 return;
1261
05e8b080 1262 ui_browser__hists_init_top(browser);
437cfe7a 1263
d1b4f249
ACM
1264 switch (whence) {
1265 case SEEK_SET:
064f1981 1266 nd = hists__filter_entries(rb_first(browser->entries),
14135663 1267 hb->min_pcnt);
d1b4f249
ACM
1268 break;
1269 case SEEK_CUR:
05e8b080 1270 nd = browser->top;
d1b4f249
ACM
1271 goto do_offset;
1272 case SEEK_END:
064f1981 1273 nd = hists__filter_prev_entries(rb_last(browser->entries),
14135663 1274 hb->min_pcnt);
d1b4f249
ACM
1275 first = false;
1276 break;
1277 default:
1278 return;
1279 }
1280
1281 /*
1282 * Moves not relative to the first visible entry invalidates its
1283 * row_offset:
1284 */
05e8b080 1285 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
1286 h->row_offset = 0;
1287
1288 /*
1289 * Here we have to check if nd is expanded (+), if it is we can't go
1290 * the next top level hist_entry, instead we must compute an offset of
1291 * what _not_ to show and not change the first visible entry.
1292 *
1293 * This offset increments when we are going from top to bottom and
1294 * decreases when we're going from bottom to top.
1295 *
1296 * As we don't have backpointers to the top level in the callchains
1297 * structure, we need to always print the whole hist_entry callchain,
1298 * skipping the first ones that are before the first visible entry
1299 * and stop when we printed enough lines to fill the screen.
1300 */
1301do_offset:
837eeb75
WN
1302 if (!nd)
1303 return;
1304
d1b4f249
ACM
1305 if (offset > 0) {
1306 do {
1307 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1308 if (h->unfolded) {
d1b4f249
ACM
1309 u16 remaining = h->nr_rows - h->row_offset;
1310 if (offset > remaining) {
1311 offset -= remaining;
1312 h->row_offset = 0;
1313 } else {
1314 h->row_offset += offset;
1315 offset = 0;
05e8b080 1316 browser->top = nd;
d1b4f249
ACM
1317 break;
1318 }
1319 }
14135663 1320 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
d1b4f249
ACM
1321 if (nd == NULL)
1322 break;
1323 --offset;
05e8b080 1324 browser->top = nd;
d1b4f249
ACM
1325 } while (offset != 0);
1326 } else if (offset < 0) {
1327 while (1) {
1328 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1329 if (h->unfolded) {
d1b4f249
ACM
1330 if (first) {
1331 if (-offset > h->row_offset) {
1332 offset += h->row_offset;
1333 h->row_offset = 0;
1334 } else {
1335 h->row_offset += offset;
1336 offset = 0;
05e8b080 1337 browser->top = nd;
d1b4f249
ACM
1338 break;
1339 }
1340 } else {
1341 if (-offset > h->nr_rows) {
1342 offset += h->nr_rows;
1343 h->row_offset = 0;
1344 } else {
1345 h->row_offset = h->nr_rows + offset;
1346 offset = 0;
05e8b080 1347 browser->top = nd;
d1b4f249
ACM
1348 break;
1349 }
1350 }
1351 }
1352
14135663 1353 nd = hists__filter_prev_entries(rb_prev(nd),
064f1981 1354 hb->min_pcnt);
d1b4f249
ACM
1355 if (nd == NULL)
1356 break;
1357 ++offset;
05e8b080 1358 browser->top = nd;
d1b4f249
ACM
1359 if (offset == 0) {
1360 /*
1361 * Last unfiltered hist_entry, check if it is
1362 * unfolded, if it is then we should have
1363 * row_offset at its last entry.
1364 */
1365 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1366 if (h->unfolded)
d1b4f249
ACM
1367 h->row_offset = h->nr_rows;
1368 break;
1369 }
1370 first = false;
1371 }
1372 } else {
05e8b080 1373 browser->top = nd;
d1b4f249
ACM
1374 h = rb_entry(nd, struct hist_entry, rb_node);
1375 h->row_offset = 0;
1376 }
1377}
1378
aff3f3f6 1379static int hist_browser__fprintf_callchain(struct hist_browser *browser,
39ee533f 1380 struct hist_entry *he, FILE *fp)
aff3f3f6 1381{
39ee533f
NK
1382 u64 total = hists__total_period(he->hists);
1383 struct callchain_print_arg arg = {
1384 .fp = fp,
1385 };
aff3f3f6 1386
39ee533f
NK
1387 if (symbol_conf.cumulate_callchain)
1388 total = he->stat_acc->period;
aff3f3f6 1389
39ee533f
NK
1390 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1391 hist_browser__fprintf_callchain_entry, &arg,
1392 hist_browser__check_dump_full);
1393 return arg.printed;
aff3f3f6
ACM
1394}
1395
1396static int hist_browser__fprintf_entry(struct hist_browser *browser,
1397 struct hist_entry *he, FILE *fp)
1398{
1399 char s[8192];
aff3f3f6
ACM
1400 int printed = 0;
1401 char folded_sign = ' ';
26d8b338
NK
1402 struct perf_hpp hpp = {
1403 .buf = s,
1404 .size = sizeof(s),
1405 };
1406 struct perf_hpp_fmt *fmt;
1407 bool first = true;
1408 int ret;
aff3f3f6
ACM
1409
1410 if (symbol_conf.use_callchain)
1411 folded_sign = hist_entry__folded(he);
1412
aff3f3f6
ACM
1413 if (symbol_conf.use_callchain)
1414 printed += fprintf(fp, "%c ", folded_sign);
1415
26d8b338 1416 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
1417 if (perf_hpp__should_skip(fmt))
1418 continue;
1419
26d8b338
NK
1420 if (!first) {
1421 ret = scnprintf(hpp.buf, hpp.size, " ");
1422 advance_hpp(&hpp, ret);
1423 } else
1424 first = false;
aff3f3f6 1425
26d8b338
NK
1426 ret = fmt->entry(fmt, &hpp, he);
1427 advance_hpp(&hpp, ret);
1428 }
aff3f3f6
ACM
1429 printed += fprintf(fp, "%s\n", rtrim(s));
1430
1431 if (folded_sign == '-')
39ee533f 1432 printed += hist_browser__fprintf_callchain(browser, he, fp);
aff3f3f6
ACM
1433
1434 return printed;
1435}
1436
1437static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1438{
064f1981 1439 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 1440 browser->min_pcnt);
aff3f3f6
ACM
1441 int printed = 0;
1442
1443 while (nd) {
1444 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1445
1446 printed += hist_browser__fprintf_entry(browser, h, fp);
14135663 1447 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
aff3f3f6
ACM
1448 }
1449
1450 return printed;
1451}
1452
1453static int hist_browser__dump(struct hist_browser *browser)
1454{
1455 char filename[64];
1456 FILE *fp;
1457
1458 while (1) {
1459 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1460 if (access(filename, F_OK))
1461 break;
1462 /*
1463 * XXX: Just an arbitrary lazy upper limit
1464 */
1465 if (++browser->print_seq == 8192) {
1466 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1467 return -1;
1468 }
1469 }
1470
1471 fp = fopen(filename, "w");
1472 if (fp == NULL) {
1473 char bf[64];
4cc49d4d
KS
1474 const char *err = strerror_r(errno, bf, sizeof(bf));
1475 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1476 return -1;
1477 }
1478
1479 ++browser->print_seq;
1480 hist_browser__fprintf(browser, fp);
1481 fclose(fp);
1482 ui_helpline__fpush("%s written!", filename);
1483
1484 return 0;
1485}
1486
c2a51ab8 1487static struct hist_browser *hist_browser__new(struct hists *hists,
b1a9ceef 1488 struct hist_browser_timer *hbt,
ce80d3be 1489 struct perf_env *env)
d1b4f249 1490{
05e8b080 1491 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1492
05e8b080
ACM
1493 if (browser) {
1494 browser->hists = hists;
1495 browser->b.refresh = hist_browser__refresh;
357cfff1 1496 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
05e8b080
ACM
1497 browser->b.seek = ui_browser__hists_seek;
1498 browser->b.use_navkeypressed = true;
c8302367 1499 browser->show_headers = symbol_conf.show_hist_headers;
c2a51ab8 1500 browser->hbt = hbt;
b1a9ceef 1501 browser->env = env;
d1b4f249
ACM
1502 }
1503
05e8b080 1504 return browser;
d1b4f249
ACM
1505}
1506
05e8b080 1507static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1508{
05e8b080 1509 free(browser);
d1b4f249
ACM
1510}
1511
05e8b080 1512static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1513{
05e8b080 1514 return browser->he_selection;
d1b4f249
ACM
1515}
1516
05e8b080 1517static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1518{
05e8b080 1519 return browser->he_selection->thread;
d1b4f249
ACM
1520}
1521
1e378ebd
TS
1522/* Check whether the browser is for 'top' or 'report' */
1523static inline bool is_report_browser(void *timer)
1524{
1525 return timer == NULL;
1526}
1527
1528static int hists__browser_title(struct hists *hists,
1529 struct hist_browser_timer *hbt,
1530 char *bf, size_t size)
d1b4f249 1531{
469917ce
ACM
1532 char unit;
1533 int printed;
05e8b080
ACM
1534 const struct dso *dso = hists->dso_filter;
1535 const struct thread *thread = hists->thread_filter;
84734b06 1536 int socket_id = hists->socket_filter;
05e8b080
ACM
1537 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1538 u64 nr_events = hists->stats.total_period;
717e263f 1539 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 1540 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
1541 char buf[512];
1542 size_t buflen = sizeof(buf);
9e207ddf
KL
1543 char ref[30] = " show reference callgraph, ";
1544 bool enable_ref = false;
717e263f 1545
f2148330
NK
1546 if (symbol_conf.filter_relative) {
1547 nr_samples = hists->stats.nr_non_filtered_samples;
1548 nr_events = hists->stats.total_non_filtered_period;
1549 }
1550
759ff497 1551 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1552 struct perf_evsel *pos;
1553
1554 perf_evsel__group_desc(evsel, buf, buflen);
1555 ev_name = buf;
1556
1557 for_each_group_member(pos, evsel) {
4ea062ed
ACM
1558 struct hists *pos_hists = evsel__hists(pos);
1559
f2148330 1560 if (symbol_conf.filter_relative) {
4ea062ed
ACM
1561 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1562 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 1563 } else {
4ea062ed
ACM
1564 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1565 nr_events += pos_hists->stats.total_period;
f2148330 1566 }
717e263f
NK
1567 }
1568 }
cc686280 1569
9e207ddf
KL
1570 if (symbol_conf.show_ref_callgraph &&
1571 strstr(ev_name, "call-graph=no"))
1572 enable_ref = true;
cc686280
AR
1573 nr_samples = convert_unit(nr_samples, &unit);
1574 printed = scnprintf(bf, size,
9e207ddf
KL
1575 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1576 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
469917ce 1577
d1b4f249 1578
05e8b080 1579 if (hists->uid_filter_str)
0d37aa34 1580 printed += snprintf(bf + printed, size - printed,
05e8b080 1581 ", UID: %s", hists->uid_filter_str);
d1b4f249 1582 if (thread)
e7f01d1e 1583 printed += scnprintf(bf + printed, size - printed,
469917ce 1584 ", Thread: %s(%d)",
b9c5143a 1585 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1586 thread->tid);
d1b4f249 1587 if (dso)
e7f01d1e 1588 printed += scnprintf(bf + printed, size - printed,
469917ce 1589 ", DSO: %s", dso->short_name);
84734b06 1590 if (socket_id > -1)
21394d94 1591 printed += scnprintf(bf + printed, size - printed,
84734b06 1592 ", Processor Socket: %d", socket_id);
1e378ebd
TS
1593 if (!is_report_browser(hbt)) {
1594 struct perf_top *top = hbt->arg;
1595
1596 if (top->zero)
1597 printed += scnprintf(bf + printed, size - printed, " [z]");
1598 }
1599
469917ce 1600 return printed;
d1b4f249
ACM
1601}
1602
24bff2dc
SE
1603static inline void free_popup_options(char **options, int n)
1604{
1605 int i;
1606
04662523
ACM
1607 for (i = 0; i < n; ++i)
1608 zfree(&options[i]);
24bff2dc
SE
1609}
1610
341487ab
FT
1611/*
1612 * Only runtime switching of perf data file will make "input_name" point
1613 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1614 * whether we need to call free() for current "input_name" during the switch.
1615 */
1616static bool is_input_name_malloced = false;
1617
1618static int switch_data_file(void)
1619{
1620 char *pwd, *options[32], *abs_path[32], *tmp;
1621 DIR *pwd_dir;
1622 int nr_options = 0, choice = -1, ret = -1;
1623 struct dirent *dent;
1624
1625 pwd = getenv("PWD");
1626 if (!pwd)
1627 return ret;
1628
1629 pwd_dir = opendir(pwd);
1630 if (!pwd_dir)
1631 return ret;
1632
1633 memset(options, 0, sizeof(options));
1634 memset(options, 0, sizeof(abs_path));
1635
1636 while ((dent = readdir(pwd_dir))) {
1637 char path[PATH_MAX];
1638 u64 magic;
1639 char *name = dent->d_name;
1640 FILE *file;
1641
1642 if (!(dent->d_type == DT_REG))
1643 continue;
1644
1645 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1646
1647 file = fopen(path, "r");
1648 if (!file)
1649 continue;
1650
1651 if (fread(&magic, 1, 8, file) < 8)
1652 goto close_file_and_continue;
1653
1654 if (is_perf_magic(magic)) {
1655 options[nr_options] = strdup(name);
1656 if (!options[nr_options])
1657 goto close_file_and_continue;
1658
1659 abs_path[nr_options] = strdup(path);
1660 if (!abs_path[nr_options]) {
74cf249d 1661 zfree(&options[nr_options]);
341487ab
FT
1662 ui__warning("Can't search all data files due to memory shortage.\n");
1663 fclose(file);
1664 break;
1665 }
1666
1667 nr_options++;
1668 }
1669
1670close_file_and_continue:
1671 fclose(file);
1672 if (nr_options >= 32) {
1673 ui__warning("Too many perf data files in PWD!\n"
1674 "Only the first 32 files will be listed.\n");
1675 break;
1676 }
1677 }
1678 closedir(pwd_dir);
1679
1680 if (nr_options) {
1681 choice = ui__popup_menu(nr_options, options);
1682 if (choice < nr_options && choice >= 0) {
1683 tmp = strdup(abs_path[choice]);
1684 if (tmp) {
1685 if (is_input_name_malloced)
1686 free((void *)input_name);
1687 input_name = tmp;
1688 is_input_name_malloced = true;
1689 ret = 0;
1690 } else
1691 ui__warning("Data switch failed due to memory shortage!\n");
1692 }
1693 }
1694
1695 free_popup_options(options, nr_options);
1696 free_popup_options(abs_path, nr_options);
1697 return ret;
1698}
1699
ea7cd592
NK
1700struct popup_action {
1701 struct thread *thread;
ea7cd592 1702 struct map_symbol ms;
84734b06 1703 int socket;
ea7cd592
NK
1704
1705 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1706};
1707
bc7cad42 1708static int
ea7cd592 1709do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
1710{
1711 struct perf_evsel *evsel;
1712 struct annotation *notes;
1713 struct hist_entry *he;
1714 int err;
1715
eebd0bfc 1716 if (!objdump_path && perf_env__lookup_objdump(browser->env))
bc7cad42
NK
1717 return 0;
1718
ea7cd592 1719 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
1720 if (!notes->src)
1721 return 0;
1722
1723 evsel = hists_to_evsel(browser->hists);
ea7cd592 1724 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
1725 he = hist_browser__selected_entry(browser);
1726 /*
1727 * offer option to annotate the other branch source or target
1728 * (if they exists) when returning from annotate
1729 */
1730 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1731 return 1;
1732
1733 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1734 if (err)
1735 ui_browser__handle_resize(&browser->b);
1736 return 0;
1737}
1738
1739static int
ea7cd592
NK
1740add_annotate_opt(struct hist_browser *browser __maybe_unused,
1741 struct popup_action *act, char **optstr,
1742 struct map *map, struct symbol *sym)
bc7cad42 1743{
ea7cd592
NK
1744 if (sym == NULL || map->dso->annotate_warned)
1745 return 0;
1746
1747 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1748 return 0;
1749
1750 act->ms.map = map;
1751 act->ms.sym = sym;
1752 act->fn = do_annotate;
1753 return 1;
1754}
1755
1756static int
1757do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1758{
1759 struct thread *thread = act->thread;
1760
bc7cad42
NK
1761 if (browser->hists->thread_filter) {
1762 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1763 perf_hpp__set_elide(HISTC_THREAD, false);
1764 thread__zput(browser->hists->thread_filter);
1765 ui_helpline__pop();
1766 } else {
7727a925 1767 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
bc7cad42
NK
1768 thread->comm_set ? thread__comm_str(thread) : "",
1769 thread->tid);
1770 browser->hists->thread_filter = thread__get(thread);
1771 perf_hpp__set_elide(HISTC_THREAD, false);
1772 pstack__push(browser->pstack, &browser->hists->thread_filter);
1773 }
1774
1775 hists__filter_by_thread(browser->hists);
1776 hist_browser__reset(browser);
1777 return 0;
1778}
1779
1780static int
ea7cd592
NK
1781add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1782 char **optstr, struct thread *thread)
1783{
1784 if (thread == NULL)
1785 return 0;
1786
1787 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1788 browser->hists->thread_filter ? "out of" : "into",
1789 thread->comm_set ? thread__comm_str(thread) : "",
1790 thread->tid) < 0)
1791 return 0;
1792
1793 act->thread = thread;
1794 act->fn = do_zoom_thread;
1795 return 1;
1796}
1797
1798static int
1799do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 1800{
045b80dd 1801 struct map *map = act->ms.map;
ea7cd592 1802
bc7cad42
NK
1803 if (browser->hists->dso_filter) {
1804 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1805 perf_hpp__set_elide(HISTC_DSO, false);
1806 browser->hists->dso_filter = NULL;
1807 ui_helpline__pop();
1808 } else {
045b80dd 1809 if (map == NULL)
bc7cad42 1810 return 0;
7727a925 1811 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
045b80dd
ACM
1812 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1813 browser->hists->dso_filter = map->dso;
bc7cad42
NK
1814 perf_hpp__set_elide(HISTC_DSO, true);
1815 pstack__push(browser->pstack, &browser->hists->dso_filter);
1816 }
1817
1818 hists__filter_by_dso(browser->hists);
1819 hist_browser__reset(browser);
1820 return 0;
1821}
1822
1823static int
ea7cd592 1824add_dso_opt(struct hist_browser *browser, struct popup_action *act,
045b80dd 1825 char **optstr, struct map *map)
bc7cad42 1826{
045b80dd 1827 if (map == NULL)
ea7cd592
NK
1828 return 0;
1829
1830 if (asprintf(optstr, "Zoom %s %s DSO",
1831 browser->hists->dso_filter ? "out of" : "into",
045b80dd 1832 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
ea7cd592
NK
1833 return 0;
1834
045b80dd 1835 act->ms.map = map;
ea7cd592
NK
1836 act->fn = do_zoom_dso;
1837 return 1;
1838}
1839
1840static int
1841do_browse_map(struct hist_browser *browser __maybe_unused,
1842 struct popup_action *act)
1843{
1844 map__browse(act->ms.map);
bc7cad42
NK
1845 return 0;
1846}
1847
ea7cd592
NK
1848static int
1849add_map_opt(struct hist_browser *browser __maybe_unused,
1850 struct popup_action *act, char **optstr, struct map *map)
1851{
1852 if (map == NULL)
1853 return 0;
1854
1855 if (asprintf(optstr, "Browse map details") < 0)
1856 return 0;
1857
1858 act->ms.map = map;
1859 act->fn = do_browse_map;
1860 return 1;
1861}
1862
bc7cad42
NK
1863static int
1864do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 1865 struct popup_action *act)
bc7cad42
NK
1866{
1867 char script_opt[64];
1868 memset(script_opt, 0, sizeof(script_opt));
1869
ea7cd592 1870 if (act->thread) {
bc7cad42 1871 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
1872 thread__comm_str(act->thread));
1873 } else if (act->ms.sym) {
bc7cad42 1874 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 1875 act->ms.sym->name);
bc7cad42
NK
1876 }
1877
1878 script_browse(script_opt);
1879 return 0;
1880}
1881
1882static int
ea7cd592
NK
1883add_script_opt(struct hist_browser *browser __maybe_unused,
1884 struct popup_action *act, char **optstr,
1885 struct thread *thread, struct symbol *sym)
1886{
1887 if (thread) {
1888 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1889 thread__comm_str(thread)) < 0)
1890 return 0;
1891 } else if (sym) {
1892 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1893 sym->name) < 0)
1894 return 0;
1895 } else {
1896 if (asprintf(optstr, "Run scripts for all samples") < 0)
1897 return 0;
1898 }
1899
1900 act->thread = thread;
1901 act->ms.sym = sym;
1902 act->fn = do_run_script;
1903 return 1;
1904}
1905
1906static int
1907do_switch_data(struct hist_browser *browser __maybe_unused,
1908 struct popup_action *act __maybe_unused)
bc7cad42
NK
1909{
1910 if (switch_data_file()) {
1911 ui__warning("Won't switch the data files due to\n"
1912 "no valid data file get selected!\n");
ea7cd592 1913 return 0;
bc7cad42
NK
1914 }
1915
1916 return K_SWITCH_INPUT_DATA;
1917}
1918
ea7cd592
NK
1919static int
1920add_switch_opt(struct hist_browser *browser,
1921 struct popup_action *act, char **optstr)
1922{
1923 if (!is_report_browser(browser->hbt))
1924 return 0;
1925
1926 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1927 return 0;
1928
1929 act->fn = do_switch_data;
1930 return 1;
1931}
1932
1933static int
1934do_exit_browser(struct hist_browser *browser __maybe_unused,
1935 struct popup_action *act __maybe_unused)
1936{
1937 return 0;
1938}
1939
1940static int
1941add_exit_opt(struct hist_browser *browser __maybe_unused,
1942 struct popup_action *act, char **optstr)
1943{
1944 if (asprintf(optstr, "Exit") < 0)
1945 return 0;
1946
1947 act->fn = do_exit_browser;
1948 return 1;
1949}
1950
84734b06
KL
1951static int
1952do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1953{
1954 if (browser->hists->socket_filter > -1) {
1955 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1956 browser->hists->socket_filter = -1;
1957 perf_hpp__set_elide(HISTC_SOCKET, false);
1958 } else {
1959 browser->hists->socket_filter = act->socket;
1960 perf_hpp__set_elide(HISTC_SOCKET, true);
1961 pstack__push(browser->pstack, &browser->hists->socket_filter);
1962 }
1963
1964 hists__filter_by_socket(browser->hists);
1965 hist_browser__reset(browser);
1966 return 0;
1967}
1968
1969static int
1970add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1971 char **optstr, int socket_id)
1972{
1973 if (socket_id < 0)
1974 return 0;
1975
1976 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1977 (browser->hists->socket_filter > -1) ? "out of" : "into",
1978 socket_id) < 0)
1979 return 0;
1980
1981 act->socket = socket_id;
1982 act->fn = do_zoom_socket;
1983 return 1;
1984}
1985
112f761f 1986static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
1987{
1988 u64 nr_entries = 0;
1989 struct rb_node *nd = rb_first(&hb->hists->entries);
1990
268397cb
NK
1991 if (hb->min_pcnt == 0) {
1992 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1993 return;
1994 }
1995
14135663 1996 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 1997 nr_entries++;
c481f930 1998 nd = rb_next(nd);
064f1981
NK
1999 }
2000
112f761f 2001 hb->nr_non_filtered_entries = nr_entries;
064f1981 2002}
341487ab 2003
34958544 2004static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 2005 const char *helpline,
81cce8de 2006 bool left_exits,
68d80758 2007 struct hist_browser_timer *hbt,
064f1981 2008 float min_pcnt,
ce80d3be 2009 struct perf_env *env)
d1b4f249 2010{
4ea062ed 2011 struct hists *hists = evsel__hists(evsel);
b1a9ceef 2012 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
a68c2c58 2013 struct branch_info *bi;
f2b487db
NK
2014#define MAX_OPTIONS 16
2015 char *options[MAX_OPTIONS];
ea7cd592 2016 struct popup_action actions[MAX_OPTIONS];
24bff2dc 2017 int nr_options = 0;
d1b4f249 2018 int key = -1;
938a23ae 2019 char buf[64];
9783adf7 2020 int delay_secs = hbt ? hbt->refresh : 0;
59dc9f25 2021 struct perf_hpp_fmt *fmt;
d1b4f249 2022
e8e684a5
NK
2023#define HIST_BROWSER_HELP_COMMON \
2024 "h/?/F1 Show this window\n" \
2025 "UP/DOWN/PGUP\n" \
2026 "PGDN/SPACE Navigate\n" \
2027 "q/ESC/CTRL+C Exit browser\n\n" \
2028 "For multiple event sessions:\n\n" \
2029 "TAB/UNTAB Switch events\n\n" \
2030 "For symbolic views (--sort has sym):\n\n" \
7727a925
ACM
2031 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2032 "ESC Zoom out\n" \
e8e684a5
NK
2033 "a Annotate current symbol\n" \
2034 "C Collapse all callchains\n" \
2035 "d Zoom into current DSO\n" \
2036 "E Expand all callchains\n" \
105eb30f 2037 "F Toggle percentage of filtered entries\n" \
025bf7ea 2038 "H Display column headers\n" \
31eb4360 2039 "m Display context menu\n" \
84734b06 2040 "S Zoom into current Processor Socket\n" \
e8e684a5
NK
2041
2042 /* help messages are sorted by lexical order of the hotkey */
2043 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 2044 "i Show header information\n"
e8e684a5
NK
2045 "P Print histograms to perf.hist.N\n"
2046 "r Run available scripts\n"
2047 "s Switch to another data file in PWD\n"
2048 "t Zoom into current Thread\n"
2049 "V Verbose (DSO names in callchains, etc)\n"
2050 "/ Filter symbol by name";
2051 const char top_help[] = HIST_BROWSER_HELP_COMMON
2052 "P Print histograms to perf.hist.N\n"
2053 "t Zoom into current Thread\n"
2054 "V Verbose (DSO names in callchains, etc)\n"
42337a22 2055 "z Toggle zeroing of samples\n"
fbb7997e 2056 "f Enable/Disable events\n"
e8e684a5
NK
2057 "/ Filter symbol by name";
2058
d1b4f249
ACM
2059 if (browser == NULL)
2060 return -1;
2061
ed426915
NK
2062 /* reset abort key so that it can get Ctrl-C as a key */
2063 SLang_reset_tty();
2064 SLang_init_tty(0, 0, 0);
2065
03905048 2066 if (min_pcnt)
064f1981 2067 browser->min_pcnt = min_pcnt;
03905048 2068 hist_browser__update_nr_entries(browser);
064f1981 2069
84734b06 2070 browser->pstack = pstack__new(3);
01f00a1c 2071 if (browser->pstack == NULL)
d1b4f249
ACM
2072 goto out;
2073
2074 ui_helpline__push(helpline);
2075
24bff2dc 2076 memset(options, 0, sizeof(options));
ea7cd592 2077 memset(actions, 0, sizeof(actions));
24bff2dc 2078
c6c3c02d 2079 perf_hpp__for_each_format(fmt) {
59dc9f25 2080 perf_hpp__reset_width(fmt, hists);
c6c3c02d
ACM
2081 /*
2082 * This is done just once, and activates the horizontal scrolling
2083 * code in the ui_browser code, it would be better to have a the
2084 * counter in the perf_hpp code, but I couldn't find doing it here
2085 * works, FIXME by setting this in hist_browser__new, for now, be
2086 * clever 8-)
2087 */
2088 ++browser->b.columns;
2089 }
59dc9f25 2090
5b591669
NK
2091 if (symbol_conf.col_width_list_str)
2092 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2093
d1b4f249 2094 while (1) {
f3b623b8 2095 struct thread *thread = NULL;
045b80dd 2096 struct map *map = NULL;
ea7cd592 2097 int choice = 0;
84734b06 2098 int socked_id = -1;
d1b4f249 2099
24bff2dc
SE
2100 nr_options = 0;
2101
5f00b0f4 2102 key = hist_browser__run(browser, helpline);
d1b4f249 2103
60098917
ACM
2104 if (browser->he_selection != NULL) {
2105 thread = hist_browser__selected_thread(browser);
045b80dd 2106 map = browser->selection->map;
84734b06 2107 socked_id = browser->he_selection->socket;
60098917 2108 }
b50e003d 2109 switch (key) {
cf958003
ACM
2110 case K_TAB:
2111 case K_UNTAB:
e4419b8e
DA
2112 if (nr_events == 1)
2113 continue;
b50e003d
ACM
2114 /*
2115 * Exit the browser, let hists__browser_tree
2116 * go to the next or previous
2117 */
2118 goto out_free_stack;
2119 case 'a':
9c796ec8 2120 if (!sort__has_sym) {
7b27509f 2121 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 2122 "Annotation is only available for symbolic views, "
a68c2c58 2123 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
2124 continue;
2125 }
2126
60098917 2127 if (browser->selection == NULL ||
db9a9cbc 2128 browser->selection->sym == NULL ||
b50e003d 2129 browser->selection->map->dso->annotate_warned)
d1b4f249 2130 continue;
bc7cad42 2131
ea7cd592
NK
2132 actions->ms.map = browser->selection->map;
2133 actions->ms.sym = browser->selection->sym;
2134 do_annotate(browser, actions);
bc7cad42 2135 continue;
aff3f3f6
ACM
2136 case 'P':
2137 hist_browser__dump(browser);
2138 continue;
b50e003d 2139 case 'd':
fae00650 2140 actions->ms.map = map;
ea7cd592 2141 do_zoom_dso(browser, actions);
bc7cad42 2142 continue;
a7cb8863
ACM
2143 case 'V':
2144 browser->show_dso = !browser->show_dso;
2145 continue;
b50e003d 2146 case 't':
ea7cd592
NK
2147 actions->thread = thread;
2148 do_zoom_thread(browser, actions);
bc7cad42 2149 continue;
84734b06
KL
2150 case 'S':
2151 actions->socket = socked_id;
2152 do_zoom_socket(browser, actions);
2153 continue;
5a5626b1 2154 case '/':
938a23ae 2155 if (ui_browser__input_window("Symbol to show",
4aa8e454
ACM
2156 "Please enter the name of symbol you want to see.\n"
2157 "To remove the filter later, press / + ENTER.",
938a23ae
NK
2158 buf, "ENTER: OK, ESC: Cancel",
2159 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
2160 hists->symbol_filter_str = *buf ? buf : NULL;
2161 hists__filter_by_symbol(hists);
938a23ae
NK
2162 hist_browser__reset(browser);
2163 }
2164 continue;
cdbab7c2 2165 case 'r':
ea7cd592
NK
2166 if (is_report_browser(hbt)) {
2167 actions->thread = NULL;
2168 actions->ms.sym = NULL;
2169 do_run_script(browser, actions);
2170 }
c77d8d70 2171 continue;
341487ab 2172 case 's':
bc7cad42 2173 if (is_report_browser(hbt)) {
ea7cd592 2174 key = do_switch_data(browser, actions);
bc7cad42
NK
2175 if (key == K_SWITCH_INPUT_DATA)
2176 goto out_free_stack;
2177 }
341487ab 2178 continue;
6dd60135
NK
2179 case 'i':
2180 /* env->arch is NULL for live-mode (i.e. perf top) */
2181 if (env->arch)
2182 tui__header_window(env);
2183 continue;
105eb30f
NK
2184 case 'F':
2185 symbol_conf.filter_relative ^= 1;
2186 continue;
42337a22
NK
2187 case 'z':
2188 if (!is_report_browser(hbt)) {
2189 struct perf_top *top = hbt->arg;
2190
2191 top->zero = !top->zero;
2192 }
2193 continue;
cf958003 2194 case K_F1:
b50e003d
ACM
2195 case 'h':
2196 case '?':
4610e413 2197 ui_browser__help_window(&browser->b,
e8e684a5 2198 is_report_browser(hbt) ? report_help : top_help);
b50e003d 2199 continue;
cf958003
ACM
2200 case K_ENTER:
2201 case K_RIGHT:
31eb4360 2202 case 'm':
b50e003d
ACM
2203 /* menu */
2204 break;
63ab1749 2205 case K_ESC:
cf958003 2206 case K_LEFT: {
b50e003d 2207 const void *top;
d1b4f249 2208
01f00a1c 2209 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
2210 /*
2211 * Go back to the perf_evsel_menu__run or other user
2212 */
2213 if (left_exits)
2214 goto out_free_stack;
63ab1749
ACM
2215
2216 if (key == K_ESC &&
2217 ui_browser__dialog_yesno(&browser->b,
2218 "Do you really want to exit?"))
2219 goto out_free_stack;
2220
d1b4f249 2221 continue;
7f0030b2 2222 }
6422184b 2223 top = pstack__peek(browser->pstack);
bc7cad42 2224 if (top == &browser->hists->dso_filter) {
6422184b
NK
2225 /*
2226 * No need to set actions->dso here since
2227 * it's just to remove the current filter.
2228 * Ditto for thread below.
2229 */
2230 do_zoom_dso(browser, actions);
84734b06 2231 } else if (top == &browser->hists->thread_filter) {
6422184b 2232 do_zoom_thread(browser, actions);
84734b06
KL
2233 } else if (top == &browser->hists->socket_filter) {
2234 do_zoom_socket(browser, actions);
2235 }
b50e003d
ACM
2236 continue;
2237 }
ed7e5662
ACM
2238 case 'q':
2239 case CTRL('c'):
516e5368 2240 goto out_free_stack;
fbb7997e 2241 case 'f':
13d1e536
NK
2242 if (!is_report_browser(hbt)) {
2243 struct perf_top *top = hbt->arg;
2244
2245 perf_evlist__toggle_enable(top->evlist);
2246 /*
2247 * No need to refresh, resort/decay histogram
2248 * entries if we are not collecting samples:
2249 */
2250 if (top->evlist->enabled) {
2251 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2252 hbt->refresh = delay_secs;
2253 } else {
2254 helpline = "Press 'f' again to re-enable the events";
2255 hbt->refresh = 0;
2256 }
2257 continue;
2258 }
3e323dc0 2259 /* Fall thru */
ed7e5662 2260 default:
3e323dc0 2261 helpline = "Press '?' for help on key bindings";
ed7e5662 2262 continue;
d1b4f249
ACM
2263 }
2264
9c796ec8 2265 if (!sort__has_sym)
724c9c9f
ACM
2266 goto add_exit_option;
2267
0ba332f7
ACM
2268 if (browser->selection == NULL)
2269 goto skip_annotation;
2270
55369fc1 2271 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 2272 bi = browser->he_selection->branch_info;
0ba332f7
ACM
2273
2274 if (bi == NULL)
2275 goto skip_annotation;
2276
ea7cd592
NK
2277 nr_options += add_annotate_opt(browser,
2278 &actions[nr_options],
2279 &options[nr_options],
2280 bi->from.map,
2281 bi->from.sym);
2282 if (bi->to.sym != bi->from.sym)
2283 nr_options += add_annotate_opt(browser,
2284 &actions[nr_options],
2285 &options[nr_options],
2286 bi->to.map,
2287 bi->to.sym);
a68c2c58 2288 } else {
ea7cd592
NK
2289 nr_options += add_annotate_opt(browser,
2290 &actions[nr_options],
2291 &options[nr_options],
2292 browser->selection->map,
2293 browser->selection->sym);
a68c2c58 2294 }
0ba332f7 2295skip_annotation:
ea7cd592
NK
2296 nr_options += add_thread_opt(browser, &actions[nr_options],
2297 &options[nr_options], thread);
2298 nr_options += add_dso_opt(browser, &actions[nr_options],
045b80dd 2299 &options[nr_options], map);
ea7cd592
NK
2300 nr_options += add_map_opt(browser, &actions[nr_options],
2301 &options[nr_options],
bd315aab
WN
2302 browser->selection ?
2303 browser->selection->map : NULL);
84734b06
KL
2304 nr_options += add_socket_opt(browser, &actions[nr_options],
2305 &options[nr_options],
2306 socked_id);
cdbab7c2
FT
2307 /* perf script support */
2308 if (browser->he_selection) {
ea7cd592
NK
2309 nr_options += add_script_opt(browser,
2310 &actions[nr_options],
2311 &options[nr_options],
2312 thread, NULL);
bd315aab
WN
2313 /*
2314 * Note that browser->selection != NULL
2315 * when browser->he_selection is not NULL,
2316 * so we don't need to check browser->selection
2317 * before fetching browser->selection->sym like what
2318 * we do before fetching browser->selection->map.
2319 *
2320 * See hist_browser__show_entry.
2321 */
ea7cd592
NK
2322 nr_options += add_script_opt(browser,
2323 &actions[nr_options],
2324 &options[nr_options],
2325 NULL, browser->selection->sym);
cdbab7c2 2326 }
ea7cd592
NK
2327 nr_options += add_script_opt(browser, &actions[nr_options],
2328 &options[nr_options], NULL, NULL);
2329 nr_options += add_switch_opt(browser, &actions[nr_options],
2330 &options[nr_options]);
724c9c9f 2331add_exit_option:
ea7cd592
NK
2332 nr_options += add_exit_opt(browser, &actions[nr_options],
2333 &options[nr_options]);
d1b4f249 2334
ea7cd592
NK
2335 do {
2336 struct popup_action *act;
68d80758 2337
ea7cd592
NK
2338 choice = ui__popup_menu(nr_options, options);
2339 if (choice == -1 || choice >= nr_options)
2340 break;
a68c2c58 2341
ea7cd592
NK
2342 act = &actions[choice];
2343 key = act->fn(browser, act);
2344 } while (key == 1);
a68c2c58 2345
ea7cd592
NK
2346 if (key == K_SWITCH_INPUT_DATA)
2347 break;
d1b4f249
ACM
2348 }
2349out_free_stack:
01f00a1c 2350 pstack__delete(browser->pstack);
d1b4f249
ACM
2351out:
2352 hist_browser__delete(browser);
f2b487db 2353 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
2354 return key;
2355}
2356
7f0030b2
ACM
2357struct perf_evsel_menu {
2358 struct ui_browser b;
2359 struct perf_evsel *selection;
7b27509f 2360 bool lost_events, lost_events_warned;
064f1981 2361 float min_pcnt;
ce80d3be 2362 struct perf_env *env;
7f0030b2
ACM
2363};
2364
2365static void perf_evsel_menu__write(struct ui_browser *browser,
2366 void *entry, int row)
2367{
2368 struct perf_evsel_menu *menu = container_of(browser,
2369 struct perf_evsel_menu, b);
2370 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 2371 struct hists *hists = evsel__hists(evsel);
7f0030b2 2372 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 2373 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 2374 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 2375 char bf[256], unit;
7b27509f
ACM
2376 const char *warn = " ";
2377 size_t printed;
7f0030b2
ACM
2378
2379 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2380 HE_COLORSET_NORMAL);
2381
759ff497 2382 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2383 struct perf_evsel *pos;
2384
2385 ev_name = perf_evsel__group_name(evsel);
2386
2387 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2388 struct hists *pos_hists = evsel__hists(pos);
2389 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
2390 }
2391 }
2392
7f0030b2 2393 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 2394 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f 2395 unit, unit == ' ' ? "" : " ", ev_name);
517dfdb3 2396 ui_browser__printf(browser, "%s", bf);
7b27509f 2397
4ea062ed 2398 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
2399 if (nr_events != 0) {
2400 menu->lost_events = true;
2401 if (!current_entry)
2402 ui_browser__set_color(browser, HE_COLORSET_TOP);
2403 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
2404 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2405 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
2406 warn = bf;
2407 }
2408
26270a00 2409 ui_browser__write_nstring(browser, warn, browser->width - printed);
7f0030b2
ACM
2410
2411 if (current_entry)
2412 menu->selection = evsel;
2413}
2414
34958544
ACM
2415static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2416 int nr_events, const char *help,
9783adf7 2417 struct hist_browser_timer *hbt)
d1b4f249 2418{
7f0030b2 2419 struct perf_evlist *evlist = menu->b.priv;
e248de33 2420 struct perf_evsel *pos;
dd00d486 2421 const char *title = "Available samples";
9783adf7 2422 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 2423 int key;
d1b4f249 2424
7f0030b2
ACM
2425 if (ui_browser__show(&menu->b, title,
2426 "ESC: exit, ENTER|->: Browse histograms") < 0)
2427 return -1;
2428
7f0030b2 2429 while (1) {
3af6e338 2430 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
2431
2432 switch (key) {
cf958003 2433 case K_TIMER:
9783adf7 2434 hbt->timer(hbt->arg);
7b27509f
ACM
2435
2436 if (!menu->lost_events_warned && menu->lost_events) {
2437 ui_browser__warn_lost_events(&menu->b);
2438 menu->lost_events_warned = true;
2439 }
81cce8de 2440 continue;
cf958003
ACM
2441 case K_RIGHT:
2442 case K_ENTER:
7f0030b2
ACM
2443 if (!menu->selection)
2444 continue;
2445 pos = menu->selection;
2446browse_hists:
18eaf0b8
ACM
2447 perf_evlist__set_selected(evlist, pos);
2448 /*
2449 * Give the calling tool a chance to populate the non
2450 * default evsel resorted hists tree.
2451 */
9783adf7
NK
2452 if (hbt)
2453 hbt->timer(hbt->arg);
34958544 2454 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 2455 true, hbt,
064f1981 2456 menu->min_pcnt,
68d80758 2457 menu->env);
7f0030b2 2458 ui_browser__show_title(&menu->b, title);
18eaf0b8 2459 switch (key) {
cf958003 2460 case K_TAB:
18eaf0b8 2461 if (pos->node.next == &evlist->entries)
9a354cdc 2462 pos = perf_evlist__first(evlist);
18eaf0b8 2463 else
9a354cdc 2464 pos = perf_evsel__next(pos);
18eaf0b8 2465 goto browse_hists;
cf958003 2466 case K_UNTAB:
18eaf0b8 2467 if (pos->node.prev == &evlist->entries)
9a354cdc 2468 pos = perf_evlist__last(evlist);
18eaf0b8 2469 else
d87fcb4a 2470 pos = perf_evsel__prev(pos);
18eaf0b8 2471 goto browse_hists;
341487ab 2472 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
2473 case 'q':
2474 case CTRL('c'):
2475 goto out;
63ab1749 2476 case K_ESC:
18eaf0b8
ACM
2477 default:
2478 continue;
2479 }
cf958003 2480 case K_LEFT:
7f0030b2 2481 continue;
cf958003 2482 case K_ESC:
4610e413
ACM
2483 if (!ui_browser__dialog_yesno(&menu->b,
2484 "Do you really want to exit?"))
ed7e5662
ACM
2485 continue;
2486 /* Fall thru */
7f0030b2
ACM
2487 case 'q':
2488 case CTRL('c'):
2489 goto out;
d1b4f249 2490 default:
18eaf0b8 2491 continue;
d1b4f249
ACM
2492 }
2493 }
2494
7f0030b2
ACM
2495out:
2496 ui_browser__hide(&menu->b);
2497 return key;
2498}
2499
316c7136 2500static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2501 void *entry)
2502{
2503 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2504
2505 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2506 return true;
2507
2508 return false;
2509}
2510
7f0030b2 2511static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2512 int nr_entries, const char *help,
68d80758 2513 struct hist_browser_timer *hbt,
064f1981 2514 float min_pcnt,
ce80d3be 2515 struct perf_env *env)
7f0030b2
ACM
2516{
2517 struct perf_evsel *pos;
2518 struct perf_evsel_menu menu = {
2519 .b = {
2520 .entries = &evlist->entries,
2521 .refresh = ui_browser__list_head_refresh,
2522 .seek = ui_browser__list_head_seek,
2523 .write = perf_evsel_menu__write,
fc24d7c2
NK
2524 .filter = filter_group_entries,
2525 .nr_entries = nr_entries,
7f0030b2
ACM
2526 .priv = evlist,
2527 },
064f1981 2528 .min_pcnt = min_pcnt,
68d80758 2529 .env = env,
7f0030b2
ACM
2530 };
2531
2532 ui_helpline__push("Press ESC to exit");
2533
0050f7aa 2534 evlist__for_each(evlist, pos) {
7289f83c 2535 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2536 size_t line_len = strlen(ev_name) + 7;
2537
2538 if (menu.b.width < line_len)
2539 menu.b.width = line_len;
7f0030b2
ACM
2540 }
2541
fc24d7c2 2542 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2543}
2544
81cce8de 2545int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2546 struct hist_browser_timer *hbt,
064f1981 2547 float min_pcnt,
ce80d3be 2548 struct perf_env *env)
7f0030b2 2549{
fc24d7c2
NK
2550 int nr_entries = evlist->nr_entries;
2551
2552single_entry:
2553 if (nr_entries == 1) {
9a354cdc 2554 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2555
2556 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2557 false, hbt, min_pcnt,
064f1981 2558 env);
7f0030b2
ACM
2559 }
2560
fc24d7c2
NK
2561 if (symbol_conf.event_group) {
2562 struct perf_evsel *pos;
2563
2564 nr_entries = 0;
0050f7aa 2565 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2566 if (perf_evsel__is_group_leader(pos))
2567 nr_entries++;
0050f7aa 2568 }
fc24d7c2
NK
2569
2570 if (nr_entries == 1)
2571 goto single_entry;
2572 }
2573
2574 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2575 hbt, min_pcnt, env);
d1b4f249 2576}
This page took 0.305928 seconds and 5 git commands to generate.