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