perf report: Update documention of --percent-limit option
[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
090cff3e 483 if (ui_browser__show(&browser->b, title, "%s", 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
59c624e2
NK
660static bool check_percent_display(struct rb_node *node, u64 parent_total)
661{
662 struct callchain_node *child;
663
664 if (node == NULL)
665 return false;
666
667 if (rb_next(node))
668 return true;
669
670 child = rb_entry(node, struct callchain_node, rb_node);
671 return callchain_cumul_hits(child) != parent_total;
672}
673
4b3a3212
NK
674static int hist_browser__show_callchain_flat(struct hist_browser *browser,
675 struct rb_root *root,
676 unsigned short row, u64 total,
59c624e2 677 u64 parent_total,
4b3a3212
NK
678 print_callchain_entry_fn print,
679 struct callchain_print_arg *arg,
680 check_output_full_fn is_output_full)
681{
682 struct rb_node *node;
683 int first_row = row, offset = LEVEL_OFFSET_STEP;
684 bool need_percent;
685
686 node = rb_first(root);
59c624e2 687 need_percent = check_percent_display(node, parent_total);
4b3a3212
NK
688
689 while (node) {
690 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
691 struct rb_node *next = rb_next(node);
692 struct callchain_list *chain;
693 char folded_sign = ' ';
694 int first = true;
695 int extra_offset = 0;
696
697 list_for_each_entry(chain, &child->parent_val, list) {
698 bool was_first = first;
699
700 if (first)
701 first = false;
702 else if (need_percent)
703 extra_offset = LEVEL_OFFSET_STEP;
704
705 folded_sign = callchain_list__folded(chain);
706
707 row += hist_browser__show_callchain_list(browser, child,
708 chain, row, total,
709 was_first && need_percent,
710 offset + extra_offset,
711 print, arg);
712
713 if (is_output_full(browser, row))
714 goto out;
715
716 if (folded_sign == '+')
717 goto next;
718 }
719
720 list_for_each_entry(chain, &child->val, list) {
721 bool was_first = first;
722
723 if (first)
724 first = false;
725 else if (need_percent)
726 extra_offset = LEVEL_OFFSET_STEP;
727
728 folded_sign = callchain_list__folded(chain);
729
730 row += hist_browser__show_callchain_list(browser, child,
731 chain, row, total,
732 was_first && need_percent,
733 offset + extra_offset,
734 print, arg);
735
736 if (is_output_full(browser, row))
737 goto out;
738
739 if (folded_sign == '+')
740 break;
741 }
742
743next:
744 if (is_output_full(browser, row))
745 break;
746 node = next;
747 }
748out:
749 return row - first_row;
750}
751
8c430a34
NK
752static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
753 struct callchain_list *chain,
754 char *value_str, char *old_str)
755{
756 char bf[1024];
757 const char *str;
758 char *new;
759
760 str = callchain_list__sym_name(chain, bf, sizeof(bf),
761 browser->show_dso);
762 if (old_str) {
763 if (asprintf(&new, "%s%s%s", old_str,
764 symbol_conf.field_sep ?: ";", str) < 0)
765 new = NULL;
766 } else {
767 if (value_str) {
768 if (asprintf(&new, "%s %s", value_str, str) < 0)
769 new = NULL;
770 } else {
771 if (asprintf(&new, "%s", str) < 0)
772 new = NULL;
773 }
774 }
775 return new;
776}
777
778static int hist_browser__show_callchain_folded(struct hist_browser *browser,
779 struct rb_root *root,
780 unsigned short row, u64 total,
59c624e2 781 u64 parent_total,
8c430a34
NK
782 print_callchain_entry_fn print,
783 struct callchain_print_arg *arg,
784 check_output_full_fn is_output_full)
785{
786 struct rb_node *node;
787 int first_row = row, offset = LEVEL_OFFSET_STEP;
788 bool need_percent;
789
790 node = rb_first(root);
59c624e2 791 need_percent = check_percent_display(node, parent_total);
8c430a34
NK
792
793 while (node) {
794 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
795 struct rb_node *next = rb_next(node);
796 struct callchain_list *chain, *first_chain = NULL;
797 int first = true;
798 char *value_str = NULL, *value_str_alloc = NULL;
799 char *chain_str = NULL, *chain_str_alloc = NULL;
800
801 if (arg->row_offset != 0) {
802 arg->row_offset--;
803 goto next;
804 }
805
806 if (need_percent) {
807 char buf[64];
808
809 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
810 if (asprintf(&value_str, "%s", buf) < 0) {
811 value_str = (char *)"<...>";
812 goto do_print;
813 }
814 value_str_alloc = value_str;
815 }
816
817 list_for_each_entry(chain, &child->parent_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
833 list_for_each_entry(chain, &child->val, list) {
834 chain_str = hist_browser__folded_callchain_str(browser,
835 chain, value_str, chain_str);
836 if (first) {
837 first = false;
838 first_chain = chain;
839 }
840
841 if (chain_str == NULL) {
842 chain_str = (char *)"Not enough memory!";
843 goto do_print;
844 }
845
846 chain_str_alloc = chain_str;
847 }
848
849do_print:
850 print(browser, first_chain, chain_str, offset, row++, arg);
851 free(value_str_alloc);
852 free(chain_str_alloc);
853
854next:
855 if (is_output_full(browser, row))
856 break;
857 node = next;
858 }
859
860 return row - first_row;
861}
862
0c841c6c 863static int hist_browser__show_callchain_graph(struct hist_browser *browser,
c09a7e75 864 struct rb_root *root, int level,
39ee533f 865 unsigned short row, u64 total,
5eca104e 866 u64 parent_total,
39ee533f
NK
867 print_callchain_entry_fn print,
868 struct callchain_print_arg *arg,
869 check_output_full_fn is_output_full)
d1b4f249
ACM
870{
871 struct rb_node *node;
f4536ddd 872 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
4087d11c 873 bool need_percent;
5eca104e
NK
874 u64 percent_total = total;
875
876 if (callchain_param.mode == CHAIN_GRAPH_REL)
877 percent_total = parent_total;
d1b4f249 878
c09a7e75 879 node = rb_first(root);
59c624e2 880 need_percent = check_percent_display(node, parent_total);
4087d11c 881
d1b4f249
ACM
882 while (node) {
883 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
884 struct rb_node *next = rb_next(node);
d1b4f249
ACM
885 struct callchain_list *chain;
886 char folded_sign = ' ';
887 int first = true;
888 int extra_offset = 0;
889
d1b4f249 890 list_for_each_entry(chain, &child->val, list) {
d1b4f249
ACM
891 bool was_first = first;
892
163caed9 893 if (first)
d1b4f249 894 first = false;
4087d11c 895 else if (need_percent)
d1b4f249 896 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
897
898 folded_sign = callchain_list__folded(chain);
c09a7e75 899
18bb8381 900 row += hist_browser__show_callchain_list(browser, child,
5eca104e 901 chain, row, percent_total,
18bb8381
NK
902 was_first && need_percent,
903 offset + extra_offset,
904 print, arg);
d1b4f249 905
18bb8381 906 if (is_output_full(browser, row))
d1b4f249 907 goto out;
18bb8381 908
d1b4f249
ACM
909 if (folded_sign == '+')
910 break;
911 }
912
913 if (folded_sign == '-') {
914 const int new_level = level + (extra_offset ? 2 : 1);
d1b4f249 915
0c841c6c 916 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
5eca104e
NK
917 new_level, row, total,
918 child->children_hit,
39ee533f 919 print, arg, is_output_full);
d1b4f249 920 }
39ee533f 921 if (is_output_full(browser, row))
d1b4f249 922 break;
c09a7e75 923 node = next;
d1b4f249 924 }
c09a7e75 925out:
d1b4f249
ACM
926 return row - first_row;
927}
928
0c841c6c
NK
929static int hist_browser__show_callchain(struct hist_browser *browser,
930 struct hist_entry *entry, int level,
931 unsigned short row,
932 print_callchain_entry_fn print,
933 struct callchain_print_arg *arg,
934 check_output_full_fn is_output_full)
935{
936 u64 total = hists__total_period(entry->hists);
5eca104e 937 u64 parent_total;
0c841c6c
NK
938 int printed;
939
5eca104e
NK
940 if (symbol_conf.cumulate_callchain)
941 parent_total = entry->stat_acc->period;
942 else
943 parent_total = entry->stat.period;
0c841c6c
NK
944
945 if (callchain_param.mode == CHAIN_FLAT) {
946 printed = hist_browser__show_callchain_flat(browser,
5eca104e
NK
947 &entry->sorted_chain, row,
948 total, parent_total, print, arg,
949 is_output_full);
0c841c6c
NK
950 } else if (callchain_param.mode == CHAIN_FOLDED) {
951 printed = hist_browser__show_callchain_folded(browser,
5eca104e
NK
952 &entry->sorted_chain, row,
953 total, parent_total, print, arg,
954 is_output_full);
0c841c6c
NK
955 } else {
956 printed = hist_browser__show_callchain_graph(browser,
5eca104e
NK
957 &entry->sorted_chain, level, row,
958 total, parent_total, print, arg,
959 is_output_full);
0c841c6c
NK
960 }
961
962 if (arg->is_current_entry)
963 browser->he_selection = entry;
964
965 return printed;
966}
967
89701460
NK
968struct hpp_arg {
969 struct ui_browser *b;
970 char folded_sign;
971 bool current_entry;
972};
973
2f6d9009
NK
974static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
975{
976 struct hpp_arg *arg = hpp->ptr;
d675107c 977 int ret, len;
2f6d9009
NK
978 va_list args;
979 double percent;
371d8c40 980
2f6d9009 981 va_start(args, fmt);
d675107c 982 len = va_arg(args, int);
2f6d9009
NK
983 percent = va_arg(args, double);
984 va_end(args);
371d8c40 985
2f6d9009 986 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 987
d675107c 988 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
517dfdb3 989 ui_browser__printf(arg->b, "%s", hpp->buf);
5aed9d24 990
2f6d9009 991 advance_hpp(hpp, ret);
5aed9d24
NK
992 return ret;
993}
994
fb821c9e 995#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
996static u64 __hpp_get_##_field(struct hist_entry *he) \
997{ \
998 return he->stat._field; \
999} \
1000 \
2c5d4b4a 1001static int \
5b591669 1002hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
2c5d4b4a
JO
1003 struct perf_hpp *hpp, \
1004 struct hist_entry *he) \
f5951d56 1005{ \
5b591669
NK
1006 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1007 __hpp__slsmg_color_printf, true); \
f5951d56
NK
1008}
1009
0434ddd2
NK
1010#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1011static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1012{ \
1013 return he->stat_acc->_field; \
1014} \
1015 \
1016static int \
5b591669 1017hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
0434ddd2
NK
1018 struct perf_hpp *hpp, \
1019 struct hist_entry *he) \
1020{ \
1021 if (!symbol_conf.cumulate_callchain) { \
517dfdb3 1022 struct hpp_arg *arg = hpp->ptr; \
5b591669 1023 int len = fmt->user_len ?: fmt->len; \
d675107c 1024 int ret = scnprintf(hpp->buf, hpp->size, \
5b591669 1025 "%*s", len, "N/A"); \
517dfdb3 1026 ui_browser__printf(arg->b, "%s", hpp->buf); \
0434ddd2
NK
1027 \
1028 return ret; \
1029 } \
5b591669
NK
1030 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1031 " %*.2f%%", __hpp__slsmg_color_printf, true); \
0434ddd2
NK
1032}
1033
fb821c9e
NK
1034__HPP_COLOR_PERCENT_FN(overhead, period)
1035__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1036__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1037__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1038__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 1039__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 1040
5aed9d24 1041#undef __HPP_COLOR_PERCENT_FN
0434ddd2 1042#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
1043
1044void hist_browser__init_hpp(void)
1045{
f5951d56
NK
1046 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1047 hist_browser__hpp_color_overhead;
1048 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1049 hist_browser__hpp_color_overhead_sys;
1050 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1051 hist_browser__hpp_color_overhead_us;
1052 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1053 hist_browser__hpp_color_overhead_guest_sys;
1054 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1055 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
1056 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1057 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
1058}
1059
05e8b080 1060static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
1061 struct hist_entry *entry,
1062 unsigned short row)
1063{
1064 char s[256];
1240005e 1065 int printed = 0;
67d25916 1066 int width = browser->b.width;
d1b4f249 1067 char folded_sign = ' ';
05e8b080 1068 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 1069 off_t row_offset = entry->row_offset;
63a1a3d8 1070 bool first = true;
1240005e 1071 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
1072
1073 if (current_entry) {
05e8b080
ACM
1074 browser->he_selection = entry;
1075 browser->selection = &entry->ms;
d1b4f249
ACM
1076 }
1077
1078 if (symbol_conf.use_callchain) {
163caed9 1079 hist_entry__init_have_children(entry);
d1b4f249
ACM
1080 folded_sign = hist_entry__folded(entry);
1081 }
1082
1083 if (row_offset == 0) {
89701460 1084 struct hpp_arg arg = {
fb821c9e 1085 .b = &browser->b,
89701460
NK
1086 .folded_sign = folded_sign,
1087 .current_entry = current_entry,
1088 };
f5951d56
NK
1089 struct perf_hpp hpp = {
1090 .buf = s,
1091 .size = sizeof(s),
89701460 1092 .ptr = &arg,
f5951d56 1093 };
c6c3c02d 1094 int column = 0;
d1b4f249 1095
ca3ff33b 1096 hist_browser__gotorc(browser, row, 0);
c172f742 1097
f0786af5 1098 hists__for_each_format(browser->hists, fmt) {
361459f1
NK
1099 if (perf_hpp__should_skip(fmt, entry->hists) ||
1100 column++ < browser->b.horiz_scroll)
e67d49a7
NK
1101 continue;
1102
fb821c9e
NK
1103 if (current_entry && browser->b.navkeypressed) {
1104 ui_browser__set_color(&browser->b,
1105 HE_COLORSET_SELECTED);
1106 } else {
1107 ui_browser__set_color(&browser->b,
1108 HE_COLORSET_NORMAL);
1109 }
1110
1111 if (first) {
1112 if (symbol_conf.use_callchain) {
517dfdb3 1113 ui_browser__printf(&browser->b, "%c ", folded_sign);
fb821c9e
NK
1114 width -= 2;
1115 }
1116 first = false;
1117 } else {
517dfdb3 1118 ui_browser__printf(&browser->b, " ");
f5951d56
NK
1119 width -= 2;
1120 }
c172f742 1121
1240005e 1122 if (fmt->color) {
2c5d4b4a 1123 width -= fmt->color(fmt, &hpp, entry);
f5951d56 1124 } else {
2c5d4b4a 1125 width -= fmt->entry(fmt, &hpp, entry);
517dfdb3 1126 ui_browser__printf(&browser->b, "%s", s);
f5951d56 1127 }
2cf9cebf
ACM
1128 }
1129
f5951d56
NK
1130 /* The scroll bar isn't being used */
1131 if (!browser->b.navkeypressed)
1132 width += 1;
1133
26270a00 1134 ui_browser__write_nstring(&browser->b, "", width);
26d8b338 1135
d1b4f249
ACM
1136 ++row;
1137 ++printed;
1138 } else
1139 --row_offset;
1140
62c95ae3 1141 if (folded_sign == '-' && row != browser->b.rows) {
39ee533f
NK
1142 struct callchain_print_arg arg = {
1143 .row_offset = row_offset,
1144 .is_current_entry = current_entry,
1145 };
c09a7e75 1146
0c841c6c 1147 printed += hist_browser__show_callchain(browser, entry, 1, row,
39ee533f
NK
1148 hist_browser__show_callchain_entry, &arg,
1149 hist_browser__check_output_full);
d1b4f249
ACM
1150 }
1151
1152 return printed;
1153}
1154
81a888fe
JO
1155static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1156{
1157 advance_hpp(hpp, inc);
1158 return hpp->size <= 0;
1159}
1160
c6c3c02d 1161static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
81a888fe 1162{
c6c3c02d 1163 struct hists *hists = browser->hists;
81a888fe
JO
1164 struct perf_hpp dummy_hpp = {
1165 .buf = buf,
1166 .size = size,
1167 };
1168 struct perf_hpp_fmt *fmt;
1169 size_t ret = 0;
c6c3c02d 1170 int column = 0;
81a888fe
JO
1171
1172 if (symbol_conf.use_callchain) {
1173 ret = scnprintf(buf, size, " ");
1174 if (advance_hpp_check(&dummy_hpp, ret))
1175 return ret;
1176 }
1177
f0786af5 1178 hists__for_each_format(browser->hists, fmt) {
361459f1 1179 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
81a888fe
JO
1180 continue;
1181
81a888fe
JO
1182 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1183 if (advance_hpp_check(&dummy_hpp, ret))
1184 break;
1185
1186 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1187 if (advance_hpp_check(&dummy_hpp, ret))
1188 break;
1189 }
1190
1191 return ret;
1192}
1193
025bf7ea
ACM
1194static void hist_browser__show_headers(struct hist_browser *browser)
1195{
81a888fe
JO
1196 char headers[1024];
1197
c6c3c02d 1198 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
025bf7ea
ACM
1199 ui_browser__gotorc(&browser->b, 0, 0);
1200 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
26270a00 1201 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
025bf7ea
ACM
1202}
1203
437cfe7a
ACM
1204static void ui_browser__hists_init_top(struct ui_browser *browser)
1205{
1206 if (browser->top == NULL) {
1207 struct hist_browser *hb;
1208
1209 hb = container_of(browser, struct hist_browser, b);
1210 browser->top = rb_first(&hb->hists->entries);
1211 }
1212}
1213
05e8b080 1214static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
1215{
1216 unsigned row = 0;
025bf7ea 1217 u16 header_offset = 0;
d1b4f249 1218 struct rb_node *nd;
05e8b080 1219 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 1220
025bf7ea
ACM
1221 if (hb->show_headers) {
1222 hist_browser__show_headers(hb);
1223 header_offset = 1;
1224 }
1225
05e8b080 1226 ui_browser__hists_init_top(browser);
979d2cac
WN
1227 hb->he_selection = NULL;
1228 hb->selection = NULL;
d1b4f249 1229
05e8b080 1230 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249 1231 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1232 float percent;
d1b4f249
ACM
1233
1234 if (h->filtered)
1235 continue;
1236
14135663 1237 percent = hist_entry__get_percent_limit(h);
064f1981
NK
1238 if (percent < hb->min_pcnt)
1239 continue;
1240
d1b4f249 1241 row += hist_browser__show_entry(hb, h, row);
62c95ae3 1242 if (row == browser->rows)
d1b4f249
ACM
1243 break;
1244 }
1245
025bf7ea 1246 return row + header_offset;
d1b4f249
ACM
1247}
1248
064f1981 1249static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 1250 float min_pcnt)
d1b4f249
ACM
1251{
1252 while (nd != NULL) {
1253 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1254 float percent = hist_entry__get_percent_limit(h);
064f1981 1255
c0f1527b 1256 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1257 return nd;
1258
1259 nd = rb_next(nd);
1260 }
1261
1262 return NULL;
1263}
1264
064f1981 1265static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 1266 float min_pcnt)
d1b4f249
ACM
1267{
1268 while (nd != NULL) {
1269 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1270 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
1271
1272 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1273 return nd;
1274
1275 nd = rb_prev(nd);
1276 }
1277
1278 return NULL;
1279}
1280
05e8b080 1281static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
1282 off_t offset, int whence)
1283{
1284 struct hist_entry *h;
1285 struct rb_node *nd;
1286 bool first = true;
064f1981
NK
1287 struct hist_browser *hb;
1288
1289 hb = container_of(browser, struct hist_browser, b);
d1b4f249 1290
05e8b080 1291 if (browser->nr_entries == 0)
60098917
ACM
1292 return;
1293
05e8b080 1294 ui_browser__hists_init_top(browser);
437cfe7a 1295
d1b4f249
ACM
1296 switch (whence) {
1297 case SEEK_SET:
064f1981 1298 nd = hists__filter_entries(rb_first(browser->entries),
14135663 1299 hb->min_pcnt);
d1b4f249
ACM
1300 break;
1301 case SEEK_CUR:
05e8b080 1302 nd = browser->top;
d1b4f249
ACM
1303 goto do_offset;
1304 case SEEK_END:
064f1981 1305 nd = hists__filter_prev_entries(rb_last(browser->entries),
14135663 1306 hb->min_pcnt);
d1b4f249
ACM
1307 first = false;
1308 break;
1309 default:
1310 return;
1311 }
1312
1313 /*
1314 * Moves not relative to the first visible entry invalidates its
1315 * row_offset:
1316 */
05e8b080 1317 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
1318 h->row_offset = 0;
1319
1320 /*
1321 * Here we have to check if nd is expanded (+), if it is we can't go
1322 * the next top level hist_entry, instead we must compute an offset of
1323 * what _not_ to show and not change the first visible entry.
1324 *
1325 * This offset increments when we are going from top to bottom and
1326 * decreases when we're going from bottom to top.
1327 *
1328 * As we don't have backpointers to the top level in the callchains
1329 * structure, we need to always print the whole hist_entry callchain,
1330 * skipping the first ones that are before the first visible entry
1331 * and stop when we printed enough lines to fill the screen.
1332 */
1333do_offset:
837eeb75
WN
1334 if (!nd)
1335 return;
1336
d1b4f249
ACM
1337 if (offset > 0) {
1338 do {
1339 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1340 if (h->unfolded) {
d1b4f249
ACM
1341 u16 remaining = h->nr_rows - h->row_offset;
1342 if (offset > remaining) {
1343 offset -= remaining;
1344 h->row_offset = 0;
1345 } else {
1346 h->row_offset += offset;
1347 offset = 0;
05e8b080 1348 browser->top = nd;
d1b4f249
ACM
1349 break;
1350 }
1351 }
14135663 1352 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
d1b4f249
ACM
1353 if (nd == NULL)
1354 break;
1355 --offset;
05e8b080 1356 browser->top = nd;
d1b4f249
ACM
1357 } while (offset != 0);
1358 } else if (offset < 0) {
1359 while (1) {
1360 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1361 if (h->unfolded) {
d1b4f249
ACM
1362 if (first) {
1363 if (-offset > h->row_offset) {
1364 offset += h->row_offset;
1365 h->row_offset = 0;
1366 } else {
1367 h->row_offset += offset;
1368 offset = 0;
05e8b080 1369 browser->top = nd;
d1b4f249
ACM
1370 break;
1371 }
1372 } else {
1373 if (-offset > h->nr_rows) {
1374 offset += h->nr_rows;
1375 h->row_offset = 0;
1376 } else {
1377 h->row_offset = h->nr_rows + offset;
1378 offset = 0;
05e8b080 1379 browser->top = nd;
d1b4f249
ACM
1380 break;
1381 }
1382 }
1383 }
1384
14135663 1385 nd = hists__filter_prev_entries(rb_prev(nd),
064f1981 1386 hb->min_pcnt);
d1b4f249
ACM
1387 if (nd == NULL)
1388 break;
1389 ++offset;
05e8b080 1390 browser->top = nd;
d1b4f249
ACM
1391 if (offset == 0) {
1392 /*
1393 * Last unfiltered hist_entry, check if it is
1394 * unfolded, if it is then we should have
1395 * row_offset at its last entry.
1396 */
1397 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1398 if (h->unfolded)
d1b4f249
ACM
1399 h->row_offset = h->nr_rows;
1400 break;
1401 }
1402 first = false;
1403 }
1404 } else {
05e8b080 1405 browser->top = nd;
d1b4f249
ACM
1406 h = rb_entry(nd, struct hist_entry, rb_node);
1407 h->row_offset = 0;
1408 }
1409}
1410
aff3f3f6 1411static int hist_browser__fprintf_callchain(struct hist_browser *browser,
39ee533f 1412 struct hist_entry *he, FILE *fp)
aff3f3f6 1413{
39ee533f
NK
1414 struct callchain_print_arg arg = {
1415 .fp = fp,
1416 };
aff3f3f6 1417
0c841c6c 1418 hist_browser__show_callchain(browser, he, 1, 0,
39ee533f
NK
1419 hist_browser__fprintf_callchain_entry, &arg,
1420 hist_browser__check_dump_full);
1421 return arg.printed;
aff3f3f6
ACM
1422}
1423
1424static int hist_browser__fprintf_entry(struct hist_browser *browser,
1425 struct hist_entry *he, FILE *fp)
1426{
1427 char s[8192];
aff3f3f6
ACM
1428 int printed = 0;
1429 char folded_sign = ' ';
26d8b338
NK
1430 struct perf_hpp hpp = {
1431 .buf = s,
1432 .size = sizeof(s),
1433 };
1434 struct perf_hpp_fmt *fmt;
1435 bool first = true;
1436 int ret;
aff3f3f6
ACM
1437
1438 if (symbol_conf.use_callchain)
1439 folded_sign = hist_entry__folded(he);
1440
aff3f3f6
ACM
1441 if (symbol_conf.use_callchain)
1442 printed += fprintf(fp, "%c ", folded_sign);
1443
f0786af5 1444 hists__for_each_format(browser->hists, fmt) {
361459f1 1445 if (perf_hpp__should_skip(fmt, he->hists))
e67d49a7
NK
1446 continue;
1447
26d8b338
NK
1448 if (!first) {
1449 ret = scnprintf(hpp.buf, hpp.size, " ");
1450 advance_hpp(&hpp, ret);
1451 } else
1452 first = false;
aff3f3f6 1453
26d8b338
NK
1454 ret = fmt->entry(fmt, &hpp, he);
1455 advance_hpp(&hpp, ret);
1456 }
aff3f3f6
ACM
1457 printed += fprintf(fp, "%s\n", rtrim(s));
1458
1459 if (folded_sign == '-')
39ee533f 1460 printed += hist_browser__fprintf_callchain(browser, he, fp);
aff3f3f6
ACM
1461
1462 return printed;
1463}
1464
1465static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1466{
064f1981 1467 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 1468 browser->min_pcnt);
aff3f3f6
ACM
1469 int printed = 0;
1470
1471 while (nd) {
1472 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1473
1474 printed += hist_browser__fprintf_entry(browser, h, fp);
14135663 1475 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
aff3f3f6
ACM
1476 }
1477
1478 return printed;
1479}
1480
1481static int hist_browser__dump(struct hist_browser *browser)
1482{
1483 char filename[64];
1484 FILE *fp;
1485
1486 while (1) {
1487 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1488 if (access(filename, F_OK))
1489 break;
1490 /*
1491 * XXX: Just an arbitrary lazy upper limit
1492 */
1493 if (++browser->print_seq == 8192) {
1494 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1495 return -1;
1496 }
1497 }
1498
1499 fp = fopen(filename, "w");
1500 if (fp == NULL) {
1501 char bf[64];
4cc49d4d
KS
1502 const char *err = strerror_r(errno, bf, sizeof(bf));
1503 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1504 return -1;
1505 }
1506
1507 ++browser->print_seq;
1508 hist_browser__fprintf(browser, fp);
1509 fclose(fp);
1510 ui_helpline__fpush("%s written!", filename);
1511
1512 return 0;
1513}
1514
c2a51ab8 1515static struct hist_browser *hist_browser__new(struct hists *hists,
b1a9ceef 1516 struct hist_browser_timer *hbt,
ce80d3be 1517 struct perf_env *env)
d1b4f249 1518{
05e8b080 1519 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1520
05e8b080
ACM
1521 if (browser) {
1522 browser->hists = hists;
1523 browser->b.refresh = hist_browser__refresh;
357cfff1 1524 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
05e8b080
ACM
1525 browser->b.seek = ui_browser__hists_seek;
1526 browser->b.use_navkeypressed = true;
c8302367 1527 browser->show_headers = symbol_conf.show_hist_headers;
c2a51ab8 1528 browser->hbt = hbt;
b1a9ceef 1529 browser->env = env;
d1b4f249
ACM
1530 }
1531
05e8b080 1532 return browser;
d1b4f249
ACM
1533}
1534
05e8b080 1535static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1536{
05e8b080 1537 free(browser);
d1b4f249
ACM
1538}
1539
05e8b080 1540static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1541{
05e8b080 1542 return browser->he_selection;
d1b4f249
ACM
1543}
1544
05e8b080 1545static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1546{
05e8b080 1547 return browser->he_selection->thread;
d1b4f249
ACM
1548}
1549
1e378ebd
TS
1550/* Check whether the browser is for 'top' or 'report' */
1551static inline bool is_report_browser(void *timer)
1552{
1553 return timer == NULL;
1554}
1555
1556static int hists__browser_title(struct hists *hists,
1557 struct hist_browser_timer *hbt,
1558 char *bf, size_t size)
d1b4f249 1559{
469917ce
ACM
1560 char unit;
1561 int printed;
05e8b080
ACM
1562 const struct dso *dso = hists->dso_filter;
1563 const struct thread *thread = hists->thread_filter;
84734b06 1564 int socket_id = hists->socket_filter;
05e8b080
ACM
1565 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1566 u64 nr_events = hists->stats.total_period;
717e263f 1567 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 1568 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
1569 char buf[512];
1570 size_t buflen = sizeof(buf);
9e207ddf
KL
1571 char ref[30] = " show reference callgraph, ";
1572 bool enable_ref = false;
717e263f 1573
f2148330
NK
1574 if (symbol_conf.filter_relative) {
1575 nr_samples = hists->stats.nr_non_filtered_samples;
1576 nr_events = hists->stats.total_non_filtered_period;
1577 }
1578
759ff497 1579 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1580 struct perf_evsel *pos;
1581
1582 perf_evsel__group_desc(evsel, buf, buflen);
1583 ev_name = buf;
1584
1585 for_each_group_member(pos, evsel) {
4ea062ed
ACM
1586 struct hists *pos_hists = evsel__hists(pos);
1587
f2148330 1588 if (symbol_conf.filter_relative) {
4ea062ed
ACM
1589 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1590 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 1591 } else {
4ea062ed
ACM
1592 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1593 nr_events += pos_hists->stats.total_period;
f2148330 1594 }
717e263f
NK
1595 }
1596 }
cc686280 1597
9e207ddf
KL
1598 if (symbol_conf.show_ref_callgraph &&
1599 strstr(ev_name, "call-graph=no"))
1600 enable_ref = true;
cc686280
AR
1601 nr_samples = convert_unit(nr_samples, &unit);
1602 printed = scnprintf(bf, size,
9e207ddf
KL
1603 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1604 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
469917ce 1605
d1b4f249 1606
05e8b080 1607 if (hists->uid_filter_str)
0d37aa34 1608 printed += snprintf(bf + printed, size - printed,
05e8b080 1609 ", UID: %s", hists->uid_filter_str);
d1b4f249 1610 if (thread)
e7f01d1e 1611 printed += scnprintf(bf + printed, size - printed,
469917ce 1612 ", Thread: %s(%d)",
b9c5143a 1613 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1614 thread->tid);
d1b4f249 1615 if (dso)
e7f01d1e 1616 printed += scnprintf(bf + printed, size - printed,
469917ce 1617 ", DSO: %s", dso->short_name);
84734b06 1618 if (socket_id > -1)
21394d94 1619 printed += scnprintf(bf + printed, size - printed,
84734b06 1620 ", Processor Socket: %d", socket_id);
1e378ebd
TS
1621 if (!is_report_browser(hbt)) {
1622 struct perf_top *top = hbt->arg;
1623
1624 if (top->zero)
1625 printed += scnprintf(bf + printed, size - printed, " [z]");
1626 }
1627
469917ce 1628 return printed;
d1b4f249
ACM
1629}
1630
24bff2dc
SE
1631static inline void free_popup_options(char **options, int n)
1632{
1633 int i;
1634
04662523
ACM
1635 for (i = 0; i < n; ++i)
1636 zfree(&options[i]);
24bff2dc
SE
1637}
1638
341487ab
FT
1639/*
1640 * Only runtime switching of perf data file will make "input_name" point
1641 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1642 * whether we need to call free() for current "input_name" during the switch.
1643 */
1644static bool is_input_name_malloced = false;
1645
1646static int switch_data_file(void)
1647{
1648 char *pwd, *options[32], *abs_path[32], *tmp;
1649 DIR *pwd_dir;
1650 int nr_options = 0, choice = -1, ret = -1;
1651 struct dirent *dent;
1652
1653 pwd = getenv("PWD");
1654 if (!pwd)
1655 return ret;
1656
1657 pwd_dir = opendir(pwd);
1658 if (!pwd_dir)
1659 return ret;
1660
1661 memset(options, 0, sizeof(options));
1662 memset(options, 0, sizeof(abs_path));
1663
1664 while ((dent = readdir(pwd_dir))) {
1665 char path[PATH_MAX];
1666 u64 magic;
1667 char *name = dent->d_name;
1668 FILE *file;
1669
1670 if (!(dent->d_type == DT_REG))
1671 continue;
1672
1673 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1674
1675 file = fopen(path, "r");
1676 if (!file)
1677 continue;
1678
1679 if (fread(&magic, 1, 8, file) < 8)
1680 goto close_file_and_continue;
1681
1682 if (is_perf_magic(magic)) {
1683 options[nr_options] = strdup(name);
1684 if (!options[nr_options])
1685 goto close_file_and_continue;
1686
1687 abs_path[nr_options] = strdup(path);
1688 if (!abs_path[nr_options]) {
74cf249d 1689 zfree(&options[nr_options]);
341487ab
FT
1690 ui__warning("Can't search all data files due to memory shortage.\n");
1691 fclose(file);
1692 break;
1693 }
1694
1695 nr_options++;
1696 }
1697
1698close_file_and_continue:
1699 fclose(file);
1700 if (nr_options >= 32) {
1701 ui__warning("Too many perf data files in PWD!\n"
1702 "Only the first 32 files will be listed.\n");
1703 break;
1704 }
1705 }
1706 closedir(pwd_dir);
1707
1708 if (nr_options) {
1709 choice = ui__popup_menu(nr_options, options);
1710 if (choice < nr_options && choice >= 0) {
1711 tmp = strdup(abs_path[choice]);
1712 if (tmp) {
1713 if (is_input_name_malloced)
1714 free((void *)input_name);
1715 input_name = tmp;
1716 is_input_name_malloced = true;
1717 ret = 0;
1718 } else
1719 ui__warning("Data switch failed due to memory shortage!\n");
1720 }
1721 }
1722
1723 free_popup_options(options, nr_options);
1724 free_popup_options(abs_path, nr_options);
1725 return ret;
1726}
1727
ea7cd592
NK
1728struct popup_action {
1729 struct thread *thread;
ea7cd592 1730 struct map_symbol ms;
84734b06 1731 int socket;
ea7cd592
NK
1732
1733 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1734};
1735
bc7cad42 1736static int
ea7cd592 1737do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
1738{
1739 struct perf_evsel *evsel;
1740 struct annotation *notes;
1741 struct hist_entry *he;
1742 int err;
1743
eebd0bfc 1744 if (!objdump_path && perf_env__lookup_objdump(browser->env))
bc7cad42
NK
1745 return 0;
1746
ea7cd592 1747 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
1748 if (!notes->src)
1749 return 0;
1750
1751 evsel = hists_to_evsel(browser->hists);
ea7cd592 1752 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
1753 he = hist_browser__selected_entry(browser);
1754 /*
1755 * offer option to annotate the other branch source or target
1756 * (if they exists) when returning from annotate
1757 */
1758 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1759 return 1;
1760
1761 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1762 if (err)
1763 ui_browser__handle_resize(&browser->b);
1764 return 0;
1765}
1766
1767static int
ea7cd592
NK
1768add_annotate_opt(struct hist_browser *browser __maybe_unused,
1769 struct popup_action *act, char **optstr,
1770 struct map *map, struct symbol *sym)
bc7cad42 1771{
ea7cd592
NK
1772 if (sym == NULL || map->dso->annotate_warned)
1773 return 0;
1774
1775 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1776 return 0;
1777
1778 act->ms.map = map;
1779 act->ms.sym = sym;
1780 act->fn = do_annotate;
1781 return 1;
1782}
1783
1784static int
1785do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1786{
1787 struct thread *thread = act->thread;
1788
bc7cad42
NK
1789 if (browser->hists->thread_filter) {
1790 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1791 perf_hpp__set_elide(HISTC_THREAD, false);
1792 thread__zput(browser->hists->thread_filter);
1793 ui_helpline__pop();
1794 } else {
7727a925 1795 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
bc7cad42
NK
1796 thread->comm_set ? thread__comm_str(thread) : "",
1797 thread->tid);
1798 browser->hists->thread_filter = thread__get(thread);
1799 perf_hpp__set_elide(HISTC_THREAD, false);
1800 pstack__push(browser->pstack, &browser->hists->thread_filter);
1801 }
1802
1803 hists__filter_by_thread(browser->hists);
1804 hist_browser__reset(browser);
1805 return 0;
1806}
1807
1808static int
ea7cd592
NK
1809add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1810 char **optstr, struct thread *thread)
1811{
2eafd410 1812 if (!sort__has_thread || thread == NULL)
ea7cd592
NK
1813 return 0;
1814
1815 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1816 browser->hists->thread_filter ? "out of" : "into",
1817 thread->comm_set ? thread__comm_str(thread) : "",
1818 thread->tid) < 0)
1819 return 0;
1820
1821 act->thread = thread;
1822 act->fn = do_zoom_thread;
1823 return 1;
1824}
1825
1826static int
1827do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 1828{
045b80dd 1829 struct map *map = act->ms.map;
ea7cd592 1830
bc7cad42
NK
1831 if (browser->hists->dso_filter) {
1832 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1833 perf_hpp__set_elide(HISTC_DSO, false);
1834 browser->hists->dso_filter = NULL;
1835 ui_helpline__pop();
1836 } else {
045b80dd 1837 if (map == NULL)
bc7cad42 1838 return 0;
7727a925 1839 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
045b80dd
ACM
1840 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1841 browser->hists->dso_filter = map->dso;
bc7cad42
NK
1842 perf_hpp__set_elide(HISTC_DSO, true);
1843 pstack__push(browser->pstack, &browser->hists->dso_filter);
1844 }
1845
1846 hists__filter_by_dso(browser->hists);
1847 hist_browser__reset(browser);
1848 return 0;
1849}
1850
1851static int
ea7cd592 1852add_dso_opt(struct hist_browser *browser, struct popup_action *act,
045b80dd 1853 char **optstr, struct map *map)
bc7cad42 1854{
b1447a54 1855 if (!sort__has_dso || map == NULL)
ea7cd592
NK
1856 return 0;
1857
1858 if (asprintf(optstr, "Zoom %s %s DSO",
1859 browser->hists->dso_filter ? "out of" : "into",
045b80dd 1860 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
ea7cd592
NK
1861 return 0;
1862
045b80dd 1863 act->ms.map = map;
ea7cd592
NK
1864 act->fn = do_zoom_dso;
1865 return 1;
1866}
1867
1868static int
1869do_browse_map(struct hist_browser *browser __maybe_unused,
1870 struct popup_action *act)
1871{
1872 map__browse(act->ms.map);
bc7cad42
NK
1873 return 0;
1874}
1875
ea7cd592
NK
1876static int
1877add_map_opt(struct hist_browser *browser __maybe_unused,
1878 struct popup_action *act, char **optstr, struct map *map)
1879{
b1447a54 1880 if (!sort__has_dso || map == NULL)
ea7cd592
NK
1881 return 0;
1882
1883 if (asprintf(optstr, "Browse map details") < 0)
1884 return 0;
1885
1886 act->ms.map = map;
1887 act->fn = do_browse_map;
1888 return 1;
1889}
1890
bc7cad42
NK
1891static int
1892do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 1893 struct popup_action *act)
bc7cad42
NK
1894{
1895 char script_opt[64];
1896 memset(script_opt, 0, sizeof(script_opt));
1897
ea7cd592 1898 if (act->thread) {
bc7cad42 1899 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
1900 thread__comm_str(act->thread));
1901 } else if (act->ms.sym) {
bc7cad42 1902 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 1903 act->ms.sym->name);
bc7cad42
NK
1904 }
1905
1906 script_browse(script_opt);
1907 return 0;
1908}
1909
1910static int
ea7cd592
NK
1911add_script_opt(struct hist_browser *browser __maybe_unused,
1912 struct popup_action *act, char **optstr,
1913 struct thread *thread, struct symbol *sym)
1914{
1915 if (thread) {
1916 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1917 thread__comm_str(thread)) < 0)
1918 return 0;
1919 } else if (sym) {
1920 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1921 sym->name) < 0)
1922 return 0;
1923 } else {
1924 if (asprintf(optstr, "Run scripts for all samples") < 0)
1925 return 0;
1926 }
1927
1928 act->thread = thread;
1929 act->ms.sym = sym;
1930 act->fn = do_run_script;
1931 return 1;
1932}
1933
1934static int
1935do_switch_data(struct hist_browser *browser __maybe_unused,
1936 struct popup_action *act __maybe_unused)
bc7cad42
NK
1937{
1938 if (switch_data_file()) {
1939 ui__warning("Won't switch the data files due to\n"
1940 "no valid data file get selected!\n");
ea7cd592 1941 return 0;
bc7cad42
NK
1942 }
1943
1944 return K_SWITCH_INPUT_DATA;
1945}
1946
ea7cd592
NK
1947static int
1948add_switch_opt(struct hist_browser *browser,
1949 struct popup_action *act, char **optstr)
1950{
1951 if (!is_report_browser(browser->hbt))
1952 return 0;
1953
1954 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1955 return 0;
1956
1957 act->fn = do_switch_data;
1958 return 1;
1959}
1960
1961static int
1962do_exit_browser(struct hist_browser *browser __maybe_unused,
1963 struct popup_action *act __maybe_unused)
1964{
1965 return 0;
1966}
1967
1968static int
1969add_exit_opt(struct hist_browser *browser __maybe_unused,
1970 struct popup_action *act, char **optstr)
1971{
1972 if (asprintf(optstr, "Exit") < 0)
1973 return 0;
1974
1975 act->fn = do_exit_browser;
1976 return 1;
1977}
1978
84734b06
KL
1979static int
1980do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1981{
1982 if (browser->hists->socket_filter > -1) {
1983 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1984 browser->hists->socket_filter = -1;
1985 perf_hpp__set_elide(HISTC_SOCKET, false);
1986 } else {
1987 browser->hists->socket_filter = act->socket;
1988 perf_hpp__set_elide(HISTC_SOCKET, true);
1989 pstack__push(browser->pstack, &browser->hists->socket_filter);
1990 }
1991
1992 hists__filter_by_socket(browser->hists);
1993 hist_browser__reset(browser);
1994 return 0;
1995}
1996
1997static int
1998add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1999 char **optstr, int socket_id)
2000{
d9695d9f 2001 if (!sort__has_socket || socket_id < 0)
84734b06
KL
2002 return 0;
2003
2004 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2005 (browser->hists->socket_filter > -1) ? "out of" : "into",
2006 socket_id) < 0)
2007 return 0;
2008
2009 act->socket = socket_id;
2010 act->fn = do_zoom_socket;
2011 return 1;
2012}
2013
112f761f 2014static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
2015{
2016 u64 nr_entries = 0;
2017 struct rb_node *nd = rb_first(&hb->hists->entries);
2018
268397cb
NK
2019 if (hb->min_pcnt == 0) {
2020 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2021 return;
2022 }
2023
14135663 2024 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 2025 nr_entries++;
c481f930 2026 nd = rb_next(nd);
064f1981
NK
2027 }
2028
112f761f 2029 hb->nr_non_filtered_entries = nr_entries;
064f1981 2030}
341487ab 2031
34958544 2032static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 2033 const char *helpline,
81cce8de 2034 bool left_exits,
68d80758 2035 struct hist_browser_timer *hbt,
064f1981 2036 float min_pcnt,
ce80d3be 2037 struct perf_env *env)
d1b4f249 2038{
4ea062ed 2039 struct hists *hists = evsel__hists(evsel);
b1a9ceef 2040 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
a68c2c58 2041 struct branch_info *bi;
f2b487db
NK
2042#define MAX_OPTIONS 16
2043 char *options[MAX_OPTIONS];
ea7cd592 2044 struct popup_action actions[MAX_OPTIONS];
24bff2dc 2045 int nr_options = 0;
d1b4f249 2046 int key = -1;
938a23ae 2047 char buf[64];
9783adf7 2048 int delay_secs = hbt ? hbt->refresh : 0;
59dc9f25 2049 struct perf_hpp_fmt *fmt;
d1b4f249 2050
e8e684a5
NK
2051#define HIST_BROWSER_HELP_COMMON \
2052 "h/?/F1 Show this window\n" \
2053 "UP/DOWN/PGUP\n" \
2054 "PGDN/SPACE Navigate\n" \
2055 "q/ESC/CTRL+C Exit browser\n\n" \
2056 "For multiple event sessions:\n\n" \
2057 "TAB/UNTAB Switch events\n\n" \
2058 "For symbolic views (--sort has sym):\n\n" \
7727a925
ACM
2059 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2060 "ESC Zoom out\n" \
e8e684a5
NK
2061 "a Annotate current symbol\n" \
2062 "C Collapse all callchains\n" \
2063 "d Zoom into current DSO\n" \
2064 "E Expand all callchains\n" \
105eb30f 2065 "F Toggle percentage of filtered entries\n" \
025bf7ea 2066 "H Display column headers\n" \
31eb4360 2067 "m Display context menu\n" \
84734b06 2068 "S Zoom into current Processor Socket\n" \
e8e684a5
NK
2069
2070 /* help messages are sorted by lexical order of the hotkey */
2071 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 2072 "i Show header information\n"
e8e684a5
NK
2073 "P Print histograms to perf.hist.N\n"
2074 "r Run available scripts\n"
2075 "s Switch to another data file in PWD\n"
2076 "t Zoom into current Thread\n"
2077 "V Verbose (DSO names in callchains, etc)\n"
2078 "/ Filter symbol by name";
2079 const char top_help[] = HIST_BROWSER_HELP_COMMON
2080 "P Print histograms to perf.hist.N\n"
2081 "t Zoom into current Thread\n"
2082 "V Verbose (DSO names in callchains, etc)\n"
42337a22 2083 "z Toggle zeroing of samples\n"
fbb7997e 2084 "f Enable/Disable events\n"
e8e684a5
NK
2085 "/ Filter symbol by name";
2086
d1b4f249
ACM
2087 if (browser == NULL)
2088 return -1;
2089
ed426915
NK
2090 /* reset abort key so that it can get Ctrl-C as a key */
2091 SLang_reset_tty();
2092 SLang_init_tty(0, 0, 0);
2093
03905048 2094 if (min_pcnt)
064f1981 2095 browser->min_pcnt = min_pcnt;
03905048 2096 hist_browser__update_nr_entries(browser);
064f1981 2097
84734b06 2098 browser->pstack = pstack__new(3);
01f00a1c 2099 if (browser->pstack == NULL)
d1b4f249
ACM
2100 goto out;
2101
2102 ui_helpline__push(helpline);
2103
24bff2dc 2104 memset(options, 0, sizeof(options));
ea7cd592 2105 memset(actions, 0, sizeof(actions));
24bff2dc 2106
f0786af5 2107 hists__for_each_format(browser->hists, fmt) {
59dc9f25 2108 perf_hpp__reset_width(fmt, hists);
c6c3c02d
ACM
2109 /*
2110 * This is done just once, and activates the horizontal scrolling
2111 * code in the ui_browser code, it would be better to have a the
2112 * counter in the perf_hpp code, but I couldn't find doing it here
2113 * works, FIXME by setting this in hist_browser__new, for now, be
2114 * clever 8-)
2115 */
2116 ++browser->b.columns;
2117 }
59dc9f25 2118
5b591669
NK
2119 if (symbol_conf.col_width_list_str)
2120 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2121
d1b4f249 2122 while (1) {
f3b623b8 2123 struct thread *thread = NULL;
045b80dd 2124 struct map *map = NULL;
ea7cd592 2125 int choice = 0;
84734b06 2126 int socked_id = -1;
d1b4f249 2127
24bff2dc
SE
2128 nr_options = 0;
2129
5f00b0f4 2130 key = hist_browser__run(browser, helpline);
d1b4f249 2131
60098917
ACM
2132 if (browser->he_selection != NULL) {
2133 thread = hist_browser__selected_thread(browser);
045b80dd 2134 map = browser->selection->map;
84734b06 2135 socked_id = browser->he_selection->socket;
60098917 2136 }
b50e003d 2137 switch (key) {
cf958003
ACM
2138 case K_TAB:
2139 case K_UNTAB:
e4419b8e
DA
2140 if (nr_events == 1)
2141 continue;
b50e003d
ACM
2142 /*
2143 * Exit the browser, let hists__browser_tree
2144 * go to the next or previous
2145 */
2146 goto out_free_stack;
2147 case 'a':
9c796ec8 2148 if (!sort__has_sym) {
7b27509f 2149 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 2150 "Annotation is only available for symbolic views, "
a68c2c58 2151 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
2152 continue;
2153 }
2154
60098917 2155 if (browser->selection == NULL ||
db9a9cbc 2156 browser->selection->sym == NULL ||
b50e003d 2157 browser->selection->map->dso->annotate_warned)
d1b4f249 2158 continue;
bc7cad42 2159
ea7cd592
NK
2160 actions->ms.map = browser->selection->map;
2161 actions->ms.sym = browser->selection->sym;
2162 do_annotate(browser, actions);
bc7cad42 2163 continue;
aff3f3f6
ACM
2164 case 'P':
2165 hist_browser__dump(browser);
2166 continue;
b50e003d 2167 case 'd':
fae00650 2168 actions->ms.map = map;
ea7cd592 2169 do_zoom_dso(browser, actions);
bc7cad42 2170 continue;
a7cb8863
ACM
2171 case 'V':
2172 browser->show_dso = !browser->show_dso;
2173 continue;
b50e003d 2174 case 't':
ea7cd592
NK
2175 actions->thread = thread;
2176 do_zoom_thread(browser, actions);
bc7cad42 2177 continue;
84734b06
KL
2178 case 'S':
2179 actions->socket = socked_id;
2180 do_zoom_socket(browser, actions);
2181 continue;
5a5626b1 2182 case '/':
938a23ae 2183 if (ui_browser__input_window("Symbol to show",
4aa8e454
ACM
2184 "Please enter the name of symbol you want to see.\n"
2185 "To remove the filter later, press / + ENTER.",
938a23ae
NK
2186 buf, "ENTER: OK, ESC: Cancel",
2187 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
2188 hists->symbol_filter_str = *buf ? buf : NULL;
2189 hists__filter_by_symbol(hists);
938a23ae
NK
2190 hist_browser__reset(browser);
2191 }
2192 continue;
cdbab7c2 2193 case 'r':
ea7cd592
NK
2194 if (is_report_browser(hbt)) {
2195 actions->thread = NULL;
2196 actions->ms.sym = NULL;
2197 do_run_script(browser, actions);
2198 }
c77d8d70 2199 continue;
341487ab 2200 case 's':
bc7cad42 2201 if (is_report_browser(hbt)) {
ea7cd592 2202 key = do_switch_data(browser, actions);
bc7cad42
NK
2203 if (key == K_SWITCH_INPUT_DATA)
2204 goto out_free_stack;
2205 }
341487ab 2206 continue;
6dd60135
NK
2207 case 'i':
2208 /* env->arch is NULL for live-mode (i.e. perf top) */
2209 if (env->arch)
2210 tui__header_window(env);
2211 continue;
105eb30f
NK
2212 case 'F':
2213 symbol_conf.filter_relative ^= 1;
2214 continue;
42337a22
NK
2215 case 'z':
2216 if (!is_report_browser(hbt)) {
2217 struct perf_top *top = hbt->arg;
2218
2219 top->zero = !top->zero;
2220 }
2221 continue;
cf958003 2222 case K_F1:
b50e003d
ACM
2223 case 'h':
2224 case '?':
4610e413 2225 ui_browser__help_window(&browser->b,
e8e684a5 2226 is_report_browser(hbt) ? report_help : top_help);
b50e003d 2227 continue;
cf958003
ACM
2228 case K_ENTER:
2229 case K_RIGHT:
31eb4360 2230 case 'm':
b50e003d
ACM
2231 /* menu */
2232 break;
63ab1749 2233 case K_ESC:
cf958003 2234 case K_LEFT: {
b50e003d 2235 const void *top;
d1b4f249 2236
01f00a1c 2237 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
2238 /*
2239 * Go back to the perf_evsel_menu__run or other user
2240 */
2241 if (left_exits)
2242 goto out_free_stack;
63ab1749
ACM
2243
2244 if (key == K_ESC &&
2245 ui_browser__dialog_yesno(&browser->b,
2246 "Do you really want to exit?"))
2247 goto out_free_stack;
2248
d1b4f249 2249 continue;
7f0030b2 2250 }
6422184b 2251 top = pstack__peek(browser->pstack);
bc7cad42 2252 if (top == &browser->hists->dso_filter) {
6422184b
NK
2253 /*
2254 * No need to set actions->dso here since
2255 * it's just to remove the current filter.
2256 * Ditto for thread below.
2257 */
2258 do_zoom_dso(browser, actions);
84734b06 2259 } else if (top == &browser->hists->thread_filter) {
6422184b 2260 do_zoom_thread(browser, actions);
84734b06
KL
2261 } else if (top == &browser->hists->socket_filter) {
2262 do_zoom_socket(browser, actions);
2263 }
b50e003d
ACM
2264 continue;
2265 }
ed7e5662
ACM
2266 case 'q':
2267 case CTRL('c'):
516e5368 2268 goto out_free_stack;
fbb7997e 2269 case 'f':
13d1e536
NK
2270 if (!is_report_browser(hbt)) {
2271 struct perf_top *top = hbt->arg;
2272
2273 perf_evlist__toggle_enable(top->evlist);
2274 /*
2275 * No need to refresh, resort/decay histogram
2276 * entries if we are not collecting samples:
2277 */
2278 if (top->evlist->enabled) {
2279 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2280 hbt->refresh = delay_secs;
2281 } else {
2282 helpline = "Press 'f' again to re-enable the events";
2283 hbt->refresh = 0;
2284 }
2285 continue;
2286 }
3e323dc0 2287 /* Fall thru */
ed7e5662 2288 default:
3e323dc0 2289 helpline = "Press '?' for help on key bindings";
ed7e5662 2290 continue;
d1b4f249
ACM
2291 }
2292
4056132e 2293 if (!sort__has_sym || browser->selection == NULL)
0ba332f7
ACM
2294 goto skip_annotation;
2295
55369fc1 2296 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 2297 bi = browser->he_selection->branch_info;
0ba332f7
ACM
2298
2299 if (bi == NULL)
2300 goto skip_annotation;
2301
ea7cd592
NK
2302 nr_options += add_annotate_opt(browser,
2303 &actions[nr_options],
2304 &options[nr_options],
2305 bi->from.map,
2306 bi->from.sym);
2307 if (bi->to.sym != bi->from.sym)
2308 nr_options += add_annotate_opt(browser,
2309 &actions[nr_options],
2310 &options[nr_options],
2311 bi->to.map,
2312 bi->to.sym);
a68c2c58 2313 } else {
ea7cd592
NK
2314 nr_options += add_annotate_opt(browser,
2315 &actions[nr_options],
2316 &options[nr_options],
2317 browser->selection->map,
2318 browser->selection->sym);
a68c2c58 2319 }
0ba332f7 2320skip_annotation:
ea7cd592
NK
2321 nr_options += add_thread_opt(browser, &actions[nr_options],
2322 &options[nr_options], thread);
2323 nr_options += add_dso_opt(browser, &actions[nr_options],
045b80dd 2324 &options[nr_options], map);
ea7cd592
NK
2325 nr_options += add_map_opt(browser, &actions[nr_options],
2326 &options[nr_options],
bd315aab
WN
2327 browser->selection ?
2328 browser->selection->map : NULL);
84734b06
KL
2329 nr_options += add_socket_opt(browser, &actions[nr_options],
2330 &options[nr_options],
2331 socked_id);
cdbab7c2 2332 /* perf script support */
b1baae89
NK
2333 if (!is_report_browser(hbt))
2334 goto skip_scripting;
2335
cdbab7c2 2336 if (browser->he_selection) {
2eafd410
NK
2337 if (sort__has_thread && thread) {
2338 nr_options += add_script_opt(browser,
2339 &actions[nr_options],
2340 &options[nr_options],
2341 thread, NULL);
2342 }
bd315aab
WN
2343 /*
2344 * Note that browser->selection != NULL
2345 * when browser->he_selection is not NULL,
2346 * so we don't need to check browser->selection
2347 * before fetching browser->selection->sym like what
2348 * we do before fetching browser->selection->map.
2349 *
2350 * See hist_browser__show_entry.
2351 */
c221acb0
NK
2352 if (sort__has_sym && browser->selection->sym) {
2353 nr_options += add_script_opt(browser,
2354 &actions[nr_options],
2355 &options[nr_options],
2356 NULL, browser->selection->sym);
2357 }
cdbab7c2 2358 }
ea7cd592
NK
2359 nr_options += add_script_opt(browser, &actions[nr_options],
2360 &options[nr_options], NULL, NULL);
2361 nr_options += add_switch_opt(browser, &actions[nr_options],
2362 &options[nr_options]);
b1baae89 2363skip_scripting:
ea7cd592
NK
2364 nr_options += add_exit_opt(browser, &actions[nr_options],
2365 &options[nr_options]);
d1b4f249 2366
ea7cd592
NK
2367 do {
2368 struct popup_action *act;
68d80758 2369
ea7cd592
NK
2370 choice = ui__popup_menu(nr_options, options);
2371 if (choice == -1 || choice >= nr_options)
2372 break;
a68c2c58 2373
ea7cd592
NK
2374 act = &actions[choice];
2375 key = act->fn(browser, act);
2376 } while (key == 1);
a68c2c58 2377
ea7cd592
NK
2378 if (key == K_SWITCH_INPUT_DATA)
2379 break;
d1b4f249
ACM
2380 }
2381out_free_stack:
01f00a1c 2382 pstack__delete(browser->pstack);
d1b4f249
ACM
2383out:
2384 hist_browser__delete(browser);
f2b487db 2385 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
2386 return key;
2387}
2388
7f0030b2
ACM
2389struct perf_evsel_menu {
2390 struct ui_browser b;
2391 struct perf_evsel *selection;
7b27509f 2392 bool lost_events, lost_events_warned;
064f1981 2393 float min_pcnt;
ce80d3be 2394 struct perf_env *env;
7f0030b2
ACM
2395};
2396
2397static void perf_evsel_menu__write(struct ui_browser *browser,
2398 void *entry, int row)
2399{
2400 struct perf_evsel_menu *menu = container_of(browser,
2401 struct perf_evsel_menu, b);
2402 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 2403 struct hists *hists = evsel__hists(evsel);
7f0030b2 2404 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 2405 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 2406 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 2407 char bf[256], unit;
7b27509f
ACM
2408 const char *warn = " ";
2409 size_t printed;
7f0030b2
ACM
2410
2411 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2412 HE_COLORSET_NORMAL);
2413
759ff497 2414 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2415 struct perf_evsel *pos;
2416
2417 ev_name = perf_evsel__group_name(evsel);
2418
2419 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2420 struct hists *pos_hists = evsel__hists(pos);
2421 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
2422 }
2423 }
2424
7f0030b2 2425 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 2426 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f 2427 unit, unit == ' ' ? "" : " ", ev_name);
517dfdb3 2428 ui_browser__printf(browser, "%s", bf);
7b27509f 2429
4ea062ed 2430 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
2431 if (nr_events != 0) {
2432 menu->lost_events = true;
2433 if (!current_entry)
2434 ui_browser__set_color(browser, HE_COLORSET_TOP);
2435 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
2436 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2437 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
2438 warn = bf;
2439 }
2440
26270a00 2441 ui_browser__write_nstring(browser, warn, browser->width - printed);
7f0030b2
ACM
2442
2443 if (current_entry)
2444 menu->selection = evsel;
2445}
2446
34958544
ACM
2447static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2448 int nr_events, const char *help,
9783adf7 2449 struct hist_browser_timer *hbt)
d1b4f249 2450{
7f0030b2 2451 struct perf_evlist *evlist = menu->b.priv;
e248de33 2452 struct perf_evsel *pos;
dd00d486 2453 const char *title = "Available samples";
9783adf7 2454 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 2455 int key;
d1b4f249 2456
7f0030b2
ACM
2457 if (ui_browser__show(&menu->b, title,
2458 "ESC: exit, ENTER|->: Browse histograms") < 0)
2459 return -1;
2460
7f0030b2 2461 while (1) {
3af6e338 2462 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
2463
2464 switch (key) {
cf958003 2465 case K_TIMER:
9783adf7 2466 hbt->timer(hbt->arg);
7b27509f
ACM
2467
2468 if (!menu->lost_events_warned && menu->lost_events) {
2469 ui_browser__warn_lost_events(&menu->b);
2470 menu->lost_events_warned = true;
2471 }
81cce8de 2472 continue;
cf958003
ACM
2473 case K_RIGHT:
2474 case K_ENTER:
7f0030b2
ACM
2475 if (!menu->selection)
2476 continue;
2477 pos = menu->selection;
2478browse_hists:
18eaf0b8
ACM
2479 perf_evlist__set_selected(evlist, pos);
2480 /*
2481 * Give the calling tool a chance to populate the non
2482 * default evsel resorted hists tree.
2483 */
9783adf7
NK
2484 if (hbt)
2485 hbt->timer(hbt->arg);
34958544 2486 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 2487 true, hbt,
064f1981 2488 menu->min_pcnt,
68d80758 2489 menu->env);
7f0030b2 2490 ui_browser__show_title(&menu->b, title);
18eaf0b8 2491 switch (key) {
cf958003 2492 case K_TAB:
18eaf0b8 2493 if (pos->node.next == &evlist->entries)
9a354cdc 2494 pos = perf_evlist__first(evlist);
18eaf0b8 2495 else
9a354cdc 2496 pos = perf_evsel__next(pos);
18eaf0b8 2497 goto browse_hists;
cf958003 2498 case K_UNTAB:
18eaf0b8 2499 if (pos->node.prev == &evlist->entries)
9a354cdc 2500 pos = perf_evlist__last(evlist);
18eaf0b8 2501 else
d87fcb4a 2502 pos = perf_evsel__prev(pos);
18eaf0b8 2503 goto browse_hists;
341487ab 2504 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
2505 case 'q':
2506 case CTRL('c'):
2507 goto out;
63ab1749 2508 case K_ESC:
18eaf0b8
ACM
2509 default:
2510 continue;
2511 }
cf958003 2512 case K_LEFT:
7f0030b2 2513 continue;
cf958003 2514 case K_ESC:
4610e413
ACM
2515 if (!ui_browser__dialog_yesno(&menu->b,
2516 "Do you really want to exit?"))
ed7e5662
ACM
2517 continue;
2518 /* Fall thru */
7f0030b2
ACM
2519 case 'q':
2520 case CTRL('c'):
2521 goto out;
d1b4f249 2522 default:
18eaf0b8 2523 continue;
d1b4f249
ACM
2524 }
2525 }
2526
7f0030b2
ACM
2527out:
2528 ui_browser__hide(&menu->b);
2529 return key;
2530}
2531
316c7136 2532static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2533 void *entry)
2534{
2535 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2536
2537 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2538 return true;
2539
2540 return false;
2541}
2542
7f0030b2 2543static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2544 int nr_entries, const char *help,
68d80758 2545 struct hist_browser_timer *hbt,
064f1981 2546 float min_pcnt,
ce80d3be 2547 struct perf_env *env)
7f0030b2
ACM
2548{
2549 struct perf_evsel *pos;
2550 struct perf_evsel_menu menu = {
2551 .b = {
2552 .entries = &evlist->entries,
2553 .refresh = ui_browser__list_head_refresh,
2554 .seek = ui_browser__list_head_seek,
2555 .write = perf_evsel_menu__write,
fc24d7c2
NK
2556 .filter = filter_group_entries,
2557 .nr_entries = nr_entries,
7f0030b2
ACM
2558 .priv = evlist,
2559 },
064f1981 2560 .min_pcnt = min_pcnt,
68d80758 2561 .env = env,
7f0030b2
ACM
2562 };
2563
2564 ui_helpline__push("Press ESC to exit");
2565
0050f7aa 2566 evlist__for_each(evlist, pos) {
7289f83c 2567 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2568 size_t line_len = strlen(ev_name) + 7;
2569
2570 if (menu.b.width < line_len)
2571 menu.b.width = line_len;
7f0030b2
ACM
2572 }
2573
fc24d7c2 2574 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2575}
2576
81cce8de 2577int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2578 struct hist_browser_timer *hbt,
064f1981 2579 float min_pcnt,
ce80d3be 2580 struct perf_env *env)
7f0030b2 2581{
fc24d7c2
NK
2582 int nr_entries = evlist->nr_entries;
2583
2584single_entry:
2585 if (nr_entries == 1) {
9a354cdc 2586 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2587
2588 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2589 false, hbt, min_pcnt,
064f1981 2590 env);
7f0030b2
ACM
2591 }
2592
fc24d7c2
NK
2593 if (symbol_conf.event_group) {
2594 struct perf_evsel *pos;
2595
2596 nr_entries = 0;
0050f7aa 2597 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2598 if (perf_evsel__is_group_leader(pos))
2599 nr_entries++;
0050f7aa 2600 }
fc24d7c2
NK
2601
2602 if (nr_entries == 1)
2603 goto single_entry;
2604 }
2605
2606 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2607 hbt, min_pcnt, env);
d1b4f249 2608}
This page took 0.37448 seconds and 5 git commands to generate.