perf report: Bypass non-leader events when event group is enabled
[deliverable/linux.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;
cc686280
AR
1192
1193 nr_samples = convert_unit(nr_samples, &unit);
1194 printed = scnprintf(bf, size,
1195 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1196 nr_samples, unit, ev_name, nr_events);
469917ce 1197
d1b4f249 1198
05e8b080 1199 if (hists->uid_filter_str)
0d37aa34 1200 printed += snprintf(bf + printed, size - printed,
05e8b080 1201 ", UID: %s", hists->uid_filter_str);
d1b4f249 1202 if (thread)
e7f01d1e 1203 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1204 ", Thread: %s(%d)",
1205 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
1206 thread->pid);
1207 if (dso)
e7f01d1e 1208 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1209 ", DSO: %s", dso->short_name);
1210 return printed;
d1b4f249
ACM
1211}
1212
24bff2dc
SE
1213static inline void free_popup_options(char **options, int n)
1214{
1215 int i;
1216
1217 for (i = 0; i < n; ++i) {
1218 free(options[i]);
1219 options[i] = NULL;
1220 }
1221}
1222
c77d8d70
FT
1223/* Check whether the browser is for 'top' or 'report' */
1224static inline bool is_report_browser(void *timer)
1225{
1226 return timer == NULL;
1227}
1228
34958544 1229static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
7f0030b2 1230 const char *helpline, const char *ev_name,
81cce8de 1231 bool left_exits,
68d80758
NK
1232 struct hist_browser_timer *hbt,
1233 struct perf_session_env *env)
d1b4f249 1234{
05e8b080
ACM
1235 struct hists *hists = &evsel->hists;
1236 struct hist_browser *browser = hist_browser__new(hists);
a68c2c58 1237 struct branch_info *bi;
d1b4f249 1238 struct pstack *fstack;
24bff2dc
SE
1239 char *options[16];
1240 int nr_options = 0;
d1b4f249 1241 int key = -1;
938a23ae 1242 char buf[64];
cdbab7c2 1243 char script_opt[64];
9783adf7 1244 int delay_secs = hbt ? hbt->refresh : 0;
d1b4f249
ACM
1245
1246 if (browser == NULL)
1247 return -1;
1248
1249 fstack = pstack__new(2);
1250 if (fstack == NULL)
1251 goto out;
1252
1253 ui_helpline__push(helpline);
1254
24bff2dc
SE
1255 memset(options, 0, sizeof(options));
1256
d1b4f249 1257 while (1) {
60098917
ACM
1258 const struct thread *thread = NULL;
1259 const struct dso *dso = NULL;
24bff2dc 1260 int choice = 0,
d1b4f249 1261 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1262 annotate_f = -2, annotate_t = -2, browse_map = -2;
cdbab7c2 1263 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
d1b4f249 1264
24bff2dc
SE
1265 nr_options = 0;
1266
9783adf7 1267 key = hist_browser__run(browser, ev_name, hbt);
d1b4f249 1268
60098917
ACM
1269 if (browser->he_selection != NULL) {
1270 thread = hist_browser__selected_thread(browser);
1271 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1272 }
b50e003d 1273 switch (key) {
cf958003
ACM
1274 case K_TAB:
1275 case K_UNTAB:
e4419b8e
DA
1276 if (nr_events == 1)
1277 continue;
b50e003d
ACM
1278 /*
1279 * Exit the browser, let hists__browser_tree
1280 * go to the next or previous
1281 */
1282 goto out_free_stack;
1283 case 'a':
a6e51f9f 1284 if (!browser->has_symbols) {
7b27509f 1285 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1286 "Annotation is only available for symbolic views, "
a68c2c58 1287 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1288 continue;
1289 }
1290
60098917 1291 if (browser->selection == NULL ||
db9a9cbc 1292 browser->selection->sym == NULL ||
b50e003d 1293 browser->selection->map->dso->annotate_warned)
d1b4f249 1294 continue;
b50e003d 1295 goto do_annotate;
aff3f3f6
ACM
1296 case 'P':
1297 hist_browser__dump(browser);
1298 continue;
b50e003d
ACM
1299 case 'd':
1300 goto zoom_dso;
a7cb8863
ACM
1301 case 'V':
1302 browser->show_dso = !browser->show_dso;
1303 continue;
b50e003d
ACM
1304 case 't':
1305 goto zoom_thread;
5a5626b1 1306 case '/':
938a23ae
NK
1307 if (ui_browser__input_window("Symbol to show",
1308 "Please enter the name of symbol you want to see",
1309 buf, "ENTER: OK, ESC: Cancel",
1310 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1311 hists->symbol_filter_str = *buf ? buf : NULL;
1312 hists__filter_by_symbol(hists);
938a23ae
NK
1313 hist_browser__reset(browser);
1314 }
1315 continue;
cdbab7c2 1316 case 'r':
9783adf7 1317 if (is_report_browser(hbt))
c77d8d70
FT
1318 goto do_scripts;
1319 continue;
cf958003 1320 case K_F1:
b50e003d
ACM
1321 case 'h':
1322 case '?':
4610e413
ACM
1323 ui_browser__help_window(&browser->b,
1324 "h/?/F1 Show this window\n"
2d5646c0
ACM
1325 "UP/DOWN/PGUP\n"
1326 "PGDN/SPACE Navigate\n"
1327 "q/ESC/CTRL+C Exit browser\n\n"
1328 "For multiple event sessions:\n\n"
1329 "TAB/UNTAB Switch events\n\n"
724c9c9f 1330 "For symbolic views (--sort has sym):\n\n"
2d5646c0
ACM
1331 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1332 "<- Zoom out\n"
1333 "a Annotate current symbol\n"
1334 "C Collapse all callchains\n"
1335 "E Expand all callchains\n"
1336 "d Zoom into current DSO\n"
938a23ae 1337 "t Zoom into current Thread\n"
c77d8d70 1338 "r Run available scripts('perf report' only)\n"
aff3f3f6 1339 "P Print histograms to perf.hist.N\n"
a7cb8863 1340 "V Verbose (DSO names in callchains, etc)\n"
5a5626b1 1341 "/ Filter symbol by name");
b50e003d 1342 continue;
cf958003
ACM
1343 case K_ENTER:
1344 case K_RIGHT:
b50e003d
ACM
1345 /* menu */
1346 break;
cf958003 1347 case K_LEFT: {
b50e003d 1348 const void *top;
d1b4f249 1349
7f0030b2
ACM
1350 if (pstack__empty(fstack)) {
1351 /*
1352 * Go back to the perf_evsel_menu__run or other user
1353 */
1354 if (left_exits)
1355 goto out_free_stack;
d1b4f249 1356 continue;
7f0030b2 1357 }
b50e003d 1358 top = pstack__pop(fstack);
d7b76f09 1359 if (top == &browser->hists->dso_filter)
b50e003d 1360 goto zoom_out_dso;
d7b76f09 1361 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1362 goto zoom_out_thread;
1363 continue;
1364 }
cf958003 1365 case K_ESC:
7f0030b2 1366 if (!left_exits &&
4610e413
ACM
1367 !ui_browser__dialog_yesno(&browser->b,
1368 "Do you really want to exit?"))
b50e003d
ACM
1369 continue;
1370 /* Fall thru */
ed7e5662
ACM
1371 case 'q':
1372 case CTRL('c'):
b50e003d 1373 goto out_free_stack;
ed7e5662
ACM
1374 default:
1375 continue;
d1b4f249
ACM
1376 }
1377
724c9c9f
ACM
1378 if (!browser->has_symbols)
1379 goto add_exit_option;
1380
a68c2c58
SE
1381 if (sort__branch_mode == 1) {
1382 bi = browser->he_selection->branch_info;
1383 if (browser->selection != NULL &&
1384 bi &&
1385 bi->from.sym != NULL &&
1386 !bi->from.map->dso->annotate_warned &&
1387 asprintf(&options[nr_options], "Annotate %s",
1388 bi->from.sym->name) > 0)
1389 annotate_f = nr_options++;
1390
1391 if (browser->selection != NULL &&
1392 bi &&
1393 bi->to.sym != NULL &&
1394 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1395 (bi->to.sym != bi->from.sym ||
1396 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1397 asprintf(&options[nr_options], "Annotate %s",
1398 bi->to.sym->name) > 0)
1399 annotate_t = nr_options++;
1400 } else {
1401
1402 if (browser->selection != NULL &&
1403 browser->selection->sym != NULL &&
1404 !browser->selection->map->dso->annotate_warned &&
1405 asprintf(&options[nr_options], "Annotate %s",
1406 browser->selection->sym->name) > 0)
1407 annotate = nr_options++;
1408 }
d1b4f249
ACM
1409
1410 if (thread != NULL &&
1411 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1412 (browser->hists->thread_filter ? "out of" : "into"),
d1b4f249
ACM
1413 (thread->comm_set ? thread->comm : ""),
1414 thread->pid) > 0)
1415 zoom_thread = nr_options++;
1416
1417 if (dso != NULL &&
1418 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1419 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1420 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1421 zoom_dso = nr_options++;
1422
60098917
ACM
1423 if (browser->selection != NULL &&
1424 browser->selection->map != NULL &&
d1b4f249
ACM
1425 asprintf(&options[nr_options], "Browse map details") > 0)
1426 browse_map = nr_options++;
cdbab7c2
FT
1427
1428 /* perf script support */
1429 if (browser->he_selection) {
1430 struct symbol *sym;
1431
1432 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1433 browser->he_selection->thread->comm) > 0)
1434 scripts_comm = nr_options++;
1435
1436 sym = browser->he_selection->ms.sym;
1437 if (sym && sym->namelen &&
1438 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1439 sym->name) > 0)
1440 scripts_symbol = nr_options++;
1441 }
1442
1443 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1444 scripts_all = nr_options++;
1445
724c9c9f 1446add_exit_option:
d1b4f249 1447 options[nr_options++] = (char *)"Exit";
24bff2dc 1448retry_popup_menu:
1e6dd077 1449 choice = ui__popup_menu(nr_options, options);
d1b4f249 1450
d1b4f249
ACM
1451 if (choice == nr_options - 1)
1452 break;
1453
24bff2dc
SE
1454 if (choice == -1) {
1455 free_popup_options(options, nr_options - 1);
d1b4f249 1456 continue;
24bff2dc 1457 }
d1b4f249 1458
a68c2c58 1459 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1460 struct hist_entry *he;
4610e413 1461 int err;
d1b4f249 1462do_annotate:
68d80758
NK
1463 if (!objdump_path && perf_session_env__lookup_objdump(env))
1464 continue;
1465
d1b4f249
ACM
1466 he = hist_browser__selected_entry(browser);
1467 if (he == NULL)
1468 continue;
a68c2c58
SE
1469
1470 /*
1471 * we stash the branch_info symbol + map into the
1472 * the ms so we don't have to rewrite all the annotation
1473 * code to use branch_info.
1474 * in branch mode, the ms struct is not used
1475 */
1476 if (choice == annotate_f) {
1477 he->ms.sym = he->branch_info->from.sym;
1478 he->ms.map = he->branch_info->from.map;
1479 } else if (choice == annotate_t) {
1480 he->ms.sym = he->branch_info->to.sym;
1481 he->ms.map = he->branch_info->to.map;
1482 }
1483
df71d95f
ACM
1484 /*
1485 * Don't let this be freed, say, by hists__decay_entry.
1486 */
1487 he->used = true;
9783adf7 1488 err = hist_entry__tui_annotate(he, evsel->idx, hbt);
df71d95f 1489 he->used = false;
24bff2dc
SE
1490 /*
1491 * offer option to annotate the other branch source or target
1492 * (if they exists) when returning from annotate
1493 */
1494 if ((err == 'q' || err == CTRL('c'))
1495 && annotate_t != -2 && annotate_f != -2)
1496 goto retry_popup_menu;
1497
900e14a8 1498 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1499 if (err)
1500 ui_browser__handle_resize(&browser->b);
24bff2dc 1501
d1b4f249
ACM
1502 } else if (choice == browse_map)
1503 map__browse(browser->selection->map);
1504 else if (choice == zoom_dso) {
1505zoom_dso:
d7b76f09
ACM
1506 if (browser->hists->dso_filter) {
1507 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1508zoom_out_dso:
1509 ui_helpline__pop();
d7b76f09 1510 browser->hists->dso_filter = NULL;
cc02c921 1511 sort_dso.elide = false;
d1b4f249
ACM
1512 } else {
1513 if (dso == NULL)
1514 continue;
1515 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1516 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1517 browser->hists->dso_filter = dso;
cc02c921 1518 sort_dso.elide = true;
d7b76f09 1519 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1520 }
05e8b080 1521 hists__filter_by_dso(hists);
d1b4f249
ACM
1522 hist_browser__reset(browser);
1523 } else if (choice == zoom_thread) {
1524zoom_thread:
d7b76f09
ACM
1525 if (browser->hists->thread_filter) {
1526 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1527zoom_out_thread:
1528 ui_helpline__pop();
d7b76f09 1529 browser->hists->thread_filter = NULL;
cc02c921 1530 sort_thread.elide = false;
d1b4f249
ACM
1531 } else {
1532 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1533 thread->comm_set ? thread->comm : "",
1534 thread->pid);
d7b76f09 1535 browser->hists->thread_filter = thread;
cc02c921 1536 sort_thread.elide = true;
d7b76f09 1537 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1538 }
05e8b080 1539 hists__filter_by_thread(hists);
d1b4f249
ACM
1540 hist_browser__reset(browser);
1541 }
cdbab7c2
FT
1542 /* perf scripts support */
1543 else if (choice == scripts_all || choice == scripts_comm ||
1544 choice == scripts_symbol) {
1545do_scripts:
1546 memset(script_opt, 0, 64);
1547
1548 if (choice == scripts_comm)
1549 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1550
1551 if (choice == scripts_symbol)
1552 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1553
1554 script_browse(script_opt);
1555 }
d1b4f249
ACM
1556 }
1557out_free_stack:
1558 pstack__delete(fstack);
1559out:
1560 hist_browser__delete(browser);
24bff2dc 1561 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1562 return key;
1563}
1564
7f0030b2
ACM
1565struct perf_evsel_menu {
1566 struct ui_browser b;
1567 struct perf_evsel *selection;
7b27509f 1568 bool lost_events, lost_events_warned;
68d80758 1569 struct perf_session_env *env;
7f0030b2
ACM
1570};
1571
1572static void perf_evsel_menu__write(struct ui_browser *browser,
1573 void *entry, int row)
1574{
1575 struct perf_evsel_menu *menu = container_of(browser,
1576 struct perf_evsel_menu, b);
1577 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1578 bool current_entry = ui_browser__is_current_entry(browser, row);
1579 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1580 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1581 char bf[256], unit;
7b27509f
ACM
1582 const char *warn = " ";
1583 size_t printed;
7f0030b2
ACM
1584
1585 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1586 HE_COLORSET_NORMAL);
1587
1588 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1589 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1590 unit, unit == ' ' ? "" : " ", ev_name);
1591 slsmg_printf("%s", bf);
1592
1593 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1594 if (nr_events != 0) {
1595 menu->lost_events = true;
1596 if (!current_entry)
1597 ui_browser__set_color(browser, HE_COLORSET_TOP);
1598 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1599 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1600 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1601 warn = bf;
1602 }
1603
1604 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1605
1606 if (current_entry)
1607 menu->selection = evsel;
1608}
1609
34958544
ACM
1610static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1611 int nr_events, const char *help,
9783adf7 1612 struct hist_browser_timer *hbt)
d1b4f249 1613{
7f0030b2 1614 struct perf_evlist *evlist = menu->b.priv;
e248de33 1615 struct perf_evsel *pos;
7f0030b2 1616 const char *ev_name, *title = "Available samples";
9783adf7 1617 int delay_secs = hbt ? hbt->refresh : 0;
7f0030b2 1618 int key;
d1b4f249 1619
7f0030b2
ACM
1620 if (ui_browser__show(&menu->b, title,
1621 "ESC: exit, ENTER|->: Browse histograms") < 0)
1622 return -1;
1623
7f0030b2 1624 while (1) {
3af6e338 1625 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1626
1627 switch (key) {
cf958003 1628 case K_TIMER:
9783adf7 1629 hbt->timer(hbt->arg);
7b27509f
ACM
1630
1631 if (!menu->lost_events_warned && menu->lost_events) {
1632 ui_browser__warn_lost_events(&menu->b);
1633 menu->lost_events_warned = true;
1634 }
81cce8de 1635 continue;
cf958003
ACM
1636 case K_RIGHT:
1637 case K_ENTER:
7f0030b2
ACM
1638 if (!menu->selection)
1639 continue;
1640 pos = menu->selection;
1641browse_hists:
18eaf0b8
ACM
1642 perf_evlist__set_selected(evlist, pos);
1643 /*
1644 * Give the calling tool a chance to populate the non
1645 * default evsel resorted hists tree.
1646 */
9783adf7
NK
1647 if (hbt)
1648 hbt->timer(hbt->arg);
7289f83c 1649 ev_name = perf_evsel__name(pos);
34958544 1650 key = perf_evsel__hists_browse(pos, nr_events, help,
68d80758
NK
1651 ev_name, true, hbt,
1652 menu->env);
7f0030b2 1653 ui_browser__show_title(&menu->b, title);
18eaf0b8 1654 switch (key) {
cf958003 1655 case K_TAB:
18eaf0b8
ACM
1656 if (pos->node.next == &evlist->entries)
1657 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1658 else
1659 pos = list_entry(pos->node.next, struct perf_evsel, node);
1660 goto browse_hists;
cf958003 1661 case K_UNTAB:
18eaf0b8
ACM
1662 if (pos->node.prev == &evlist->entries)
1663 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1664 else
1665 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1666 goto browse_hists;
cf958003 1667 case K_ESC:
4610e413
ACM
1668 if (!ui_browser__dialog_yesno(&menu->b,
1669 "Do you really want to exit?"))
18eaf0b8
ACM
1670 continue;
1671 /* Fall thru */
1672 case 'q':
1673 case CTRL('c'):
1674 goto out;
1675 default:
1676 continue;
1677 }
cf958003 1678 case K_LEFT:
7f0030b2 1679 continue;
cf958003 1680 case K_ESC:
4610e413
ACM
1681 if (!ui_browser__dialog_yesno(&menu->b,
1682 "Do you really want to exit?"))
ed7e5662
ACM
1683 continue;
1684 /* Fall thru */
7f0030b2
ACM
1685 case 'q':
1686 case CTRL('c'):
1687 goto out;
d1b4f249 1688 default:
18eaf0b8 1689 continue;
d1b4f249
ACM
1690 }
1691 }
1692
7f0030b2
ACM
1693out:
1694 ui_browser__hide(&menu->b);
1695 return key;
1696}
1697
fc24d7c2
NK
1698static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1699 void *entry)
1700{
1701 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1702
1703 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1704 return true;
1705
1706 return false;
1707}
1708
7f0030b2 1709static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
fc24d7c2 1710 int nr_entries, const char *help,
68d80758
NK
1711 struct hist_browser_timer *hbt,
1712 struct perf_session_env *env)
7f0030b2
ACM
1713{
1714 struct perf_evsel *pos;
1715 struct perf_evsel_menu menu = {
1716 .b = {
1717 .entries = &evlist->entries,
1718 .refresh = ui_browser__list_head_refresh,
1719 .seek = ui_browser__list_head_seek,
1720 .write = perf_evsel_menu__write,
fc24d7c2
NK
1721 .filter = filter_group_entries,
1722 .nr_entries = nr_entries,
7f0030b2
ACM
1723 .priv = evlist,
1724 },
68d80758 1725 .env = env,
7f0030b2
ACM
1726 };
1727
1728 ui_helpline__push("Press ESC to exit");
1729
1730 list_for_each_entry(pos, &evlist->entries, node) {
7289f83c 1731 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
1732 size_t line_len = strlen(ev_name) + 7;
1733
1734 if (menu.b.width < line_len)
1735 menu.b.width = line_len;
7f0030b2
ACM
1736 }
1737
fc24d7c2 1738 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
7f0030b2
ACM
1739}
1740
81cce8de 1741int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
68d80758
NK
1742 struct hist_browser_timer *hbt,
1743 struct perf_session_env *env)
7f0030b2 1744{
fc24d7c2
NK
1745 int nr_entries = evlist->nr_entries;
1746
1747single_entry:
1748 if (nr_entries == 1) {
7f0030b2
ACM
1749 struct perf_evsel *first = list_entry(evlist->entries.next,
1750 struct perf_evsel, node);
7289f83c 1751 const char *ev_name = perf_evsel__name(first);
fc24d7c2
NK
1752
1753 return perf_evsel__hists_browse(first, nr_entries, help,
68d80758 1754 ev_name, false, hbt, env);
7f0030b2
ACM
1755 }
1756
fc24d7c2
NK
1757 if (symbol_conf.event_group) {
1758 struct perf_evsel *pos;
1759
1760 nr_entries = 0;
1761 list_for_each_entry(pos, &evlist->entries, node)
1762 if (perf_evsel__is_group_leader(pos))
1763 nr_entries++;
1764
1765 if (nr_entries == 1)
1766 goto single_entry;
1767 }
1768
1769 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1770 hbt, env);
d1b4f249 1771}
This page took 0.215257 seconds and 5 git commands to generate.