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