]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - tools/perf/ui/browsers/hists.c
perf tools: Remove warnings on JIT samples for srcline sort key
[mirror_ubuntu-artful-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
ACM
612 off_t row_offset = entry->row_offset;
613
614 if (current_entry) {
05e8b080
ACM
615 browser->he_selection = entry;
616 browser->selection = &entry->ms;
d1b4f249
ACM
617 }
618
619 if (symbol_conf.use_callchain) {
163caed9 620 hist_entry__init_have_children(entry);
d1b4f249
ACM
621 folded_sign = hist_entry__folded(entry);
622 }
623
624 if (row_offset == 0) {
f5951d56
NK
625 struct perf_hpp hpp = {
626 .buf = s,
627 .size = sizeof(s),
f5951d56 628 };
d1b4f249 629
67d25916 630 ui_browser__gotorc(&browser->b, row, 0);
c172f742 631
f5951d56
NK
632 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
633 if (!perf_hpp__format[i].cond)
634 continue;
f1cf602c 635
f5951d56
NK
636 if (i) {
637 slsmg_printf(" ");
638 width -= 2;
639 }
c172f742 640
f5951d56
NK
641 if (perf_hpp__format[i].color) {
642 hpp.ptr = &percent;
643 /* It will set percent for us. See HPP__COLOR_FN above. */
644 width -= perf_hpp__format[i].color(&hpp, entry);
33f62b3f 645
f5951d56
NK
646 ui_browser__set_percent_color(&browser->b, percent, current_entry);
647
648 if (i == 0 && symbol_conf.use_callchain) {
649 slsmg_printf("%c ", folded_sign);
650 width -= 2;
651 }
652
653 slsmg_printf("%s", s);
2cf9cebf 654
f5951d56
NK
655 if (!current_entry || !browser->b.navkeypressed)
656 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
657 } else {
658 width -= perf_hpp__format[i].entry(&hpp, entry);
659 slsmg_printf("%s", s);
660 }
2cf9cebf
ACM
661 }
662
f5951d56
NK
663 /* The scroll bar isn't being used */
664 if (!browser->b.navkeypressed)
665 width += 1;
666
667 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
d1b4f249
ACM
668 slsmg_write_nstring(s, width);
669 ++row;
670 ++printed;
671 } else
672 --row_offset;
673
05e8b080
ACM
674 if (folded_sign == '-' && row != browser->b.height) {
675 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
d1b4f249
ACM
676 1, row, &row_offset,
677 &current_entry);
678 if (current_entry)
05e8b080 679 browser->he_selection = entry;
d1b4f249
ACM
680 }
681
682 return printed;
683}
684
437cfe7a
ACM
685static void ui_browser__hists_init_top(struct ui_browser *browser)
686{
687 if (browser->top == NULL) {
688 struct hist_browser *hb;
689
690 hb = container_of(browser, struct hist_browser, b);
691 browser->top = rb_first(&hb->hists->entries);
692 }
693}
694
05e8b080 695static unsigned int hist_browser__refresh(struct ui_browser *browser)
d1b4f249
ACM
696{
697 unsigned row = 0;
698 struct rb_node *nd;
05e8b080 699 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
d1b4f249 700
05e8b080 701 ui_browser__hists_init_top(browser);
d1b4f249 702
05e8b080 703 for (nd = browser->top; nd; nd = rb_next(nd)) {
d1b4f249
ACM
704 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
705
706 if (h->filtered)
707 continue;
708
709 row += hist_browser__show_entry(hb, h, row);
05e8b080 710 if (row == browser->height)
d1b4f249
ACM
711 break;
712 }
713
714 return row;
715}
716
717static struct rb_node *hists__filter_entries(struct rb_node *nd)
718{
719 while (nd != NULL) {
720 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
721 if (!h->filtered)
722 return nd;
723
724 nd = rb_next(nd);
725 }
726
727 return NULL;
728}
729
730static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
731{
732 while (nd != NULL) {
733 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
734 if (!h->filtered)
735 return nd;
736
737 nd = rb_prev(nd);
738 }
739
740 return NULL;
741}
742
05e8b080 743static void ui_browser__hists_seek(struct ui_browser *browser,
d1b4f249
ACM
744 off_t offset, int whence)
745{
746 struct hist_entry *h;
747 struct rb_node *nd;
748 bool first = true;
749
05e8b080 750 if (browser->nr_entries == 0)
60098917
ACM
751 return;
752
05e8b080 753 ui_browser__hists_init_top(browser);
437cfe7a 754
d1b4f249
ACM
755 switch (whence) {
756 case SEEK_SET:
05e8b080 757 nd = hists__filter_entries(rb_first(browser->entries));
d1b4f249
ACM
758 break;
759 case SEEK_CUR:
05e8b080 760 nd = browser->top;
d1b4f249
ACM
761 goto do_offset;
762 case SEEK_END:
05e8b080 763 nd = hists__filter_prev_entries(rb_last(browser->entries));
d1b4f249
ACM
764 first = false;
765 break;
766 default:
767 return;
768 }
769
770 /*
771 * Moves not relative to the first visible entry invalidates its
772 * row_offset:
773 */
05e8b080 774 h = rb_entry(browser->top, struct hist_entry, rb_node);
d1b4f249
ACM
775 h->row_offset = 0;
776
777 /*
778 * Here we have to check if nd is expanded (+), if it is we can't go
779 * the next top level hist_entry, instead we must compute an offset of
780 * what _not_ to show and not change the first visible entry.
781 *
782 * This offset increments when we are going from top to bottom and
783 * decreases when we're going from bottom to top.
784 *
785 * As we don't have backpointers to the top level in the callchains
786 * structure, we need to always print the whole hist_entry callchain,
787 * skipping the first ones that are before the first visible entry
788 * and stop when we printed enough lines to fill the screen.
789 */
790do_offset:
791 if (offset > 0) {
792 do {
793 h = rb_entry(nd, struct hist_entry, rb_node);
794 if (h->ms.unfolded) {
795 u16 remaining = h->nr_rows - h->row_offset;
796 if (offset > remaining) {
797 offset -= remaining;
798 h->row_offset = 0;
799 } else {
800 h->row_offset += offset;
801 offset = 0;
05e8b080 802 browser->top = nd;
d1b4f249
ACM
803 break;
804 }
805 }
806 nd = hists__filter_entries(rb_next(nd));
807 if (nd == NULL)
808 break;
809 --offset;
05e8b080 810 browser->top = nd;
d1b4f249
ACM
811 } while (offset != 0);
812 } else if (offset < 0) {
813 while (1) {
814 h = rb_entry(nd, struct hist_entry, rb_node);
815 if (h->ms.unfolded) {
816 if (first) {
817 if (-offset > h->row_offset) {
818 offset += h->row_offset;
819 h->row_offset = 0;
820 } else {
821 h->row_offset += offset;
822 offset = 0;
05e8b080 823 browser->top = nd;
d1b4f249
ACM
824 break;
825 }
826 } else {
827 if (-offset > h->nr_rows) {
828 offset += h->nr_rows;
829 h->row_offset = 0;
830 } else {
831 h->row_offset = h->nr_rows + offset;
832 offset = 0;
05e8b080 833 browser->top = nd;
d1b4f249
ACM
834 break;
835 }
836 }
837 }
838
839 nd = hists__filter_prev_entries(rb_prev(nd));
840 if (nd == NULL)
841 break;
842 ++offset;
05e8b080 843 browser->top = nd;
d1b4f249
ACM
844 if (offset == 0) {
845 /*
846 * Last unfiltered hist_entry, check if it is
847 * unfolded, if it is then we should have
848 * row_offset at its last entry.
849 */
850 h = rb_entry(nd, struct hist_entry, rb_node);
851 if (h->ms.unfolded)
852 h->row_offset = h->nr_rows;
853 break;
854 }
855 first = false;
856 }
857 } else {
05e8b080 858 browser->top = nd;
d1b4f249
ACM
859 h = rb_entry(nd, struct hist_entry, rb_node);
860 h->row_offset = 0;
861 }
862}
863
aff3f3f6
ACM
864static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
865 struct callchain_node *chain_node,
866 u64 total, int level,
867 FILE *fp)
868{
869 struct rb_node *node;
870 int offset = level * LEVEL_OFFSET_STEP;
871 u64 new_total, remaining;
872 int printed = 0;
873
874 if (callchain_param.mode == CHAIN_GRAPH_REL)
875 new_total = chain_node->children_hit;
876 else
877 new_total = total;
878
879 remaining = new_total;
880 node = rb_first(&chain_node->rb_root);
881 while (node) {
882 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
883 struct rb_node *next = rb_next(node);
884 u64 cumul = callchain_cumul_hits(child);
885 struct callchain_list *chain;
886 char folded_sign = ' ';
887 int first = true;
888 int extra_offset = 0;
889
890 remaining -= cumul;
891
892 list_for_each_entry(chain, &child->val, list) {
a7cb8863 893 char bf[1024], *alloc_str;
aff3f3f6
ACM
894 const char *str;
895 bool was_first = first;
896
897 if (first)
898 first = false;
899 else
900 extra_offset = LEVEL_OFFSET_STEP;
901
902 folded_sign = callchain_list__folded(chain);
903
904 alloc_str = NULL;
a7cb8863
ACM
905 str = callchain_list__sym_name(chain, bf, sizeof(bf),
906 browser->show_dso);
aff3f3f6
ACM
907 if (was_first) {
908 double percent = cumul * 100.0 / new_total;
909
910 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
911 str = "Not enough memory!";
912 else
913 str = alloc_str;
914 }
915
916 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
917 free(alloc_str);
918 if (folded_sign == '+')
919 break;
920 }
921
922 if (folded_sign == '-') {
923 const int new_level = level + (extra_offset ? 2 : 1);
924 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
925 new_level, fp);
926 }
927
928 node = next;
929 }
930
931 return printed;
932}
933
934static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
935 struct callchain_node *node,
936 int level, FILE *fp)
937{
938 struct callchain_list *chain;
939 int offset = level * LEVEL_OFFSET_STEP;
940 char folded_sign = ' ';
941 int printed = 0;
942
943 list_for_each_entry(chain, &node->val, list) {
a7cb8863 944 char bf[1024], *s;
aff3f3f6
ACM
945
946 folded_sign = callchain_list__folded(chain);
a7cb8863 947 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
aff3f3f6
ACM
948 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
949 }
950
951 if (folded_sign == '-')
952 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
953 browser->hists->stats.total_period,
954 level + 1, fp);
955 return printed;
956}
957
958static int hist_browser__fprintf_callchain(struct hist_browser *browser,
959 struct rb_root *chain, int level, FILE *fp)
960{
961 struct rb_node *nd;
962 int printed = 0;
963
964 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
965 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
966
967 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
968 }
969
970 return printed;
971}
972
973static int hist_browser__fprintf_entry(struct hist_browser *browser,
974 struct hist_entry *he, FILE *fp)
975{
976 char s[8192];
977 double percent;
978 int printed = 0;
979 char folded_sign = ' ';
980
981 if (symbol_conf.use_callchain)
982 folded_sign = hist_entry__folded(he);
983
000078bc 984 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
b24c28f7 985 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
aff3f3f6
ACM
986
987 if (symbol_conf.use_callchain)
988 printed += fprintf(fp, "%c ", folded_sign);
989
990 printed += fprintf(fp, " %5.2f%%", percent);
991
992 if (symbol_conf.show_nr_samples)
b24c28f7 993 printed += fprintf(fp, " %11u", he->stat.nr_events);
aff3f3f6
ACM
994
995 if (symbol_conf.show_total_period)
b24c28f7 996 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
aff3f3f6
ACM
997
998 printed += fprintf(fp, "%s\n", rtrim(s));
999
1000 if (folded_sign == '-')
1001 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1002
1003 return printed;
1004}
1005
1006static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1007{
1008 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1009 int printed = 0;
1010
1011 while (nd) {
1012 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1013
1014 printed += hist_browser__fprintf_entry(browser, h, fp);
1015 nd = hists__filter_entries(rb_next(nd));
1016 }
1017
1018 return printed;
1019}
1020
1021static int hist_browser__dump(struct hist_browser *browser)
1022{
1023 char filename[64];
1024 FILE *fp;
1025
1026 while (1) {
1027 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1028 if (access(filename, F_OK))
1029 break;
1030 /*
1031 * XXX: Just an arbitrary lazy upper limit
1032 */
1033 if (++browser->print_seq == 8192) {
1034 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1035 return -1;
1036 }
1037 }
1038
1039 fp = fopen(filename, "w");
1040 if (fp == NULL) {
1041 char bf[64];
4cc49d4d
KS
1042 const char *err = strerror_r(errno, bf, sizeof(bf));
1043 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
aff3f3f6
ACM
1044 return -1;
1045 }
1046
1047 ++browser->print_seq;
1048 hist_browser__fprintf(browser, fp);
1049 fclose(fp);
1050 ui_helpline__fpush("%s written!", filename);
1051
1052 return 0;
1053}
1054
d1b4f249
ACM
1055static struct hist_browser *hist_browser__new(struct hists *hists)
1056{
05e8b080 1057 struct hist_browser *browser = zalloc(sizeof(*browser));
d1b4f249 1058
05e8b080
ACM
1059 if (browser) {
1060 browser->hists = hists;
1061 browser->b.refresh = hist_browser__refresh;
1062 browser->b.seek = ui_browser__hists_seek;
1063 browser->b.use_navkeypressed = true;
a68c2c58 1064 if (sort__branch_mode == 1)
05e8b080 1065 browser->has_symbols = sort_sym_from.list.next != NULL;
a68c2c58 1066 else
05e8b080 1067 browser->has_symbols = sort_sym.list.next != NULL;
d1b4f249
ACM
1068 }
1069
05e8b080 1070 return browser;
d1b4f249
ACM
1071}
1072
05e8b080 1073static void hist_browser__delete(struct hist_browser *browser)
d1b4f249 1074{
05e8b080 1075 free(browser);
d1b4f249
ACM
1076}
1077
05e8b080 1078static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
d1b4f249 1079{
05e8b080 1080 return browser->he_selection;
d1b4f249
ACM
1081}
1082
05e8b080 1083static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
d1b4f249 1084{
05e8b080 1085 return browser->he_selection->thread;
d1b4f249
ACM
1086}
1087
05e8b080 1088static int hists__browser_title(struct hists *hists, char *bf, size_t size,
d7b76f09 1089 const char *ev_name)
d1b4f249 1090{
469917ce
ACM
1091 char unit;
1092 int printed;
05e8b080
ACM
1093 const struct dso *dso = hists->dso_filter;
1094 const struct thread *thread = hists->thread_filter;
1095 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1096 u64 nr_events = hists->stats.total_period;
cc686280
AR
1097
1098 nr_samples = convert_unit(nr_samples, &unit);
1099 printed = scnprintf(bf, size,
1100 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1101 nr_samples, unit, ev_name, nr_events);
469917ce 1102
d1b4f249 1103
05e8b080 1104 if (hists->uid_filter_str)
0d37aa34 1105 printed += snprintf(bf + printed, size - printed,
05e8b080 1106 ", UID: %s", hists->uid_filter_str);
d1b4f249 1107 if (thread)
e7f01d1e 1108 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1109 ", Thread: %s(%d)",
1110 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
1111 thread->pid);
1112 if (dso)
e7f01d1e 1113 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
1114 ", DSO: %s", dso->short_name);
1115 return printed;
d1b4f249
ACM
1116}
1117
24bff2dc
SE
1118static inline void free_popup_options(char **options, int n)
1119{
1120 int i;
1121
1122 for (i = 0; i < n; ++i) {
1123 free(options[i]);
1124 options[i] = NULL;
1125 }
1126}
1127
34958544 1128static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
7f0030b2 1129 const char *helpline, const char *ev_name,
81cce8de
ACM
1130 bool left_exits,
1131 void(*timer)(void *arg), void *arg,
1132 int delay_secs)
d1b4f249 1133{
05e8b080
ACM
1134 struct hists *hists = &evsel->hists;
1135 struct hist_browser *browser = hist_browser__new(hists);
a68c2c58 1136 struct branch_info *bi;
d1b4f249 1137 struct pstack *fstack;
24bff2dc
SE
1138 char *options[16];
1139 int nr_options = 0;
d1b4f249 1140 int key = -1;
938a23ae 1141 char buf[64];
d1b4f249
ACM
1142
1143 if (browser == NULL)
1144 return -1;
1145
1146 fstack = pstack__new(2);
1147 if (fstack == NULL)
1148 goto out;
1149
1150 ui_helpline__push(helpline);
1151
24bff2dc
SE
1152 memset(options, 0, sizeof(options));
1153
d1b4f249 1154 while (1) {
60098917
ACM
1155 const struct thread *thread = NULL;
1156 const struct dso *dso = NULL;
24bff2dc 1157 int choice = 0,
d1b4f249 1158 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 1159 annotate_f = -2, annotate_t = -2, browse_map = -2;
d1b4f249 1160
24bff2dc
SE
1161 nr_options = 0;
1162
81cce8de 1163 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
d1b4f249 1164
60098917
ACM
1165 if (browser->he_selection != NULL) {
1166 thread = hist_browser__selected_thread(browser);
1167 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1168 }
b50e003d 1169 switch (key) {
cf958003
ACM
1170 case K_TAB:
1171 case K_UNTAB:
e4419b8e
DA
1172 if (nr_events == 1)
1173 continue;
b50e003d
ACM
1174 /*
1175 * Exit the browser, let hists__browser_tree
1176 * go to the next or previous
1177 */
1178 goto out_free_stack;
1179 case 'a':
a6e51f9f 1180 if (!browser->has_symbols) {
7b27509f 1181 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 1182 "Annotation is only available for symbolic views, "
a68c2c58 1183 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
1184 continue;
1185 }
1186
60098917 1187 if (browser->selection == NULL ||
db9a9cbc 1188 browser->selection->sym == NULL ||
b50e003d 1189 browser->selection->map->dso->annotate_warned)
d1b4f249 1190 continue;
b50e003d 1191 goto do_annotate;
aff3f3f6
ACM
1192 case 'P':
1193 hist_browser__dump(browser);
1194 continue;
b50e003d
ACM
1195 case 'd':
1196 goto zoom_dso;
a7cb8863
ACM
1197 case 'V':
1198 browser->show_dso = !browser->show_dso;
1199 continue;
b50e003d
ACM
1200 case 't':
1201 goto zoom_thread;
5a5626b1 1202 case '/':
938a23ae
NK
1203 if (ui_browser__input_window("Symbol to show",
1204 "Please enter the name of symbol you want to see",
1205 buf, "ENTER: OK, ESC: Cancel",
1206 delay_secs * 2) == K_ENTER) {
05e8b080
ACM
1207 hists->symbol_filter_str = *buf ? buf : NULL;
1208 hists__filter_by_symbol(hists);
938a23ae
NK
1209 hist_browser__reset(browser);
1210 }
1211 continue;
cf958003 1212 case K_F1:
b50e003d
ACM
1213 case 'h':
1214 case '?':
4610e413
ACM
1215 ui_browser__help_window(&browser->b,
1216 "h/?/F1 Show this window\n"
2d5646c0
ACM
1217 "UP/DOWN/PGUP\n"
1218 "PGDN/SPACE Navigate\n"
1219 "q/ESC/CTRL+C Exit browser\n\n"
1220 "For multiple event sessions:\n\n"
1221 "TAB/UNTAB Switch events\n\n"
724c9c9f 1222 "For symbolic views (--sort has sym):\n\n"
2d5646c0
ACM
1223 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1224 "<- Zoom out\n"
1225 "a Annotate current symbol\n"
1226 "C Collapse all callchains\n"
1227 "E Expand all callchains\n"
1228 "d Zoom into current DSO\n"
938a23ae 1229 "t Zoom into current Thread\n"
aff3f3f6 1230 "P Print histograms to perf.hist.N\n"
a7cb8863 1231 "V Verbose (DSO names in callchains, etc)\n"
5a5626b1 1232 "/ Filter symbol by name");
b50e003d 1233 continue;
cf958003
ACM
1234 case K_ENTER:
1235 case K_RIGHT:
b50e003d
ACM
1236 /* menu */
1237 break;
cf958003 1238 case K_LEFT: {
b50e003d 1239 const void *top;
d1b4f249 1240
7f0030b2
ACM
1241 if (pstack__empty(fstack)) {
1242 /*
1243 * Go back to the perf_evsel_menu__run or other user
1244 */
1245 if (left_exits)
1246 goto out_free_stack;
d1b4f249 1247 continue;
7f0030b2 1248 }
b50e003d 1249 top = pstack__pop(fstack);
d7b76f09 1250 if (top == &browser->hists->dso_filter)
b50e003d 1251 goto zoom_out_dso;
d7b76f09 1252 if (top == &browser->hists->thread_filter)
b50e003d
ACM
1253 goto zoom_out_thread;
1254 continue;
1255 }
cf958003 1256 case K_ESC:
7f0030b2 1257 if (!left_exits &&
4610e413
ACM
1258 !ui_browser__dialog_yesno(&browser->b,
1259 "Do you really want to exit?"))
b50e003d
ACM
1260 continue;
1261 /* Fall thru */
ed7e5662
ACM
1262 case 'q':
1263 case CTRL('c'):
b50e003d 1264 goto out_free_stack;
ed7e5662
ACM
1265 default:
1266 continue;
d1b4f249
ACM
1267 }
1268
724c9c9f
ACM
1269 if (!browser->has_symbols)
1270 goto add_exit_option;
1271
a68c2c58
SE
1272 if (sort__branch_mode == 1) {
1273 bi = browser->he_selection->branch_info;
1274 if (browser->selection != NULL &&
1275 bi &&
1276 bi->from.sym != NULL &&
1277 !bi->from.map->dso->annotate_warned &&
1278 asprintf(&options[nr_options], "Annotate %s",
1279 bi->from.sym->name) > 0)
1280 annotate_f = nr_options++;
1281
1282 if (browser->selection != NULL &&
1283 bi &&
1284 bi->to.sym != NULL &&
1285 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1286 (bi->to.sym != bi->from.sym ||
1287 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1288 asprintf(&options[nr_options], "Annotate %s",
1289 bi->to.sym->name) > 0)
1290 annotate_t = nr_options++;
1291 } else {
1292
1293 if (browser->selection != NULL &&
1294 browser->selection->sym != NULL &&
1295 !browser->selection->map->dso->annotate_warned &&
1296 asprintf(&options[nr_options], "Annotate %s",
1297 browser->selection->sym->name) > 0)
1298 annotate = nr_options++;
1299 }
d1b4f249
ACM
1300
1301 if (thread != NULL &&
1302 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1303 (browser->hists->thread_filter ? "out of" : "into"),
d1b4f249
ACM
1304 (thread->comm_set ? thread->comm : ""),
1305 thread->pid) > 0)
1306 zoom_thread = nr_options++;
1307
1308 if (dso != NULL &&
1309 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1310 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1311 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1312 zoom_dso = nr_options++;
1313
60098917
ACM
1314 if (browser->selection != NULL &&
1315 browser->selection->map != NULL &&
d1b4f249
ACM
1316 asprintf(&options[nr_options], "Browse map details") > 0)
1317 browse_map = nr_options++;
724c9c9f 1318add_exit_option:
d1b4f249 1319 options[nr_options++] = (char *)"Exit";
24bff2dc 1320retry_popup_menu:
1e6dd077 1321 choice = ui__popup_menu(nr_options, options);
d1b4f249 1322
d1b4f249
ACM
1323 if (choice == nr_options - 1)
1324 break;
1325
24bff2dc
SE
1326 if (choice == -1) {
1327 free_popup_options(options, nr_options - 1);
d1b4f249 1328 continue;
24bff2dc 1329 }
d1b4f249 1330
a68c2c58 1331 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1332 struct hist_entry *he;
4610e413 1333 int err;
d1b4f249 1334do_annotate:
d1b4f249
ACM
1335 he = hist_browser__selected_entry(browser);
1336 if (he == NULL)
1337 continue;
a68c2c58
SE
1338
1339 /*
1340 * we stash the branch_info symbol + map into the
1341 * the ms so we don't have to rewrite all the annotation
1342 * code to use branch_info.
1343 * in branch mode, the ms struct is not used
1344 */
1345 if (choice == annotate_f) {
1346 he->ms.sym = he->branch_info->from.sym;
1347 he->ms.map = he->branch_info->from.map;
1348 } else if (choice == annotate_t) {
1349 he->ms.sym = he->branch_info->to.sym;
1350 he->ms.map = he->branch_info->to.map;
1351 }
1352
df71d95f
ACM
1353 /*
1354 * Don't let this be freed, say, by hists__decay_entry.
1355 */
1356 he->used = true;
d04b35f8 1357 err = hist_entry__tui_annotate(he, evsel->idx,
4610e413 1358 timer, arg, delay_secs);
df71d95f 1359 he->used = false;
24bff2dc
SE
1360 /*
1361 * offer option to annotate the other branch source or target
1362 * (if they exists) when returning from annotate
1363 */
1364 if ((err == 'q' || err == CTRL('c'))
1365 && annotate_t != -2 && annotate_f != -2)
1366 goto retry_popup_menu;
1367
900e14a8 1368 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1369 if (err)
1370 ui_browser__handle_resize(&browser->b);
24bff2dc 1371
d1b4f249
ACM
1372 } else if (choice == browse_map)
1373 map__browse(browser->selection->map);
1374 else if (choice == zoom_dso) {
1375zoom_dso:
d7b76f09
ACM
1376 if (browser->hists->dso_filter) {
1377 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1378zoom_out_dso:
1379 ui_helpline__pop();
d7b76f09 1380 browser->hists->dso_filter = NULL;
cc02c921 1381 sort_dso.elide = false;
d1b4f249
ACM
1382 } else {
1383 if (dso == NULL)
1384 continue;
1385 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1386 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1387 browser->hists->dso_filter = dso;
cc02c921 1388 sort_dso.elide = true;
d7b76f09 1389 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1390 }
05e8b080 1391 hists__filter_by_dso(hists);
d1b4f249
ACM
1392 hist_browser__reset(browser);
1393 } else if (choice == zoom_thread) {
1394zoom_thread:
d7b76f09
ACM
1395 if (browser->hists->thread_filter) {
1396 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1397zoom_out_thread:
1398 ui_helpline__pop();
d7b76f09 1399 browser->hists->thread_filter = NULL;
cc02c921 1400 sort_thread.elide = false;
d1b4f249
ACM
1401 } else {
1402 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1403 thread->comm_set ? thread->comm : "",
1404 thread->pid);
d7b76f09 1405 browser->hists->thread_filter = thread;
cc02c921 1406 sort_thread.elide = true;
d7b76f09 1407 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1408 }
05e8b080 1409 hists__filter_by_thread(hists);
d1b4f249
ACM
1410 hist_browser__reset(browser);
1411 }
1412 }
1413out_free_stack:
1414 pstack__delete(fstack);
1415out:
1416 hist_browser__delete(browser);
24bff2dc 1417 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1418 return key;
1419}
1420
7f0030b2
ACM
1421struct perf_evsel_menu {
1422 struct ui_browser b;
1423 struct perf_evsel *selection;
7b27509f 1424 bool lost_events, lost_events_warned;
7f0030b2
ACM
1425};
1426
1427static void perf_evsel_menu__write(struct ui_browser *browser,
1428 void *entry, int row)
1429{
1430 struct perf_evsel_menu *menu = container_of(browser,
1431 struct perf_evsel_menu, b);
1432 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1433 bool current_entry = ui_browser__is_current_entry(browser, row);
1434 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
7289f83c 1435 const char *ev_name = perf_evsel__name(evsel);
7f0030b2 1436 char bf[256], unit;
7b27509f
ACM
1437 const char *warn = " ";
1438 size_t printed;
7f0030b2
ACM
1439
1440 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1441 HE_COLORSET_NORMAL);
1442
1443 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1444 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1445 unit, unit == ' ' ? "" : " ", ev_name);
1446 slsmg_printf("%s", bf);
1447
1448 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1449 if (nr_events != 0) {
1450 menu->lost_events = true;
1451 if (!current_entry)
1452 ui_browser__set_color(browser, HE_COLORSET_TOP);
1453 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1454 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1455 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1456 warn = bf;
1457 }
1458
1459 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1460
1461 if (current_entry)
1462 menu->selection = evsel;
1463}
1464
34958544
ACM
1465static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1466 int nr_events, const char *help,
81cce8de 1467 void(*timer)(void *arg), void *arg, int delay_secs)
d1b4f249 1468{
7f0030b2 1469 struct perf_evlist *evlist = menu->b.priv;
e248de33 1470 struct perf_evsel *pos;
7f0030b2
ACM
1471 const char *ev_name, *title = "Available samples";
1472 int key;
d1b4f249 1473
7f0030b2
ACM
1474 if (ui_browser__show(&menu->b, title,
1475 "ESC: exit, ENTER|->: Browse histograms") < 0)
1476 return -1;
1477
7f0030b2 1478 while (1) {
3af6e338 1479 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1480
1481 switch (key) {
cf958003 1482 case K_TIMER:
81cce8de 1483 timer(arg);
7b27509f
ACM
1484
1485 if (!menu->lost_events_warned && menu->lost_events) {
1486 ui_browser__warn_lost_events(&menu->b);
1487 menu->lost_events_warned = true;
1488 }
81cce8de 1489 continue;
cf958003
ACM
1490 case K_RIGHT:
1491 case K_ENTER:
7f0030b2
ACM
1492 if (!menu->selection)
1493 continue;
1494 pos = menu->selection;
1495browse_hists:
18eaf0b8
ACM
1496 perf_evlist__set_selected(evlist, pos);
1497 /*
1498 * Give the calling tool a chance to populate the non
1499 * default evsel resorted hists tree.
1500 */
1501 if (timer)
1502 timer(arg);
7289f83c 1503 ev_name = perf_evsel__name(pos);
34958544
ACM
1504 key = perf_evsel__hists_browse(pos, nr_events, help,
1505 ev_name, true, timer,
1506 arg, delay_secs);
7f0030b2 1507 ui_browser__show_title(&menu->b, title);
18eaf0b8 1508 switch (key) {
cf958003 1509 case K_TAB:
18eaf0b8
ACM
1510 if (pos->node.next == &evlist->entries)
1511 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1512 else
1513 pos = list_entry(pos->node.next, struct perf_evsel, node);
1514 goto browse_hists;
cf958003 1515 case K_UNTAB:
18eaf0b8
ACM
1516 if (pos->node.prev == &evlist->entries)
1517 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1518 else
1519 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1520 goto browse_hists;
cf958003 1521 case K_ESC:
4610e413
ACM
1522 if (!ui_browser__dialog_yesno(&menu->b,
1523 "Do you really want to exit?"))
18eaf0b8
ACM
1524 continue;
1525 /* Fall thru */
1526 case 'q':
1527 case CTRL('c'):
1528 goto out;
1529 default:
1530 continue;
1531 }
cf958003 1532 case K_LEFT:
7f0030b2 1533 continue;
cf958003 1534 case K_ESC:
4610e413
ACM
1535 if (!ui_browser__dialog_yesno(&menu->b,
1536 "Do you really want to exit?"))
ed7e5662
ACM
1537 continue;
1538 /* Fall thru */
7f0030b2
ACM
1539 case 'q':
1540 case CTRL('c'):
1541 goto out;
d1b4f249 1542 default:
18eaf0b8 1543 continue;
d1b4f249
ACM
1544 }
1545 }
1546
7f0030b2
ACM
1547out:
1548 ui_browser__hide(&menu->b);
1549 return key;
1550}
1551
1552static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
81cce8de
ACM
1553 const char *help,
1554 void(*timer)(void *arg), void *arg,
1555 int delay_secs)
7f0030b2
ACM
1556{
1557 struct perf_evsel *pos;
1558 struct perf_evsel_menu menu = {
1559 .b = {
1560 .entries = &evlist->entries,
1561 .refresh = ui_browser__list_head_refresh,
1562 .seek = ui_browser__list_head_seek,
1563 .write = perf_evsel_menu__write,
1564 .nr_entries = evlist->nr_entries,
1565 .priv = evlist,
1566 },
1567 };
1568
1569 ui_helpline__push("Press ESC to exit");
1570
1571 list_for_each_entry(pos, &evlist->entries, node) {
7289f83c 1572 const char *ev_name = perf_evsel__name(pos);
7f0030b2
ACM
1573 size_t line_len = strlen(ev_name) + 7;
1574
1575 if (menu.b.width < line_len)
1576 menu.b.width = line_len;
7f0030b2
ACM
1577 }
1578
34958544
ACM
1579 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1580 arg, delay_secs);
7f0030b2
ACM
1581}
1582
81cce8de
ACM
1583int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1584 void(*timer)(void *arg), void *arg,
1585 int delay_secs)
7f0030b2 1586{
7f0030b2
ACM
1587 if (evlist->nr_entries == 1) {
1588 struct perf_evsel *first = list_entry(evlist->entries.next,
1589 struct perf_evsel, node);
7289f83c 1590 const char *ev_name = perf_evsel__name(first);
34958544
ACM
1591 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1592 ev_name, false, timer, arg,
1593 delay_secs);
7f0030b2
ACM
1594 }
1595
81cce8de
ACM
1596 return __perf_evlist__tui_browse_hists(evlist, help,
1597 timer, arg, delay_secs);
d1b4f249 1598}