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