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