]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - tools/perf/ui/browsers/hists.c
perf/x86: Allow for architecture specific RDPMC indexes
[mirror_ubuntu-hirsute-kernel.git] / tools / perf / ui / browsers / hists.c
CommitLineData
d1b4f249 1#include <stdio.h>
d1b4f249
ACM
2#include "../libslang.h"
3#include <stdlib.h>
4#include <string.h>
5#include <newt.h>
6#include <linux/rbtree.h>
7
aca7a94d
NK
8#include "../../util/evsel.h"
9#include "../../util/evlist.h"
10#include "../../util/hist.h"
11#include "../../util/pstack.h"
12#include "../../util/sort.h"
13#include "../../util/util.h"
68d80758 14#include "../../arch/common.h"
d1b4f249
ACM
15
16#include "../browser.h"
17#include "../helpline.h"
18#include "../util.h"
4610e413 19#include "../ui.h"
d1b4f249
ACM
20#include "map.h"
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;
aff3f3f6 27 int print_seq;
a7cb8863 28 bool show_dso;
724c9c9f 29 bool has_symbols;
d1b4f249
ACM
30};
31
f5951d56
NK
32extern void hist_browser__init_hpp(void);
33
05e8b080 34static int hists__browser_title(struct hists *hists, char *bf, size_t size,
d7b76f09 35 const char *ev_name);
81cce8de 36
05e8b080 37static void hist_browser__refresh_dimensions(struct hist_browser *browser)
d1b4f249
ACM
38{
39 /* 3 == +/- toggle symbol before actual hist_entry rendering */
05e8b080 40 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
d1b4f249
ACM
41 sizeof("[k]"));
42}
43
05e8b080 44static void hist_browser__reset(struct hist_browser *browser)
d1b4f249 45{
05e8b080
ACM
46 browser->b.nr_entries = browser->hists->nr_entries;
47 hist_browser__refresh_dimensions(browser);
48 ui_browser__reset_index(&browser->b);
d1b4f249
ACM
49}
50
51static char tree__folded_sign(bool unfolded)
52{
53 return unfolded ? '-' : '+';
54}
55
05e8b080 56static char map_symbol__folded(const struct map_symbol *ms)
d1b4f249 57{
05e8b080 58 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
d1b4f249
ACM
59}
60
05e8b080 61static char hist_entry__folded(const struct hist_entry *he)
d1b4f249 62{
05e8b080 63 return map_symbol__folded(&he->ms);
d1b4f249
ACM
64}
65
05e8b080 66static char callchain_list__folded(const struct callchain_list *cl)
d1b4f249 67{
05e8b080 68 return map_symbol__folded(&cl->ms);
d1b4f249
ACM
69}
70
05e8b080 71static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
3c916cc2 72{
05e8b080 73 ms->unfolded = unfold ? ms->has_children : false;
3c916cc2
ACM
74}
75
05e8b080 76static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
d1b4f249
ACM
77{
78 int n = 0;
79 struct rb_node *nd;
80
05e8b080 81 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
82 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83 struct callchain_list *chain;
84 char folded_sign = ' '; /* No children */
85
86 list_for_each_entry(chain, &child->val, list) {
87 ++n;
88 /* We need this because we may not have children */
89 folded_sign = callchain_list__folded(chain);
90 if (folded_sign == '+')
91 break;
92 }
93
94 if (folded_sign == '-') /* Have children and they're unfolded */
95 n += callchain_node__count_rows_rb_tree(child);
96 }
97
98 return n;
99}
100
101static int callchain_node__count_rows(struct callchain_node *node)
102{
103 struct callchain_list *chain;
104 bool unfolded = false;
105 int n = 0;
106
107 list_for_each_entry(chain, &node->val, list) {
108 ++n;
109 unfolded = chain->ms.unfolded;
110 }
111
112 if (unfolded)
113 n += callchain_node__count_rows_rb_tree(node);
114
115 return n;
116}
117
118static int callchain__count_rows(struct rb_root *chain)
119{
120 struct rb_node *nd;
121 int n = 0;
122
123 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125 n += callchain_node__count_rows(node);
126 }
127
128 return n;
129}
130
05e8b080 131static bool map_symbol__toggle_fold(struct map_symbol *ms)
d1b4f249 132{
05e8b080 133 if (!ms)
8493fe1d
JO
134 return false;
135
05e8b080 136 if (!ms->has_children)
d1b4f249
ACM
137 return false;
138
05e8b080 139 ms->unfolded = !ms->unfolded;
d1b4f249
ACM
140 return true;
141}
142
05e8b080 143static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
d1b4f249 144{
05e8b080 145 struct rb_node *nd = rb_first(&node->rb_root);
d1b4f249 146
05e8b080 147 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
148 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149 struct callchain_list *chain;
293db47f 150 bool first = true;
d1b4f249
ACM
151
152 list_for_each_entry(chain, &child->val, list) {
153 if (first) {
154 first = false;
155 chain->ms.has_children = chain->list.next != &child->val ||
293db47f 156 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
157 } else
158 chain->ms.has_children = chain->list.next == &child->val &&
293db47f 159 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
160 }
161
162 callchain_node__init_have_children_rb_tree(child);
163 }
164}
165
05e8b080 166static void callchain_node__init_have_children(struct callchain_node *node)
d1b4f249
ACM
167{
168 struct callchain_list *chain;
169
05e8b080
ACM
170 list_for_each_entry(chain, &node->val, list)
171 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
d1b4f249 172
05e8b080 173 callchain_node__init_have_children_rb_tree(node);
d1b4f249
ACM
174}
175
05e8b080 176static void callchain__init_have_children(struct rb_root *root)
d1b4f249
ACM
177{
178 struct rb_node *nd;
179
05e8b080 180 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
d1b4f249
ACM
181 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182 callchain_node__init_have_children(node);
183 }
184}
185
05e8b080 186static void hist_entry__init_have_children(struct hist_entry *he)
d1b4f249 187{
05e8b080
ACM
188 if (!he->init_have_children) {
189 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190 callchain__init_have_children(&he->sorted_chain);
191 he->init_have_children = true;
d1b4f249
ACM
192 }
193}
194
05e8b080 195static bool hist_browser__toggle_fold(struct hist_browser *browser)
d1b4f249 196{
05e8b080
ACM
197 if (map_symbol__toggle_fold(browser->selection)) {
198 struct hist_entry *he = browser->he_selection;
d1b4f249
ACM
199
200 hist_entry__init_have_children(he);
05e8b080 201 browser->hists->nr_entries -= he->nr_rows;
d1b4f249
ACM
202
203 if (he->ms.unfolded)
204 he->nr_rows = callchain__count_rows(&he->sorted_chain);
205 else
206 he->nr_rows = 0;
05e8b080
ACM
207 browser->hists->nr_entries += he->nr_rows;
208 browser->b.nr_entries = browser->hists->nr_entries;
d1b4f249
ACM
209
210 return true;
211 }
212
213 /* If it doesn't have children, no toggling performed */
214 return false;
215}
216
05e8b080 217static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
3c916cc2
ACM
218{
219 int n = 0;
220 struct rb_node *nd;
221
05e8b080 222 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
3c916cc2
ACM
223 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224 struct callchain_list *chain;
225 bool has_children = false;
226
227 list_for_each_entry(chain, &child->val, list) {
228 ++n;
229 map_symbol__set_folding(&chain->ms, unfold);
230 has_children = chain->ms.has_children;
231 }
232
233 if (has_children)
234 n += callchain_node__set_folding_rb_tree(child, unfold);
235 }
236
237 return n;
238}
239
240static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241{
242 struct callchain_list *chain;
243 bool has_children = false;
244 int n = 0;
245
246 list_for_each_entry(chain, &node->val, list) {
247 ++n;
248 map_symbol__set_folding(&chain->ms, unfold);
249 has_children = chain->ms.has_children;
250 }
251
252 if (has_children)
253 n += callchain_node__set_folding_rb_tree(node, unfold);
254
255 return n;
256}
257
258static int callchain__set_folding(struct rb_root *chain, bool unfold)
259{
260 struct rb_node *nd;
261 int n = 0;
262
263 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265 n += callchain_node__set_folding(node, unfold);
266 }
267
268 return n;
269}
270
05e8b080 271static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
3c916cc2 272{
05e8b080
ACM
273 hist_entry__init_have_children(he);
274 map_symbol__set_folding(&he->ms, unfold);
3c916cc2 275
05e8b080
ACM
276 if (he->ms.has_children) {
277 int n = callchain__set_folding(&he->sorted_chain, unfold);
278 he->nr_rows = unfold ? n : 0;
3c916cc2 279 } else
05e8b080 280 he->nr_rows = 0;
3c916cc2
ACM
281}
282
05e8b080 283static void hists__set_folding(struct hists *hists, bool unfold)
3c916cc2
ACM
284{
285 struct rb_node *nd;
286
05e8b080 287 hists->nr_entries = 0;
3c916cc2 288
05e8b080 289 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
3c916cc2
ACM
290 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 hist_entry__set_folding(he, unfold);
05e8b080 292 hists->nr_entries += 1 + he->nr_rows;
3c916cc2
ACM
293 }
294}
295
05e8b080 296static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
3c916cc2 297{
05e8b080
ACM
298 hists__set_folding(browser->hists, unfold);
299 browser->b.nr_entries = browser->hists->nr_entries;
3c916cc2 300 /* Go to the start, we may be way after valid entries after a collapse */
05e8b080 301 ui_browser__reset_index(&browser->b);
3c916cc2
ACM
302}
303
7b27509f
ACM
304static void ui_browser__warn_lost_events(struct ui_browser *browser)
305{
306 ui_browser__warning(browser, 4,
307 "Events are being lost, check IO/CPU overload!\n\n"
308 "You may want to run 'perf' using a RT scheduler policy:\n\n"
309 " perf top -r 80\n\n"
310 "Or reduce the sampling frequency.");
311}
312
05e8b080 313static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
9783adf7 314 struct hist_browser_timer *hbt)
d1b4f249 315{
b50e003d 316 int key;
81cce8de 317 char title[160];
9783adf7 318 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249 319
05e8b080
ACM
320 browser->b.entries = &browser->hists->entries;
321 browser->b.nr_entries = browser->hists->nr_entries;
d1b4f249 322
05e8b080
ACM
323 hist_browser__refresh_dimensions(browser);
324 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
d1b4f249 325
05e8b080 326 if (ui_browser__show(&browser->b, title,
59e8fe32 327 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
328 return -1;
329
d1b4f249 330 while (1) {
05e8b080 331 key = ui_browser__run(&browser->b, delay_secs);
d1b4f249 332
b50e003d 333 switch (key) {
13d8f96c 334 case K_TIMER:
9783adf7 335 hbt->timer(hbt->arg);
05e8b080 336 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
7b27509f 337
05e8b080
ACM
338 if (browser->hists->stats.nr_lost_warned !=
339 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
340 browser->hists->stats.nr_lost_warned =
341 browser->hists->stats.nr_events[PERF_RECORD_LOST];
342 ui_browser__warn_lost_events(&browser->b);
7b27509f
ACM
343 }
344
05e8b080
ACM
345 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
346 ui_browser__show_title(&browser->b, title);
81cce8de 347 continue;
4694153c 348 case 'D': { /* Debug */
d1b4f249 349 static int seq;
05e8b080 350 struct hist_entry *h = rb_entry(browser->b.top,
d1b4f249
ACM
351 struct hist_entry, rb_node);
352 ui_helpline__pop();
353 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
05e8b080
ACM
354 seq++, browser->b.nr_entries,
355 browser->hists->nr_entries,
356 browser->b.height,
357 browser->b.index,
358 browser->b.top_idx,
d1b4f249
ACM
359 h->row_offset, h->nr_rows);
360 }
3c916cc2
ACM
361 break;
362 case 'C':
363 /* Collapse the whole world. */
05e8b080 364 hist_browser__set_folding(browser, false);
3c916cc2
ACM
365 break;
366 case 'E':
367 /* Expand the whole world. */
05e8b080 368 hist_browser__set_folding(browser, true);
3c916cc2 369 break;
cf958003 370 case K_ENTER:
05e8b080 371 if (hist_browser__toggle_fold(browser))
d1b4f249
ACM
372 break;
373 /* fall thru */
374 default:
b50e003d 375 goto out;
d1b4f249
ACM
376 }
377 }
b50e003d 378out:
05e8b080 379 ui_browser__hide(&browser->b);
b50e003d 380 return key;
d1b4f249
ACM
381}
382
05e8b080 383static char *callchain_list__sym_name(struct callchain_list *cl,
a7cb8863 384 char *bf, size_t bfsize, bool show_dso)
d1b4f249 385{
a7cb8863
ACM
386 int printed;
387
05e8b080 388 if (cl->ms.sym)
a7cb8863
ACM
389 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
390 else
391 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
392
393 if (show_dso)
394 scnprintf(bf + printed, bfsize - printed, " %s",
395 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
d1b4f249 396
d1b4f249
ACM
397 return bf;
398}
399
400#define LEVEL_OFFSET_STEP 3
401
05e8b080 402static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
d1b4f249
ACM
403 struct callchain_node *chain_node,
404 u64 total, int level,
405 unsigned short row,
406 off_t *row_offset,
407 bool *is_current_entry)
408{
409 struct rb_node *node;
410 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
411 u64 new_total, remaining;
412
413 if (callchain_param.mode == CHAIN_GRAPH_REL)
414 new_total = chain_node->children_hit;
415 else
416 new_total = total;
417
418 remaining = new_total;
419 node = rb_first(&chain_node->rb_root);
420 while (node) {
421 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
422 struct rb_node *next = rb_next(node);
f08c3154 423 u64 cumul = callchain_cumul_hits(child);
d1b4f249
ACM
424 struct callchain_list *chain;
425 char folded_sign = ' ';
426 int first = true;
427 int extra_offset = 0;
428
429 remaining -= cumul;
430
431 list_for_each_entry(chain, &child->val, list) {
a7cb8863 432 char bf[1024], *alloc_str;
d1b4f249
ACM
433 const char *str;
434 int color;
435 bool was_first = first;
436
163caed9 437 if (first)
d1b4f249 438 first = false;
163caed9 439 else
d1b4f249 440 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
441
442 folded_sign = callchain_list__folded(chain);
443 if (*row_offset != 0) {
444 --*row_offset;
445 goto do_next;
446 }
447
448 alloc_str = NULL;
a7cb8863
ACM
449 str = callchain_list__sym_name(chain, bf, sizeof(bf),
450 browser->show_dso);
d1b4f249
ACM
451 if (was_first) {
452 double percent = cumul * 100.0 / new_total;
453
454 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
455 str = "Not enough memory!";
456 else
457 str = alloc_str;
458 }
459
460 color = HE_COLORSET_NORMAL;
05e8b080
ACM
461 width = browser->b.width - (offset + extra_offset + 2);
462 if (ui_browser__is_current_entry(&browser->b, row)) {
463 browser->selection = &chain->ms;
d1b4f249
ACM
464 color = HE_COLORSET_SELECTED;
465 *is_current_entry = true;
466 }
467
05e8b080
ACM
468 ui_browser__set_color(&browser->b, color);
469 ui_browser__gotorc(&browser->b, row, 0);
d1b4f249
ACM
470 slsmg_write_nstring(" ", offset + extra_offset);
471 slsmg_printf("%c ", folded_sign);
472 slsmg_write_nstring(str, width);
473 free(alloc_str);
474
05e8b080 475 if (++row == browser->b.height)
d1b4f249
ACM
476 goto out;
477do_next:
478 if (folded_sign == '+')
479 break;
480 }
481
482 if (folded_sign == '-') {
483 const int new_level = level + (extra_offset ? 2 : 1);
05e8b080 484 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
d1b4f249
ACM
485 new_level, row, row_offset,
486 is_current_entry);
487 }
05e8b080 488 if (row == browser->b.height)
d1b4f249
ACM
489 goto out;
490 node = next;
491 }
492out:
493 return row - first_row;
494}
495
05e8b080 496static int hist_browser__show_callchain_node(struct hist_browser *browser,
d1b4f249
ACM
497 struct callchain_node *node,
498 int level, unsigned short row,
499 off_t *row_offset,
500 bool *is_current_entry)
501{
502 struct callchain_list *chain;
503 int first_row = row,
504 offset = level * LEVEL_OFFSET_STEP,
05e8b080 505 width = browser->b.width - offset;
d1b4f249
ACM
506 char folded_sign = ' ';
507
508 list_for_each_entry(chain, &node->val, list) {
a7cb8863 509 char bf[1024], *s;
d1b4f249 510 int color;
163caed9 511
d1b4f249
ACM
512 folded_sign = callchain_list__folded(chain);
513
514 if (*row_offset != 0) {
515 --*row_offset;
516 continue;
517 }
518
519 color = HE_COLORSET_NORMAL;
05e8b080
ACM
520 if (ui_browser__is_current_entry(&browser->b, row)) {
521 browser->selection = &chain->ms;
d1b4f249
ACM
522 color = HE_COLORSET_SELECTED;
523 *is_current_entry = true;
524 }
525
a7cb8863
ACM
526 s = callchain_list__sym_name(chain, bf, sizeof(bf),
527 browser->show_dso);
05e8b080
ACM
528 ui_browser__gotorc(&browser->b, row, 0);
529 ui_browser__set_color(&browser->b, color);
d1b4f249
ACM
530 slsmg_write_nstring(" ", offset);
531 slsmg_printf("%c ", folded_sign);
532 slsmg_write_nstring(s, width - 2);
533
05e8b080 534 if (++row == browser->b.height)
d1b4f249
ACM
535 goto out;
536 }
537
538 if (folded_sign == '-')
05e8b080
ACM
539 row += hist_browser__show_callchain_node_rb_tree(browser, node,
540 browser->hists->stats.total_period,
d1b4f249
ACM
541 level + 1, row,
542 row_offset,
543 is_current_entry);
544out:
545 return row - first_row;
546}
547
05e8b080 548static int hist_browser__show_callchain(struct hist_browser *browser,
d1b4f249
ACM
549 struct rb_root *chain,
550 int level, unsigned short row,
551 off_t *row_offset,
552 bool *is_current_entry)
553{
554 struct rb_node *nd;
555 int first_row = row;
556
557 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
558 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
559
05e8b080 560 row += hist_browser__show_callchain_node(browser, node, level,
d1b4f249
ACM
561 row, row_offset,
562 is_current_entry);
05e8b080 563 if (row == browser->b.height)
d1b4f249
ACM
564 break;
565 }
566
567 return row - first_row;
568}
569
89701460
NK
570struct hpp_arg {
571 struct ui_browser *b;
572 char folded_sign;
573 bool current_entry;
574};
575
576static int __hpp__color_callchain(struct hpp_arg *arg)
577{
578 if (!symbol_conf.use_callchain)
579 return 0;
580
581 slsmg_printf("%c ", arg->folded_sign);
582 return 2;
583}
584
5aed9d24 585static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
89701460
NK
586 u64 (*get_field)(struct hist_entry *),
587 int (*callchain_cb)(struct hpp_arg *))
5aed9d24 588{
89701460 589 int ret = 0;
5aed9d24
NK
590 double percent = 0.0;
591 struct hists *hists = he->hists;
89701460 592 struct hpp_arg *arg = hpp->ptr;
5aed9d24
NK
593
594 if (hists->stats.total_period)
595 percent = 100.0 * get_field(he) / hists->stats.total_period;
596
89701460
NK
597 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
598
599 if (callchain_cb)
600 ret += callchain_cb(arg);
601
602 ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
603 slsmg_printf("%s", hpp->buf);
604
371d8c40
NK
605 if (symbol_conf.event_group) {
606 int prev_idx, idx_delta;
607 struct perf_evsel *evsel = hists_to_evsel(hists);
608 struct hist_entry *pair;
609 int nr_members = evsel->nr_members;
610
611 if (nr_members <= 1)
612 goto out;
613
614 prev_idx = perf_evsel__group_idx(evsel);
615
616 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
617 u64 period = get_field(pair);
618 u64 total = pair->hists->stats.total_period;
619
620 if (!total)
621 continue;
622
623 evsel = hists_to_evsel(pair->hists);
624 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
625
626 while (idx_delta--) {
627 /*
628 * zero-fill group members in the middle which
629 * have no sample
630 */
631 ui_browser__set_percent_color(arg->b, 0.0,
632 arg->current_entry);
633 ret += scnprintf(hpp->buf, hpp->size,
634 " %6.2f%%", 0.0);
635 slsmg_printf("%s", hpp->buf);
636 }
637
638 percent = 100.0 * period / total;
639 ui_browser__set_percent_color(arg->b, percent,
640 arg->current_entry);
641 ret += scnprintf(hpp->buf, hpp->size,
642 " %6.2f%%", percent);
643 slsmg_printf("%s", hpp->buf);
644
645 prev_idx = perf_evsel__group_idx(evsel);
646 }
647
648 idx_delta = nr_members - prev_idx - 1;
649
650 while (idx_delta--) {
651 /*
652 * zero-fill group members at last which have no sample
653 */
654 ui_browser__set_percent_color(arg->b, 0.0,
655 arg->current_entry);
656 ret += scnprintf(hpp->buf, hpp->size,
657 " %6.2f%%", 0.0);
658 slsmg_printf("%s", hpp->buf);
659 }
660 }
661out:
89701460
NK
662 if (!arg->current_entry || !arg->b->navkeypressed)
663 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
5aed9d24 664
5aed9d24
NK
665 return ret;
666}
667
89701460 668#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
5aed9d24
NK
669static u64 __hpp_get_##_field(struct hist_entry *he) \
670{ \
671 return he->stat._field; \
672} \
673 \
674static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
675 struct hist_entry *he) \
f5951d56 676{ \
89701460 677 return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
f5951d56
NK
678}
679
89701460
NK
680__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
681__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
682__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
683__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
684__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
f5951d56 685
5aed9d24 686#undef __HPP_COLOR_PERCENT_FN
f5951d56
NK
687
688void hist_browser__init_hpp(void)
689{
1240005e
JO
690 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
691
1d77822e 692 perf_hpp__init();
f5951d56
NK
693
694 perf_hpp__format[PERF_HPP__OVERHEAD].color =
695 hist_browser__hpp_color_overhead;
696 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
697 hist_browser__hpp_color_overhead_sys;
698 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
699 hist_browser__hpp_color_overhead_us;
700 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
701 hist_browser__hpp_color_overhead_guest_sys;
702 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
703 hist_browser__hpp_color_overhead_guest_us;
704}
705
05e8b080 706static int hist_browser__show_entry(struct hist_browser *browser,
d1b4f249
ACM
707 struct hist_entry *entry,
708 unsigned short row)
709{
710 char s[256];
1240005e 711 int printed = 0;
67d25916 712 int width = browser->b.width;
d1b4f249 713 char folded_sign = ' ';
05e8b080 714 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
d1b4f249 715 off_t row_offset = entry->row_offset;
63a1a3d8 716 bool first = true;
1240005e 717 struct perf_hpp_fmt *fmt;
d1b4f249
ACM
718
719 if (current_entry) {
05e8b080
ACM
720 browser->he_selection = entry;
721 browser->selection = &entry->ms;
d1b4f249
ACM
722 }
723
724 if (symbol_conf.use_callchain) {
163caed9 725 hist_entry__init_have_children(entry);
d1b4f249
ACM
726 folded_sign = hist_entry__folded(entry);
727 }
728
729 if (row_offset == 0) {
89701460
NK
730 struct hpp_arg arg = {
731 .b = &browser->b,
732 .folded_sign = folded_sign,
733 .current_entry = current_entry,
734 };
f5951d56
NK
735 struct perf_hpp hpp = {
736 .buf = s,
737 .size = sizeof(s),
89701460 738 .ptr = &arg,
f5951d56 739 };
d1b4f249 740
67d25916 741 ui_browser__gotorc(&browser->b, row, 0);
c172f742 742
1240005e 743 perf_hpp__for_each_format(fmt) {
63a1a3d8 744 if (!first) {
f5951d56
NK
745 slsmg_printf(" ");
746 width -= 2;
747 }
63a1a3d8 748 first = false;
c172f742 749
1240005e 750 if (fmt->color) {
1240005e 751 width -= fmt->color(&hpp, entry);
f5951d56 752 } else {
1240005e 753 width -= fmt->entry(&hpp, entry);
f5951d56
NK
754 slsmg_printf("%s", s);
755 }
2cf9cebf
ACM
756 }
757
f5951d56
NK
758 /* The scroll bar isn't being used */
759 if (!browser->b.navkeypressed)
760 width += 1;
761
762 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
d1b4f249
ACM
763 slsmg_write_nstring(s, width);
764 ++row;
765 ++printed;
766 } else
767 --row_offset;
768
05e8b080
ACM
769 if (folded_sign == '-' && row != browser->b.height) {
770 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
d1b4f249
ACM
771 1, row, &row_offset,
772 &current_entry);
773 if (current_entry)
05e8b080 774 browser->he_selection = entry;
d1b4f249
ACM
775 }
776
777 return printed;
778}
779
437cfe7a
ACM
780static void ui_browser__hists_init_top(struct ui_browser *browser)
781{
782 if (browser->top == NULL) {
783 struct hist_browser *hb;
784
785 hb = container_of(browser, struct hist_browser, b);
786 browser->top = rb_first(&hb->hists->entries);
787 }
788}
789
05e8b080 790static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
791{
792 unsigned row = 0;
793 struct rb_node *nd;
05e8b080 794 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 795
05e8b080 796 ui_browser__hists_init_top(browser);
d1b4f249 797
05e8b080 798 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249
ACM
799 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
800
801 if (h->filtered)
802 continue;
803
804 row += hist_browser__show_entry(hb, h, row);
05e8b080 805 if (row == browser->height)
d1b4f249
ACM
806 break;
807 }
808
809 return row;
810}
811
812static struct rb_node *hists__filter_entries(struct rb_node *nd)
813{
814 while (nd != NULL) {
815 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
816 if (!h->filtered)
817 return nd;
818
819 nd = rb_next(nd);
820 }
821
822 return NULL;
823}
824
825static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
826{
827 while (nd != NULL) {
828 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
829 if (!h->filtered)
830 return nd;
831
832 nd = rb_prev(nd);
833 }
834
835 return NULL;
836}
837
05e8b080 838static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
839 off_t offset, int whence)
840{
841 struct hist_entry *h;
842 struct rb_node *nd;
843 bool first = true;
844
05e8b080 845 if (browser->nr_entries == 0)
60098917
ACM
846 return;
847
05e8b080 848 ui_browser__hists_init_top(browser);
437cfe7a 849
d1b4f249
ACM
850 switch (whence) {
851 case SEEK_SET:
05e8b080 852 nd = hists__filter_entries(rb_first(browser->entries));
d1b4f249
ACM
853 break;
854 case SEEK_CUR:
05e8b080 855 nd = browser->top;
d1b4f249
ACM
856 goto do_offset;
857 case SEEK_END:
05e8b080 858 nd = hists__filter_prev_entries(rb_last(browser->entries));
d1b4f249
ACM
859 first = false;
860 break;
861 default:
862 return;
863 }
864
865 /*
866 * Moves not relative to the first visible entry invalidates its
867 * row_offset:
868 */
05e8b080 869 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
870 h->row_offset = 0;
871
872 /*
873 * Here we have to check if nd is expanded (+), if it is we can't go
874 * the next top level hist_entry, instead we must compute an offset of
875 * what _not_ to show and not change the first visible entry.
876 *
877 * This offset increments when we are going from top to bottom and
878 * decreases when we're going from bottom to top.
879 *
880 * As we don't have backpointers to the top level in the callchains
881 * structure, we need to always print the whole hist_entry callchain,
882 * skipping the first ones that are before the first visible entry
883 * and stop when we printed enough lines to fill the screen.
884 */
885do_offset:
886 if (offset > 0) {
887 do {
888 h = rb_entry(nd, struct hist_entry, rb_node);
889 if (h->ms.unfolded) {
890 u16 remaining = h->nr_rows - h->row_offset;
891 if (offset > remaining) {
892 offset -= remaining;
893 h->row_offset = 0;
894 } else {
895 h->row_offset += offset;
896 offset = 0;
05e8b080 897 browser->top = nd;
d1b4f249
ACM
898 break;
899 }
900 }
901 nd = hists__filter_entries(rb_next(nd));
902 if (nd == NULL)
903 break;
904 --offset;
05e8b080 905 browser->top = nd;
d1b4f249
ACM
906 } while (offset != 0);
907 } else if (offset < 0) {
908 while (1) {
909 h = rb_entry(nd, struct hist_entry, rb_node);
910 if (h->ms.unfolded) {
911 if (first) {
912 if (-offset > h->row_offset) {
913 offset += h->row_offset;
914 h->row_offset = 0;
915 } else {
916 h->row_offset += offset;
917 offset = 0;
05e8b080 918 browser->top = nd;
d1b4f249
ACM
919 break;
920 }
921 } else {
922 if (-offset > h->nr_rows) {
923 offset += h->nr_rows;
924 h->row_offset = 0;
925 } else {
926 h->row_offset = h->nr_rows + offset;
927 offset = 0;
05e8b080 928 browser->top = nd;
d1b4f249
ACM
929 break;
930 }
931 }
932 }
933
934 nd = hists__filter_prev_entries(rb_prev(nd));
935 if (nd == NULL)
936 break;
937 ++offset;
05e8b080 938 browser->top = nd;
d1b4f249
ACM
939 if (offset == 0) {
940 /*
941 * Last unfiltered hist_entry, check if it is
942 * unfolded, if it is then we should have
943 * row_offset at its last entry.
944 */
945 h = rb_entry(nd, struct hist_entry, rb_node);
946 if (h->ms.unfolded)
947 h->row_offset = h->nr_rows;
948 break;
949 }
950 first = false;
951 }
952 } else {
05e8b080 953 browser->top = nd;
d1b4f249
ACM
954 h = rb_entry(nd, struct hist_entry, rb_node);
955 h->row_offset = 0;
956 }
957}
958
aff3f3f6
ACM
959static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
960 struct callchain_node *chain_node,
961 u64 total, int level,
962 FILE *fp)
963{
964 struct rb_node *node;
965 int offset = level * LEVEL_OFFSET_STEP;
966 u64 new_total, remaining;
967 int printed = 0;
968
969 if (callchain_param.mode == CHAIN_GRAPH_REL)
970 new_total = chain_node->children_hit;
971 else
972 new_total = total;
973
974 remaining = new_total;
975 node = rb_first(&chain_node->rb_root);
976 while (node) {
977 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
978 struct rb_node *next = rb_next(node);
979 u64 cumul = callchain_cumul_hits(child);
980 struct callchain_list *chain;
981 char folded_sign = ' ';
982 int first = true;
983 int extra_offset = 0;
984
985 remaining -= cumul;
986
987 list_for_each_entry(chain, &child->val, list) {
a7cb8863 988 char bf[1024], *alloc_str;
aff3f3f6
ACM
989 const char *str;
990 bool was_first = first;
991
992 if (first)
993 first = false;
994 else
995 extra_offset = LEVEL_OFFSET_STEP;
996
997 folded_sign = callchain_list__folded(chain);
998
999 alloc_str = NULL;
a7cb8863
ACM
1000 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1001 browser->show_dso);
aff3f3f6
ACM
1002 if (was_first) {
1003 double percent = cumul * 100.0 / new_total;
1004
1005 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1006 str = "Not enough memory!";
1007 else
1008 str = alloc_str;
1009 }
1010
1011 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1012 free(alloc_str);
1013 if (folded_sign == '+')
1014 break;
1015 }
1016
1017 if (folded_sign == '-') {
1018 const int new_level = level + (extra_offset ? 2 : 1);
1019 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1020 new_level, fp);
1021 }
1022
1023 node = next;
1024 }
1025
1026 return printed;
1027}
1028
1029static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1030 struct callchain_node *node,
1031 int level, FILE *fp)
1032{
1033 struct callchain_list *chain;
1034 int offset = level * LEVEL_OFFSET_STEP;
1035 char folded_sign = ' ';
1036 int printed = 0;
1037
1038 list_for_each_entry(chain, &node->val, list) {
a7cb8863 1039 char bf[1024], *s;
aff3f3f6
ACM
1040
1041 folded_sign = callchain_list__folded(chain);
a7cb8863 1042 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
aff3f3f6
ACM
1043 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1044 }
1045
1046 if (folded_sign == '-')
1047 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1048 browser->hists->stats.total_period,
1049 level + 1, fp);
1050 return printed;
1051}
1052
1053static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1054 struct rb_root *chain, int level, FILE *fp)
1055{
1056 struct rb_node *nd;
1057 int printed = 0;
1058
1059 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1060 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1061
1062 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1063 }
1064
1065 return printed;
1066}
1067
1068static int hist_browser__fprintf_entry(struct hist_browser *browser,
1069 struct hist_entry *he, FILE *fp)
1070{
1071 char s[8192];
1072 double percent;
1073 int printed = 0;
1074 char folded_sign = ' ';
1075
1076 if (symbol_conf.use_callchain)
1077 folded_sign = hist_entry__folded(he);
1078
000078bc 1079 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
b24c28f7 1080 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
aff3f3f6
ACM
1081
1082 if (symbol_conf.use_callchain)
1083 printed += fprintf(fp, "%c ", folded_sign);
1084
1085 printed += fprintf(fp, " %5.2f%%", percent);
1086
1087 if (symbol_conf.show_nr_samples)
b24c28f7 1088 printed += fprintf(fp, " %11u", he->stat.nr_events);
aff3f3f6
ACM
1089
1090 if (symbol_conf.show_total_period)
b24c28f7 1091 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
aff3f3f6
ACM
1092
1093 printed += fprintf(fp, "%s\n", rtrim(s));
1094
1095 if (folded_sign == '-')
1096 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1097
1098 return printed;
1099}
1100
1101static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1102{
1103 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1104 int printed = 0;
1105
1106 while (nd) {
1107 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1108
1109 printed += hist_browser__fprintf_entry(browser, h, fp);
1110 nd = hists__filter_entries(rb_next(nd));
1111 }
1112
1113 return printed;
1114}
1115
1116static int hist_browser__dump(struct hist_browser *browser)
1117{
1118 char filename[64];
1119 FILE *fp;
1120
1121 while (1) {
1122 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1123 if (access(filename, F_OK))
1124 break;
1125 /*
1126 * XXX: Just an arbitrary lazy upper limit
1127 */
1128 if (++browser->print_seq == 8192) {
1129 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1130 return -1;
1131 }
1132 }
1133
1134 fp = fopen(filename, "w");
1135 if (fp == NULL) {
1136 char bf[64];
4cc49d4d
KS
1137 const char *err = strerror_r(errno, bf, sizeof(bf));
1138 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1139 return -1;
1140 }
1141
1142 ++browser->print_seq;
1143 hist_browser__fprintf(browser, fp);
1144 fclose(fp);
1145 ui_helpline__fpush("%s written!", filename);
1146
1147 return 0;
1148}
1149
d1b4f249
ACM
1150static struct hist_browser *hist_browser__new(struct hists *hists)
1151{
05e8b080 1152 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1153
05e8b080
ACM
1154 if (browser) {
1155 browser->hists = hists;
1156 browser->b.refresh = hist_browser__refresh;
1157 browser->b.seek = ui_browser__hists_seek;
1158 browser->b.use_navkeypressed = true;
a68c2c58 1159 if (sort__branch_mode == 1)
05e8b080 1160 browser->has_symbols = sort_sym_from.list.next != NULL;
a68c2c58 1161 else
05e8b080 1162 browser->has_symbols = sort_sym.list.next != NULL;
d1b4f249
ACM
1163 }
1164
05e8b080 1165 return browser;
d1b4f249
ACM
1166}
1167
05e8b080 1168static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1169{
05e8b080 1170 free(browser);
d1b4f249
ACM
1171}
1172
05e8b080 1173static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1174{
05e8b080 1175 return browser->he_selection;
d1b4f249
ACM
1176}
1177
05e8b080 1178static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1179{
05e8b080 1180 return browser->he_selection->thread;
d1b4f249
ACM
1181}
1182
05e8b080 1183static int hists__browser_title(struct hists *hists, char *bf, size_t size,
d7b76f09 1184 const char *ev_name)
d1b4f249 1185{
469917ce
ACM
1186 char unit;
1187 int printed;
05e8b080
ACM
1188 const struct dso *dso = hists->dso_filter;
1189 const struct thread *thread = hists->thread_filter;
1190 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1191 u64 nr_events = hists->stats.total_period;
717e263f
NK
1192 struct perf_evsel *evsel = hists_to_evsel(hists);
1193 char buf[512];
1194 size_t buflen = sizeof(buf);
1195
1196 if (symbol_conf.event_group && evsel->nr_members > 1) {
1197 struct perf_evsel *pos;
1198
1199 perf_evsel__group_desc(evsel, buf, buflen);
1200 ev_name = buf;
1201
1202 for_each_group_member(pos, evsel) {
1203 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1204 nr_events += pos->hists.stats.total_period;
1205 }
1206 }
cc686280
AR
1207
1208 nr_samples = convert_unit(nr_samples, &unit);
1209 printed = scnprintf(bf, size,
1210 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1211 nr_samples, unit, ev_name, nr_events);
469917ce 1212
d1b4f249 1213
05e8b080 1214 if (hists->uid_filter_str)
0d37aa34 1215 printed += snprintf(bf + printed, size - printed,
05e8b080 1216 ", UID: %s", hists->uid_filter_str);
d1b4f249 1217 if (thread)
e7f01d1e 1218 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1219 ", Thread: %s(%d)",
1220 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
1221 thread->pid);
1222 if (dso)
e7f01d1e 1223 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1224 ", DSO: %s", dso->short_name);
1225 return printed;
d1b4f249
ACM
1226}
1227
24bff2dc
SE
1228static inline void free_popup_options(char **options, int n)
1229{
1230 int i;
1231
1232 for (i = 0; i < n; ++i) {
1233 free(options[i]);
1234 options[i] = NULL;
1235 }
1236}
1237
c77d8d70
FT
1238/* Check whether the browser is for 'top' or 'report' */
1239static inline bool is_report_browser(void *timer)
1240{
1241 return timer == NULL;
1242}
1243
34958544 1244static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
7f0030b2 1245 const char *helpline, const char *ev_name,
81cce8de 1246 bool left_exits,
68d80758
NK
1247 struct hist_browser_timer *hbt,
1248 struct perf_session_env *env)
d1b4f249 1249{
05e8b080
ACM
1250 struct hists *hists = &evsel->hists;
1251 struct hist_browser *browser = hist_browser__new(hists);
a68c2c58 1252 struct branch_info *bi;
d1b4f249 1253 struct pstack *fstack;
24bff2dc
SE
1254 char *options[16];
1255 int nr_options = 0;
d1b4f249 1256 int key = -1;
938a23ae 1257 char buf[64];
cdbab7c2 1258 char script_opt[64];
9783adf7 1259 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249
ACM
1260
1261 if (browser == NULL)
1262 return -1;
1263
1264 fstack = pstack__new(2);
1265 if (fstack == NULL)
1266 goto out;
1267
1268 ui_helpline__push(helpline);
1269
24bff2dc
SE
1270 memset(options, 0, sizeof(options));
1271
d1b4f249 1272 while (1) {
60098917
ACM
1273 const struct thread *thread = NULL;
1274 const struct dso *dso = NULL;
24bff2dc 1275 int choice = 0,
d1b4f249 1276 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1277 annotate_f = -2, annotate_t = -2, browse_map = -2;
cdbab7c2 1278 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
d1b4f249 1279
24bff2dc
SE
1280 nr_options = 0;
1281
9783adf7 1282 key = hist_browser__run(browser, ev_name, hbt);
d1b4f249 1283
60098917
ACM
1284 if (browser->he_selection != NULL) {
1285 thread = hist_browser__selected_thread(browser);
1286 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1287 }
b50e003d 1288 switch (key) {
cf958003
ACM
1289 case K_TAB:
1290 case K_UNTAB:
e4419b8e
DA
1291 if (nr_events == 1)
1292 continue;
b50e003d
ACM
1293 /*
1294 * Exit the browser, let hists__browser_tree
1295 * go to the next or previous
1296 */
1297 goto out_free_stack;
1298 case 'a':
a6e51f9f 1299 if (!browser->has_symbols) {
7b27509f 1300 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1301 "Annotation is only available for symbolic views, "
a68c2c58 1302 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1303 continue;
1304 }
1305
60098917 1306 if (browser->selection == NULL ||
db9a9cbc 1307 browser->selection->sym == NULL ||
b50e003d 1308 browser->selection->map->dso->annotate_warned)
d1b4f249 1309 continue;
b50e003d 1310 goto do_annotate;
aff3f3f6
ACM
1311 case 'P':
1312 hist_browser__dump(browser);
1313 continue;
b50e003d
ACM
1314 case 'd':
1315 goto zoom_dso;
a7cb8863
ACM
1316 case 'V':
1317 browser->show_dso = !browser->show_dso;
1318 continue;
b50e003d
ACM
1319 case 't':
1320 goto zoom_thread;
5a5626b1 1321 case '/':
938a23ae
NK
1322 if (ui_browser__input_window("Symbol to show",
1323 "Please enter the name of symbol you want to see",
1324 buf, "ENTER: OK, ESC: Cancel",
1325 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1326 hists->symbol_filter_str = *buf ? buf : NULL;
1327 hists__filter_by_symbol(hists);
938a23ae
NK
1328 hist_browser__reset(browser);
1329 }
1330 continue;
cdbab7c2 1331 case 'r':
9783adf7 1332 if (is_report_browser(hbt))
c77d8d70
FT
1333 goto do_scripts;
1334 continue;
cf958003 1335 case K_F1:
b50e003d
ACM
1336 case 'h':
1337 case '?':
4610e413
ACM
1338 ui_browser__help_window(&browser->b,
1339 "h/?/F1 Show this window\n"
2d5646c0
ACM
1340 "UP/DOWN/PGUP\n"
1341 "PGDN/SPACE Navigate\n"
1342 "q/ESC/CTRL+C Exit browser\n\n"
1343 "For multiple event sessions:\n\n"
1344 "TAB/UNTAB Switch events\n\n"
724c9c9f 1345 "For symbolic views (--sort has sym):\n\n"
2d5646c0
ACM
1346 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1347 "<- Zoom out\n"
1348 "a Annotate current symbol\n"
1349 "C Collapse all callchains\n"
1350 "E Expand all callchains\n"
1351 "d Zoom into current DSO\n"
938a23ae 1352 "t Zoom into current Thread\n"
c77d8d70 1353 "r Run available scripts('perf report' only)\n"
aff3f3f6 1354 "P Print histograms to perf.hist.N\n"
a7cb8863 1355 "V Verbose (DSO names in callchains, etc)\n"
5a5626b1 1356 "/ Filter symbol by name");
b50e003d 1357 continue;
cf958003
ACM
1358 case K_ENTER:
1359 case K_RIGHT:
b50e003d
ACM
1360 /* menu */
1361 break;
cf958003 1362 case K_LEFT: {
b50e003d 1363 const void *top;
d1b4f249 1364
7f0030b2
ACM
1365 if (pstack__empty(fstack)) {
1366 /*
1367 * Go back to the perf_evsel_menu__run or other user
1368 */
1369 if (left_exits)
1370 goto out_free_stack;
d1b4f249 1371 continue;
7f0030b2 1372 }
b50e003d 1373 top = pstack__pop(fstack);
d7b76f09 1374 if (top == &browser->hists->dso_filter)
b50e003d 1375 goto zoom_out_dso;
d7b76f09 1376 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1377 goto zoom_out_thread;
1378 continue;
1379 }
cf958003 1380 case K_ESC:
7f0030b2 1381 if (!left_exits &&
4610e413
ACM
1382 !ui_browser__dialog_yesno(&browser->b,
1383 "Do you really want to exit?"))
b50e003d
ACM
1384 continue;
1385 /* Fall thru */
ed7e5662
ACM
1386 case 'q':
1387 case CTRL('c'):
b50e003d 1388 goto out_free_stack;
ed7e5662
ACM
1389 default:
1390 continue;
d1b4f249
ACM
1391 }
1392
724c9c9f
ACM
1393 if (!browser->has_symbols)
1394 goto add_exit_option;
1395
a68c2c58
SE
1396 if (sort__branch_mode == 1) {
1397 bi = browser->he_selection->branch_info;
1398 if (browser->selection != NULL &&
1399 bi &&
1400 bi->from.sym != NULL &&
1401 !bi->from.map->dso->annotate_warned &&
1402 asprintf(&options[nr_options], "Annotate %s",
1403 bi->from.sym->name) > 0)
1404 annotate_f = nr_options++;
1405
1406 if (browser->selection != NULL &&
1407 bi &&
1408 bi->to.sym != NULL &&
1409 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1410 (bi->to.sym != bi->from.sym ||
1411 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1412 asprintf(&options[nr_options], "Annotate %s",
1413 bi->to.sym->name) > 0)
1414 annotate_t = nr_options++;
1415 } else {
1416
1417 if (browser->selection != NULL &&
1418 browser->selection->sym != NULL &&
1419 !browser->selection->map->dso->annotate_warned &&
1420 asprintf(&options[nr_options], "Annotate %s",
1421 browser->selection->sym->name) > 0)
1422 annotate = nr_options++;
1423 }
d1b4f249
ACM
1424
1425 if (thread != NULL &&
1426 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1427 (browser->hists->thread_filter ? "out of" : "into"),
d1b4f249
ACM
1428 (thread->comm_set ? thread->comm : ""),
1429 thread->pid) > 0)
1430 zoom_thread = nr_options++;
1431
1432 if (dso != NULL &&
1433 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1434 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1435 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1436 zoom_dso = nr_options++;
1437
60098917
ACM
1438 if (browser->selection != NULL &&
1439 browser->selection->map != NULL &&
d1b4f249
ACM
1440 asprintf(&options[nr_options], "Browse map details") > 0)
1441 browse_map = nr_options++;
cdbab7c2
FT
1442
1443 /* perf script support */
1444 if (browser->he_selection) {
1445 struct symbol *sym;
1446
1447 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1448 browser->he_selection->thread->comm) > 0)
1449 scripts_comm = nr_options++;
1450
1451 sym = browser->he_selection->ms.sym;
1452 if (sym && sym->namelen &&
1453 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1454 sym->name) > 0)
1455 scripts_symbol = nr_options++;
1456 }
1457
1458 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1459 scripts_all = nr_options++;
1460
724c9c9f 1461add_exit_option:
d1b4f249 1462 options[nr_options++] = (char *)"Exit";
24bff2dc 1463retry_popup_menu:
1e6dd077 1464 choice = ui__popup_menu(nr_options, options);
d1b4f249 1465
d1b4f249
ACM
1466 if (choice == nr_options - 1)
1467 break;
1468
24bff2dc
SE
1469 if (choice == -1) {
1470 free_popup_options(options, nr_options - 1);
d1b4f249 1471 continue;
24bff2dc 1472 }
d1b4f249 1473
a68c2c58 1474 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1475 struct hist_entry *he;
4610e413 1476 int err;
d1b4f249 1477do_annotate:
68d80758
NK
1478 if (!objdump_path && perf_session_env__lookup_objdump(env))
1479 continue;
1480
d1b4f249
ACM
1481 he = hist_browser__selected_entry(browser);
1482 if (he == NULL)
1483 continue;
a68c2c58
SE
1484
1485 /*
1486 * we stash the branch_info symbol + map into the
1487 * the ms so we don't have to rewrite all the annotation
1488 * code to use branch_info.
1489 * in branch mode, the ms struct is not used
1490 */
1491 if (choice == annotate_f) {
1492 he->ms.sym = he->branch_info->from.sym;
1493 he->ms.map = he->branch_info->from.map;
1494 } else if (choice == annotate_t) {
1495 he->ms.sym = he->branch_info->to.sym;
1496 he->ms.map = he->branch_info->to.map;
1497 }
1498
df71d95f
ACM
1499 /*
1500 * Don't let this be freed, say, by hists__decay_entry.
1501 */
1502 he->used = true;
9783adf7 1503 err = hist_entry__tui_annotate(he, evsel->idx, hbt);
df71d95f 1504 he->used = false;
24bff2dc
SE
1505 /*
1506 * offer option to annotate the other branch source or target
1507 * (if they exists) when returning from annotate
1508 */
1509 if ((err == 'q' || err == CTRL('c'))
1510 && annotate_t != -2 && annotate_f != -2)
1511 goto retry_popup_menu;
1512
900e14a8 1513 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1514 if (err)
1515 ui_browser__handle_resize(&browser->b);
24bff2dc 1516
d1b4f249
ACM
1517 } else if (choice == browse_map)
1518 map__browse(browser->selection->map);
1519 else if (choice == zoom_dso) {
1520zoom_dso:
d7b76f09
ACM
1521 if (browser->hists->dso_filter) {
1522 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1523zoom_out_dso:
1524 ui_helpline__pop();
d7b76f09 1525 browser->hists->dso_filter = NULL;
cc02c921 1526 sort_dso.elide = false;
d1b4f249
ACM
1527 } else {
1528 if (dso == NULL)
1529 continue;
1530 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1532 browser->hists->dso_filter = dso;
cc02c921 1533 sort_dso.elide = true;
d7b76f09 1534 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1535 }
05e8b080 1536 hists__filter_by_dso(hists);
d1b4f249
ACM
1537 hist_browser__reset(browser);
1538 } else if (choice == zoom_thread) {
1539zoom_thread:
d7b76f09
ACM
1540 if (browser->hists->thread_filter) {
1541 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1542zoom_out_thread:
1543 ui_helpline__pop();
d7b76f09 1544 browser->hists->thread_filter = NULL;
cc02c921 1545 sort_thread.elide = false;
d1b4f249
ACM
1546 } else {
1547 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1548 thread->comm_set ? thread->comm : "",
1549 thread->pid);
d7b76f09 1550 browser->hists->thread_filter = thread;
cc02c921 1551 sort_thread.elide = true;
d7b76f09 1552 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1553 }
05e8b080 1554 hists__filter_by_thread(hists);
d1b4f249
ACM
1555 hist_browser__reset(browser);
1556 }
cdbab7c2
FT
1557 /* perf scripts support */
1558 else if (choice == scripts_all || choice == scripts_comm ||
1559 choice == scripts_symbol) {
1560do_scripts:
1561 memset(script_opt, 0, 64);
1562
1563 if (choice == scripts_comm)
1564 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1565
1566 if (choice == scripts_symbol)
1567 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1568
1569 script_browse(script_opt);
1570 }
d1b4f249
ACM
1571 }
1572out_free_stack:
1573 pstack__delete(fstack);
1574out:
1575 hist_browser__delete(browser);
24bff2dc 1576 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1577 return key;
1578}
1579
7f0030b2
ACM
1580struct perf_evsel_menu {
1581 struct ui_browser b;
1582 struct perf_evsel *selection;
7b27509f 1583 bool lost_events, lost_events_warned;
68d80758 1584 struct perf_session_env *env;
7f0030b2
ACM
1585};
1586
1587static void perf_evsel_menu__write(struct ui_browser *browser,
1588 void *entry, int row)
1589{
1590 struct perf_evsel_menu *menu = container_of(browser,
1591 struct perf_evsel_menu, b);
1592 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1593 bool current_entry = ui_browser__is_current_entry(browser, row);
1594 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1595 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1596 char bf[256], unit;
7b27509f
ACM
1597 const char *warn = " ";
1598 size_t printed;
7f0030b2
ACM
1599
1600 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1601 HE_COLORSET_NORMAL);
1602
717e263f
NK
1603 if (symbol_conf.event_group && evsel->nr_members > 1) {
1604 struct perf_evsel *pos;
1605
1606 ev_name = perf_evsel__group_name(evsel);
1607
1608 for_each_group_member(pos, evsel) {
1609 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1610 }
1611 }
1612
7f0030b2 1613 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1614 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1615 unit, unit == ' ' ? "" : " ", ev_name);
1616 slsmg_printf("%s", bf);
1617
1618 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1619 if (nr_events != 0) {
1620 menu->lost_events = true;
1621 if (!current_entry)
1622 ui_browser__set_color(browser, HE_COLORSET_TOP);
1623 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1624 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1625 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1626 warn = bf;
1627 }
1628
1629 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1630
1631 if (current_entry)
1632 menu->selection = evsel;
1633}
1634
34958544
ACM
1635static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1636 int nr_events, const char *help,
9783adf7 1637 struct hist_browser_timer *hbt)
d1b4f249 1638{
7f0030b2 1639 struct perf_evlist *evlist = menu->b.priv;
e248de33 1640 struct perf_evsel *pos;
7f0030b2 1641 const char *ev_name, *title = "Available samples";
9783adf7 1642 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 1643 int key;
d1b4f249 1644
7f0030b2
ACM
1645 if (ui_browser__show(&menu->b, title,
1646 "ESC: exit, ENTER|->: Browse histograms") < 0)
1647 return -1;
1648
7f0030b2 1649 while (1) {
3af6e338 1650 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1651
1652 switch (key) {
cf958003 1653 case K_TIMER:
9783adf7 1654 hbt->timer(hbt->arg);
7b27509f
ACM
1655
1656 if (!menu->lost_events_warned && menu->lost_events) {
1657 ui_browser__warn_lost_events(&menu->b);
1658 menu->lost_events_warned = true;
1659 }
81cce8de 1660 continue;
cf958003
ACM
1661 case K_RIGHT:
1662 case K_ENTER:
7f0030b2
ACM
1663 if (!menu->selection)
1664 continue;
1665 pos = menu->selection;
1666browse_hists:
18eaf0b8
ACM
1667 perf_evlist__set_selected(evlist, pos);
1668 /*
1669 * Give the calling tool a chance to populate the non
1670 * default evsel resorted hists tree.
1671 */
9783adf7
NK
1672 if (hbt)
1673 hbt->timer(hbt->arg);
7289f83c 1674 ev_name = perf_evsel__name(pos);
34958544 1675 key = perf_evsel__hists_browse(pos, nr_events, help,
68d80758
NK
1676 ev_name, true, hbt,
1677 menu->env);
7f0030b2 1678 ui_browser__show_title(&menu->b, title);
18eaf0b8 1679 switch (key) {
cf958003 1680 case K_TAB:
18eaf0b8
ACM
1681 if (pos->node.next == &evlist->entries)
1682 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1683 else
1684 pos = list_entry(pos->node.next, struct perf_evsel, node);
1685 goto browse_hists;
cf958003 1686 case K_UNTAB:
18eaf0b8
ACM
1687 if (pos->node.prev == &evlist->entries)
1688 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1689 else
1690 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1691 goto browse_hists;
cf958003 1692 case K_ESC:
4610e413
ACM
1693 if (!ui_browser__dialog_yesno(&menu->b,
1694 "Do you really want to exit?"))
18eaf0b8
ACM
1695 continue;
1696 /* Fall thru */
1697 case 'q':
1698 case CTRL('c'):
1699 goto out;
1700 default:
1701 continue;
1702 }
cf958003 1703 case K_LEFT:
7f0030b2 1704 continue;
cf958003 1705 case K_ESC:
4610e413
ACM
1706 if (!ui_browser__dialog_yesno(&menu->b,
1707 "Do you really want to exit?"))
ed7e5662
ACM
1708 continue;
1709 /* Fall thru */
7f0030b2
ACM
1710 case 'q':
1711 case CTRL('c'):
1712 goto out;
d1b4f249 1713 default:
18eaf0b8 1714 continue;
d1b4f249
ACM
1715 }
1716 }
1717
7f0030b2
ACM
1718out:
1719 ui_browser__hide(&menu->b);
1720 return key;
1721}
1722
fc24d7c2
NK
1723static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1724 void *entry)
1725{
1726 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1727
1728 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1729 return true;
1730
1731 return false;
1732}
1733
7f0030b2 1734static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 1735 int nr_entries, const char *help,
68d80758
NK
1736 struct hist_browser_timer *hbt,
1737 struct perf_session_env *env)
7f0030b2
ACM
1738{
1739 struct perf_evsel *pos;
1740 struct perf_evsel_menu menu = {
1741 .b = {
1742 .entries = &evlist->entries,
1743 .refresh = ui_browser__list_head_refresh,
1744 .seek = ui_browser__list_head_seek,
1745 .write = perf_evsel_menu__write,
fc24d7c2
NK
1746 .filter = filter_group_entries,
1747 .nr_entries = nr_entries,
7f0030b2
ACM
1748 .priv = evlist,
1749 },
68d80758 1750 .env = env,
7f0030b2
ACM
1751 };
1752
1753 ui_helpline__push("Press ESC to exit");
1754
1755 list_for_each_entry(pos, &evlist->entries, node) {
7289f83c 1756 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
1757 size_t line_len = strlen(ev_name) + 7;
1758
1759 if (menu.b.width < line_len)
1760 menu.b.width = line_len;
7f0030b2
ACM
1761 }
1762
fc24d7c2 1763 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
1764}
1765
81cce8de 1766int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758
NK
1767 struct hist_browser_timer *hbt,
1768 struct perf_session_env *env)
7f0030b2 1769{
fc24d7c2
NK
1770 int nr_entries = evlist->nr_entries;
1771
1772single_entry:
1773 if (nr_entries == 1) {
7f0030b2
ACM
1774 struct perf_evsel *first = list_entry(evlist->entries.next,
1775 struct perf_evsel, node);
7289f83c 1776 const char *ev_name = perf_evsel__name(first);
fc24d7c2
NK
1777
1778 return perf_evsel__hists_browse(first, nr_entries, help,
68d80758 1779 ev_name, false, hbt, env);
7f0030b2
ACM
1780 }
1781
fc24d7c2
NK
1782 if (symbol_conf.event_group) {
1783 struct perf_evsel *pos;
1784
1785 nr_entries = 0;
1786 list_for_each_entry(pos, &evlist->entries, node)
1787 if (perf_evsel__is_group_leader(pos))
1788 nr_entries++;
1789
1790 if (nr_entries == 1)
1791 goto single_entry;
1792 }
1793
1794 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1795 hbt, env);
d1b4f249 1796}