perf tools: Include errno.h where needed
[linux-2.6-block.git] / tools / perf / ui / browsers / hists.c
CommitLineData
a43783ae 1#include <errno.h>
fd20e811 2#include <inttypes.h>
d1b4f249 3#include <stdio.h>
d1b4f249
ACM
4#include <stdlib.h>
5#include <string.h>
d1b4f249
ACM
6#include <linux/rbtree.h>
7
aca7a94d
NK
8#include "../../util/evsel.h"
9#include "../../util/evlist.h"
10#include "../../util/hist.h"
11#include "../../util/pstack.h"
12#include "../../util/sort.h"
13#include "../../util/util.h"
42337a22 14#include "../../util/top.h"
68d80758 15#include "../../arch/common.h"
d1b4f249 16
f758990f 17#include "../browsers/hists.h"
d1b4f249
ACM
18#include "../helpline.h"
19#include "../util.h"
4610e413 20#include "../ui.h"
d1b4f249 21#include "map.h"
d755330c 22#include "annotate.h"
632a5cab 23#include "srcline.h"
a067558e 24#include "string2.h"
d1b4f249 25
3d689ed6
ACM
26#include "sane_ctype.h"
27
f5951d56
NK
28extern void hist_browser__init_hpp(void);
29
5b91a86f
JO
30static int perf_evsel_browser_title(struct hist_browser *browser,
31 char *bf, size_t size);
112f761f 32static void hist_browser__update_nr_entries(struct hist_browser *hb);
81cce8de 33
c3b78952 34static struct rb_node *hists__filter_entries(struct rb_node *nd,
c3b78952
NK
35 float min_pcnt);
36
268397cb
NK
37static bool hist_browser__has_filter(struct hist_browser *hb)
38{
5a1a99cd 39 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
268397cb
NK
40}
41
4fabf3d1
HK
42static int hist_browser__get_folding(struct hist_browser *browser)
43{
44 struct rb_node *nd;
45 struct hists *hists = browser->hists;
46 int unfolded_rows = 0;
47
48 for (nd = rb_first(&hists->entries);
49 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
f5b763fe 50 nd = rb_hierarchy_next(nd)) {
4fabf3d1
HK
51 struct hist_entry *he =
52 rb_entry(nd, struct hist_entry, rb_node);
53
f5b763fe 54 if (he->leaf && he->unfolded)
4fabf3d1
HK
55 unfolded_rows += he->nr_rows;
56 }
57 return unfolded_rows;
58}
59
c3b78952
NK
60static u32 hist_browser__nr_entries(struct hist_browser *hb)
61{
62 u32 nr_entries;
63
f5b763fe
NK
64 if (symbol_conf.report_hierarchy)
65 nr_entries = hb->nr_hierarchy_entries;
66 else if (hist_browser__has_filter(hb))
c3b78952
NK
67 nr_entries = hb->nr_non_filtered_entries;
68 else
69 nr_entries = hb->hists->nr_entries;
70
4fabf3d1 71 hb->nr_callchain_rows = hist_browser__get_folding(hb);
c3b78952
NK
72 return nr_entries + hb->nr_callchain_rows;
73}
74
025bf7ea
ACM
75static void hist_browser__update_rows(struct hist_browser *hb)
76{
77 struct ui_browser *browser = &hb->b;
f8e6710d
JO
78 struct hists *hists = hb->hists;
79 struct perf_hpp_list *hpp_list = hists->hpp_list;
80 u16 header_offset, index_row;
025bf7ea 81
f8e6710d 82 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
025bf7ea
ACM
83 browser->rows = browser->height - header_offset;
84 /*
85 * Verify if we were at the last line and that line isn't
86 * visibe because we now show the header line(s).
87 */
88 index_row = browser->index - browser->top_idx;
89 if (index_row >= browser->rows)
90 browser->index -= index_row - browser->rows + 1;
91}
92
357cfff1 93static void hist_browser__refresh_dimensions(struct ui_browser *browser)
d1b4f249 94{
357cfff1
ACM
95 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
96
d1b4f249 97 /* 3 == +/- toggle symbol before actual hist_entry rendering */
357cfff1
ACM
98 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
99 /*
100 * FIXME: Just keeping existing behaviour, but this really should be
101 * before updating browser->width, as it will invalidate the
102 * calculation above. Fix this and the fallout in another
103 * changeset.
104 */
105 ui_browser__refresh_dimensions(browser);
025bf7ea 106 hist_browser__update_rows(hb);
d1b4f249
ACM
107}
108
ca3ff33b
ACM
109static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
110{
f8e6710d
JO
111 struct hists *hists = browser->hists;
112 struct perf_hpp_list *hpp_list = hists->hpp_list;
113 u16 header_offset;
025bf7ea 114
f8e6710d 115 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
025bf7ea 116 ui_browser__gotorc(&browser->b, row + header_offset, column);
ca3ff33b
ACM
117}
118
05e8b080 119static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 120{
c3b78952
NK
121 /*
122 * The hists__remove_entry_filter() already folds non-filtered
123 * entries so we can assume it has 0 callchain rows.
124 */
125 browser->nr_callchain_rows = 0;
126
268397cb 127 hist_browser__update_nr_entries(browser);
c3b78952 128 browser->b.nr_entries = hist_browser__nr_entries(browser);
357cfff1 129 hist_browser__refresh_dimensions(&browser->b);
05e8b080 130 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
131}
132
133static char tree__folded_sign(bool unfolded)
134{
135 return unfolded ? '-' : '+';
136}
137
05e8b080 138static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 139{
3698dab1 140 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
d1b4f249
ACM
141}
142
05e8b080 143static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 144{
3698dab1 145 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
d1b4f249
ACM
146}
147
3698dab1 148static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
3c916cc2 149{
3698dab1 150 cl->unfolded = unfold ? cl->has_children : false;
3c916cc2
ACM
151}
152
0d3eb0b7
JY
153static struct inline_node *inline_node__create(struct map *map, u64 ip)
154{
155 struct dso *dso;
156 struct inline_node *node;
157
158 if (map == NULL)
159 return NULL;
160
161 dso = map->dso;
162 if (dso == NULL)
163 return NULL;
164
165 if (dso->kernel != DSO_TYPE_USER)
166 return NULL;
167
168 node = dso__parse_addr_inlines(dso,
169 map__rip_2objdump(map, ip));
170
171 return node;
172}
173
174static int inline__count_rows(struct inline_node *node)
175{
176 struct inline_list *ilist;
177 int i = 0;
178
179 if (node == NULL)
180 return 0;
181
182 list_for_each_entry(ilist, &node->val, list) {
183 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
184 i++;
185 }
186
187 return i;
188}
189
190static int callchain_list__inline_rows(struct callchain_list *chain)
191{
192 struct inline_node *node;
193 int rows;
194
195 node = inline_node__create(chain->ms.map, chain->ip);
196 if (node == NULL)
197 return 0;
198
199 rows = inline__count_rows(node);
200 inline_node__delete(node);
201 return rows;
202}
203
05e8b080 204static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249 205{
0d3eb0b7 206 int n = 0, inline_rows;
d1b4f249
ACM
207 struct rb_node *nd;
208
05e8b080 209 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
210 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
211 struct callchain_list *chain;
212 char folded_sign = ' '; /* No children */
213
214 list_for_each_entry(chain, &child->val, list) {
215 ++n;
0d3eb0b7
JY
216
217 if (symbol_conf.inline_name) {
218 inline_rows =
219 callchain_list__inline_rows(chain);
220 n += inline_rows;
221 }
222
d1b4f249
ACM
223 /* We need this because we may not have children */
224 folded_sign = callchain_list__folded(chain);
225 if (folded_sign == '+')
226 break;
227 }
228
229 if (folded_sign == '-') /* Have children and they're unfolded */
230 n += callchain_node__count_rows_rb_tree(child);
231 }
232
233 return n;
234}
235
4b3a3212
NK
236static int callchain_node__count_flat_rows(struct callchain_node *node)
237{
238 struct callchain_list *chain;
239 char folded_sign = 0;
240 int n = 0;
241
242 list_for_each_entry(chain, &node->parent_val, list) {
243 if (!folded_sign) {
244 /* only check first chain list entry */
245 folded_sign = callchain_list__folded(chain);
246 if (folded_sign == '+')
247 return 1;
248 }
249 n++;
250 }
251
252 list_for_each_entry(chain, &node->val, list) {
253 if (!folded_sign) {
254 /* node->parent_val list might be empty */
255 folded_sign = callchain_list__folded(chain);
256 if (folded_sign == '+')
257 return 1;
258 }
259 n++;
260 }
261
262 return n;
263}
264
8c430a34
NK
265static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
266{
267 return 1;
268}
269
d1b4f249
ACM
270static int callchain_node__count_rows(struct callchain_node *node)
271{
272 struct callchain_list *chain;
273 bool unfolded = false;
0d3eb0b7 274 int n = 0, inline_rows;
d1b4f249 275
4b3a3212
NK
276 if (callchain_param.mode == CHAIN_FLAT)
277 return callchain_node__count_flat_rows(node);
8c430a34
NK
278 else if (callchain_param.mode == CHAIN_FOLDED)
279 return callchain_node__count_folded_rows(node);
4b3a3212 280
d1b4f249
ACM
281 list_for_each_entry(chain, &node->val, list) {
282 ++n;
0d3eb0b7
JY
283 if (symbol_conf.inline_name) {
284 inline_rows = callchain_list__inline_rows(chain);
285 n += inline_rows;
286 }
287
3698dab1 288 unfolded = chain->unfolded;
d1b4f249
ACM
289 }
290
291 if (unfolded)
292 n += callchain_node__count_rows_rb_tree(node);
293
294 return n;
295}
296
297static int callchain__count_rows(struct rb_root *chain)
298{
299 struct rb_node *nd;
300 int n = 0;
301
302 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
303 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
304 n += callchain_node__count_rows(node);
305 }
306
307 return n;
308}
309
f5b763fe
NK
310static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
311 bool include_children)
312{
313 int count = 0;
314 struct rb_node *node;
315 struct hist_entry *child;
316
317 if (he->leaf)
318 return callchain__count_rows(&he->sorted_chain);
319
79dded87
NK
320 if (he->has_no_entry)
321 return 1;
322
f5b763fe
NK
323 node = rb_first(&he->hroot_out);
324 while (node) {
325 float percent;
326
327 child = rb_entry(node, struct hist_entry, rb_node);
328 percent = hist_entry__get_percent_limit(child);
329
330 if (!child->filtered && percent >= hb->min_pcnt) {
331 count++;
332
333 if (include_children && child->unfolded)
334 count += hierarchy_count_rows(hb, child, true);
335 }
336
337 node = rb_next(node);
338 }
339 return count;
340}
341
3698dab1 342static bool hist_entry__toggle_fold(struct hist_entry *he)
d1b4f249 343{
3698dab1 344 if (!he)
8493fe1d
JO
345 return false;
346
3698dab1 347 if (!he->has_children)
d1b4f249
ACM
348 return false;
349
3698dab1
NK
350 he->unfolded = !he->unfolded;
351 return true;
352}
353
354static bool callchain_list__toggle_fold(struct callchain_list *cl)
355{
356 if (!cl)
357 return false;
358
359 if (!cl->has_children)
360 return false;
361
362 cl->unfolded = !cl->unfolded;
d1b4f249
ACM
363 return true;
364}
365
05e8b080 366static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 367{
05e8b080 368 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 369
05e8b080 370 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
371 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
372 struct callchain_list *chain;
293db47f 373 bool first = true;
d1b4f249
ACM
374
375 list_for_each_entry(chain, &child->val, list) {
376 if (first) {
377 first = false;
3698dab1 378 chain->has_children = chain->list.next != &child->val ||
293db47f 379 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249 380 } else
3698dab1 381 chain->has_children = chain->list.next == &child->val &&
293db47f 382 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
383 }
384
385 callchain_node__init_have_children_rb_tree(child);
386 }
387}
388
a7444af6
NK
389static void callchain_node__init_have_children(struct callchain_node *node,
390 bool has_sibling)
d1b4f249
ACM
391{
392 struct callchain_list *chain;
393
a7444af6 394 chain = list_entry(node->val.next, struct callchain_list, list);
3698dab1 395 chain->has_children = has_sibling;
a7444af6 396
90989035 397 if (!list_empty(&node->val)) {
82162b5a 398 chain = list_entry(node->val.prev, struct callchain_list, list);
3698dab1 399 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
82162b5a 400 }
d1b4f249 401
05e8b080 402 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
403}
404
05e8b080 405static void callchain__init_have_children(struct rb_root *root)
d1b4f249 406{
a7444af6
NK
407 struct rb_node *nd = rb_first(root);
408 bool has_sibling = nd && rb_next(nd);
d1b4f249 409
05e8b080 410 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249 411 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
a7444af6 412 callchain_node__init_have_children(node, has_sibling);
8c430a34
NK
413 if (callchain_param.mode == CHAIN_FLAT ||
414 callchain_param.mode == CHAIN_FOLDED)
4b3a3212 415 callchain_node__make_parent_list(node);
d1b4f249
ACM
416 }
417}
418
05e8b080 419static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 420{
f5b763fe
NK
421 if (he->init_have_children)
422 return;
423
424 if (he->leaf) {
3698dab1 425 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
05e8b080 426 callchain__init_have_children(&he->sorted_chain);
f5b763fe
NK
427 } else {
428 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
d1b4f249 429 }
f5b763fe
NK
430
431 he->init_have_children = true;
d1b4f249
ACM
432}
433
0d3eb0b7
JY
434static void hist_entry_init_inline_node(struct hist_entry *he)
435{
436 if (he->inline_node)
437 return;
438
439 he->inline_node = inline_node__create(he->ms.map, he->ip);
440
441 if (he->inline_node == NULL)
442 return;
443
444 he->has_children = true;
445}
446
05e8b080 447static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 448{
3698dab1
NK
449 struct hist_entry *he = browser->he_selection;
450 struct map_symbol *ms = browser->selection;
451 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
452 bool has_children;
453
4938cf0c
WN
454 if (!he || !ms)
455 return false;
456
3698dab1
NK
457 if (ms == &he->ms)
458 has_children = hist_entry__toggle_fold(he);
459 else
460 has_children = callchain_list__toggle_fold(cl);
d1b4f249 461
3698dab1 462 if (has_children) {
f5b763fe
NK
463 int child_rows = 0;
464
d1b4f249 465 hist_entry__init_have_children(he);
c3b78952 466 browser->b.nr_entries -= he->nr_rows;
d1b4f249 467
f5b763fe
NK
468 if (he->leaf)
469 browser->nr_callchain_rows -= he->nr_rows;
d1b4f249 470 else
f5b763fe
NK
471 browser->nr_hierarchy_entries -= he->nr_rows;
472
473 if (symbol_conf.report_hierarchy)
474 child_rows = hierarchy_count_rows(browser, he, true);
475
476 if (he->unfolded) {
477 if (he->leaf)
0d3eb0b7
JY
478 if (he->inline_node)
479 he->nr_rows = inline__count_rows(
480 he->inline_node);
481 else
482 he->nr_rows = callchain__count_rows(
483 &he->sorted_chain);
f5b763fe
NK
484 else
485 he->nr_rows = hierarchy_count_rows(browser, he, false);
486
487 /* account grand children */
488 if (symbol_conf.report_hierarchy)
489 browser->b.nr_entries += child_rows - he->nr_rows;
79dded87
NK
490
491 if (!he->leaf && he->nr_rows == 0) {
492 he->has_no_entry = true;
493 he->nr_rows = 1;
494 }
f5b763fe
NK
495 } else {
496 if (symbol_conf.report_hierarchy)
497 browser->b.nr_entries -= child_rows - he->nr_rows;
498
79dded87
NK
499 if (he->has_no_entry)
500 he->has_no_entry = false;
501
d1b4f249 502 he->nr_rows = 0;
f5b763fe 503 }
c3b78952
NK
504
505 browser->b.nr_entries += he->nr_rows;
f5b763fe
NK
506
507 if (he->leaf)
508 browser->nr_callchain_rows += he->nr_rows;
509 else
510 browser->nr_hierarchy_entries += he->nr_rows;
d1b4f249
ACM
511
512 return true;
513 }
514
515 /* If it doesn't have children, no toggling performed */
516 return false;
517}
518
05e8b080 519static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
520{
521 int n = 0;
522 struct rb_node *nd;
523
05e8b080 524 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
525 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
526 struct callchain_list *chain;
527 bool has_children = false;
528
529 list_for_each_entry(chain, &child->val, list) {
530 ++n;
3698dab1
NK
531 callchain_list__set_folding(chain, unfold);
532 has_children = chain->has_children;
3c916cc2
ACM
533 }
534
535 if (has_children)
536 n += callchain_node__set_folding_rb_tree(child, unfold);
537 }
538
539 return n;
540}
541
542static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
543{
544 struct callchain_list *chain;
545 bool has_children = false;
546 int n = 0;
547
548 list_for_each_entry(chain, &node->val, list) {
549 ++n;
3698dab1
NK
550 callchain_list__set_folding(chain, unfold);
551 has_children = chain->has_children;
3c916cc2
ACM
552 }
553
554 if (has_children)
555 n += callchain_node__set_folding_rb_tree(node, unfold);
556
557 return n;
558}
559
560static int callchain__set_folding(struct rb_root *chain, bool unfold)
561{
562 struct rb_node *nd;
563 int n = 0;
564
565 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
566 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
567 n += callchain_node__set_folding(node, unfold);
568 }
569
570 return n;
571}
572
492b1010
NK
573static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
574 bool unfold __maybe_unused)
575{
576 float percent;
577 struct rb_node *nd;
578 struct hist_entry *child;
579 int n = 0;
580
581 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
582 child = rb_entry(nd, struct hist_entry, rb_node);
583 percent = hist_entry__get_percent_limit(child);
584 if (!child->filtered && percent >= hb->min_pcnt)
585 n++;
586 }
587
588 return n;
589}
590
b33f9226
JO
591static void __hist_entry__set_folding(struct hist_entry *he,
592 struct hist_browser *hb, bool unfold)
3c916cc2 593{
05e8b080 594 hist_entry__init_have_children(he);
3698dab1 595 he->unfolded = unfold ? he->has_children : false;
3c916cc2 596
3698dab1 597 if (he->has_children) {
492b1010
NK
598 int n;
599
600 if (he->leaf)
601 n = callchain__set_folding(&he->sorted_chain, unfold);
602 else
603 n = hierarchy_set_folding(hb, he, unfold);
604
05e8b080 605 he->nr_rows = unfold ? n : 0;
3c916cc2 606 } else
05e8b080 607 he->nr_rows = 0;
3c916cc2
ACM
608}
609
b33f9226
JO
610static void hist_entry__set_folding(struct hist_entry *he,
611 struct hist_browser *browser, bool unfold)
612{
613 double percent;
614
615 percent = hist_entry__get_percent_limit(he);
616 if (he->filtered || percent < browser->min_pcnt)
617 return;
618
619 __hist_entry__set_folding(he, browser, unfold);
620
621 if (!he->depth || unfold)
622 browser->nr_hierarchy_entries++;
623 if (he->leaf)
624 browser->nr_callchain_rows += he->nr_rows;
625 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
626 browser->nr_hierarchy_entries++;
627 he->has_no_entry = true;
628 he->nr_rows = 1;
629 } else
630 he->has_no_entry = false;
631}
632
c3b78952
NK
633static void
634__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2
ACM
635{
636 struct rb_node *nd;
492b1010 637 struct hist_entry *he;
3c916cc2 638
492b1010
NK
639 nd = rb_first(&browser->hists->entries);
640 while (nd) {
641 he = rb_entry(nd, struct hist_entry, rb_node);
642
643 /* set folding state even if it's currently folded */
644 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
645
646 hist_entry__set_folding(he, browser, unfold);
3c916cc2
ACM
647 }
648}
649
05e8b080 650static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 651{
492b1010 652 browser->nr_hierarchy_entries = 0;
c3b78952
NK
653 browser->nr_callchain_rows = 0;
654 __hist_browser__set_folding(browser, unfold);
655
656 browser->b.nr_entries = hist_browser__nr_entries(browser);
3c916cc2 657 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 658 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
659}
660
0e3fa7a7
JO
661static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
662{
663 if (!browser->he_selection)
664 return;
665
666 hist_entry__set_folding(browser->he_selection, browser, unfold);
667 browser->b.nr_entries = hist_browser__nr_entries(browser);
668}
669
7b27509f
ACM
670static void ui_browser__warn_lost_events(struct ui_browser *browser)
671{
672 ui_browser__warning(browser, 4,
673 "Events are being lost, check IO/CPU overload!\n\n"
674 "You may want to run 'perf' using a RT scheduler policy:\n\n"
675 " perf top -r 80\n\n"
676 "Or reduce the sampling frequency.");
677}
678
5b91a86f
JO
679static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
680{
681 return browser->title ? browser->title(browser, bf, size) : 0;
682}
683
dabd2012 684int hist_browser__run(struct hist_browser *browser, const char *help)
d1b4f249 685{
b50e003d 686 int key;
81cce8de 687 char title[160];
c2a51ab8 688 struct hist_browser_timer *hbt = browser->hbt;
9783adf7 689 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 690
05e8b080 691 browser->b.entries = &browser->hists->entries;
c3b78952 692 browser->b.nr_entries = hist_browser__nr_entries(browser);
d1b4f249 693
5b91a86f 694 hist_browser__title(browser, title, sizeof(title));
d1b4f249 695
090cff3e 696 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
d1b4f249
ACM
697 return -1;
698
d1b4f249 699 while (1) {
05e8b080 700 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 701
b50e003d 702 switch (key) {
fa5df943
NK
703 case K_TIMER: {
704 u64 nr_entries;
9783adf7 705 hbt->timer(hbt->arg);
fa5df943 706
c6111523
NK
707 if (hist_browser__has_filter(browser) ||
708 symbol_conf.report_hierarchy)
112f761f 709 hist_browser__update_nr_entries(browser);
fa5df943 710
c3b78952 711 nr_entries = hist_browser__nr_entries(browser);
fa5df943 712 ui_browser__update_nr_entries(&browser->b, nr_entries);
7b27509f 713
05e8b080
ACM
714 if (browser->hists->stats.nr_lost_warned !=
715 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
716 browser->hists->stats.nr_lost_warned =
717 browser->hists->stats.nr_events[PERF_RECORD_LOST];
718 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
719 }
720
5b91a86f 721 hist_browser__title(browser, title, sizeof(title));
05e8b080 722 ui_browser__show_title(&browser->b, title);
81cce8de 723 continue;
fa5df943 724 }
4694153c 725 case 'D': { /* Debug */
d1b4f249 726 static int seq;
05e8b080 727 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
728 struct hist_entry, rb_node);
729 ui_helpline__pop();
62c95ae3 730 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
731 seq++, browser->b.nr_entries,
732 browser->hists->nr_entries,
62c95ae3 733 browser->b.rows,
05e8b080
ACM
734 browser->b.index,
735 browser->b.top_idx,
d1b4f249
ACM
736 h->row_offset, h->nr_rows);
737 }
3c916cc2
ACM
738 break;
739 case 'C':
740 /* Collapse the whole world. */
05e8b080 741 hist_browser__set_folding(browser, false);
3c916cc2 742 break;
0e3fa7a7
JO
743 case 'c':
744 /* Collapse the selected entry. */
745 hist_browser__set_folding_selected(browser, false);
746 break;
3c916cc2
ACM
747 case 'E':
748 /* Expand the whole world. */
05e8b080 749 hist_browser__set_folding(browser, true);
3c916cc2 750 break;
0e3fa7a7
JO
751 case 'e':
752 /* Expand the selected entry. */
753 hist_browser__set_folding_selected(browser, true);
754 break;
025bf7ea
ACM
755 case 'H':
756 browser->show_headers = !browser->show_headers;
757 hist_browser__update_rows(browser);
758 break;
cf958003 759 case K_ENTER:
05e8b080 760 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
761 break;
762 /* fall thru */
763 default:
b50e003d 764 goto out;
d1b4f249
ACM
765 }
766 }
b50e003d 767out:
05e8b080 768 ui_browser__hide(&browser->b);
b50e003d 769 return key;
d1b4f249
ACM
770}
771
39ee533f
NK
772struct callchain_print_arg {
773 /* for hists browser */
774 off_t row_offset;
775 bool is_current_entry;
776
777 /* for file dump */
778 FILE *fp;
779 int printed;
780};
781
782typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
783 struct callchain_list *chain,
784 const char *str, int offset,
785 unsigned short row,
786 struct callchain_print_arg *arg);
787
f4536ddd
NK
788static void hist_browser__show_callchain_entry(struct hist_browser *browser,
789 struct callchain_list *chain,
39ee533f
NK
790 const char *str, int offset,
791 unsigned short row,
792 struct callchain_print_arg *arg)
f4536ddd
NK
793{
794 int color, width;
39ee533f 795 char folded_sign = callchain_list__folded(chain);
70e97278 796 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
f4536ddd
NK
797
798 color = HE_COLORSET_NORMAL;
799 width = browser->b.width - (offset + 2);
800 if (ui_browser__is_current_entry(&browser->b, row)) {
801 browser->selection = &chain->ms;
802 color = HE_COLORSET_SELECTED;
39ee533f 803 arg->is_current_entry = true;
f4536ddd
NK
804 }
805
806 ui_browser__set_color(&browser->b, color);
807 hist_browser__gotorc(browser, row, 0);
26270a00 808 ui_browser__write_nstring(&browser->b, " ", offset);
517dfdb3 809 ui_browser__printf(&browser->b, "%c", folded_sign);
70e97278 810 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
26270a00 811 ui_browser__write_nstring(&browser->b, str, width);
f4536ddd
NK
812}
813
39ee533f
NK
814static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
815 struct callchain_list *chain,
816 const char *str, int offset,
817 unsigned short row __maybe_unused,
818 struct callchain_print_arg *arg)
819{
820 char folded_sign = callchain_list__folded(chain);
821
822 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
823 folded_sign, str);
824}
825
826typedef bool (*check_output_full_fn)(struct hist_browser *browser,
827 unsigned short row);
828
829static bool hist_browser__check_output_full(struct hist_browser *browser,
830 unsigned short row)
831{
832 return browser->b.rows == row;
833}
834
835static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
836 unsigned short row __maybe_unused)
837{
838 return false;
839}
840
d1b4f249
ACM
841#define LEVEL_OFFSET_STEP 3
842
0d3eb0b7
JY
843static int hist_browser__show_inline(struct hist_browser *browser,
844 struct inline_node *node,
845 unsigned short row,
846 int offset)
847{
848 struct inline_list *ilist;
849 char buf[1024];
850 int color, width, first_row;
851
852 first_row = row;
853 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
854 list_for_each_entry(ilist, &node->val, list) {
855 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
856 color = HE_COLORSET_NORMAL;
857 if (ui_browser__is_current_entry(&browser->b, row))
858 color = HE_COLORSET_SELECTED;
859
5dfa210e
MW
860 if (callchain_param.key == CCKEY_ADDRESS ||
861 callchain_param.key == CCKEY_SRCLINE) {
0d3eb0b7
JY
862 if (ilist->filename != NULL)
863 scnprintf(buf, sizeof(buf),
864 "%s:%d (inline)",
865 ilist->filename,
866 ilist->line_nr);
867 else
868 scnprintf(buf, sizeof(buf), "??");
869 } else if (ilist->funcname != NULL)
870 scnprintf(buf, sizeof(buf), "%s (inline)",
871 ilist->funcname);
872 else if (ilist->filename != NULL)
873 scnprintf(buf, sizeof(buf),
874 "%s:%d (inline)",
875 ilist->filename,
876 ilist->line_nr);
877 else
878 scnprintf(buf, sizeof(buf), "??");
879
880 ui_browser__set_color(&browser->b, color);
881 hist_browser__gotorc(browser, row, 0);
882 ui_browser__write_nstring(&browser->b, " ",
883 LEVEL_OFFSET_STEP + offset);
884 ui_browser__write_nstring(&browser->b, buf, width);
885 row++;
886 }
887 }
888
889 return row - first_row;
890}
891
892static size_t show_inline_list(struct hist_browser *browser, struct map *map,
893 u64 ip, int row, int offset)
894{
895 struct inline_node *node;
896 int ret;
897
898 node = inline_node__create(map, ip);
899 if (node == NULL)
900 return 0;
901
902 ret = hist_browser__show_inline(browser, node, row, offset);
903
904 inline_node__delete(node);
905 return ret;
906}
907
18bb8381
NK
908static int hist_browser__show_callchain_list(struct hist_browser *browser,
909 struct callchain_node *node,
910 struct callchain_list *chain,
911 unsigned short row, u64 total,
912 bool need_percent, int offset,
913 print_callchain_entry_fn print,
914 struct callchain_print_arg *arg)
915{
916 char bf[1024], *alloc_str;
fef51ecd 917 char buf[64], *alloc_str2;
18bb8381 918 const char *str;
0d3eb0b7 919 int inline_rows = 0, ret = 1;
18bb8381
NK
920
921 if (arg->row_offset != 0) {
922 arg->row_offset--;
923 return 0;
924 }
925
926 alloc_str = NULL;
fef51ecd
JY
927 alloc_str2 = NULL;
928
18bb8381
NK
929 str = callchain_list__sym_name(chain, bf, sizeof(bf),
930 browser->show_dso);
931
fef51ecd
JY
932 if (symbol_conf.show_branchflag_count) {
933 if (need_percent)
934 callchain_list_counts__printf_value(node, chain, NULL,
935 buf, sizeof(buf));
936 else
937 callchain_list_counts__printf_value(NULL, chain, NULL,
938 buf, sizeof(buf));
939
940 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
941 str = "Not enough memory!";
942 else
943 str = alloc_str2;
944 }
18bb8381 945
fef51ecd 946 if (need_percent) {
18bb8381
NK
947 callchain_node__scnprintf_value(node, buf, sizeof(buf),
948 total);
949
950 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
951 str = "Not enough memory!";
952 else
953 str = alloc_str;
954 }
955
956 print(browser, chain, str, offset, row, arg);
18bb8381 957 free(alloc_str);
fef51ecd 958 free(alloc_str2);
0d3eb0b7
JY
959
960 if (symbol_conf.inline_name) {
961 inline_rows = show_inline_list(browser, chain->ms.map,
962 chain->ip, row + 1, offset);
963 }
964
965 return ret + inline_rows;
18bb8381
NK
966}
967
59c624e2
NK
968static bool check_percent_display(struct rb_node *node, u64 parent_total)
969{
970 struct callchain_node *child;
971
972 if (node == NULL)
973 return false;
974
975 if (rb_next(node))
976 return true;
977
978 child = rb_entry(node, struct callchain_node, rb_node);
979 return callchain_cumul_hits(child) != parent_total;
980}
981
4b3a3212
NK
982static int hist_browser__show_callchain_flat(struct hist_browser *browser,
983 struct rb_root *root,
984 unsigned short row, u64 total,
59c624e2 985 u64 parent_total,
4b3a3212
NK
986 print_callchain_entry_fn print,
987 struct callchain_print_arg *arg,
988 check_output_full_fn is_output_full)
989{
990 struct rb_node *node;
991 int first_row = row, offset = LEVEL_OFFSET_STEP;
992 bool need_percent;
993
994 node = rb_first(root);
59c624e2 995 need_percent = check_percent_display(node, parent_total);
4b3a3212
NK
996
997 while (node) {
998 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
999 struct rb_node *next = rb_next(node);
1000 struct callchain_list *chain;
1001 char folded_sign = ' ';
1002 int first = true;
1003 int extra_offset = 0;
1004
1005 list_for_each_entry(chain, &child->parent_val, list) {
1006 bool was_first = first;
1007
1008 if (first)
1009 first = false;
1010 else if (need_percent)
1011 extra_offset = LEVEL_OFFSET_STEP;
1012
1013 folded_sign = callchain_list__folded(chain);
1014
1015 row += hist_browser__show_callchain_list(browser, child,
1016 chain, row, total,
1017 was_first && need_percent,
1018 offset + extra_offset,
1019 print, arg);
1020
1021 if (is_output_full(browser, row))
1022 goto out;
1023
1024 if (folded_sign == '+')
1025 goto next;
1026 }
1027
1028 list_for_each_entry(chain, &child->val, list) {
1029 bool was_first = first;
1030
1031 if (first)
1032 first = false;
1033 else if (need_percent)
1034 extra_offset = LEVEL_OFFSET_STEP;
1035
1036 folded_sign = callchain_list__folded(chain);
1037
1038 row += hist_browser__show_callchain_list(browser, child,
1039 chain, row, total,
1040 was_first && need_percent,
1041 offset + extra_offset,
1042 print, arg);
1043
1044 if (is_output_full(browser, row))
1045 goto out;
1046
1047 if (folded_sign == '+')
1048 break;
1049 }
1050
1051next:
1052 if (is_output_full(browser, row))
1053 break;
1054 node = next;
1055 }
1056out:
1057 return row - first_row;
1058}
1059
8c430a34
NK
1060static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1061 struct callchain_list *chain,
1062 char *value_str, char *old_str)
1063{
1064 char bf[1024];
1065 const char *str;
1066 char *new;
1067
1068 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1069 browser->show_dso);
1070 if (old_str) {
1071 if (asprintf(&new, "%s%s%s", old_str,
1072 symbol_conf.field_sep ?: ";", str) < 0)
1073 new = NULL;
1074 } else {
1075 if (value_str) {
1076 if (asprintf(&new, "%s %s", value_str, str) < 0)
1077 new = NULL;
1078 } else {
1079 if (asprintf(&new, "%s", str) < 0)
1080 new = NULL;
1081 }
1082 }
1083 return new;
1084}
1085
1086static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1087 struct rb_root *root,
1088 unsigned short row, u64 total,
59c624e2 1089 u64 parent_total,
8c430a34
NK
1090 print_callchain_entry_fn print,
1091 struct callchain_print_arg *arg,
1092 check_output_full_fn is_output_full)
1093{
1094 struct rb_node *node;
1095 int first_row = row, offset = LEVEL_OFFSET_STEP;
1096 bool need_percent;
1097
1098 node = rb_first(root);
59c624e2 1099 need_percent = check_percent_display(node, parent_total);
8c430a34
NK
1100
1101 while (node) {
1102 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1103 struct rb_node *next = rb_next(node);
1104 struct callchain_list *chain, *first_chain = NULL;
1105 int first = true;
1106 char *value_str = NULL, *value_str_alloc = NULL;
1107 char *chain_str = NULL, *chain_str_alloc = NULL;
1108
1109 if (arg->row_offset != 0) {
1110 arg->row_offset--;
1111 goto next;
1112 }
1113
1114 if (need_percent) {
1115 char buf[64];
1116
1117 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1118 if (asprintf(&value_str, "%s", buf) < 0) {
1119 value_str = (char *)"<...>";
1120 goto do_print;
1121 }
1122 value_str_alloc = value_str;
1123 }
1124
1125 list_for_each_entry(chain, &child->parent_val, list) {
1126 chain_str = hist_browser__folded_callchain_str(browser,
1127 chain, value_str, chain_str);
1128 if (first) {
1129 first = false;
1130 first_chain = chain;
1131 }
1132
1133 if (chain_str == NULL) {
1134 chain_str = (char *)"Not enough memory!";
1135 goto do_print;
1136 }
1137
1138 chain_str_alloc = chain_str;
1139 }
1140
1141 list_for_each_entry(chain, &child->val, list) {
1142 chain_str = hist_browser__folded_callchain_str(browser,
1143 chain, value_str, chain_str);
1144 if (first) {
1145 first = false;
1146 first_chain = chain;
1147 }
1148
1149 if (chain_str == NULL) {
1150 chain_str = (char *)"Not enough memory!";
1151 goto do_print;
1152 }
1153
1154 chain_str_alloc = chain_str;
1155 }
1156
1157do_print:
1158 print(browser, first_chain, chain_str, offset, row++, arg);
1159 free(value_str_alloc);
1160 free(chain_str_alloc);
1161
1162next:
1163 if (is_output_full(browser, row))
1164 break;
1165 node = next;
1166 }
1167
1168 return row - first_row;
1169}
1170
0c841c6c 1171static int hist_browser__show_callchain_graph(struct hist_browser *browser,
c09a7e75 1172 struct rb_root *root, int level,
39ee533f 1173 unsigned short row, u64 total,
5eca104e 1174 u64 parent_total,
39ee533f
NK
1175 print_callchain_entry_fn print,
1176 struct callchain_print_arg *arg,
1177 check_output_full_fn is_output_full)
d1b4f249
ACM
1178{
1179 struct rb_node *node;
f4536ddd 1180 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
4087d11c 1181 bool need_percent;
5eca104e
NK
1182 u64 percent_total = total;
1183
1184 if (callchain_param.mode == CHAIN_GRAPH_REL)
1185 percent_total = parent_total;
d1b4f249 1186
c09a7e75 1187 node = rb_first(root);
59c624e2 1188 need_percent = check_percent_display(node, parent_total);
4087d11c 1189
d1b4f249
ACM
1190 while (node) {
1191 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1192 struct rb_node *next = rb_next(node);
d1b4f249
ACM
1193 struct callchain_list *chain;
1194 char folded_sign = ' ';
1195 int first = true;
1196 int extra_offset = 0;
1197
d1b4f249 1198 list_for_each_entry(chain, &child->val, list) {
d1b4f249
ACM
1199 bool was_first = first;
1200
163caed9 1201 if (first)
d1b4f249 1202 first = false;
4087d11c 1203 else if (need_percent)
d1b4f249 1204 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
1205
1206 folded_sign = callchain_list__folded(chain);
c09a7e75 1207
18bb8381 1208 row += hist_browser__show_callchain_list(browser, child,
5eca104e 1209 chain, row, percent_total,
18bb8381
NK
1210 was_first && need_percent,
1211 offset + extra_offset,
1212 print, arg);
d1b4f249 1213
18bb8381 1214 if (is_output_full(browser, row))
d1b4f249 1215 goto out;
18bb8381 1216
d1b4f249
ACM
1217 if (folded_sign == '+')
1218 break;
1219 }
1220
1221 if (folded_sign == '-') {
1222 const int new_level = level + (extra_offset ? 2 : 1);
d1b4f249 1223
0c841c6c 1224 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
5eca104e
NK
1225 new_level, row, total,
1226 child->children_hit,
39ee533f 1227 print, arg, is_output_full);
d1b4f249 1228 }
39ee533f 1229 if (is_output_full(browser, row))
d1b4f249 1230 break;
c09a7e75 1231 node = next;
d1b4f249 1232 }
c09a7e75 1233out:
d1b4f249
ACM
1234 return row - first_row;
1235}
1236
0c841c6c
NK
1237static int hist_browser__show_callchain(struct hist_browser *browser,
1238 struct hist_entry *entry, int level,
1239 unsigned short row,
1240 print_callchain_entry_fn print,
1241 struct callchain_print_arg *arg,
1242 check_output_full_fn is_output_full)
1243{
1244 u64 total = hists__total_period(entry->hists);
5eca104e 1245 u64 parent_total;
0c841c6c
NK
1246 int printed;
1247
5eca104e
NK
1248 if (symbol_conf.cumulate_callchain)
1249 parent_total = entry->stat_acc->period;
1250 else
1251 parent_total = entry->stat.period;
0c841c6c
NK
1252
1253 if (callchain_param.mode == CHAIN_FLAT) {
1254 printed = hist_browser__show_callchain_flat(browser,
5eca104e
NK
1255 &entry->sorted_chain, row,
1256 total, parent_total, print, arg,
1257 is_output_full);
0c841c6c
NK
1258 } else if (callchain_param.mode == CHAIN_FOLDED) {
1259 printed = hist_browser__show_callchain_folded(browser,
5eca104e
NK
1260 &entry->sorted_chain, row,
1261 total, parent_total, print, arg,
1262 is_output_full);
0c841c6c
NK
1263 } else {
1264 printed = hist_browser__show_callchain_graph(browser,
5eca104e
NK
1265 &entry->sorted_chain, level, row,
1266 total, parent_total, print, arg,
1267 is_output_full);
0c841c6c
NK
1268 }
1269
1270 if (arg->is_current_entry)
1271 browser->he_selection = entry;
1272
1273 return printed;
1274}
1275
89701460
NK
1276struct hpp_arg {
1277 struct ui_browser *b;
1278 char folded_sign;
1279 bool current_entry;
1280};
1281
98ba1609 1282int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
2f6d9009
NK
1283{
1284 struct hpp_arg *arg = hpp->ptr;
d675107c 1285 int ret, len;
2f6d9009
NK
1286 va_list args;
1287 double percent;
371d8c40 1288
2f6d9009 1289 va_start(args, fmt);
d675107c 1290 len = va_arg(args, int);
2f6d9009
NK
1291 percent = va_arg(args, double);
1292 va_end(args);
371d8c40 1293
2f6d9009 1294 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
371d8c40 1295
d675107c 1296 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
517dfdb3 1297 ui_browser__printf(arg->b, "%s", hpp->buf);
5aed9d24 1298
5aed9d24
NK
1299 return ret;
1300}
1301
fb821c9e 1302#define __HPP_COLOR_PERCENT_FN(_type, _field) \
5aed9d24
NK
1303static u64 __hpp_get_##_field(struct hist_entry *he) \
1304{ \
1305 return he->stat._field; \
1306} \
1307 \
2c5d4b4a 1308static int \
5b591669 1309hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
2c5d4b4a
JO
1310 struct perf_hpp *hpp, \
1311 struct hist_entry *he) \
f5951d56 1312{ \
5b591669
NK
1313 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1314 __hpp__slsmg_color_printf, true); \
f5951d56
NK
1315}
1316
0434ddd2
NK
1317#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1318static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1319{ \
1320 return he->stat_acc->_field; \
1321} \
1322 \
1323static int \
5b591669 1324hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
0434ddd2
NK
1325 struct perf_hpp *hpp, \
1326 struct hist_entry *he) \
1327{ \
1328 if (!symbol_conf.cumulate_callchain) { \
517dfdb3 1329 struct hpp_arg *arg = hpp->ptr; \
5b591669 1330 int len = fmt->user_len ?: fmt->len; \
d675107c 1331 int ret = scnprintf(hpp->buf, hpp->size, \
5b591669 1332 "%*s", len, "N/A"); \
517dfdb3 1333 ui_browser__printf(arg->b, "%s", hpp->buf); \
0434ddd2
NK
1334 \
1335 return ret; \
1336 } \
5b591669
NK
1337 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1338 " %*.2f%%", __hpp__slsmg_color_printf, true); \
0434ddd2
NK
1339}
1340
fb821c9e
NK
1341__HPP_COLOR_PERCENT_FN(overhead, period)
1342__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1343__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1344__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1345__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
0434ddd2 1346__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
f5951d56 1347
5aed9d24 1348#undef __HPP_COLOR_PERCENT_FN
0434ddd2 1349#undef __HPP_COLOR_ACC_PERCENT_FN
f5951d56
NK
1350
1351void hist_browser__init_hpp(void)
1352{
f5951d56
NK
1353 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1354 hist_browser__hpp_color_overhead;
1355 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1356 hist_browser__hpp_color_overhead_sys;
1357 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1358 hist_browser__hpp_color_overhead_us;
1359 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1360 hist_browser__hpp_color_overhead_guest_sys;
1361 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1362 hist_browser__hpp_color_overhead_guest_us;
0434ddd2
NK
1363 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1364 hist_browser__hpp_color_overhead_acc;
f5951d56
NK
1365}
1366
05e8b080 1367static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
1368 struct hist_entry *entry,
1369 unsigned short row)
1370{
1240005e 1371 int printed = 0;
67d25916 1372 int width = browser->b.width;
d1b4f249 1373 char folded_sign = ' ';
05e8b080 1374 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 1375 off_t row_offset = entry->row_offset;
63a1a3d8 1376 bool first = true;
1240005e 1377 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
1378
1379 if (current_entry) {
05e8b080
ACM
1380 browser->he_selection = entry;
1381 browser->selection = &entry->ms;
d1b4f249
ACM
1382 }
1383
1384 if (symbol_conf.use_callchain) {
163caed9 1385 hist_entry__init_have_children(entry);
d1b4f249
ACM
1386 folded_sign = hist_entry__folded(entry);
1387 }
1388
0d3eb0b7
JY
1389 if (symbol_conf.inline_name &&
1390 (!entry->has_children)) {
1391 hist_entry_init_inline_node(entry);
1392 folded_sign = hist_entry__folded(entry);
1393 }
1394
d1b4f249 1395 if (row_offset == 0) {
89701460 1396 struct hpp_arg arg = {
fb821c9e 1397 .b = &browser->b,
89701460
NK
1398 .folded_sign = folded_sign,
1399 .current_entry = current_entry,
1400 };
c6c3c02d 1401 int column = 0;
d1b4f249 1402
ca3ff33b 1403 hist_browser__gotorc(browser, row, 0);
c172f742 1404
f0786af5 1405 hists__for_each_format(browser->hists, fmt) {
89fee709
ACM
1406 char s[2048];
1407 struct perf_hpp hpp = {
1408 .buf = s,
1409 .size = sizeof(s),
1410 .ptr = &arg,
1411 };
1412
361459f1
NK
1413 if (perf_hpp__should_skip(fmt, entry->hists) ||
1414 column++ < browser->b.horiz_scroll)
e67d49a7
NK
1415 continue;
1416
fb821c9e
NK
1417 if (current_entry && browser->b.navkeypressed) {
1418 ui_browser__set_color(&browser->b,
1419 HE_COLORSET_SELECTED);
1420 } else {
1421 ui_browser__set_color(&browser->b,
1422 HE_COLORSET_NORMAL);
1423 }
1424
1425 if (first) {
0d3eb0b7
JY
1426 if (symbol_conf.use_callchain ||
1427 symbol_conf.inline_name) {
517dfdb3 1428 ui_browser__printf(&browser->b, "%c ", folded_sign);
fb821c9e
NK
1429 width -= 2;
1430 }
1431 first = false;
1432 } else {
517dfdb3 1433 ui_browser__printf(&browser->b, " ");
f5951d56
NK
1434 width -= 2;
1435 }
c172f742 1436
1240005e 1437 if (fmt->color) {
89fee709
ACM
1438 int ret = fmt->color(fmt, &hpp, entry);
1439 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1440 /*
1441 * fmt->color() already used ui_browser to
1442 * print the non alignment bits, skip it (+ret):
1443 */
1444 ui_browser__printf(&browser->b, "%s", s + ret);
f5951d56 1445 } else {
89fee709 1446 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
517dfdb3 1447 ui_browser__printf(&browser->b, "%s", s);
f5951d56 1448 }
89fee709 1449 width -= hpp.buf - s;
2cf9cebf
ACM
1450 }
1451
f5951d56
NK
1452 /* The scroll bar isn't being used */
1453 if (!browser->b.navkeypressed)
1454 width += 1;
1455
26270a00 1456 ui_browser__write_nstring(&browser->b, "", width);
26d8b338 1457
d1b4f249
ACM
1458 ++row;
1459 ++printed;
1460 } else
1461 --row_offset;
1462
62c95ae3 1463 if (folded_sign == '-' && row != browser->b.rows) {
39ee533f
NK
1464 struct callchain_print_arg arg = {
1465 .row_offset = row_offset,
1466 .is_current_entry = current_entry,
1467 };
c09a7e75 1468
0d3eb0b7
JY
1469 if (entry->inline_node)
1470 printed += hist_browser__show_inline(browser,
1471 entry->inline_node, row, 0);
1472 else
1473 printed += hist_browser__show_callchain(browser,
1474 entry, 1, row,
1475 hist_browser__show_callchain_entry,
1476 &arg,
39ee533f 1477 hist_browser__check_output_full);
d1b4f249
ACM
1478 }
1479
1480 return printed;
1481}
1482
d0506edb
NK
1483static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1484 struct hist_entry *entry,
1485 unsigned short row,
2dbbe9f2 1486 int level)
d0506edb
NK
1487{
1488 int printed = 0;
1489 int width = browser->b.width;
1490 char folded_sign = ' ';
1491 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1492 off_t row_offset = entry->row_offset;
1493 bool first = true;
1494 struct perf_hpp_fmt *fmt;
a61a22f6 1495 struct perf_hpp_list_node *fmt_node;
d0506edb
NK
1496 struct hpp_arg arg = {
1497 .b = &browser->b,
1498 .current_entry = current_entry,
1499 };
1500 int column = 0;
2dbbe9f2 1501 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
d0506edb
NK
1502
1503 if (current_entry) {
1504 browser->he_selection = entry;
1505 browser->selection = &entry->ms;
1506 }
1507
1508 hist_entry__init_have_children(entry);
1509 folded_sign = hist_entry__folded(entry);
1510 arg.folded_sign = folded_sign;
1511
1512 if (entry->leaf && row_offset) {
1513 row_offset--;
1514 goto show_callchain;
1515 }
1516
1517 hist_browser__gotorc(browser, row, 0);
1518
1519 if (current_entry && browser->b.navkeypressed)
1520 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1521 else
1522 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1523
1524 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1525 width -= level * HIERARCHY_INDENT;
1526
a61a22f6
NK
1527 /* the first hpp_list_node is for overhead columns */
1528 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1529 struct perf_hpp_list_node, list);
1530 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d0506edb
NK
1531 char s[2048];
1532 struct perf_hpp hpp = {
1533 .buf = s,
1534 .size = sizeof(s),
1535 .ptr = &arg,
1536 };
1537
1538 if (perf_hpp__should_skip(fmt, entry->hists) ||
1539 column++ < browser->b.horiz_scroll)
1540 continue;
1541
d0506edb
NK
1542 if (current_entry && browser->b.navkeypressed) {
1543 ui_browser__set_color(&browser->b,
1544 HE_COLORSET_SELECTED);
1545 } else {
1546 ui_browser__set_color(&browser->b,
1547 HE_COLORSET_NORMAL);
1548 }
1549
1550 if (first) {
3d9f4683
NK
1551 ui_browser__printf(&browser->b, "%c ", folded_sign);
1552 width -= 2;
d0506edb
NK
1553 first = false;
1554 } else {
1555 ui_browser__printf(&browser->b, " ");
1556 width -= 2;
1557 }
1558
1559 if (fmt->color) {
1560 int ret = fmt->color(fmt, &hpp, entry);
1561 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1562 /*
1563 * fmt->color() already used ui_browser to
1564 * print the non alignment bits, skip it (+ret):
1565 */
1566 ui_browser__printf(&browser->b, "%s", s + ret);
1567 } else {
1568 int ret = fmt->entry(fmt, &hpp, entry);
1569 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1570 ui_browser__printf(&browser->b, "%s", s);
1571 }
1572 width -= hpp.buf - s;
1573 }
1574
b9bf911e
NK
1575 if (!first) {
1576 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1577 width -= hierarchy_indent;
1578 }
d0506edb
NK
1579
1580 if (column >= browser->b.horiz_scroll) {
1581 char s[2048];
1582 struct perf_hpp hpp = {
1583 .buf = s,
1584 .size = sizeof(s),
1585 .ptr = &arg,
1586 };
1587
1588 if (current_entry && browser->b.navkeypressed) {
1589 ui_browser__set_color(&browser->b,
1590 HE_COLORSET_SELECTED);
1591 } else {
1592 ui_browser__set_color(&browser->b,
1593 HE_COLORSET_NORMAL);
1594 }
1595
1b2dbbf4 1596 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
131d51eb
NK
1597 if (first) {
1598 ui_browser__printf(&browser->b, "%c ", folded_sign);
1599 first = false;
1600 } else {
1601 ui_browser__write_nstring(&browser->b, "", 2);
1602 }
1603
1b2dbbf4 1604 width -= 2;
d0506edb 1605
1b2dbbf4
NK
1606 /*
1607 * No need to call hist_entry__snprintf_alignment()
1608 * since this fmt is always the last column in the
1609 * hierarchy mode.
1610 */
1611 if (fmt->color) {
1612 width -= fmt->color(fmt, &hpp, entry);
1613 } else {
1614 int i = 0;
cb1fab91 1615
1b2dbbf4
NK
1616 width -= fmt->entry(fmt, &hpp, entry);
1617 ui_browser__printf(&browser->b, "%s", ltrim(s));
cb1fab91 1618
1b2dbbf4
NK
1619 while (isspace(s[i++]))
1620 width++;
1621 }
d0506edb
NK
1622 }
1623 }
1624
1625 /* The scroll bar isn't being used */
1626 if (!browser->b.navkeypressed)
1627 width += 1;
1628
1629 ui_browser__write_nstring(&browser->b, "", width);
1630
1631 ++row;
1632 ++printed;
1633
1634show_callchain:
1635 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1636 struct callchain_print_arg carg = {
1637 .row_offset = row_offset,
1638 };
1639
1640 printed += hist_browser__show_callchain(browser, entry,
1641 level + 1, row,
1642 hist_browser__show_callchain_entry, &carg,
1643 hist_browser__check_output_full);
1644 }
1645
1646 return printed;
1647}
1648
79dded87 1649static int hist_browser__show_no_entry(struct hist_browser *browser,
2dbbe9f2 1650 unsigned short row, int level)
79dded87
NK
1651{
1652 int width = browser->b.width;
1653 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1654 bool first = true;
1655 int column = 0;
1656 int ret;
1657 struct perf_hpp_fmt *fmt;
a61a22f6 1658 struct perf_hpp_list_node *fmt_node;
2dbbe9f2 1659 int indent = browser->hists->nr_hpp_node - 2;
79dded87
NK
1660
1661 if (current_entry) {
1662 browser->he_selection = NULL;
1663 browser->selection = NULL;
1664 }
1665
1666 hist_browser__gotorc(browser, row, 0);
1667
1668 if (current_entry && browser->b.navkeypressed)
1669 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1670 else
1671 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1672
1673 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1674 width -= level * HIERARCHY_INDENT;
1675
a61a22f6
NK
1676 /* the first hpp_list_node is for overhead columns */
1677 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1678 struct perf_hpp_list_node, list);
1679 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
79dded87
NK
1680 if (perf_hpp__should_skip(fmt, browser->hists) ||
1681 column++ < browser->b.horiz_scroll)
1682 continue;
1683
da1b0407 1684 ret = fmt->width(fmt, NULL, browser->hists);
79dded87
NK
1685
1686 if (first) {
1687 /* for folded sign */
1688 first = false;
1689 ret++;
1690 } else {
1691 /* space between columns */
1692 ret += 2;
1693 }
1694
1695 ui_browser__write_nstring(&browser->b, "", ret);
1696 width -= ret;
1697 }
1698
2dbbe9f2
NK
1699 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1700 width -= indent * HIERARCHY_INDENT;
79dded87
NK
1701
1702 if (column >= browser->b.horiz_scroll) {
1703 char buf[32];
1704
1705 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1706 ui_browser__printf(&browser->b, " %s", buf);
1707 width -= ret + 2;
1708 }
1709
1710 /* The scroll bar isn't being used */
1711 if (!browser->b.navkeypressed)
1712 width += 1;
1713
1714 ui_browser__write_nstring(&browser->b, "", width);
1715 return 1;
1716}
1717
81a888fe
JO
1718static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1719{
1720 advance_hpp(hpp, inc);
1721 return hpp->size <= 0;
1722}
1723
69705b35
JO
1724static int
1725hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1726 size_t size, int line)
81a888fe 1727{
c6c3c02d 1728 struct hists *hists = browser->hists;
81a888fe
JO
1729 struct perf_hpp dummy_hpp = {
1730 .buf = buf,
1731 .size = size,
1732 };
1733 struct perf_hpp_fmt *fmt;
1734 size_t ret = 0;
c6c3c02d 1735 int column = 0;
29659ab4 1736 int span = 0;
81a888fe
JO
1737
1738 if (symbol_conf.use_callchain) {
1739 ret = scnprintf(buf, size, " ");
1740 if (advance_hpp_check(&dummy_hpp, ret))
1741 return ret;
1742 }
1743
f0786af5 1744 hists__for_each_format(browser->hists, fmt) {
361459f1 1745 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
81a888fe
JO
1746 continue;
1747
29659ab4 1748 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
81a888fe
JO
1749 if (advance_hpp_check(&dummy_hpp, ret))
1750 break;
1751
29659ab4
JO
1752 if (span)
1753 continue;
1754
81a888fe
JO
1755 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1756 if (advance_hpp_check(&dummy_hpp, ret))
1757 break;
1758 }
1759
1760 return ret;
1761}
1762
d8b92400
NK
1763static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1764{
1765 struct hists *hists = browser->hists;
1766 struct perf_hpp dummy_hpp = {
1767 .buf = buf,
1768 .size = size,
1769 };
1770 struct perf_hpp_fmt *fmt;
a61a22f6 1771 struct perf_hpp_list_node *fmt_node;
d8b92400
NK
1772 size_t ret = 0;
1773 int column = 0;
2dbbe9f2 1774 int indent = hists->nr_hpp_node - 2;
a61a22f6 1775 bool first_node, first_col;
d8b92400 1776
3d9f4683 1777 ret = scnprintf(buf, size, " ");
d8b92400
NK
1778 if (advance_hpp_check(&dummy_hpp, ret))
1779 return ret;
1780
b9bf911e 1781 first_node = true;
a61a22f6
NK
1782 /* the first hpp_list_node is for overhead columns */
1783 fmt_node = list_first_entry(&hists->hpp_formats,
1784 struct perf_hpp_list_node, list);
1785 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d8b92400
NK
1786 if (column++ < browser->b.horiz_scroll)
1787 continue;
1788
29659ab4 1789 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
d8b92400
NK
1790 if (advance_hpp_check(&dummy_hpp, ret))
1791 break;
1792
1793 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1794 if (advance_hpp_check(&dummy_hpp, ret))
1795 break;
b9bf911e
NK
1796
1797 first_node = false;
d8b92400
NK
1798 }
1799
b9bf911e
NK
1800 if (!first_node) {
1801 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1802 indent * HIERARCHY_INDENT, "");
1803 if (advance_hpp_check(&dummy_hpp, ret))
1804 return ret;
1805 }
d8b92400 1806
a61a22f6
NK
1807 first_node = true;
1808 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1809 if (!first_node) {
d8b92400
NK
1810 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1811 if (advance_hpp_check(&dummy_hpp, ret))
1812 break;
1813 }
a61a22f6 1814 first_node = false;
d8b92400 1815
a61a22f6
NK
1816 first_col = true;
1817 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1818 char *start;
d8b92400 1819
a61a22f6
NK
1820 if (perf_hpp__should_skip(fmt, hists))
1821 continue;
cb1fab91 1822
a61a22f6
NK
1823 if (!first_col) {
1824 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1825 if (advance_hpp_check(&dummy_hpp, ret))
1826 break;
1827 }
1828 first_col = false;
cb1fab91 1829
29659ab4 1830 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
a61a22f6 1831 dummy_hpp.buf[ret] = '\0';
a61a22f6 1832
7d6a7e78 1833 start = trim(dummy_hpp.buf);
a61a22f6
NK
1834 ret = strlen(start);
1835
1836 if (start != dummy_hpp.buf)
1837 memmove(dummy_hpp.buf, start, ret + 1);
1838
1839 if (advance_hpp_check(&dummy_hpp, ret))
1840 break;
1841 }
d8b92400
NK
1842 }
1843
1844 return ret;
1845}
1846
01b4770d 1847static void hists_browser__hierarchy_headers(struct hist_browser *browser)
025bf7ea 1848{
81a888fe
JO
1849 char headers[1024];
1850
01b4770d
JO
1851 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1852 sizeof(headers));
1853
025bf7ea
ACM
1854 ui_browser__gotorc(&browser->b, 0, 0);
1855 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
26270a00 1856 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
025bf7ea
ACM
1857}
1858
01b4770d
JO
1859static void hists_browser__headers(struct hist_browser *browser)
1860{
69705b35
JO
1861 struct hists *hists = browser->hists;
1862 struct perf_hpp_list *hpp_list = hists->hpp_list;
01b4770d 1863
69705b35 1864 int line;
01b4770d 1865
69705b35
JO
1866 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1867 char headers[1024];
1868
1869 hists_browser__scnprintf_headers(browser, headers,
1870 sizeof(headers), line);
1871
1872 ui_browser__gotorc(&browser->b, line, 0);
1873 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1874 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1875 }
01b4770d
JO
1876}
1877
1878static void hist_browser__show_headers(struct hist_browser *browser)
1879{
1880 if (symbol_conf.report_hierarchy)
1881 hists_browser__hierarchy_headers(browser);
1882 else
1883 hists_browser__headers(browser);
1884}
1885
437cfe7a
ACM
1886static void ui_browser__hists_init_top(struct ui_browser *browser)
1887{
1888 if (browser->top == NULL) {
1889 struct hist_browser *hb;
1890
1891 hb = container_of(browser, struct hist_browser, b);
1892 browser->top = rb_first(&hb->hists->entries);
1893 }
1894}
1895
05e8b080 1896static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
1897{
1898 unsigned row = 0;
025bf7ea 1899 u16 header_offset = 0;
d1b4f249 1900 struct rb_node *nd;
05e8b080 1901 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
f8e6710d 1902 struct hists *hists = hb->hists;
d1b4f249 1903
025bf7ea 1904 if (hb->show_headers) {
f8e6710d
JO
1905 struct perf_hpp_list *hpp_list = hists->hpp_list;
1906
025bf7ea 1907 hist_browser__show_headers(hb);
f8e6710d 1908 header_offset = hpp_list->nr_header_lines;
025bf7ea
ACM
1909 }
1910
05e8b080 1911 ui_browser__hists_init_top(browser);
979d2cac
WN
1912 hb->he_selection = NULL;
1913 hb->selection = NULL;
d1b4f249 1914
d0506edb 1915 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
d1b4f249 1916 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1917 float percent;
d1b4f249 1918
d0506edb
NK
1919 if (h->filtered) {
1920 /* let it move to sibling */
1921 h->unfolded = false;
d1b4f249 1922 continue;
d0506edb 1923 }
d1b4f249 1924
14135663 1925 percent = hist_entry__get_percent_limit(h);
064f1981
NK
1926 if (percent < hb->min_pcnt)
1927 continue;
1928
d0506edb
NK
1929 if (symbol_conf.report_hierarchy) {
1930 row += hist_browser__show_hierarchy_entry(hb, h, row,
2dbbe9f2 1931 h->depth);
79dded87
NK
1932 if (row == browser->rows)
1933 break;
1934
1935 if (h->has_no_entry) {
a61a22f6 1936 hist_browser__show_no_entry(hb, row, h->depth + 1);
79dded87
NK
1937 row++;
1938 }
d0506edb
NK
1939 } else {
1940 row += hist_browser__show_entry(hb, h, row);
1941 }
1942
62c95ae3 1943 if (row == browser->rows)
d1b4f249
ACM
1944 break;
1945 }
1946
025bf7ea 1947 return row + header_offset;
d1b4f249
ACM
1948}
1949
064f1981 1950static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 1951 float min_pcnt)
d1b4f249
ACM
1952{
1953 while (nd != NULL) {
1954 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1955 float percent = hist_entry__get_percent_limit(h);
064f1981 1956
c0f1527b 1957 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1958 return nd;
1959
d0506edb
NK
1960 /*
1961 * If it's filtered, its all children also were filtered.
1962 * So move to sibling node.
1963 */
1964 if (rb_next(nd))
1965 nd = rb_next(nd);
1966 else
1967 nd = rb_hierarchy_next(nd);
d1b4f249
ACM
1968 }
1969
1970 return NULL;
1971}
1972
064f1981 1973static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 1974 float min_pcnt)
d1b4f249
ACM
1975{
1976 while (nd != NULL) {
1977 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 1978 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
1979
1980 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
1981 return nd;
1982
d0506edb 1983 nd = rb_hierarchy_prev(nd);
d1b4f249
ACM
1984 }
1985
1986 return NULL;
1987}
1988
05e8b080 1989static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
1990 off_t offset, int whence)
1991{
1992 struct hist_entry *h;
1993 struct rb_node *nd;
1994 bool first = true;
064f1981
NK
1995 struct hist_browser *hb;
1996
1997 hb = container_of(browser, struct hist_browser, b);
d1b4f249 1998
05e8b080 1999 if (browser->nr_entries == 0)
60098917
ACM
2000 return;
2001
05e8b080 2002 ui_browser__hists_init_top(browser);
437cfe7a 2003
d1b4f249
ACM
2004 switch (whence) {
2005 case SEEK_SET:
064f1981 2006 nd = hists__filter_entries(rb_first(browser->entries),
14135663 2007 hb->min_pcnt);
d1b4f249
ACM
2008 break;
2009 case SEEK_CUR:
05e8b080 2010 nd = browser->top;
d1b4f249
ACM
2011 goto do_offset;
2012 case SEEK_END:
d0506edb
NK
2013 nd = rb_hierarchy_last(rb_last(browser->entries));
2014 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
d1b4f249
ACM
2015 first = false;
2016 break;
2017 default:
2018 return;
2019 }
2020
2021 /*
2022 * Moves not relative to the first visible entry invalidates its
2023 * row_offset:
2024 */
05e8b080 2025 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
2026 h->row_offset = 0;
2027
2028 /*
2029 * Here we have to check if nd is expanded (+), if it is we can't go
2030 * the next top level hist_entry, instead we must compute an offset of
2031 * what _not_ to show and not change the first visible entry.
2032 *
2033 * This offset increments when we are going from top to bottom and
2034 * decreases when we're going from bottom to top.
2035 *
2036 * As we don't have backpointers to the top level in the callchains
2037 * structure, we need to always print the whole hist_entry callchain,
2038 * skipping the first ones that are before the first visible entry
2039 * and stop when we printed enough lines to fill the screen.
2040 */
2041do_offset:
837eeb75
WN
2042 if (!nd)
2043 return;
2044
d1b4f249
ACM
2045 if (offset > 0) {
2046 do {
2047 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 2048 if (h->unfolded && h->leaf) {
d1b4f249
ACM
2049 u16 remaining = h->nr_rows - h->row_offset;
2050 if (offset > remaining) {
2051 offset -= remaining;
2052 h->row_offset = 0;
2053 } else {
2054 h->row_offset += offset;
2055 offset = 0;
05e8b080 2056 browser->top = nd;
d1b4f249
ACM
2057 break;
2058 }
2059 }
d0506edb
NK
2060 nd = hists__filter_entries(rb_hierarchy_next(nd),
2061 hb->min_pcnt);
d1b4f249
ACM
2062 if (nd == NULL)
2063 break;
2064 --offset;
05e8b080 2065 browser->top = nd;
d1b4f249
ACM
2066 } while (offset != 0);
2067 } else if (offset < 0) {
2068 while (1) {
2069 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 2070 if (h->unfolded && h->leaf) {
d1b4f249
ACM
2071 if (first) {
2072 if (-offset > h->row_offset) {
2073 offset += h->row_offset;
2074 h->row_offset = 0;
2075 } else {
2076 h->row_offset += offset;
2077 offset = 0;
05e8b080 2078 browser->top = nd;
d1b4f249
ACM
2079 break;
2080 }
2081 } else {
2082 if (-offset > h->nr_rows) {
2083 offset += h->nr_rows;
2084 h->row_offset = 0;
2085 } else {
2086 h->row_offset = h->nr_rows + offset;
2087 offset = 0;
05e8b080 2088 browser->top = nd;
d1b4f249
ACM
2089 break;
2090 }
2091 }
2092 }
2093
d0506edb 2094 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
064f1981 2095 hb->min_pcnt);
d1b4f249
ACM
2096 if (nd == NULL)
2097 break;
2098 ++offset;
05e8b080 2099 browser->top = nd;
d1b4f249
ACM
2100 if (offset == 0) {
2101 /*
2102 * Last unfiltered hist_entry, check if it is
2103 * unfolded, if it is then we should have
2104 * row_offset at its last entry.
2105 */
2106 h = rb_entry(nd, struct hist_entry, rb_node);
d0506edb 2107 if (h->unfolded && h->leaf)
d1b4f249
ACM
2108 h->row_offset = h->nr_rows;
2109 break;
2110 }
2111 first = false;
2112 }
2113 } else {
05e8b080 2114 browser->top = nd;
d1b4f249
ACM
2115 h = rb_entry(nd, struct hist_entry, rb_node);
2116 h->row_offset = 0;
2117 }
2118}
2119
aff3f3f6 2120static int hist_browser__fprintf_callchain(struct hist_browser *browser,
d0506edb
NK
2121 struct hist_entry *he, FILE *fp,
2122 int level)
aff3f3f6 2123{
39ee533f
NK
2124 struct callchain_print_arg arg = {
2125 .fp = fp,
2126 };
aff3f3f6 2127
d0506edb 2128 hist_browser__show_callchain(browser, he, level, 0,
39ee533f
NK
2129 hist_browser__fprintf_callchain_entry, &arg,
2130 hist_browser__check_dump_full);
2131 return arg.printed;
aff3f3f6
ACM
2132}
2133
2134static int hist_browser__fprintf_entry(struct hist_browser *browser,
2135 struct hist_entry *he, FILE *fp)
2136{
2137 char s[8192];
aff3f3f6
ACM
2138 int printed = 0;
2139 char folded_sign = ' ';
26d8b338
NK
2140 struct perf_hpp hpp = {
2141 .buf = s,
2142 .size = sizeof(s),
2143 };
2144 struct perf_hpp_fmt *fmt;
2145 bool first = true;
2146 int ret;
aff3f3f6 2147
1b6b678e 2148 if (symbol_conf.use_callchain) {
aff3f3f6 2149 folded_sign = hist_entry__folded(he);
aff3f3f6 2150 printed += fprintf(fp, "%c ", folded_sign);
1b6b678e 2151 }
aff3f3f6 2152
f0786af5 2153 hists__for_each_format(browser->hists, fmt) {
361459f1 2154 if (perf_hpp__should_skip(fmt, he->hists))
e67d49a7
NK
2155 continue;
2156
26d8b338
NK
2157 if (!first) {
2158 ret = scnprintf(hpp.buf, hpp.size, " ");
2159 advance_hpp(&hpp, ret);
2160 } else
2161 first = false;
aff3f3f6 2162
26d8b338 2163 ret = fmt->entry(fmt, &hpp, he);
89fee709 2164 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
26d8b338
NK
2165 advance_hpp(&hpp, ret);
2166 }
89fee709 2167 printed += fprintf(fp, "%s\n", s);
aff3f3f6
ACM
2168
2169 if (folded_sign == '-')
d0506edb
NK
2170 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2171
2172 return printed;
2173}
2174
2175
2176static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2177 struct hist_entry *he,
325a6283 2178 FILE *fp, int level)
d0506edb
NK
2179{
2180 char s[8192];
2181 int printed = 0;
2182 char folded_sign = ' ';
2183 struct perf_hpp hpp = {
2184 .buf = s,
2185 .size = sizeof(s),
2186 };
2187 struct perf_hpp_fmt *fmt;
325a6283 2188 struct perf_hpp_list_node *fmt_node;
d0506edb
NK
2189 bool first = true;
2190 int ret;
325a6283 2191 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
d0506edb
NK
2192
2193 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2194
2195 folded_sign = hist_entry__folded(he);
2196 printed += fprintf(fp, "%c", folded_sign);
2197
325a6283
NK
2198 /* the first hpp_list_node is for overhead columns */
2199 fmt_node = list_first_entry(&he->hists->hpp_formats,
2200 struct perf_hpp_list_node, list);
2201 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
d0506edb
NK
2202 if (!first) {
2203 ret = scnprintf(hpp.buf, hpp.size, " ");
2204 advance_hpp(&hpp, ret);
2205 } else
2206 first = false;
2207
2208 ret = fmt->entry(fmt, &hpp, he);
2209 advance_hpp(&hpp, ret);
2210 }
2211
2212 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2213 advance_hpp(&hpp, ret);
2214
1b2dbbf4
NK
2215 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2216 ret = scnprintf(hpp.buf, hpp.size, " ");
2217 advance_hpp(&hpp, ret);
2218
2219 ret = fmt->entry(fmt, &hpp, he);
2220 advance_hpp(&hpp, ret);
2221 }
d0506edb
NK
2222
2223 printed += fprintf(fp, "%s\n", rtrim(s));
2224
2225 if (he->leaf && folded_sign == '-') {
2226 printed += hist_browser__fprintf_callchain(browser, he, fp,
2227 he->depth + 1);
2228 }
aff3f3f6
ACM
2229
2230 return printed;
2231}
2232
2233static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2234{
064f1981 2235 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 2236 browser->min_pcnt);
aff3f3f6
ACM
2237 int printed = 0;
2238
2239 while (nd) {
2240 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2241
d0506edb
NK
2242 if (symbol_conf.report_hierarchy) {
2243 printed += hist_browser__fprintf_hierarchy_entry(browser,
2244 h, fp,
325a6283 2245 h->depth);
d0506edb
NK
2246 } else {
2247 printed += hist_browser__fprintf_entry(browser, h, fp);
2248 }
2249
2250 nd = hists__filter_entries(rb_hierarchy_next(nd),
2251 browser->min_pcnt);
aff3f3f6
ACM
2252 }
2253
2254 return printed;
2255}
2256
2257static int hist_browser__dump(struct hist_browser *browser)
2258{
2259 char filename[64];
2260 FILE *fp;
2261
2262 while (1) {
2263 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2264 if (access(filename, F_OK))
2265 break;
2266 /*
2267 * XXX: Just an arbitrary lazy upper limit
2268 */
2269 if (++browser->print_seq == 8192) {
2270 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2271 return -1;
2272 }
2273 }
2274
2275 fp = fopen(filename, "w");
2276 if (fp == NULL) {
2277 char bf[64];
c8b5f2c9 2278 const char *err = str_error_r(errno, bf, sizeof(bf));
4cc49d4d 2279 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
2280 return -1;
2281 }
2282
2283 ++browser->print_seq;
2284 hist_browser__fprintf(browser, fp);
2285 fclose(fp);
2286 ui_helpline__fpush("%s written!", filename);
2287
2288 return 0;
2289}
2290
fcd86426
JO
2291void hist_browser__init(struct hist_browser *browser,
2292 struct hists *hists)
d1b4f249 2293{
fcd86426 2294 struct perf_hpp_fmt *fmt;
b1c7a8f7 2295
fcd86426
JO
2296 browser->hists = hists;
2297 browser->b.refresh = hist_browser__refresh;
2298 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2299 browser->b.seek = ui_browser__hists_seek;
2300 browser->b.use_navkeypressed = true;
2301 browser->show_headers = symbol_conf.show_hist_headers;
b1c7a8f7 2302
8a06b0be
NK
2303 if (symbol_conf.report_hierarchy) {
2304 struct perf_hpp_list_node *fmt_node;
2305
2306 /* count overhead columns (in the first node) */
2307 fmt_node = list_first_entry(&hists->hpp_formats,
2308 struct perf_hpp_list_node, list);
2309 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2310 ++browser->b.columns;
2311
2312 /* add a single column for whole hierarchy sort keys*/
fcd86426 2313 ++browser->b.columns;
8a06b0be
NK
2314 } else {
2315 hists__for_each_format(hists, fmt)
2316 ++browser->b.columns;
2317 }
e3b60bc9
NK
2318
2319 hists__reset_column_width(hists);
fcd86426
JO
2320}
2321
2322struct hist_browser *hist_browser__new(struct hists *hists)
2323{
2324 struct hist_browser *browser = zalloc(sizeof(*browser));
2325
2326 if (browser)
2327 hist_browser__init(browser, hists);
d1b4f249 2328
05e8b080 2329 return browser;
d1b4f249
ACM
2330}
2331
a6ec894d
JO
2332static struct hist_browser *
2333perf_evsel_browser__new(struct perf_evsel *evsel,
2334 struct hist_browser_timer *hbt,
2335 struct perf_env *env)
2336{
2337 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2338
2339 if (browser) {
2340 browser->hbt = hbt;
2341 browser->env = env;
2342 browser->title = perf_evsel_browser_title;
2343 }
2344 return browser;
2345}
2346
dabd2012 2347void hist_browser__delete(struct hist_browser *browser)
d1b4f249 2348{
05e8b080 2349 free(browser);
d1b4f249
ACM
2350}
2351
05e8b080 2352static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 2353{
05e8b080 2354 return browser->he_selection;
d1b4f249
ACM
2355}
2356
05e8b080 2357static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 2358{
05e8b080 2359 return browser->he_selection->thread;
d1b4f249
ACM
2360}
2361
1e378ebd
TS
2362/* Check whether the browser is for 'top' or 'report' */
2363static inline bool is_report_browser(void *timer)
2364{
2365 return timer == NULL;
2366}
2367
5b91a86f 2368static int perf_evsel_browser_title(struct hist_browser *browser,
1e378ebd 2369 char *bf, size_t size)
d1b4f249 2370{
5b91a86f
JO
2371 struct hist_browser_timer *hbt = browser->hbt;
2372 struct hists *hists = browser->hists;
469917ce
ACM
2373 char unit;
2374 int printed;
05e8b080
ACM
2375 const struct dso *dso = hists->dso_filter;
2376 const struct thread *thread = hists->thread_filter;
84734b06 2377 int socket_id = hists->socket_filter;
05e8b080
ACM
2378 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2379 u64 nr_events = hists->stats.total_period;
717e263f 2380 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 2381 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
2382 char buf[512];
2383 size_t buflen = sizeof(buf);
9e207ddf
KL
2384 char ref[30] = " show reference callgraph, ";
2385 bool enable_ref = false;
717e263f 2386
f2148330
NK
2387 if (symbol_conf.filter_relative) {
2388 nr_samples = hists->stats.nr_non_filtered_samples;
2389 nr_events = hists->stats.total_non_filtered_period;
2390 }
2391
759ff497 2392 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2393 struct perf_evsel *pos;
2394
2395 perf_evsel__group_desc(evsel, buf, buflen);
2396 ev_name = buf;
2397
2398 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2399 struct hists *pos_hists = evsel__hists(pos);
2400
f2148330 2401 if (symbol_conf.filter_relative) {
4ea062ed
ACM
2402 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2403 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 2404 } else {
4ea062ed
ACM
2405 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2406 nr_events += pos_hists->stats.total_period;
f2148330 2407 }
717e263f
NK
2408 }
2409 }
cc686280 2410
9e207ddf
KL
2411 if (symbol_conf.show_ref_callgraph &&
2412 strstr(ev_name, "call-graph=no"))
2413 enable_ref = true;
cc686280
AR
2414 nr_samples = convert_unit(nr_samples, &unit);
2415 printed = scnprintf(bf, size,
9e207ddf
KL
2416 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2417 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
469917ce 2418
d1b4f249 2419
05e8b080 2420 if (hists->uid_filter_str)
0d37aa34 2421 printed += snprintf(bf + printed, size - printed,
05e8b080 2422 ", UID: %s", hists->uid_filter_str);
6962ccb3 2423 if (thread) {
fa82911a 2424 if (hists__has(hists, thread)) {
6962ccb3 2425 printed += scnprintf(bf + printed, size - printed,
469917ce 2426 ", Thread: %s(%d)",
b9c5143a 2427 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 2428 thread->tid);
6962ccb3
NK
2429 } else {
2430 printed += scnprintf(bf + printed, size - printed,
2431 ", Thread: %s",
2432 (thread->comm_set ? thread__comm_str(thread) : ""));
2433 }
2434 }
d1b4f249 2435 if (dso)
e7f01d1e 2436 printed += scnprintf(bf + printed, size - printed,
469917ce 2437 ", DSO: %s", dso->short_name);
84734b06 2438 if (socket_id > -1)
21394d94 2439 printed += scnprintf(bf + printed, size - printed,
84734b06 2440 ", Processor Socket: %d", socket_id);
1e378ebd
TS
2441 if (!is_report_browser(hbt)) {
2442 struct perf_top *top = hbt->arg;
2443
2444 if (top->zero)
2445 printed += scnprintf(bf + printed, size - printed, " [z]");
2446 }
2447
469917ce 2448 return printed;
d1b4f249
ACM
2449}
2450
24bff2dc
SE
2451static inline void free_popup_options(char **options, int n)
2452{
2453 int i;
2454
04662523
ACM
2455 for (i = 0; i < n; ++i)
2456 zfree(&options[i]);
24bff2dc
SE
2457}
2458
341487ab
FT
2459/*
2460 * Only runtime switching of perf data file will make "input_name" point
2461 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2462 * whether we need to call free() for current "input_name" during the switch.
2463 */
2464static bool is_input_name_malloced = false;
2465
2466static int switch_data_file(void)
2467{
2468 char *pwd, *options[32], *abs_path[32], *tmp;
2469 DIR *pwd_dir;
2470 int nr_options = 0, choice = -1, ret = -1;
2471 struct dirent *dent;
2472
2473 pwd = getenv("PWD");
2474 if (!pwd)
2475 return ret;
2476
2477 pwd_dir = opendir(pwd);
2478 if (!pwd_dir)
2479 return ret;
2480
2481 memset(options, 0, sizeof(options));
3ef5b402 2482 memset(abs_path, 0, sizeof(abs_path));
341487ab
FT
2483
2484 while ((dent = readdir(pwd_dir))) {
2485 char path[PATH_MAX];
2486 u64 magic;
2487 char *name = dent->d_name;
2488 FILE *file;
2489
2490 if (!(dent->d_type == DT_REG))
2491 continue;
2492
2493 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2494
2495 file = fopen(path, "r");
2496 if (!file)
2497 continue;
2498
2499 if (fread(&magic, 1, 8, file) < 8)
2500 goto close_file_and_continue;
2501
2502 if (is_perf_magic(magic)) {
2503 options[nr_options] = strdup(name);
2504 if (!options[nr_options])
2505 goto close_file_and_continue;
2506
2507 abs_path[nr_options] = strdup(path);
2508 if (!abs_path[nr_options]) {
74cf249d 2509 zfree(&options[nr_options]);
341487ab
FT
2510 ui__warning("Can't search all data files due to memory shortage.\n");
2511 fclose(file);
2512 break;
2513 }
2514
2515 nr_options++;
2516 }
2517
2518close_file_and_continue:
2519 fclose(file);
2520 if (nr_options >= 32) {
2521 ui__warning("Too many perf data files in PWD!\n"
2522 "Only the first 32 files will be listed.\n");
2523 break;
2524 }
2525 }
2526 closedir(pwd_dir);
2527
2528 if (nr_options) {
2529 choice = ui__popup_menu(nr_options, options);
2530 if (choice < nr_options && choice >= 0) {
2531 tmp = strdup(abs_path[choice]);
2532 if (tmp) {
2533 if (is_input_name_malloced)
2534 free((void *)input_name);
2535 input_name = tmp;
2536 is_input_name_malloced = true;
2537 ret = 0;
2538 } else
2539 ui__warning("Data switch failed due to memory shortage!\n");
2540 }
2541 }
2542
2543 free_popup_options(options, nr_options);
2544 free_popup_options(abs_path, nr_options);
2545 return ret;
2546}
2547
ea7cd592
NK
2548struct popup_action {
2549 struct thread *thread;
ea7cd592 2550 struct map_symbol ms;
84734b06 2551 int socket;
ea7cd592
NK
2552
2553 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2554};
2555
bc7cad42 2556static int
ea7cd592 2557do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
2558{
2559 struct perf_evsel *evsel;
2560 struct annotation *notes;
2561 struct hist_entry *he;
2562 int err;
2563
eebd0bfc 2564 if (!objdump_path && perf_env__lookup_objdump(browser->env))
bc7cad42
NK
2565 return 0;
2566
ea7cd592 2567 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
2568 if (!notes->src)
2569 return 0;
2570
2571 evsel = hists_to_evsel(browser->hists);
ea7cd592 2572 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
2573 he = hist_browser__selected_entry(browser);
2574 /*
2575 * offer option to annotate the other branch source or target
2576 * (if they exists) when returning from annotate
2577 */
2578 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2579 return 1;
2580
2581 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2582 if (err)
2583 ui_browser__handle_resize(&browser->b);
2584 return 0;
2585}
2586
2587static int
ea7cd592
NK
2588add_annotate_opt(struct hist_browser *browser __maybe_unused,
2589 struct popup_action *act, char **optstr,
2590 struct map *map, struct symbol *sym)
bc7cad42 2591{
ea7cd592
NK
2592 if (sym == NULL || map->dso->annotate_warned)
2593 return 0;
2594
2595 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2596 return 0;
2597
2598 act->ms.map = map;
2599 act->ms.sym = sym;
2600 act->fn = do_annotate;
2601 return 1;
2602}
2603
2604static int
2605do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2606{
2607 struct thread *thread = act->thread;
2608
7cecb7fe
JO
2609 if ((!hists__has(browser->hists, thread) &&
2610 !hists__has(browser->hists, comm)) || thread == NULL)
599a2f38
NK
2611 return 0;
2612
bc7cad42
NK
2613 if (browser->hists->thread_filter) {
2614 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2615 perf_hpp__set_elide(HISTC_THREAD, false);
2616 thread__zput(browser->hists->thread_filter);
2617 ui_helpline__pop();
2618 } else {
fa82911a 2619 if (hists__has(browser->hists, thread)) {
6962ccb3
NK
2620 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2621 thread->comm_set ? thread__comm_str(thread) : "",
2622 thread->tid);
2623 } else {
2624 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2625 thread->comm_set ? thread__comm_str(thread) : "");
2626 }
2627
bc7cad42
NK
2628 browser->hists->thread_filter = thread__get(thread);
2629 perf_hpp__set_elide(HISTC_THREAD, false);
2630 pstack__push(browser->pstack, &browser->hists->thread_filter);
2631 }
2632
2633 hists__filter_by_thread(browser->hists);
2634 hist_browser__reset(browser);
2635 return 0;
2636}
2637
2638static int
ea7cd592
NK
2639add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2640 char **optstr, struct thread *thread)
2641{
6962ccb3
NK
2642 int ret;
2643
7cecb7fe
JO
2644 if ((!hists__has(browser->hists, thread) &&
2645 !hists__has(browser->hists, comm)) || thread == NULL)
ea7cd592
NK
2646 return 0;
2647
fa82911a 2648 if (hists__has(browser->hists, thread)) {
6962ccb3
NK
2649 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2650 browser->hists->thread_filter ? "out of" : "into",
2651 thread->comm_set ? thread__comm_str(thread) : "",
2652 thread->tid);
2653 } else {
2654 ret = asprintf(optstr, "Zoom %s %s thread",
2655 browser->hists->thread_filter ? "out of" : "into",
2656 thread->comm_set ? thread__comm_str(thread) : "");
2657 }
2658 if (ret < 0)
ea7cd592
NK
2659 return 0;
2660
2661 act->thread = thread;
2662 act->fn = do_zoom_thread;
2663 return 1;
2664}
2665
2666static int
2667do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 2668{
045b80dd 2669 struct map *map = act->ms.map;
ea7cd592 2670
69849fc5 2671 if (!hists__has(browser->hists, dso) || map == NULL)
599a2f38
NK
2672 return 0;
2673
bc7cad42
NK
2674 if (browser->hists->dso_filter) {
2675 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2676 perf_hpp__set_elide(HISTC_DSO, false);
2677 browser->hists->dso_filter = NULL;
2678 ui_helpline__pop();
2679 } else {
7727a925 2680 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
045b80dd
ACM
2681 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2682 browser->hists->dso_filter = map->dso;
bc7cad42
NK
2683 perf_hpp__set_elide(HISTC_DSO, true);
2684 pstack__push(browser->pstack, &browser->hists->dso_filter);
2685 }
2686
2687 hists__filter_by_dso(browser->hists);
2688 hist_browser__reset(browser);
2689 return 0;
2690}
2691
2692static int
ea7cd592 2693add_dso_opt(struct hist_browser *browser, struct popup_action *act,
045b80dd 2694 char **optstr, struct map *map)
bc7cad42 2695{
69849fc5 2696 if (!hists__has(browser->hists, dso) || map == NULL)
ea7cd592
NK
2697 return 0;
2698
2699 if (asprintf(optstr, "Zoom %s %s DSO",
2700 browser->hists->dso_filter ? "out of" : "into",
045b80dd 2701 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
ea7cd592
NK
2702 return 0;
2703
045b80dd 2704 act->ms.map = map;
ea7cd592
NK
2705 act->fn = do_zoom_dso;
2706 return 1;
2707}
2708
2709static int
2710do_browse_map(struct hist_browser *browser __maybe_unused,
2711 struct popup_action *act)
2712{
2713 map__browse(act->ms.map);
bc7cad42
NK
2714 return 0;
2715}
2716
ea7cd592 2717static int
69849fc5 2718add_map_opt(struct hist_browser *browser,
ea7cd592
NK
2719 struct popup_action *act, char **optstr, struct map *map)
2720{
69849fc5 2721 if (!hists__has(browser->hists, dso) || map == NULL)
ea7cd592
NK
2722 return 0;
2723
2724 if (asprintf(optstr, "Browse map details") < 0)
2725 return 0;
2726
2727 act->ms.map = map;
2728 act->fn = do_browse_map;
2729 return 1;
2730}
2731
bc7cad42
NK
2732static int
2733do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 2734 struct popup_action *act)
bc7cad42
NK
2735{
2736 char script_opt[64];
2737 memset(script_opt, 0, sizeof(script_opt));
2738
ea7cd592 2739 if (act->thread) {
bc7cad42 2740 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
2741 thread__comm_str(act->thread));
2742 } else if (act->ms.sym) {
bc7cad42 2743 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 2744 act->ms.sym->name);
bc7cad42
NK
2745 }
2746
2747 script_browse(script_opt);
2748 return 0;
2749}
2750
2751static int
ea7cd592
NK
2752add_script_opt(struct hist_browser *browser __maybe_unused,
2753 struct popup_action *act, char **optstr,
2754 struct thread *thread, struct symbol *sym)
2755{
2756 if (thread) {
2757 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2758 thread__comm_str(thread)) < 0)
2759 return 0;
2760 } else if (sym) {
2761 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2762 sym->name) < 0)
2763 return 0;
2764 } else {
2765 if (asprintf(optstr, "Run scripts for all samples") < 0)
2766 return 0;
2767 }
2768
2769 act->thread = thread;
2770 act->ms.sym = sym;
2771 act->fn = do_run_script;
2772 return 1;
2773}
2774
2775static int
2776do_switch_data(struct hist_browser *browser __maybe_unused,
2777 struct popup_action *act __maybe_unused)
bc7cad42
NK
2778{
2779 if (switch_data_file()) {
2780 ui__warning("Won't switch the data files due to\n"
2781 "no valid data file get selected!\n");
ea7cd592 2782 return 0;
bc7cad42
NK
2783 }
2784
2785 return K_SWITCH_INPUT_DATA;
2786}
2787
ea7cd592
NK
2788static int
2789add_switch_opt(struct hist_browser *browser,
2790 struct popup_action *act, char **optstr)
2791{
2792 if (!is_report_browser(browser->hbt))
2793 return 0;
2794
2795 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2796 return 0;
2797
2798 act->fn = do_switch_data;
2799 return 1;
2800}
2801
2802static int
2803do_exit_browser(struct hist_browser *browser __maybe_unused,
2804 struct popup_action *act __maybe_unused)
2805{
2806 return 0;
2807}
2808
2809static int
2810add_exit_opt(struct hist_browser *browser __maybe_unused,
2811 struct popup_action *act, char **optstr)
2812{
2813 if (asprintf(optstr, "Exit") < 0)
2814 return 0;
2815
2816 act->fn = do_exit_browser;
2817 return 1;
2818}
2819
84734b06
KL
2820static int
2821do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2822{
35a634f7 2823 if (!hists__has(browser->hists, socket) || act->socket < 0)
599a2f38
NK
2824 return 0;
2825
84734b06
KL
2826 if (browser->hists->socket_filter > -1) {
2827 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2828 browser->hists->socket_filter = -1;
2829 perf_hpp__set_elide(HISTC_SOCKET, false);
2830 } else {
2831 browser->hists->socket_filter = act->socket;
2832 perf_hpp__set_elide(HISTC_SOCKET, true);
2833 pstack__push(browser->pstack, &browser->hists->socket_filter);
2834 }
2835
2836 hists__filter_by_socket(browser->hists);
2837 hist_browser__reset(browser);
2838 return 0;
2839}
2840
2841static int
2842add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2843 char **optstr, int socket_id)
2844{
35a634f7 2845 if (!hists__has(browser->hists, socket) || socket_id < 0)
84734b06
KL
2846 return 0;
2847
2848 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2849 (browser->hists->socket_filter > -1) ? "out of" : "into",
2850 socket_id) < 0)
2851 return 0;
2852
2853 act->socket = socket_id;
2854 act->fn = do_zoom_socket;
2855 return 1;
2856}
2857
112f761f 2858static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
2859{
2860 u64 nr_entries = 0;
2861 struct rb_node *nd = rb_first(&hb->hists->entries);
2862
f5b763fe 2863 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
268397cb
NK
2864 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2865 return;
2866 }
2867
14135663 2868 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 2869 nr_entries++;
f5b763fe 2870 nd = rb_hierarchy_next(nd);
064f1981
NK
2871 }
2872
112f761f 2873 hb->nr_non_filtered_entries = nr_entries;
f5b763fe 2874 hb->nr_hierarchy_entries = nr_entries;
064f1981 2875}
341487ab 2876
b62e8dfc
NK
2877static void hist_browser__update_percent_limit(struct hist_browser *hb,
2878 double percent)
2879{
2880 struct hist_entry *he;
2881 struct rb_node *nd = rb_first(&hb->hists->entries);
2882 u64 total = hists__total_period(hb->hists);
2883 u64 min_callchain_hits = total * (percent / 100);
2884
2885 hb->min_pcnt = callchain_param.min_percent = percent;
2886
b62e8dfc
NK
2887 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2888 he = rb_entry(nd, struct hist_entry, rb_node);
2889
79dded87
NK
2890 if (he->has_no_entry) {
2891 he->has_no_entry = false;
2892 he->nr_rows = 0;
2893 }
2894
d0506edb
NK
2895 if (!he->leaf || !symbol_conf.use_callchain)
2896 goto next;
2897
b62e8dfc
NK
2898 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2899 total = he->stat.period;
2900
2901 if (symbol_conf.cumulate_callchain)
2902 total = he->stat_acc->period;
2903
2904 min_callchain_hits = total * (percent / 100);
2905 }
2906
2907 callchain_param.sort(&he->sorted_chain, he->callchain,
2908 min_callchain_hits, &callchain_param);
2909
d0506edb 2910next:
201fde73 2911 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
d0506edb 2912
b62e8dfc
NK
2913 /* force to re-evaluate folding state of callchains */
2914 he->init_have_children = false;
492b1010 2915 hist_entry__set_folding(he, hb, false);
b62e8dfc
NK
2916 }
2917}
2918
34958544 2919static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 2920 const char *helpline,
81cce8de 2921 bool left_exits,
68d80758 2922 struct hist_browser_timer *hbt,
064f1981 2923 float min_pcnt,
ce80d3be 2924 struct perf_env *env)
d1b4f249 2925{
4ea062ed 2926 struct hists *hists = evsel__hists(evsel);
a6ec894d 2927 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
a68c2c58 2928 struct branch_info *bi;
f2b487db
NK
2929#define MAX_OPTIONS 16
2930 char *options[MAX_OPTIONS];
ea7cd592 2931 struct popup_action actions[MAX_OPTIONS];
24bff2dc 2932 int nr_options = 0;
d1b4f249 2933 int key = -1;
938a23ae 2934 char buf[64];
9783adf7 2935 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 2936
e8e684a5
NK
2937#define HIST_BROWSER_HELP_COMMON \
2938 "h/?/F1 Show this window\n" \
2939 "UP/DOWN/PGUP\n" \
2940 "PGDN/SPACE Navigate\n" \
2941 "q/ESC/CTRL+C Exit browser\n\n" \
2942 "For multiple event sessions:\n\n" \
2943 "TAB/UNTAB Switch events\n\n" \
2944 "For symbolic views (--sort has sym):\n\n" \
7727a925
ACM
2945 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2946 "ESC Zoom out\n" \
e8e684a5
NK
2947 "a Annotate current symbol\n" \
2948 "C Collapse all callchains\n" \
2949 "d Zoom into current DSO\n" \
2950 "E Expand all callchains\n" \
105eb30f 2951 "F Toggle percentage of filtered entries\n" \
025bf7ea 2952 "H Display column headers\n" \
b62e8dfc 2953 "L Change percent limit\n" \
31eb4360 2954 "m Display context menu\n" \
84734b06 2955 "S Zoom into current Processor Socket\n" \
e8e684a5
NK
2956
2957 /* help messages are sorted by lexical order of the hotkey */
2958 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 2959 "i Show header information\n"
e8e684a5
NK
2960 "P Print histograms to perf.hist.N\n"
2961 "r Run available scripts\n"
2962 "s Switch to another data file in PWD\n"
2963 "t Zoom into current Thread\n"
2964 "V Verbose (DSO names in callchains, etc)\n"
2965 "/ Filter symbol by name";
2966 const char top_help[] = HIST_BROWSER_HELP_COMMON
2967 "P Print histograms to perf.hist.N\n"
2968 "t Zoom into current Thread\n"
2969 "V Verbose (DSO names in callchains, etc)\n"
42337a22 2970 "z Toggle zeroing of samples\n"
fbb7997e 2971 "f Enable/Disable events\n"
e8e684a5
NK
2972 "/ Filter symbol by name";
2973
d1b4f249
ACM
2974 if (browser == NULL)
2975 return -1;
2976
ed426915
NK
2977 /* reset abort key so that it can get Ctrl-C as a key */
2978 SLang_reset_tty();
2979 SLang_init_tty(0, 0, 0);
2980
03905048 2981 if (min_pcnt)
064f1981 2982 browser->min_pcnt = min_pcnt;
03905048 2983 hist_browser__update_nr_entries(browser);
064f1981 2984
84734b06 2985 browser->pstack = pstack__new(3);
01f00a1c 2986 if (browser->pstack == NULL)
d1b4f249
ACM
2987 goto out;
2988
2989 ui_helpline__push(helpline);
2990
24bff2dc 2991 memset(options, 0, sizeof(options));
ea7cd592 2992 memset(actions, 0, sizeof(actions));
24bff2dc 2993
5b591669
NK
2994 if (symbol_conf.col_width_list_str)
2995 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2996
d1b4f249 2997 while (1) {
f3b623b8 2998 struct thread *thread = NULL;
045b80dd 2999 struct map *map = NULL;
ea7cd592 3000 int choice = 0;
84734b06 3001 int socked_id = -1;
d1b4f249 3002
24bff2dc
SE
3003 nr_options = 0;
3004
5f00b0f4 3005 key = hist_browser__run(browser, helpline);
d1b4f249 3006
60098917
ACM
3007 if (browser->he_selection != NULL) {
3008 thread = hist_browser__selected_thread(browser);
045b80dd 3009 map = browser->selection->map;
84734b06 3010 socked_id = browser->he_selection->socket;
60098917 3011 }
b50e003d 3012 switch (key) {
cf958003
ACM
3013 case K_TAB:
3014 case K_UNTAB:
e4419b8e
DA
3015 if (nr_events == 1)
3016 continue;
b50e003d
ACM
3017 /*
3018 * Exit the browser, let hists__browser_tree
3019 * go to the next or previous
3020 */
3021 goto out_free_stack;
3022 case 'a':
2e0453af 3023 if (!hists__has(hists, sym)) {
7b27509f 3024 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 3025 "Annotation is only available for symbolic views, "
a68c2c58 3026 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
3027 continue;
3028 }
3029
60098917 3030 if (browser->selection == NULL ||
db9a9cbc 3031 browser->selection->sym == NULL ||
b50e003d 3032 browser->selection->map->dso->annotate_warned)
d1b4f249 3033 continue;
bc7cad42 3034
ea7cd592
NK
3035 actions->ms.map = browser->selection->map;
3036 actions->ms.sym = browser->selection->sym;
3037 do_annotate(browser, actions);
bc7cad42 3038 continue;
aff3f3f6
ACM
3039 case 'P':
3040 hist_browser__dump(browser);
3041 continue;
b50e003d 3042 case 'd':
fae00650 3043 actions->ms.map = map;
ea7cd592 3044 do_zoom_dso(browser, actions);
bc7cad42 3045 continue;
a7cb8863 3046 case 'V':
21e8c810
AB
3047 verbose = (verbose + 1) % 4;
3048 browser->show_dso = verbose > 0;
3049 ui_helpline__fpush("Verbosity level set to %d\n",
3050 verbose);
a7cb8863 3051 continue;
b50e003d 3052 case 't':
ea7cd592
NK
3053 actions->thread = thread;
3054 do_zoom_thread(browser, actions);
bc7cad42 3055 continue;
84734b06
KL
3056 case 'S':
3057 actions->socket = socked_id;
3058 do_zoom_socket(browser, actions);
3059 continue;
5a5626b1 3060 case '/':
938a23ae 3061 if (ui_browser__input_window("Symbol to show",
4aa8e454
ACM
3062 "Please enter the name of symbol you want to see.\n"
3063 "To remove the filter later, press / + ENTER.",
938a23ae
NK
3064 buf, "ENTER: OK, ESC: Cancel",
3065 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
3066 hists->symbol_filter_str = *buf ? buf : NULL;
3067 hists__filter_by_symbol(hists);
938a23ae
NK
3068 hist_browser__reset(browser);
3069 }
3070 continue;
cdbab7c2 3071 case 'r':
ea7cd592
NK
3072 if (is_report_browser(hbt)) {
3073 actions->thread = NULL;
3074 actions->ms.sym = NULL;
3075 do_run_script(browser, actions);
3076 }
c77d8d70 3077 continue;
341487ab 3078 case 's':
bc7cad42 3079 if (is_report_browser(hbt)) {
ea7cd592 3080 key = do_switch_data(browser, actions);
bc7cad42
NK
3081 if (key == K_SWITCH_INPUT_DATA)
3082 goto out_free_stack;
3083 }
341487ab 3084 continue;
6dd60135
NK
3085 case 'i':
3086 /* env->arch is NULL for live-mode (i.e. perf top) */
3087 if (env->arch)
3088 tui__header_window(env);
3089 continue;
105eb30f
NK
3090 case 'F':
3091 symbol_conf.filter_relative ^= 1;
3092 continue;
42337a22
NK
3093 case 'z':
3094 if (!is_report_browser(hbt)) {
3095 struct perf_top *top = hbt->arg;
3096
3097 top->zero = !top->zero;
3098 }
3099 continue;
b62e8dfc
NK
3100 case 'L':
3101 if (ui_browser__input_window("Percent Limit",
3102 "Please enter the value you want to hide entries under that percent.",
3103 buf, "ENTER: OK, ESC: Cancel",
3104 delay_secs * 2) == K_ENTER) {
3105 char *end;
3106 double new_percent = strtod(buf, &end);
3107
3108 if (new_percent < 0 || new_percent > 100) {
3109 ui_browser__warning(&browser->b, delay_secs * 2,
3110 "Invalid percent: %.2f", new_percent);
3111 continue;
3112 }
3113
3114 hist_browser__update_percent_limit(browser, new_percent);
3115 hist_browser__reset(browser);
3116 }
3117 continue;
cf958003 3118 case K_F1:
b50e003d
ACM
3119 case 'h':
3120 case '?':
4610e413 3121 ui_browser__help_window(&browser->b,
e8e684a5 3122 is_report_browser(hbt) ? report_help : top_help);
b50e003d 3123 continue;
cf958003
ACM
3124 case K_ENTER:
3125 case K_RIGHT:
31eb4360 3126 case 'm':
b50e003d
ACM
3127 /* menu */
3128 break;
63ab1749 3129 case K_ESC:
cf958003 3130 case K_LEFT: {
b50e003d 3131 const void *top;
d1b4f249 3132
01f00a1c 3133 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
3134 /*
3135 * Go back to the perf_evsel_menu__run or other user
3136 */
3137 if (left_exits)
3138 goto out_free_stack;
63ab1749
ACM
3139
3140 if (key == K_ESC &&
3141 ui_browser__dialog_yesno(&browser->b,
3142 "Do you really want to exit?"))
3143 goto out_free_stack;
3144
d1b4f249 3145 continue;
7f0030b2 3146 }
6422184b 3147 top = pstack__peek(browser->pstack);
bc7cad42 3148 if (top == &browser->hists->dso_filter) {
6422184b
NK
3149 /*
3150 * No need to set actions->dso here since
3151 * it's just to remove the current filter.
3152 * Ditto for thread below.
3153 */
3154 do_zoom_dso(browser, actions);
84734b06 3155 } else if (top == &browser->hists->thread_filter) {
6422184b 3156 do_zoom_thread(browser, actions);
84734b06
KL
3157 } else if (top == &browser->hists->socket_filter) {
3158 do_zoom_socket(browser, actions);
3159 }
b50e003d
ACM
3160 continue;
3161 }
ed7e5662
ACM
3162 case 'q':
3163 case CTRL('c'):
516e5368 3164 goto out_free_stack;
fbb7997e 3165 case 'f':
13d1e536
NK
3166 if (!is_report_browser(hbt)) {
3167 struct perf_top *top = hbt->arg;
3168
3169 perf_evlist__toggle_enable(top->evlist);
3170 /*
3171 * No need to refresh, resort/decay histogram
3172 * entries if we are not collecting samples:
3173 */
3174 if (top->evlist->enabled) {
3175 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3176 hbt->refresh = delay_secs;
3177 } else {
3178 helpline = "Press 'f' again to re-enable the events";
3179 hbt->refresh = 0;
3180 }
3181 continue;
3182 }
3e323dc0 3183 /* Fall thru */
ed7e5662 3184 default:
3e323dc0 3185 helpline = "Press '?' for help on key bindings";
ed7e5662 3186 continue;
d1b4f249
ACM
3187 }
3188
2e0453af 3189 if (!hists__has(hists, sym) || browser->selection == NULL)
0ba332f7
ACM
3190 goto skip_annotation;
3191
55369fc1 3192 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 3193 bi = browser->he_selection->branch_info;
0ba332f7
ACM
3194
3195 if (bi == NULL)
3196 goto skip_annotation;
3197
ea7cd592
NK
3198 nr_options += add_annotate_opt(browser,
3199 &actions[nr_options],
3200 &options[nr_options],
3201 bi->from.map,
3202 bi->from.sym);
3203 if (bi->to.sym != bi->from.sym)
3204 nr_options += add_annotate_opt(browser,
3205 &actions[nr_options],
3206 &options[nr_options],
3207 bi->to.map,
3208 bi->to.sym);
a68c2c58 3209 } else {
ea7cd592
NK
3210 nr_options += add_annotate_opt(browser,
3211 &actions[nr_options],
3212 &options[nr_options],
3213 browser->selection->map,
3214 browser->selection->sym);
a68c2c58 3215 }
0ba332f7 3216skip_annotation:
ea7cd592
NK
3217 nr_options += add_thread_opt(browser, &actions[nr_options],
3218 &options[nr_options], thread);
3219 nr_options += add_dso_opt(browser, &actions[nr_options],
045b80dd 3220 &options[nr_options], map);
ea7cd592
NK
3221 nr_options += add_map_opt(browser, &actions[nr_options],
3222 &options[nr_options],
bd315aab
WN
3223 browser->selection ?
3224 browser->selection->map : NULL);
84734b06
KL
3225 nr_options += add_socket_opt(browser, &actions[nr_options],
3226 &options[nr_options],
3227 socked_id);
cdbab7c2 3228 /* perf script support */
b1baae89
NK
3229 if (!is_report_browser(hbt))
3230 goto skip_scripting;
3231
cdbab7c2 3232 if (browser->he_selection) {
fa82911a 3233 if (hists__has(hists, thread) && thread) {
2eafd410
NK
3234 nr_options += add_script_opt(browser,
3235 &actions[nr_options],
3236 &options[nr_options],
3237 thread, NULL);
3238 }
bd315aab
WN
3239 /*
3240 * Note that browser->selection != NULL
3241 * when browser->he_selection is not NULL,
3242 * so we don't need to check browser->selection
3243 * before fetching browser->selection->sym like what
3244 * we do before fetching browser->selection->map.
3245 *
3246 * See hist_browser__show_entry.
3247 */
2e0453af 3248 if (hists__has(hists, sym) && browser->selection->sym) {
c221acb0
NK
3249 nr_options += add_script_opt(browser,
3250 &actions[nr_options],
3251 &options[nr_options],
3252 NULL, browser->selection->sym);
3253 }
cdbab7c2 3254 }
ea7cd592
NK
3255 nr_options += add_script_opt(browser, &actions[nr_options],
3256 &options[nr_options], NULL, NULL);
3257 nr_options += add_switch_opt(browser, &actions[nr_options],
3258 &options[nr_options]);
b1baae89 3259skip_scripting:
ea7cd592
NK
3260 nr_options += add_exit_opt(browser, &actions[nr_options],
3261 &options[nr_options]);
d1b4f249 3262
ea7cd592
NK
3263 do {
3264 struct popup_action *act;
68d80758 3265
ea7cd592
NK
3266 choice = ui__popup_menu(nr_options, options);
3267 if (choice == -1 || choice >= nr_options)
3268 break;
a68c2c58 3269
ea7cd592
NK
3270 act = &actions[choice];
3271 key = act->fn(browser, act);
3272 } while (key == 1);
a68c2c58 3273
ea7cd592
NK
3274 if (key == K_SWITCH_INPUT_DATA)
3275 break;
d1b4f249
ACM
3276 }
3277out_free_stack:
01f00a1c 3278 pstack__delete(browser->pstack);
d1b4f249
ACM
3279out:
3280 hist_browser__delete(browser);
f2b487db 3281 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
3282 return key;
3283}
3284
7f0030b2
ACM
3285struct perf_evsel_menu {
3286 struct ui_browser b;
3287 struct perf_evsel *selection;
7b27509f 3288 bool lost_events, lost_events_warned;
064f1981 3289 float min_pcnt;
ce80d3be 3290 struct perf_env *env;
7f0030b2
ACM
3291};
3292
3293static void perf_evsel_menu__write(struct ui_browser *browser,
3294 void *entry, int row)
3295{
3296 struct perf_evsel_menu *menu = container_of(browser,
3297 struct perf_evsel_menu, b);
3298 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 3299 struct hists *hists = evsel__hists(evsel);
7f0030b2 3300 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 3301 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 3302 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 3303 char bf[256], unit;
7b27509f
ACM
3304 const char *warn = " ";
3305 size_t printed;
7f0030b2
ACM
3306
3307 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3308 HE_COLORSET_NORMAL);
3309
759ff497 3310 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
3311 struct perf_evsel *pos;
3312
3313 ev_name = perf_evsel__group_name(evsel);
3314
3315 for_each_group_member(pos, evsel) {
4ea062ed
ACM
3316 struct hists *pos_hists = evsel__hists(pos);
3317 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
3318 }
3319 }
3320
7f0030b2 3321 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 3322 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f 3323 unit, unit == ' ' ? "" : " ", ev_name);
517dfdb3 3324 ui_browser__printf(browser, "%s", bf);
7b27509f 3325
4ea062ed 3326 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
3327 if (nr_events != 0) {
3328 menu->lost_events = true;
3329 if (!current_entry)
3330 ui_browser__set_color(browser, HE_COLORSET_TOP);
3331 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
3332 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3333 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
3334 warn = bf;
3335 }
3336
26270a00 3337 ui_browser__write_nstring(browser, warn, browser->width - printed);
7f0030b2
ACM
3338
3339 if (current_entry)
3340 menu->selection = evsel;
3341}
3342
34958544
ACM
3343static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3344 int nr_events, const char *help,
9783adf7 3345 struct hist_browser_timer *hbt)
d1b4f249 3346{
7f0030b2 3347 struct perf_evlist *evlist = menu->b.priv;
e248de33 3348 struct perf_evsel *pos;
dd00d486 3349 const char *title = "Available samples";
9783adf7 3350 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 3351 int key;
d1b4f249 3352
7f0030b2
ACM
3353 if (ui_browser__show(&menu->b, title,
3354 "ESC: exit, ENTER|->: Browse histograms") < 0)
3355 return -1;
3356
7f0030b2 3357 while (1) {
3af6e338 3358 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
3359
3360 switch (key) {
cf958003 3361 case K_TIMER:
9783adf7 3362 hbt->timer(hbt->arg);
7b27509f
ACM
3363
3364 if (!menu->lost_events_warned && menu->lost_events) {
3365 ui_browser__warn_lost_events(&menu->b);
3366 menu->lost_events_warned = true;
3367 }
81cce8de 3368 continue;
cf958003
ACM
3369 case K_RIGHT:
3370 case K_ENTER:
7f0030b2
ACM
3371 if (!menu->selection)
3372 continue;
3373 pos = menu->selection;
3374browse_hists:
18eaf0b8
ACM
3375 perf_evlist__set_selected(evlist, pos);
3376 /*
3377 * Give the calling tool a chance to populate the non
3378 * default evsel resorted hists tree.
3379 */
9783adf7
NK
3380 if (hbt)
3381 hbt->timer(hbt->arg);
34958544 3382 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 3383 true, hbt,
064f1981 3384 menu->min_pcnt,
68d80758 3385 menu->env);
7f0030b2 3386 ui_browser__show_title(&menu->b, title);
18eaf0b8 3387 switch (key) {
cf958003 3388 case K_TAB:
18eaf0b8 3389 if (pos->node.next == &evlist->entries)
9a354cdc 3390 pos = perf_evlist__first(evlist);
18eaf0b8 3391 else
9a354cdc 3392 pos = perf_evsel__next(pos);
18eaf0b8 3393 goto browse_hists;
cf958003 3394 case K_UNTAB:
18eaf0b8 3395 if (pos->node.prev == &evlist->entries)
9a354cdc 3396 pos = perf_evlist__last(evlist);
18eaf0b8 3397 else
d87fcb4a 3398 pos = perf_evsel__prev(pos);
18eaf0b8 3399 goto browse_hists;
341487ab 3400 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
3401 case 'q':
3402 case CTRL('c'):
3403 goto out;
63ab1749 3404 case K_ESC:
18eaf0b8
ACM
3405 default:
3406 continue;
3407 }
cf958003 3408 case K_LEFT:
7f0030b2 3409 continue;
cf958003 3410 case K_ESC:
4610e413
ACM
3411 if (!ui_browser__dialog_yesno(&menu->b,
3412 "Do you really want to exit?"))
ed7e5662
ACM
3413 continue;
3414 /* Fall thru */
7f0030b2
ACM
3415 case 'q':
3416 case CTRL('c'):
3417 goto out;
d1b4f249 3418 default:
18eaf0b8 3419 continue;
d1b4f249
ACM
3420 }
3421 }
3422
7f0030b2
ACM
3423out:
3424 ui_browser__hide(&menu->b);
3425 return key;
3426}
3427
316c7136 3428static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
3429 void *entry)
3430{
3431 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3432
3433 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3434 return true;
3435
3436 return false;
3437}
3438
7f0030b2 3439static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 3440 int nr_entries, const char *help,
68d80758 3441 struct hist_browser_timer *hbt,
064f1981 3442 float min_pcnt,
ce80d3be 3443 struct perf_env *env)
7f0030b2
ACM
3444{
3445 struct perf_evsel *pos;
3446 struct perf_evsel_menu menu = {
3447 .b = {
3448 .entries = &evlist->entries,
3449 .refresh = ui_browser__list_head_refresh,
3450 .seek = ui_browser__list_head_seek,
3451 .write = perf_evsel_menu__write,
fc24d7c2
NK
3452 .filter = filter_group_entries,
3453 .nr_entries = nr_entries,
7f0030b2
ACM
3454 .priv = evlist,
3455 },
064f1981 3456 .min_pcnt = min_pcnt,
68d80758 3457 .env = env,
7f0030b2
ACM
3458 };
3459
3460 ui_helpline__push("Press ESC to exit");
3461
e5cadb93 3462 evlist__for_each_entry(evlist, pos) {
7289f83c 3463 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
3464 size_t line_len = strlen(ev_name) + 7;
3465
3466 if (menu.b.width < line_len)
3467 menu.b.width = line_len;
7f0030b2
ACM
3468 }
3469
fc24d7c2 3470 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
3471}
3472
81cce8de 3473int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 3474 struct hist_browser_timer *hbt,
064f1981 3475 float min_pcnt,
ce80d3be 3476 struct perf_env *env)
7f0030b2 3477{
fc24d7c2
NK
3478 int nr_entries = evlist->nr_entries;
3479
3480single_entry:
3481 if (nr_entries == 1) {
9a354cdc 3482 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
3483
3484 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 3485 false, hbt, min_pcnt,
064f1981 3486 env);
7f0030b2
ACM
3487 }
3488
fc24d7c2
NK
3489 if (symbol_conf.event_group) {
3490 struct perf_evsel *pos;
3491
3492 nr_entries = 0;
e5cadb93 3493 evlist__for_each_entry(evlist, pos) {
fc24d7c2
NK
3494 if (perf_evsel__is_group_leader(pos))
3495 nr_entries++;
0050f7aa 3496 }
fc24d7c2
NK
3497
3498 if (nr_entries == 1)
3499 goto single_entry;
3500 }
3501
3502 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 3503 hbt, min_pcnt, env);
d1b4f249 3504}