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