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