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