perf top: Filter symbols based on __map__is_kernel(map)
[linux-2.6-block.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 };
d1b4f249 787
ca3ff33b 788 hist_browser__gotorc(browser, row, 0);
c172f742 789
1240005e 790 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
791 if (perf_hpp__should_skip(fmt))
792 continue;
793
fb821c9e
NK
794 if (current_entry && browser->b.navkeypressed) {
795 ui_browser__set_color(&browser->b,
796 HE_COLORSET_SELECTED);
797 } else {
798 ui_browser__set_color(&browser->b,
799 HE_COLORSET_NORMAL);
800 }
801
802 if (first) {
803 if (symbol_conf.use_callchain) {
517dfdb3 804 ui_browser__printf(&browser->b, "%c ", folded_sign);
fb821c9e
NK
805 width -= 2;
806 }
807 first = false;
808 } else {
517dfdb3 809 ui_browser__printf(&browser->b, " ");
f5951d56
NK
810 width -= 2;
811 }
c172f742 812
1240005e 813 if (fmt->color) {
2c5d4b4a 814 width -= fmt->color(fmt, &hpp, entry);
f5951d56 815 } else {
2c5d4b4a 816 width -= fmt->entry(fmt, &hpp, entry);
517dfdb3 817 ui_browser__printf(&browser->b, "%s", s);
f5951d56 818 }
2cf9cebf
ACM
819 }
820
f5951d56
NK
821 /* The scroll bar isn't being used */
822 if (!browser->b.navkeypressed)
823 width += 1;
824
26270a00 825 ui_browser__write_nstring(&browser->b, "", width);
26d8b338 826
d1b4f249
ACM
827 ++row;
828 ++printed;
829 } else
830 --row_offset;
831
62c95ae3 832 if (folded_sign == '-' && row != browser->b.rows) {
c09a7e75 833 u64 total = hists__total_period(entry->hists);
39ee533f
NK
834 struct callchain_print_arg arg = {
835 .row_offset = row_offset,
836 .is_current_entry = current_entry,
837 };
c09a7e75 838
4087d11c
NK
839 if (callchain_param.mode == CHAIN_GRAPH_REL) {
840 if (symbol_conf.cumulate_callchain)
841 total = entry->stat_acc->period;
842 else
843 total = entry->stat.period;
844 }
845
c09a7e75 846 printed += hist_browser__show_callchain(browser,
39ee533f
NK
847 &entry->sorted_chain, 1, row, total,
848 hist_browser__show_callchain_entry, &arg,
849 hist_browser__check_output_full);
c09a7e75 850
39ee533f 851 if (arg.is_current_entry)
05e8b080 852 browser->he_selection = entry;
d1b4f249
ACM
853 }
854
855 return printed;
856}
857
81a888fe
JO
858static int advance_hpp_check(struct perf_hpp *hpp, int inc)
859{
860 advance_hpp(hpp, inc);
861 return hpp->size <= 0;
862}
863
864static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
865{
866 struct perf_hpp dummy_hpp = {
867 .buf = buf,
868 .size = size,
869 };
870 struct perf_hpp_fmt *fmt;
871 size_t ret = 0;
872
873 if (symbol_conf.use_callchain) {
874 ret = scnprintf(buf, size, " ");
875 if (advance_hpp_check(&dummy_hpp, ret))
876 return ret;
877 }
878
879 perf_hpp__for_each_format(fmt) {
880 if (perf_hpp__should_skip(fmt))
881 continue;
882
81a888fe
JO
883 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
884 if (advance_hpp_check(&dummy_hpp, ret))
885 break;
886
887 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
888 if (advance_hpp_check(&dummy_hpp, ret))
889 break;
890 }
891
892 return ret;
893}
894
025bf7ea
ACM
895static void hist_browser__show_headers(struct hist_browser *browser)
896{
81a888fe
JO
897 char headers[1024];
898
899 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
025bf7ea
ACM
900 ui_browser__gotorc(&browser->b, 0, 0);
901 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
26270a00 902 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
025bf7ea
ACM
903}
904
437cfe7a
ACM
905static void ui_browser__hists_init_top(struct ui_browser *browser)
906{
907 if (browser->top == NULL) {
908 struct hist_browser *hb;
909
910 hb = container_of(browser, struct hist_browser, b);
911 browser->top = rb_first(&hb->hists->entries);
912 }
913}
914
05e8b080 915static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
916{
917 unsigned row = 0;
025bf7ea 918 u16 header_offset = 0;
d1b4f249 919 struct rb_node *nd;
05e8b080 920 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 921
025bf7ea
ACM
922 if (hb->show_headers) {
923 hist_browser__show_headers(hb);
924 header_offset = 1;
925 }
926
05e8b080 927 ui_browser__hists_init_top(browser);
d1b4f249 928
05e8b080 929 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249 930 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 931 float percent;
d1b4f249
ACM
932
933 if (h->filtered)
934 continue;
935
14135663 936 percent = hist_entry__get_percent_limit(h);
064f1981
NK
937 if (percent < hb->min_pcnt)
938 continue;
939
d1b4f249 940 row += hist_browser__show_entry(hb, h, row);
62c95ae3 941 if (row == browser->rows)
d1b4f249
ACM
942 break;
943 }
944
025bf7ea 945 return row + header_offset;
d1b4f249
ACM
946}
947
064f1981 948static struct rb_node *hists__filter_entries(struct rb_node *nd,
064f1981 949 float min_pcnt)
d1b4f249
ACM
950{
951 while (nd != NULL) {
952 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 953 float percent = hist_entry__get_percent_limit(h);
064f1981 954
c0f1527b 955 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
956 return nd;
957
958 nd = rb_next(nd);
959 }
960
961 return NULL;
962}
963
064f1981 964static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
064f1981 965 float min_pcnt)
d1b4f249
ACM
966{
967 while (nd != NULL) {
968 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
14135663 969 float percent = hist_entry__get_percent_limit(h);
064f1981
NK
970
971 if (!h->filtered && percent >= min_pcnt)
d1b4f249
ACM
972 return nd;
973
974 nd = rb_prev(nd);
975 }
976
977 return NULL;
978}
979
05e8b080 980static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
981 off_t offset, int whence)
982{
983 struct hist_entry *h;
984 struct rb_node *nd;
985 bool first = true;
064f1981
NK
986 struct hist_browser *hb;
987
988 hb = container_of(browser, struct hist_browser, b);
d1b4f249 989
05e8b080 990 if (browser->nr_entries == 0)
60098917
ACM
991 return;
992
05e8b080 993 ui_browser__hists_init_top(browser);
437cfe7a 994
d1b4f249
ACM
995 switch (whence) {
996 case SEEK_SET:
064f1981 997 nd = hists__filter_entries(rb_first(browser->entries),
14135663 998 hb->min_pcnt);
d1b4f249
ACM
999 break;
1000 case SEEK_CUR:
05e8b080 1001 nd = browser->top;
d1b4f249
ACM
1002 goto do_offset;
1003 case SEEK_END:
064f1981 1004 nd = hists__filter_prev_entries(rb_last(browser->entries),
14135663 1005 hb->min_pcnt);
d1b4f249
ACM
1006 first = false;
1007 break;
1008 default:
1009 return;
1010 }
1011
1012 /*
1013 * Moves not relative to the first visible entry invalidates its
1014 * row_offset:
1015 */
05e8b080 1016 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
1017 h->row_offset = 0;
1018
1019 /*
1020 * Here we have to check if nd is expanded (+), if it is we can't go
1021 * the next top level hist_entry, instead we must compute an offset of
1022 * what _not_ to show and not change the first visible entry.
1023 *
1024 * This offset increments when we are going from top to bottom and
1025 * decreases when we're going from bottom to top.
1026 *
1027 * As we don't have backpointers to the top level in the callchains
1028 * structure, we need to always print the whole hist_entry callchain,
1029 * skipping the first ones that are before the first visible entry
1030 * and stop when we printed enough lines to fill the screen.
1031 */
1032do_offset:
1033 if (offset > 0) {
1034 do {
1035 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1036 if (h->unfolded) {
d1b4f249
ACM
1037 u16 remaining = h->nr_rows - h->row_offset;
1038 if (offset > remaining) {
1039 offset -= remaining;
1040 h->row_offset = 0;
1041 } else {
1042 h->row_offset += offset;
1043 offset = 0;
05e8b080 1044 browser->top = nd;
d1b4f249
ACM
1045 break;
1046 }
1047 }
14135663 1048 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
d1b4f249
ACM
1049 if (nd == NULL)
1050 break;
1051 --offset;
05e8b080 1052 browser->top = nd;
d1b4f249
ACM
1053 } while (offset != 0);
1054 } else if (offset < 0) {
1055 while (1) {
1056 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1057 if (h->unfolded) {
d1b4f249
ACM
1058 if (first) {
1059 if (-offset > h->row_offset) {
1060 offset += h->row_offset;
1061 h->row_offset = 0;
1062 } else {
1063 h->row_offset += offset;
1064 offset = 0;
05e8b080 1065 browser->top = nd;
d1b4f249
ACM
1066 break;
1067 }
1068 } else {
1069 if (-offset > h->nr_rows) {
1070 offset += h->nr_rows;
1071 h->row_offset = 0;
1072 } else {
1073 h->row_offset = h->nr_rows + offset;
1074 offset = 0;
05e8b080 1075 browser->top = nd;
d1b4f249
ACM
1076 break;
1077 }
1078 }
1079 }
1080
14135663 1081 nd = hists__filter_prev_entries(rb_prev(nd),
064f1981 1082 hb->min_pcnt);
d1b4f249
ACM
1083 if (nd == NULL)
1084 break;
1085 ++offset;
05e8b080 1086 browser->top = nd;
d1b4f249
ACM
1087 if (offset == 0) {
1088 /*
1089 * Last unfiltered hist_entry, check if it is
1090 * unfolded, if it is then we should have
1091 * row_offset at its last entry.
1092 */
1093 h = rb_entry(nd, struct hist_entry, rb_node);
3698dab1 1094 if (h->unfolded)
d1b4f249
ACM
1095 h->row_offset = h->nr_rows;
1096 break;
1097 }
1098 first = false;
1099 }
1100 } else {
05e8b080 1101 browser->top = nd;
d1b4f249
ACM
1102 h = rb_entry(nd, struct hist_entry, rb_node);
1103 h->row_offset = 0;
1104 }
1105}
1106
aff3f3f6 1107static int hist_browser__fprintf_callchain(struct hist_browser *browser,
39ee533f 1108 struct hist_entry *he, FILE *fp)
aff3f3f6 1109{
39ee533f
NK
1110 u64 total = hists__total_period(he->hists);
1111 struct callchain_print_arg arg = {
1112 .fp = fp,
1113 };
aff3f3f6 1114
39ee533f
NK
1115 if (symbol_conf.cumulate_callchain)
1116 total = he->stat_acc->period;
aff3f3f6 1117
39ee533f
NK
1118 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1119 hist_browser__fprintf_callchain_entry, &arg,
1120 hist_browser__check_dump_full);
1121 return arg.printed;
aff3f3f6
ACM
1122}
1123
1124static int hist_browser__fprintf_entry(struct hist_browser *browser,
1125 struct hist_entry *he, FILE *fp)
1126{
1127 char s[8192];
aff3f3f6
ACM
1128 int printed = 0;
1129 char folded_sign = ' ';
26d8b338
NK
1130 struct perf_hpp hpp = {
1131 .buf = s,
1132 .size = sizeof(s),
1133 };
1134 struct perf_hpp_fmt *fmt;
1135 bool first = true;
1136 int ret;
aff3f3f6
ACM
1137
1138 if (symbol_conf.use_callchain)
1139 folded_sign = hist_entry__folded(he);
1140
aff3f3f6
ACM
1141 if (symbol_conf.use_callchain)
1142 printed += fprintf(fp, "%c ", folded_sign);
1143
26d8b338 1144 perf_hpp__for_each_format(fmt) {
e67d49a7
NK
1145 if (perf_hpp__should_skip(fmt))
1146 continue;
1147
26d8b338
NK
1148 if (!first) {
1149 ret = scnprintf(hpp.buf, hpp.size, " ");
1150 advance_hpp(&hpp, ret);
1151 } else
1152 first = false;
aff3f3f6 1153
26d8b338
NK
1154 ret = fmt->entry(fmt, &hpp, he);
1155 advance_hpp(&hpp, ret);
1156 }
aff3f3f6
ACM
1157 printed += fprintf(fp, "%s\n", rtrim(s));
1158
1159 if (folded_sign == '-')
39ee533f 1160 printed += hist_browser__fprintf_callchain(browser, he, fp);
aff3f3f6
ACM
1161
1162 return printed;
1163}
1164
1165static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1166{
064f1981 1167 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
064f1981 1168 browser->min_pcnt);
aff3f3f6
ACM
1169 int printed = 0;
1170
1171 while (nd) {
1172 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1173
1174 printed += hist_browser__fprintf_entry(browser, h, fp);
14135663 1175 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
aff3f3f6
ACM
1176 }
1177
1178 return printed;
1179}
1180
1181static int hist_browser__dump(struct hist_browser *browser)
1182{
1183 char filename[64];
1184 FILE *fp;
1185
1186 while (1) {
1187 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1188 if (access(filename, F_OK))
1189 break;
1190 /*
1191 * XXX: Just an arbitrary lazy upper limit
1192 */
1193 if (++browser->print_seq == 8192) {
1194 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1195 return -1;
1196 }
1197 }
1198
1199 fp = fopen(filename, "w");
1200 if (fp == NULL) {
1201 char bf[64];
4cc49d4d
KS
1202 const char *err = strerror_r(errno, bf, sizeof(bf));
1203 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1204 return -1;
1205 }
1206
1207 ++browser->print_seq;
1208 hist_browser__fprintf(browser, fp);
1209 fclose(fp);
1210 ui_helpline__fpush("%s written!", filename);
1211
1212 return 0;
1213}
1214
c2a51ab8 1215static struct hist_browser *hist_browser__new(struct hists *hists,
b1a9ceef 1216 struct hist_browser_timer *hbt,
ce80d3be 1217 struct perf_env *env)
d1b4f249 1218{
05e8b080 1219 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1220
05e8b080
ACM
1221 if (browser) {
1222 browser->hists = hists;
1223 browser->b.refresh = hist_browser__refresh;
357cfff1 1224 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
05e8b080
ACM
1225 browser->b.seek = ui_browser__hists_seek;
1226 browser->b.use_navkeypressed = true;
c8302367 1227 browser->show_headers = symbol_conf.show_hist_headers;
c2a51ab8 1228 browser->hbt = hbt;
b1a9ceef 1229 browser->env = env;
d1b4f249
ACM
1230 }
1231
05e8b080 1232 return browser;
d1b4f249
ACM
1233}
1234
05e8b080 1235static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1236{
05e8b080 1237 free(browser);
d1b4f249
ACM
1238}
1239
05e8b080 1240static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1241{
05e8b080 1242 return browser->he_selection;
d1b4f249
ACM
1243}
1244
05e8b080 1245static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1246{
05e8b080 1247 return browser->he_selection->thread;
d1b4f249
ACM
1248}
1249
1e378ebd
TS
1250/* Check whether the browser is for 'top' or 'report' */
1251static inline bool is_report_browser(void *timer)
1252{
1253 return timer == NULL;
1254}
1255
1256static int hists__browser_title(struct hists *hists,
1257 struct hist_browser_timer *hbt,
1258 char *bf, size_t size)
d1b4f249 1259{
469917ce
ACM
1260 char unit;
1261 int printed;
05e8b080
ACM
1262 const struct dso *dso = hists->dso_filter;
1263 const struct thread *thread = hists->thread_filter;
84734b06 1264 int socket_id = hists->socket_filter;
05e8b080
ACM
1265 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1266 u64 nr_events = hists->stats.total_period;
717e263f 1267 struct perf_evsel *evsel = hists_to_evsel(hists);
dd00d486 1268 const char *ev_name = perf_evsel__name(evsel);
717e263f
NK
1269 char buf[512];
1270 size_t buflen = sizeof(buf);
9e207ddf
KL
1271 char ref[30] = " show reference callgraph, ";
1272 bool enable_ref = false;
717e263f 1273
f2148330
NK
1274 if (symbol_conf.filter_relative) {
1275 nr_samples = hists->stats.nr_non_filtered_samples;
1276 nr_events = hists->stats.total_non_filtered_period;
1277 }
1278
759ff497 1279 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
1280 struct perf_evsel *pos;
1281
1282 perf_evsel__group_desc(evsel, buf, buflen);
1283 ev_name = buf;
1284
1285 for_each_group_member(pos, evsel) {
4ea062ed
ACM
1286 struct hists *pos_hists = evsel__hists(pos);
1287
f2148330 1288 if (symbol_conf.filter_relative) {
4ea062ed
ACM
1289 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1290 nr_events += pos_hists->stats.total_non_filtered_period;
f2148330 1291 } else {
4ea062ed
ACM
1292 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1293 nr_events += pos_hists->stats.total_period;
f2148330 1294 }
717e263f
NK
1295 }
1296 }
cc686280 1297
9e207ddf
KL
1298 if (symbol_conf.show_ref_callgraph &&
1299 strstr(ev_name, "call-graph=no"))
1300 enable_ref = true;
cc686280
AR
1301 nr_samples = convert_unit(nr_samples, &unit);
1302 printed = scnprintf(bf, size,
9e207ddf
KL
1303 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1304 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
469917ce 1305
d1b4f249 1306
05e8b080 1307 if (hists->uid_filter_str)
0d37aa34 1308 printed += snprintf(bf + printed, size - printed,
05e8b080 1309 ", UID: %s", hists->uid_filter_str);
d1b4f249 1310 if (thread)
e7f01d1e 1311 printed += scnprintf(bf + printed, size - printed,
469917ce 1312 ", Thread: %s(%d)",
b9c5143a 1313 (thread->comm_set ? thread__comm_str(thread) : ""),
38051234 1314 thread->tid);
d1b4f249 1315 if (dso)
e7f01d1e 1316 printed += scnprintf(bf + printed, size - printed,
469917ce 1317 ", DSO: %s", dso->short_name);
84734b06 1318 if (socket_id > -1)
21394d94 1319 printed += scnprintf(bf + printed, size - printed,
84734b06 1320 ", Processor Socket: %d", socket_id);
1e378ebd
TS
1321 if (!is_report_browser(hbt)) {
1322 struct perf_top *top = hbt->arg;
1323
1324 if (top->zero)
1325 printed += scnprintf(bf + printed, size - printed, " [z]");
1326 }
1327
469917ce 1328 return printed;
d1b4f249
ACM
1329}
1330
24bff2dc
SE
1331static inline void free_popup_options(char **options, int n)
1332{
1333 int i;
1334
04662523
ACM
1335 for (i = 0; i < n; ++i)
1336 zfree(&options[i]);
24bff2dc
SE
1337}
1338
341487ab
FT
1339/*
1340 * Only runtime switching of perf data file will make "input_name" point
1341 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1342 * whether we need to call free() for current "input_name" during the switch.
1343 */
1344static bool is_input_name_malloced = false;
1345
1346static int switch_data_file(void)
1347{
1348 char *pwd, *options[32], *abs_path[32], *tmp;
1349 DIR *pwd_dir;
1350 int nr_options = 0, choice = -1, ret = -1;
1351 struct dirent *dent;
1352
1353 pwd = getenv("PWD");
1354 if (!pwd)
1355 return ret;
1356
1357 pwd_dir = opendir(pwd);
1358 if (!pwd_dir)
1359 return ret;
1360
1361 memset(options, 0, sizeof(options));
1362 memset(options, 0, sizeof(abs_path));
1363
1364 while ((dent = readdir(pwd_dir))) {
1365 char path[PATH_MAX];
1366 u64 magic;
1367 char *name = dent->d_name;
1368 FILE *file;
1369
1370 if (!(dent->d_type == DT_REG))
1371 continue;
1372
1373 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1374
1375 file = fopen(path, "r");
1376 if (!file)
1377 continue;
1378
1379 if (fread(&magic, 1, 8, file) < 8)
1380 goto close_file_and_continue;
1381
1382 if (is_perf_magic(magic)) {
1383 options[nr_options] = strdup(name);
1384 if (!options[nr_options])
1385 goto close_file_and_continue;
1386
1387 abs_path[nr_options] = strdup(path);
1388 if (!abs_path[nr_options]) {
74cf249d 1389 zfree(&options[nr_options]);
341487ab
FT
1390 ui__warning("Can't search all data files due to memory shortage.\n");
1391 fclose(file);
1392 break;
1393 }
1394
1395 nr_options++;
1396 }
1397
1398close_file_and_continue:
1399 fclose(file);
1400 if (nr_options >= 32) {
1401 ui__warning("Too many perf data files in PWD!\n"
1402 "Only the first 32 files will be listed.\n");
1403 break;
1404 }
1405 }
1406 closedir(pwd_dir);
1407
1408 if (nr_options) {
1409 choice = ui__popup_menu(nr_options, options);
1410 if (choice < nr_options && choice >= 0) {
1411 tmp = strdup(abs_path[choice]);
1412 if (tmp) {
1413 if (is_input_name_malloced)
1414 free((void *)input_name);
1415 input_name = tmp;
1416 is_input_name_malloced = true;
1417 ret = 0;
1418 } else
1419 ui__warning("Data switch failed due to memory shortage!\n");
1420 }
1421 }
1422
1423 free_popup_options(options, nr_options);
1424 free_popup_options(abs_path, nr_options);
1425 return ret;
1426}
1427
ea7cd592
NK
1428struct popup_action {
1429 struct thread *thread;
1430 struct dso *dso;
1431 struct map_symbol ms;
84734b06 1432 int socket;
ea7cd592
NK
1433
1434 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1435};
1436
bc7cad42 1437static int
ea7cd592 1438do_annotate(struct hist_browser *browser, struct popup_action *act)
bc7cad42
NK
1439{
1440 struct perf_evsel *evsel;
1441 struct annotation *notes;
1442 struct hist_entry *he;
1443 int err;
1444
eebd0bfc 1445 if (!objdump_path && perf_env__lookup_objdump(browser->env))
bc7cad42
NK
1446 return 0;
1447
ea7cd592 1448 notes = symbol__annotation(act->ms.sym);
bc7cad42
NK
1449 if (!notes->src)
1450 return 0;
1451
1452 evsel = hists_to_evsel(browser->hists);
ea7cd592 1453 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
bc7cad42
NK
1454 he = hist_browser__selected_entry(browser);
1455 /*
1456 * offer option to annotate the other branch source or target
1457 * (if they exists) when returning from annotate
1458 */
1459 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1460 return 1;
1461
1462 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1463 if (err)
1464 ui_browser__handle_resize(&browser->b);
1465 return 0;
1466}
1467
1468static int
ea7cd592
NK
1469add_annotate_opt(struct hist_browser *browser __maybe_unused,
1470 struct popup_action *act, char **optstr,
1471 struct map *map, struct symbol *sym)
bc7cad42 1472{
ea7cd592
NK
1473 if (sym == NULL || map->dso->annotate_warned)
1474 return 0;
1475
1476 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1477 return 0;
1478
1479 act->ms.map = map;
1480 act->ms.sym = sym;
1481 act->fn = do_annotate;
1482 return 1;
1483}
1484
1485static int
1486do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1487{
1488 struct thread *thread = act->thread;
1489
bc7cad42
NK
1490 if (browser->hists->thread_filter) {
1491 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1492 perf_hpp__set_elide(HISTC_THREAD, false);
1493 thread__zput(browser->hists->thread_filter);
1494 ui_helpline__pop();
1495 } else {
1496 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1497 thread->comm_set ? thread__comm_str(thread) : "",
1498 thread->tid);
1499 browser->hists->thread_filter = thread__get(thread);
1500 perf_hpp__set_elide(HISTC_THREAD, false);
1501 pstack__push(browser->pstack, &browser->hists->thread_filter);
1502 }
1503
1504 hists__filter_by_thread(browser->hists);
1505 hist_browser__reset(browser);
1506 return 0;
1507}
1508
1509static int
ea7cd592
NK
1510add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1511 char **optstr, struct thread *thread)
1512{
1513 if (thread == NULL)
1514 return 0;
1515
1516 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1517 browser->hists->thread_filter ? "out of" : "into",
1518 thread->comm_set ? thread__comm_str(thread) : "",
1519 thread->tid) < 0)
1520 return 0;
1521
1522 act->thread = thread;
1523 act->fn = do_zoom_thread;
1524 return 1;
1525}
1526
1527static int
1528do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
bc7cad42 1529{
ea7cd592
NK
1530 struct dso *dso = act->dso;
1531
bc7cad42
NK
1532 if (browser->hists->dso_filter) {
1533 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1534 perf_hpp__set_elide(HISTC_DSO, false);
1535 browser->hists->dso_filter = NULL;
1536 ui_helpline__pop();
1537 } else {
1538 if (dso == NULL)
1539 return 0;
1540 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1541 dso->kernel ? "the Kernel" : dso->short_name);
1542 browser->hists->dso_filter = dso;
1543 perf_hpp__set_elide(HISTC_DSO, true);
1544 pstack__push(browser->pstack, &browser->hists->dso_filter);
1545 }
1546
1547 hists__filter_by_dso(browser->hists);
1548 hist_browser__reset(browser);
1549 return 0;
1550}
1551
1552static int
ea7cd592
NK
1553add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1554 char **optstr, struct dso *dso)
bc7cad42 1555{
ea7cd592
NK
1556 if (dso == NULL)
1557 return 0;
1558
1559 if (asprintf(optstr, "Zoom %s %s DSO",
1560 browser->hists->dso_filter ? "out of" : "into",
1561 dso->kernel ? "the Kernel" : dso->short_name) < 0)
1562 return 0;
1563
1564 act->dso = dso;
1565 act->fn = do_zoom_dso;
1566 return 1;
1567}
1568
1569static int
1570do_browse_map(struct hist_browser *browser __maybe_unused,
1571 struct popup_action *act)
1572{
1573 map__browse(act->ms.map);
bc7cad42
NK
1574 return 0;
1575}
1576
ea7cd592
NK
1577static int
1578add_map_opt(struct hist_browser *browser __maybe_unused,
1579 struct popup_action *act, char **optstr, struct map *map)
1580{
1581 if (map == NULL)
1582 return 0;
1583
1584 if (asprintf(optstr, "Browse map details") < 0)
1585 return 0;
1586
1587 act->ms.map = map;
1588 act->fn = do_browse_map;
1589 return 1;
1590}
1591
bc7cad42
NK
1592static int
1593do_run_script(struct hist_browser *browser __maybe_unused,
ea7cd592 1594 struct popup_action *act)
bc7cad42
NK
1595{
1596 char script_opt[64];
1597 memset(script_opt, 0, sizeof(script_opt));
1598
ea7cd592 1599 if (act->thread) {
bc7cad42 1600 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
ea7cd592
NK
1601 thread__comm_str(act->thread));
1602 } else if (act->ms.sym) {
bc7cad42 1603 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
ea7cd592 1604 act->ms.sym->name);
bc7cad42
NK
1605 }
1606
1607 script_browse(script_opt);
1608 return 0;
1609}
1610
1611static int
ea7cd592
NK
1612add_script_opt(struct hist_browser *browser __maybe_unused,
1613 struct popup_action *act, char **optstr,
1614 struct thread *thread, struct symbol *sym)
1615{
1616 if (thread) {
1617 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1618 thread__comm_str(thread)) < 0)
1619 return 0;
1620 } else if (sym) {
1621 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1622 sym->name) < 0)
1623 return 0;
1624 } else {
1625 if (asprintf(optstr, "Run scripts for all samples") < 0)
1626 return 0;
1627 }
1628
1629 act->thread = thread;
1630 act->ms.sym = sym;
1631 act->fn = do_run_script;
1632 return 1;
1633}
1634
1635static int
1636do_switch_data(struct hist_browser *browser __maybe_unused,
1637 struct popup_action *act __maybe_unused)
bc7cad42
NK
1638{
1639 if (switch_data_file()) {
1640 ui__warning("Won't switch the data files due to\n"
1641 "no valid data file get selected!\n");
ea7cd592 1642 return 0;
bc7cad42
NK
1643 }
1644
1645 return K_SWITCH_INPUT_DATA;
1646}
1647
ea7cd592
NK
1648static int
1649add_switch_opt(struct hist_browser *browser,
1650 struct popup_action *act, char **optstr)
1651{
1652 if (!is_report_browser(browser->hbt))
1653 return 0;
1654
1655 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1656 return 0;
1657
1658 act->fn = do_switch_data;
1659 return 1;
1660}
1661
1662static int
1663do_exit_browser(struct hist_browser *browser __maybe_unused,
1664 struct popup_action *act __maybe_unused)
1665{
1666 return 0;
1667}
1668
1669static int
1670add_exit_opt(struct hist_browser *browser __maybe_unused,
1671 struct popup_action *act, char **optstr)
1672{
1673 if (asprintf(optstr, "Exit") < 0)
1674 return 0;
1675
1676 act->fn = do_exit_browser;
1677 return 1;
1678}
1679
84734b06
KL
1680static int
1681do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1682{
1683 if (browser->hists->socket_filter > -1) {
1684 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1685 browser->hists->socket_filter = -1;
1686 perf_hpp__set_elide(HISTC_SOCKET, false);
1687 } else {
1688 browser->hists->socket_filter = act->socket;
1689 perf_hpp__set_elide(HISTC_SOCKET, true);
1690 pstack__push(browser->pstack, &browser->hists->socket_filter);
1691 }
1692
1693 hists__filter_by_socket(browser->hists);
1694 hist_browser__reset(browser);
1695 return 0;
1696}
1697
1698static int
1699add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1700 char **optstr, int socket_id)
1701{
1702 if (socket_id < 0)
1703 return 0;
1704
1705 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1706 (browser->hists->socket_filter > -1) ? "out of" : "into",
1707 socket_id) < 0)
1708 return 0;
1709
1710 act->socket = socket_id;
1711 act->fn = do_zoom_socket;
1712 return 1;
1713}
1714
112f761f 1715static void hist_browser__update_nr_entries(struct hist_browser *hb)
064f1981
NK
1716{
1717 u64 nr_entries = 0;
1718 struct rb_node *nd = rb_first(&hb->hists->entries);
1719
268397cb
NK
1720 if (hb->min_pcnt == 0) {
1721 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1722 return;
1723 }
1724
14135663 1725 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
064f1981 1726 nr_entries++;
c481f930 1727 nd = rb_next(nd);
064f1981
NK
1728 }
1729
112f761f 1730 hb->nr_non_filtered_entries = nr_entries;
064f1981 1731}
341487ab 1732
34958544 1733static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
dd00d486 1734 const char *helpline,
81cce8de 1735 bool left_exits,
68d80758 1736 struct hist_browser_timer *hbt,
064f1981 1737 float min_pcnt,
ce80d3be 1738 struct perf_env *env)
d1b4f249 1739{
4ea062ed 1740 struct hists *hists = evsel__hists(evsel);
b1a9ceef 1741 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
a68c2c58 1742 struct branch_info *bi;
f2b487db
NK
1743#define MAX_OPTIONS 16
1744 char *options[MAX_OPTIONS];
ea7cd592 1745 struct popup_action actions[MAX_OPTIONS];
24bff2dc 1746 int nr_options = 0;
d1b4f249 1747 int key = -1;
938a23ae 1748 char buf[64];
9783adf7 1749 int delay_secs = hbt ? hbt->refresh : 0;
59dc9f25 1750 struct perf_hpp_fmt *fmt;
d1b4f249 1751
e8e684a5
NK
1752#define HIST_BROWSER_HELP_COMMON \
1753 "h/?/F1 Show this window\n" \
1754 "UP/DOWN/PGUP\n" \
1755 "PGDN/SPACE Navigate\n" \
1756 "q/ESC/CTRL+C Exit browser\n\n" \
1757 "For multiple event sessions:\n\n" \
1758 "TAB/UNTAB Switch events\n\n" \
1759 "For symbolic views (--sort has sym):\n\n" \
1760 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1761 "<- Zoom out\n" \
1762 "a Annotate current symbol\n" \
1763 "C Collapse all callchains\n" \
1764 "d Zoom into current DSO\n" \
1765 "E Expand all callchains\n" \
105eb30f 1766 "F Toggle percentage of filtered entries\n" \
025bf7ea 1767 "H Display column headers\n" \
84734b06 1768 "S Zoom into current Processor Socket\n" \
e8e684a5
NK
1769
1770 /* help messages are sorted by lexical order of the hotkey */
1771 const char report_help[] = HIST_BROWSER_HELP_COMMON
6dd60135 1772 "i Show header information\n"
e8e684a5
NK
1773 "P Print histograms to perf.hist.N\n"
1774 "r Run available scripts\n"
1775 "s Switch to another data file in PWD\n"
1776 "t Zoom into current Thread\n"
1777 "V Verbose (DSO names in callchains, etc)\n"
1778 "/ Filter symbol by name";
1779 const char top_help[] = HIST_BROWSER_HELP_COMMON
1780 "P Print histograms to perf.hist.N\n"
1781 "t Zoom into current Thread\n"
1782 "V Verbose (DSO names in callchains, etc)\n"
42337a22 1783 "z Toggle zeroing of samples\n"
fbb7997e 1784 "f Enable/Disable events\n"
e8e684a5
NK
1785 "/ Filter symbol by name";
1786
d1b4f249
ACM
1787 if (browser == NULL)
1788 return -1;
1789
ed426915
NK
1790 /* reset abort key so that it can get Ctrl-C as a key */
1791 SLang_reset_tty();
1792 SLang_init_tty(0, 0, 0);
1793
064f1981
NK
1794 if (min_pcnt) {
1795 browser->min_pcnt = min_pcnt;
112f761f 1796 hist_browser__update_nr_entries(browser);
064f1981
NK
1797 }
1798
84734b06 1799 browser->pstack = pstack__new(3);
01f00a1c 1800 if (browser->pstack == NULL)
d1b4f249
ACM
1801 goto out;
1802
1803 ui_helpline__push(helpline);
1804
24bff2dc 1805 memset(options, 0, sizeof(options));
ea7cd592 1806 memset(actions, 0, sizeof(actions));
24bff2dc 1807
59dc9f25
NK
1808 perf_hpp__for_each_format(fmt)
1809 perf_hpp__reset_width(fmt, hists);
1810
5b591669
NK
1811 if (symbol_conf.col_width_list_str)
1812 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1813
d1b4f249 1814 while (1) {
f3b623b8 1815 struct thread *thread = NULL;
bc7cad42 1816 struct dso *dso = NULL;
ea7cd592 1817 int choice = 0;
84734b06 1818 int socked_id = -1;
d1b4f249 1819
24bff2dc
SE
1820 nr_options = 0;
1821
5f00b0f4 1822 key = hist_browser__run(browser, helpline);
d1b4f249 1823
60098917
ACM
1824 if (browser->he_selection != NULL) {
1825 thread = hist_browser__selected_thread(browser);
1826 dso = browser->selection->map ? browser->selection->map->dso : NULL;
84734b06 1827 socked_id = browser->he_selection->socket;
60098917 1828 }
b50e003d 1829 switch (key) {
cf958003
ACM
1830 case K_TAB:
1831 case K_UNTAB:
e4419b8e
DA
1832 if (nr_events == 1)
1833 continue;
b50e003d
ACM
1834 /*
1835 * Exit the browser, let hists__browser_tree
1836 * go to the next or previous
1837 */
1838 goto out_free_stack;
1839 case 'a':
9c796ec8 1840 if (!sort__has_sym) {
7b27509f 1841 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1842 "Annotation is only available for symbolic views, "
a68c2c58 1843 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1844 continue;
1845 }
1846
60098917 1847 if (browser->selection == NULL ||
db9a9cbc 1848 browser->selection->sym == NULL ||
b50e003d 1849 browser->selection->map->dso->annotate_warned)
d1b4f249 1850 continue;
bc7cad42 1851
ea7cd592
NK
1852 actions->ms.map = browser->selection->map;
1853 actions->ms.sym = browser->selection->sym;
1854 do_annotate(browser, actions);
bc7cad42 1855 continue;
aff3f3f6
ACM
1856 case 'P':
1857 hist_browser__dump(browser);
1858 continue;
b50e003d 1859 case 'd':
ea7cd592
NK
1860 actions->dso = dso;
1861 do_zoom_dso(browser, actions);
bc7cad42 1862 continue;
a7cb8863
ACM
1863 case 'V':
1864 browser->show_dso = !browser->show_dso;
1865 continue;
b50e003d 1866 case 't':
ea7cd592
NK
1867 actions->thread = thread;
1868 do_zoom_thread(browser, actions);
bc7cad42 1869 continue;
84734b06
KL
1870 case 'S':
1871 actions->socket = socked_id;
1872 do_zoom_socket(browser, actions);
1873 continue;
5a5626b1 1874 case '/':
938a23ae
NK
1875 if (ui_browser__input_window("Symbol to show",
1876 "Please enter the name of symbol you want to see",
1877 buf, "ENTER: OK, ESC: Cancel",
1878 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1879 hists->symbol_filter_str = *buf ? buf : NULL;
1880 hists__filter_by_symbol(hists);
938a23ae
NK
1881 hist_browser__reset(browser);
1882 }
1883 continue;
cdbab7c2 1884 case 'r':
ea7cd592
NK
1885 if (is_report_browser(hbt)) {
1886 actions->thread = NULL;
1887 actions->ms.sym = NULL;
1888 do_run_script(browser, actions);
1889 }
c77d8d70 1890 continue;
341487ab 1891 case 's':
bc7cad42 1892 if (is_report_browser(hbt)) {
ea7cd592 1893 key = do_switch_data(browser, actions);
bc7cad42
NK
1894 if (key == K_SWITCH_INPUT_DATA)
1895 goto out_free_stack;
1896 }
341487ab 1897 continue;
6dd60135
NK
1898 case 'i':
1899 /* env->arch is NULL for live-mode (i.e. perf top) */
1900 if (env->arch)
1901 tui__header_window(env);
1902 continue;
105eb30f
NK
1903 case 'F':
1904 symbol_conf.filter_relative ^= 1;
1905 continue;
42337a22
NK
1906 case 'z':
1907 if (!is_report_browser(hbt)) {
1908 struct perf_top *top = hbt->arg;
1909
1910 top->zero = !top->zero;
1911 }
1912 continue;
cf958003 1913 case K_F1:
b50e003d
ACM
1914 case 'h':
1915 case '?':
4610e413 1916 ui_browser__help_window(&browser->b,
e8e684a5 1917 is_report_browser(hbt) ? report_help : top_help);
b50e003d 1918 continue;
cf958003
ACM
1919 case K_ENTER:
1920 case K_RIGHT:
b50e003d
ACM
1921 /* menu */
1922 break;
63ab1749 1923 case K_ESC:
cf958003 1924 case K_LEFT: {
b50e003d 1925 const void *top;
d1b4f249 1926
01f00a1c 1927 if (pstack__empty(browser->pstack)) {
7f0030b2
ACM
1928 /*
1929 * Go back to the perf_evsel_menu__run or other user
1930 */
1931 if (left_exits)
1932 goto out_free_stack;
63ab1749
ACM
1933
1934 if (key == K_ESC &&
1935 ui_browser__dialog_yesno(&browser->b,
1936 "Do you really want to exit?"))
1937 goto out_free_stack;
1938
d1b4f249 1939 continue;
7f0030b2 1940 }
6422184b 1941 top = pstack__peek(browser->pstack);
bc7cad42 1942 if (top == &browser->hists->dso_filter) {
6422184b
NK
1943 /*
1944 * No need to set actions->dso here since
1945 * it's just to remove the current filter.
1946 * Ditto for thread below.
1947 */
1948 do_zoom_dso(browser, actions);
84734b06 1949 } else if (top == &browser->hists->thread_filter) {
6422184b 1950 do_zoom_thread(browser, actions);
84734b06
KL
1951 } else if (top == &browser->hists->socket_filter) {
1952 do_zoom_socket(browser, actions);
1953 }
b50e003d
ACM
1954 continue;
1955 }
ed7e5662
ACM
1956 case 'q':
1957 case CTRL('c'):
516e5368 1958 goto out_free_stack;
fbb7997e 1959 case 'f':
13d1e536
NK
1960 if (!is_report_browser(hbt)) {
1961 struct perf_top *top = hbt->arg;
1962
1963 perf_evlist__toggle_enable(top->evlist);
1964 /*
1965 * No need to refresh, resort/decay histogram
1966 * entries if we are not collecting samples:
1967 */
1968 if (top->evlist->enabled) {
1969 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1970 hbt->refresh = delay_secs;
1971 } else {
1972 helpline = "Press 'f' again to re-enable the events";
1973 hbt->refresh = 0;
1974 }
1975 continue;
1976 }
3e323dc0 1977 /* Fall thru */
ed7e5662 1978 default:
3e323dc0 1979 helpline = "Press '?' for help on key bindings";
ed7e5662 1980 continue;
d1b4f249
ACM
1981 }
1982
9c796ec8 1983 if (!sort__has_sym)
724c9c9f
ACM
1984 goto add_exit_option;
1985
0ba332f7
ACM
1986 if (browser->selection == NULL)
1987 goto skip_annotation;
1988
55369fc1 1989 if (sort__mode == SORT_MODE__BRANCH) {
a68c2c58 1990 bi = browser->he_selection->branch_info;
0ba332f7
ACM
1991
1992 if (bi == NULL)
1993 goto skip_annotation;
1994
ea7cd592
NK
1995 nr_options += add_annotate_opt(browser,
1996 &actions[nr_options],
1997 &options[nr_options],
1998 bi->from.map,
1999 bi->from.sym);
2000 if (bi->to.sym != bi->from.sym)
2001 nr_options += add_annotate_opt(browser,
2002 &actions[nr_options],
2003 &options[nr_options],
2004 bi->to.map,
2005 bi->to.sym);
a68c2c58 2006 } else {
ea7cd592
NK
2007 nr_options += add_annotate_opt(browser,
2008 &actions[nr_options],
2009 &options[nr_options],
2010 browser->selection->map,
2011 browser->selection->sym);
a68c2c58 2012 }
0ba332f7 2013skip_annotation:
ea7cd592
NK
2014 nr_options += add_thread_opt(browser, &actions[nr_options],
2015 &options[nr_options], thread);
2016 nr_options += add_dso_opt(browser, &actions[nr_options],
2017 &options[nr_options], dso);
2018 nr_options += add_map_opt(browser, &actions[nr_options],
2019 &options[nr_options],
bd315aab
WN
2020 browser->selection ?
2021 browser->selection->map : NULL);
84734b06
KL
2022 nr_options += add_socket_opt(browser, &actions[nr_options],
2023 &options[nr_options],
2024 socked_id);
cdbab7c2
FT
2025 /* perf script support */
2026 if (browser->he_selection) {
ea7cd592
NK
2027 nr_options += add_script_opt(browser,
2028 &actions[nr_options],
2029 &options[nr_options],
2030 thread, NULL);
bd315aab
WN
2031 /*
2032 * Note that browser->selection != NULL
2033 * when browser->he_selection is not NULL,
2034 * so we don't need to check browser->selection
2035 * before fetching browser->selection->sym like what
2036 * we do before fetching browser->selection->map.
2037 *
2038 * See hist_browser__show_entry.
2039 */
ea7cd592
NK
2040 nr_options += add_script_opt(browser,
2041 &actions[nr_options],
2042 &options[nr_options],
2043 NULL, browser->selection->sym);
cdbab7c2 2044 }
ea7cd592
NK
2045 nr_options += add_script_opt(browser, &actions[nr_options],
2046 &options[nr_options], NULL, NULL);
2047 nr_options += add_switch_opt(browser, &actions[nr_options],
2048 &options[nr_options]);
724c9c9f 2049add_exit_option:
ea7cd592
NK
2050 nr_options += add_exit_opt(browser, &actions[nr_options],
2051 &options[nr_options]);
d1b4f249 2052
ea7cd592
NK
2053 do {
2054 struct popup_action *act;
68d80758 2055
ea7cd592
NK
2056 choice = ui__popup_menu(nr_options, options);
2057 if (choice == -1 || choice >= nr_options)
2058 break;
a68c2c58 2059
ea7cd592
NK
2060 act = &actions[choice];
2061 key = act->fn(browser, act);
2062 } while (key == 1);
a68c2c58 2063
ea7cd592
NK
2064 if (key == K_SWITCH_INPUT_DATA)
2065 break;
d1b4f249
ACM
2066 }
2067out_free_stack:
01f00a1c 2068 pstack__delete(browser->pstack);
d1b4f249
ACM
2069out:
2070 hist_browser__delete(browser);
f2b487db 2071 free_popup_options(options, MAX_OPTIONS);
d1b4f249
ACM
2072 return key;
2073}
2074
7f0030b2
ACM
2075struct perf_evsel_menu {
2076 struct ui_browser b;
2077 struct perf_evsel *selection;
7b27509f 2078 bool lost_events, lost_events_warned;
064f1981 2079 float min_pcnt;
ce80d3be 2080 struct perf_env *env;
7f0030b2
ACM
2081};
2082
2083static void perf_evsel_menu__write(struct ui_browser *browser,
2084 void *entry, int row)
2085{
2086 struct perf_evsel_menu *menu = container_of(browser,
2087 struct perf_evsel_menu, b);
2088 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
4ea062ed 2089 struct hists *hists = evsel__hists(evsel);
7f0030b2 2090 bool current_entry = ui_browser__is_current_entry(browser, row);
4ea062ed 2091 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 2092 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 2093 char bf[256], unit;
7b27509f
ACM
2094 const char *warn = " ";
2095 size_t printed;
7f0030b2
ACM
2096
2097 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2098 HE_COLORSET_NORMAL);
2099
759ff497 2100 if (perf_evsel__is_group_event(evsel)) {
717e263f
NK
2101 struct perf_evsel *pos;
2102
2103 ev_name = perf_evsel__group_name(evsel);
2104
2105 for_each_group_member(pos, evsel) {
4ea062ed
ACM
2106 struct hists *pos_hists = evsel__hists(pos);
2107 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
717e263f
NK
2108 }
2109 }
2110
7f0030b2 2111 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 2112 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f 2113 unit, unit == ' ' ? "" : " ", ev_name);
517dfdb3 2114 ui_browser__printf(browser, "%s", bf);
7b27509f 2115
4ea062ed 2116 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
7b27509f
ACM
2117 if (nr_events != 0) {
2118 menu->lost_events = true;
2119 if (!current_entry)
2120 ui_browser__set_color(browser, HE_COLORSET_TOP);
2121 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
2122 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2123 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
2124 warn = bf;
2125 }
2126
26270a00 2127 ui_browser__write_nstring(browser, warn, browser->width - printed);
7f0030b2
ACM
2128
2129 if (current_entry)
2130 menu->selection = evsel;
2131}
2132
34958544
ACM
2133static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2134 int nr_events, const char *help,
9783adf7 2135 struct hist_browser_timer *hbt)
d1b4f249 2136{
7f0030b2 2137 struct perf_evlist *evlist = menu->b.priv;
e248de33 2138 struct perf_evsel *pos;
dd00d486 2139 const char *title = "Available samples";
9783adf7 2140 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 2141 int key;
d1b4f249 2142
7f0030b2
ACM
2143 if (ui_browser__show(&menu->b, title,
2144 "ESC: exit, ENTER|->: Browse histograms") < 0)
2145 return -1;
2146
7f0030b2 2147 while (1) {
3af6e338 2148 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
2149
2150 switch (key) {
cf958003 2151 case K_TIMER:
9783adf7 2152 hbt->timer(hbt->arg);
7b27509f
ACM
2153
2154 if (!menu->lost_events_warned && menu->lost_events) {
2155 ui_browser__warn_lost_events(&menu->b);
2156 menu->lost_events_warned = true;
2157 }
81cce8de 2158 continue;
cf958003
ACM
2159 case K_RIGHT:
2160 case K_ENTER:
7f0030b2
ACM
2161 if (!menu->selection)
2162 continue;
2163 pos = menu->selection;
2164browse_hists:
18eaf0b8
ACM
2165 perf_evlist__set_selected(evlist, pos);
2166 /*
2167 * Give the calling tool a chance to populate the non
2168 * default evsel resorted hists tree.
2169 */
9783adf7
NK
2170 if (hbt)
2171 hbt->timer(hbt->arg);
34958544 2172 key = perf_evsel__hists_browse(pos, nr_events, help,
dd00d486 2173 true, hbt,
064f1981 2174 menu->min_pcnt,
68d80758 2175 menu->env);
7f0030b2 2176 ui_browser__show_title(&menu->b, title);
18eaf0b8 2177 switch (key) {
cf958003 2178 case K_TAB:
18eaf0b8 2179 if (pos->node.next == &evlist->entries)
9a354cdc 2180 pos = perf_evlist__first(evlist);
18eaf0b8 2181 else
9a354cdc 2182 pos = perf_evsel__next(pos);
18eaf0b8 2183 goto browse_hists;
cf958003 2184 case K_UNTAB:
18eaf0b8 2185 if (pos->node.prev == &evlist->entries)
9a354cdc 2186 pos = perf_evlist__last(evlist);
18eaf0b8 2187 else
d87fcb4a 2188 pos = perf_evsel__prev(pos);
18eaf0b8 2189 goto browse_hists;
341487ab 2190 case K_SWITCH_INPUT_DATA:
18eaf0b8
ACM
2191 case 'q':
2192 case CTRL('c'):
2193 goto out;
63ab1749 2194 case K_ESC:
18eaf0b8
ACM
2195 default:
2196 continue;
2197 }
cf958003 2198 case K_LEFT:
7f0030b2 2199 continue;
cf958003 2200 case K_ESC:
4610e413
ACM
2201 if (!ui_browser__dialog_yesno(&menu->b,
2202 "Do you really want to exit?"))
ed7e5662
ACM
2203 continue;
2204 /* Fall thru */
7f0030b2
ACM
2205 case 'q':
2206 case CTRL('c'):
2207 goto out;
d1b4f249 2208 default:
18eaf0b8 2209 continue;
d1b4f249
ACM
2210 }
2211 }
2212
7f0030b2
ACM
2213out:
2214 ui_browser__hide(&menu->b);
2215 return key;
2216}
2217
316c7136 2218static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
fc24d7c2
NK
2219 void *entry)
2220{
2221 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2222
2223 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2224 return true;
2225
2226 return false;
2227}
2228
7f0030b2 2229static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 2230 int nr_entries, const char *help,
68d80758 2231 struct hist_browser_timer *hbt,
064f1981 2232 float min_pcnt,
ce80d3be 2233 struct perf_env *env)
7f0030b2
ACM
2234{
2235 struct perf_evsel *pos;
2236 struct perf_evsel_menu menu = {
2237 .b = {
2238 .entries = &evlist->entries,
2239 .refresh = ui_browser__list_head_refresh,
2240 .seek = ui_browser__list_head_seek,
2241 .write = perf_evsel_menu__write,
fc24d7c2
NK
2242 .filter = filter_group_entries,
2243 .nr_entries = nr_entries,
7f0030b2
ACM
2244 .priv = evlist,
2245 },
064f1981 2246 .min_pcnt = min_pcnt,
68d80758 2247 .env = env,
7f0030b2
ACM
2248 };
2249
2250 ui_helpline__push("Press ESC to exit");
2251
0050f7aa 2252 evlist__for_each(evlist, pos) {
7289f83c 2253 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
2254 size_t line_len = strlen(ev_name) + 7;
2255
2256 if (menu.b.width < line_len)
2257 menu.b.width = line_len;
7f0030b2
ACM
2258 }
2259
fc24d7c2 2260 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
2261}
2262
81cce8de 2263int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758 2264 struct hist_browser_timer *hbt,
064f1981 2265 float min_pcnt,
ce80d3be 2266 struct perf_env *env)
7f0030b2 2267{
fc24d7c2
NK
2268 int nr_entries = evlist->nr_entries;
2269
2270single_entry:
2271 if (nr_entries == 1) {
9a354cdc 2272 struct perf_evsel *first = perf_evlist__first(evlist);
fc24d7c2
NK
2273
2274 return perf_evsel__hists_browse(first, nr_entries, help,
dd00d486 2275 false, hbt, min_pcnt,
064f1981 2276 env);
7f0030b2
ACM
2277 }
2278
fc24d7c2
NK
2279 if (symbol_conf.event_group) {
2280 struct perf_evsel *pos;
2281
2282 nr_entries = 0;
0050f7aa 2283 evlist__for_each(evlist, pos) {
fc24d7c2
NK
2284 if (perf_evsel__is_group_leader(pos))
2285 nr_entries++;
0050f7aa 2286 }
fc24d7c2
NK
2287
2288 if (nr_entries == 1)
2289 goto single_entry;
2290 }
2291
2292 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
064f1981 2293 hbt, min_pcnt, env);
d1b4f249 2294}