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