~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/ui/browsers/hists.c

Version: ~ [ linux-5.12-rc7 ] ~ [ linux-5.11.13 ] ~ [ linux-5.10.29 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.111 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.186 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.230 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.266 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.266 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #include <stdio.h>
  2 #include "../libslang.h"
  3 #include <stdlib.h>
  4 #include <string.h>
  5 #include <linux/rbtree.h>
  6 
  7 #include "../../util/evsel.h"
  8 #include "../../util/evlist.h"
  9 #include "../../util/hist.h"
 10 #include "../../util/pstack.h"
 11 #include "../../util/sort.h"
 12 #include "../../util/util.h"
 13 #include "../../arch/common.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         int                  print_seq;
 27         bool                 show_dso;
 28         float                min_pcnt;
 29         u64                  nr_pcnt_entries;
 30 };
 31 
 32 extern void hist_browser__init_hpp(void);
 33 
 34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
 35                                 const char *ev_name);
 36 
 37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
 38 {
 39         /* 3 == +/- toggle symbol before actual hist_entry rendering */
 40         browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
 41                              sizeof("[k]"));
 42 }
 43 
 44 static void hist_browser__reset(struct hist_browser *browser)
 45 {
 46         browser->b.nr_entries = browser->hists->nr_entries;
 47         hist_browser__refresh_dimensions(browser);
 48         ui_browser__reset_index(&browser->b);
 49 }
 50 
 51 static char tree__folded_sign(bool unfolded)
 52 {
 53         return unfolded ? '-' : '+';
 54 }
 55 
 56 static char map_symbol__folded(const struct map_symbol *ms)
 57 {
 58         return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
 59 }
 60 
 61 static char hist_entry__folded(const struct hist_entry *he)
 62 {
 63         return map_symbol__folded(&he->ms);
 64 }
 65 
 66 static char callchain_list__folded(const struct callchain_list *cl)
 67 {
 68         return map_symbol__folded(&cl->ms);
 69 }
 70 
 71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
 72 {
 73         ms->unfolded = unfold ? ms->has_children : false;
 74 }
 75 
 76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
 77 {
 78         int n = 0;
 79         struct rb_node *nd;
 80 
 81         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 82                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 83                 struct callchain_list *chain;
 84                 char folded_sign = ' '; /* No children */
 85 
 86                 list_for_each_entry(chain, &child->val, list) {
 87                         ++n;
 88                         /* We need this because we may not have children */
 89                         folded_sign = callchain_list__folded(chain);
 90                         if (folded_sign == '+')
 91                                 break;
 92                 }
 93 
 94                 if (folded_sign == '-') /* Have children and they're unfolded */
 95                         n += callchain_node__count_rows_rb_tree(child);
 96         }
 97 
 98         return n;
 99 }
100 
101 static int callchain_node__count_rows(struct callchain_node *node)
102 {
103         struct callchain_list *chain;
104         bool unfolded = false;
105         int n = 0;
106 
107         list_for_each_entry(chain, &node->val, list) {
108                 ++n;
109                 unfolded = chain->ms.unfolded;
110         }
111 
112         if (unfolded)
113                 n += callchain_node__count_rows_rb_tree(node);
114 
115         return n;
116 }
117 
118 static int callchain__count_rows(struct rb_root *chain)
119 {
120         struct rb_node *nd;
121         int n = 0;
122 
123         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125                 n += callchain_node__count_rows(node);
126         }
127 
128         return n;
129 }
130 
131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
132 {
133         if (!ms)
134                 return false;
135 
136         if (!ms->has_children)
137                 return false;
138 
139         ms->unfolded = !ms->unfolded;
140         return true;
141 }
142 
143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
144 {
145         struct rb_node *nd = rb_first(&node->rb_root);
146 
147         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149                 struct callchain_list *chain;
150                 bool first = true;
151 
152                 list_for_each_entry(chain, &child->val, list) {
153                         if (first) {
154                                 first = false;
155                                 chain->ms.has_children = chain->list.next != &child->val ||
156                                                          !RB_EMPTY_ROOT(&child->rb_root);
157                         } else
158                                 chain->ms.has_children = chain->list.next == &child->val &&
159                                                          !RB_EMPTY_ROOT(&child->rb_root);
160                 }
161 
162                 callchain_node__init_have_children_rb_tree(child);
163         }
164 }
165 
166 static void callchain_node__init_have_children(struct callchain_node *node)
167 {
168         struct callchain_list *chain;
169 
170         list_for_each_entry(chain, &node->val, list)
171                 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
172 
173         callchain_node__init_have_children_rb_tree(node);
174 }
175 
176 static void callchain__init_have_children(struct rb_root *root)
177 {
178         struct rb_node *nd;
179 
180         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182                 callchain_node__init_have_children(node);
183         }
184 }
185 
186 static void hist_entry__init_have_children(struct hist_entry *he)
187 {
188         if (!he->init_have_children) {
189                 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190                 callchain__init_have_children(&he->sorted_chain);
191                 he->init_have_children = true;
192         }
193 }
194 
195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
196 {
197         if (map_symbol__toggle_fold(browser->selection)) {
198                 struct hist_entry *he = browser->he_selection;
199 
200                 hist_entry__init_have_children(he);
201                 browser->hists->nr_entries -= he->nr_rows;
202 
203                 if (he->ms.unfolded)
204                         he->nr_rows = callchain__count_rows(&he->sorted_chain);
205                 else
206                         he->nr_rows = 0;
207                 browser->hists->nr_entries += he->nr_rows;
208                 browser->b.nr_entries = browser->hists->nr_entries;
209 
210                 return true;
211         }
212 
213         /* If it doesn't have children, no toggling performed */
214         return false;
215 }
216 
217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
218 {
219         int n = 0;
220         struct rb_node *nd;
221 
222         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224                 struct callchain_list *chain;
225                 bool has_children = false;
226 
227                 list_for_each_entry(chain, &child->val, list) {
228                         ++n;
229                         map_symbol__set_folding(&chain->ms, unfold);
230                         has_children = chain->ms.has_children;
231                 }
232 
233                 if (has_children)
234                         n += callchain_node__set_folding_rb_tree(child, unfold);
235         }
236 
237         return n;
238 }
239 
240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241 {
242         struct callchain_list *chain;
243         bool has_children = false;
244         int n = 0;
245 
246         list_for_each_entry(chain, &node->val, list) {
247                 ++n;
248                 map_symbol__set_folding(&chain->ms, unfold);
249                 has_children = chain->ms.has_children;
250         }
251 
252         if (has_children)
253                 n += callchain_node__set_folding_rb_tree(node, unfold);
254 
255         return n;
256 }
257 
258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
259 {
260         struct rb_node *nd;
261         int n = 0;
262 
263         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265                 n += callchain_node__set_folding(node, unfold);
266         }
267 
268         return n;
269 }
270 
271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
272 {
273         hist_entry__init_have_children(he);
274         map_symbol__set_folding(&he->ms, unfold);
275 
276         if (he->ms.has_children) {
277                 int n = callchain__set_folding(&he->sorted_chain, unfold);
278                 he->nr_rows = unfold ? n : 0;
279         } else
280                 he->nr_rows = 0;
281 }
282 
283 static void hists__set_folding(struct hists *hists, bool unfold)
284 {
285         struct rb_node *nd;
286 
287         hists->nr_entries = 0;
288 
289         for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290                 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291                 hist_entry__set_folding(he, unfold);
292                 hists->nr_entries += 1 + he->nr_rows;
293         }
294 }
295 
296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297 {
298         hists__set_folding(browser->hists, unfold);
299         browser->b.nr_entries = browser->hists->nr_entries;
300         /* Go to the start, we may be way after valid entries after a collapse */
301         ui_browser__reset_index(&browser->b);
302 }
303 
304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
305 {
306         ui_browser__warning(browser, 4,
307                 "Events are being lost, check IO/CPU overload!\n\n"
308                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
309                 " perf top -r 80\n\n"
310                 "Or reduce the sampling frequency.");
311 }
312 
313 static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
314 
315 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
316                              struct hist_browser_timer *hbt)
317 {
318         int key;
319         char title[160];
320         int delay_secs = hbt ? hbt->refresh : 0;
321 
322         browser->b.entries = &browser->hists->entries;
323         browser->b.nr_entries = browser->hists->nr_entries;
324         if (browser->min_pcnt)
325                 browser->b.nr_entries = browser->nr_pcnt_entries;
326 
327         hist_browser__refresh_dimensions(browser);
328         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
329 
330         if (ui_browser__show(&browser->b, title,
331                              "Press '?' for help on key bindings") < 0)
332                 return -1;
333 
334         while (1) {
335                 key = ui_browser__run(&browser->b, delay_secs);
336 
337                 switch (key) {
338                 case K_TIMER: {
339                         u64 nr_entries;
340                         hbt->timer(hbt->arg);
341 
342                         if (browser->min_pcnt) {
343                                 hist_browser__update_pcnt_entries(browser);
344                                 nr_entries = browser->nr_pcnt_entries;
345                         } else {
346                                 nr_entries = browser->hists->nr_entries;
347                         }
348 
349                         ui_browser__update_nr_entries(&browser->b, nr_entries);
350 
351                         if (browser->hists->stats.nr_lost_warned !=
352                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
353                                 browser->hists->stats.nr_lost_warned =
354                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
355                                 ui_browser__warn_lost_events(&browser->b);
356                         }
357 
358                         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
359                         ui_browser__show_title(&browser->b, title);
360                         continue;
361                 }
362                 case 'D': { /* Debug */
363                         static int seq;
364                         struct hist_entry *h = rb_entry(browser->b.top,
365                                                         struct hist_entry, rb_node);
366                         ui_helpline__pop();
367                         ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
368                                            seq++, browser->b.nr_entries,
369                                            browser->hists->nr_entries,
370                                            browser->b.height,
371                                            browser->b.index,
372                                            browser->b.top_idx,
373                                            h->row_offset, h->nr_rows);
374                 }
375                         break;
376                 case 'C':
377                         /* Collapse the whole world. */
378                         hist_browser__set_folding(browser, false);
379                         break;
380                 case 'E':
381                         /* Expand the whole world. */
382                         hist_browser__set_folding(browser, true);
383                         break;
384                 case K_ENTER:
385                         if (hist_browser__toggle_fold(browser))
386                                 break;
387                         /* fall thru */
388                 default:
389                         goto out;
390                 }
391         }
392 out:
393         ui_browser__hide(&browser->b);
394         return key;
395 }
396 
397 static char *callchain_list__sym_name(struct callchain_list *cl,
398                                       char *bf, size_t bfsize, bool show_dso)
399 {
400         int printed;
401 
402         if (cl->ms.sym)
403                 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
404         else
405                 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
406 
407         if (show_dso)
408                 scnprintf(bf + printed, bfsize - printed, " %s",
409                           cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
410 
411         return bf;
412 }
413 
414 #define LEVEL_OFFSET_STEP 3
415 
416 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
417                                                      struct callchain_node *chain_node,
418                                                      u64 total, int level,
419                                                      unsigned short row,
420                                                      off_t *row_offset,
421                                                      bool *is_current_entry)
422 {
423         struct rb_node *node;
424         int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
425         u64 new_total, remaining;
426 
427         if (callchain_param.mode == CHAIN_GRAPH_REL)
428                 new_total = chain_node->children_hit;
429         else
430                 new_total = total;
431 
432         remaining = new_total;
433         node = rb_first(&chain_node->rb_root);
434         while (node) {
435                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
436                 struct rb_node *next = rb_next(node);
437                 u64 cumul = callchain_cumul_hits(child);
438                 struct callchain_list *chain;
439                 char folded_sign = ' ';
440                 int first = true;
441                 int extra_offset = 0;
442 
443                 remaining -= cumul;
444 
445                 list_for_each_entry(chain, &child->val, list) {
446                         char bf[1024], *alloc_str;
447                         const char *str;
448                         int color;
449                         bool was_first = first;
450 
451                         if (first)
452                                 first = false;
453                         else
454                                 extra_offset = LEVEL_OFFSET_STEP;
455 
456                         folded_sign = callchain_list__folded(chain);
457                         if (*row_offset != 0) {
458                                 --*row_offset;
459                                 goto do_next;
460                         }
461 
462                         alloc_str = NULL;
463                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
464                                                        browser->show_dso);
465                         if (was_first) {
466                                 double percent = cumul * 100.0 / new_total;
467 
468                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
469                                         str = "Not enough memory!";
470                                 else
471                                         str = alloc_str;
472                         }
473 
474                         color = HE_COLORSET_NORMAL;
475                         width = browser->b.width - (offset + extra_offset + 2);
476                         if (ui_browser__is_current_entry(&browser->b, row)) {
477                                 browser->selection = &chain->ms;
478                                 color = HE_COLORSET_SELECTED;
479                                 *is_current_entry = true;
480                         }
481 
482                         ui_browser__set_color(&browser->b, color);
483                         ui_browser__gotorc(&browser->b, row, 0);
484                         slsmg_write_nstring(" ", offset + extra_offset);
485                         slsmg_printf("%c ", folded_sign);
486                         slsmg_write_nstring(str, width);
487                         free(alloc_str);
488 
489                         if (++row == browser->b.height)
490                                 goto out;
491 do_next:
492                         if (folded_sign == '+')
493                                 break;
494                 }
495 
496                 if (folded_sign == '-') {
497                         const int new_level = level + (extra_offset ? 2 : 1);
498                         row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
499                                                                          new_level, row, row_offset,
500                                                                          is_current_entry);
501                 }
502                 if (row == browser->b.height)
503                         goto out;
504                 node = next;
505         }
506 out:
507         return row - first_row;
508 }
509 
510 static int hist_browser__show_callchain_node(struct hist_browser *browser,
511                                              struct callchain_node *node,
512                                              int level, unsigned short row,
513                                              off_t *row_offset,
514                                              bool *is_current_entry)
515 {
516         struct callchain_list *chain;
517         int first_row = row,
518              offset = level * LEVEL_OFFSET_STEP,
519              width = browser->b.width - offset;
520         char folded_sign = ' ';
521 
522         list_for_each_entry(chain, &node->val, list) {
523                 char bf[1024], *s;
524                 int color;
525 
526                 folded_sign = callchain_list__folded(chain);
527 
528                 if (*row_offset != 0) {
529                         --*row_offset;
530                         continue;
531                 }
532 
533                 color = HE_COLORSET_NORMAL;
534                 if (ui_browser__is_current_entry(&browser->b, row)) {
535                         browser->selection = &chain->ms;
536                         color = HE_COLORSET_SELECTED;
537                         *is_current_entry = true;
538                 }
539 
540                 s = callchain_list__sym_name(chain, bf, sizeof(bf),
541                                              browser->show_dso);
542                 ui_browser__gotorc(&browser->b, row, 0);
543                 ui_browser__set_color(&browser->b, color);
544                 slsmg_write_nstring(" ", offset);
545                 slsmg_printf("%c ", folded_sign);
546                 slsmg_write_nstring(s, width - 2);
547 
548                 if (++row == browser->b.height)
549                         goto out;
550         }
551 
552         if (folded_sign == '-')
553                 row += hist_browser__show_callchain_node_rb_tree(browser, node,
554                                                                  browser->hists->stats.total_period,
555                                                                  level + 1, row,
556                                                                  row_offset,
557                                                                  is_current_entry);
558 out:
559         return row - first_row;
560 }
561 
562 static int hist_browser__show_callchain(struct hist_browser *browser,
563                                         struct rb_root *chain,
564                                         int level, unsigned short row,
565                                         off_t *row_offset,
566                                         bool *is_current_entry)
567 {
568         struct rb_node *nd;
569         int first_row = row;
570 
571         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
572                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
573 
574                 row += hist_browser__show_callchain_node(browser, node, level,
575                                                          row, row_offset,
576                                                          is_current_entry);
577                 if (row == browser->b.height)
578                         break;
579         }
580 
581         return row - first_row;
582 }
583 
584 struct hpp_arg {
585         struct ui_browser *b;
586         char folded_sign;
587         bool current_entry;
588 };
589 
590 static int __hpp__color_callchain(struct hpp_arg *arg)
591 {
592         if (!symbol_conf.use_callchain)
593                 return 0;
594 
595         slsmg_printf("%c ", arg->folded_sign);
596         return 2;
597 }
598 
599 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
600                             u64 (*get_field)(struct hist_entry *),
601                             int (*callchain_cb)(struct hpp_arg *))
602 {
603         int ret = 0;
604         double percent = 0.0;
605         struct hists *hists = he->hists;
606         struct hpp_arg *arg = hpp->ptr;
607 
608         if (hists->stats.total_period)
609                 percent = 100.0 * get_field(he) / hists->stats.total_period;
610 
611         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
612 
613         if (callchain_cb)
614                 ret += callchain_cb(arg);
615 
616         ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
617         slsmg_printf("%s", hpp->buf);
618 
619         if (symbol_conf.event_group) {
620                 int prev_idx, idx_delta;
621                 struct perf_evsel *evsel = hists_to_evsel(hists);
622                 struct hist_entry *pair;
623                 int nr_members = evsel->nr_members;
624 
625                 if (nr_members <= 1)
626                         goto out;
627 
628                 prev_idx = perf_evsel__group_idx(evsel);
629 
630                 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
631                         u64 period = get_field(pair);
632                         u64 total = pair->hists->stats.total_period;
633 
634                         if (!total)
635                                 continue;
636 
637                         evsel = hists_to_evsel(pair->hists);
638                         idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
639 
640                         while (idx_delta--) {
641                                 /*
642                                  * zero-fill group members in the middle which
643                                  * have no sample
644                                  */
645                                 ui_browser__set_percent_color(arg->b, 0.0,
646                                                         arg->current_entry);
647                                 ret += scnprintf(hpp->buf, hpp->size,
648                                                  " %6.2f%%", 0.0);
649                                 slsmg_printf("%s", hpp->buf);
650                         }
651 
652                         percent = 100.0 * period / total;
653                         ui_browser__set_percent_color(arg->b, percent,
654                                                       arg->current_entry);
655                         ret += scnprintf(hpp->buf, hpp->size,
656                                          " %6.2f%%", percent);
657                         slsmg_printf("%s", hpp->buf);
658 
659                         prev_idx = perf_evsel__group_idx(evsel);
660                 }
661 
662                 idx_delta = nr_members - prev_idx - 1;
663 
664                 while (idx_delta--) {
665                         /*
666                          * zero-fill group members at last which have no sample
667                          */
668                         ui_browser__set_percent_color(arg->b, 0.0,
669                                                       arg->current_entry);
670                         ret += scnprintf(hpp->buf, hpp->size,
671                                          " %6.2f%%", 0.0);
672                         slsmg_printf("%s", hpp->buf);
673                 }
674         }
675 out:
676         if (!arg->current_entry || !arg->b->navkeypressed)
677                 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
678 
679         return ret;
680 }
681 
682 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)                      \
683 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
684 {                                                                       \
685         return he->stat._field;                                         \
686 }                                                                       \
687                                                                         \
688 static int                                                              \
689 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
690                                 struct perf_hpp *hpp,                   \
691                                 struct hist_entry *he)                  \
692 {                                                                       \
693         return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);      \
694 }
695 
696 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
697 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
698 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
699 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
700 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
701 
702 #undef __HPP_COLOR_PERCENT_FN
703 
704 void hist_browser__init_hpp(void)
705 {
706         perf_hpp__init();
707 
708         perf_hpp__format[PERF_HPP__OVERHEAD].color =
709                                 hist_browser__hpp_color_overhead;
710         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
711                                 hist_browser__hpp_color_overhead_sys;
712         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
713                                 hist_browser__hpp_color_overhead_us;
714         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
715                                 hist_browser__hpp_color_overhead_guest_sys;
716         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
717                                 hist_browser__hpp_color_overhead_guest_us;
718 }
719 
720 static int hist_browser__show_entry(struct hist_browser *browser,
721                                     struct hist_entry *entry,
722                                     unsigned short row)
723 {
724         char s[256];
725         int printed = 0;
726         int width = browser->b.width;
727         char folded_sign = ' ';
728         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
729         off_t row_offset = entry->row_offset;
730         bool first = true;
731         struct perf_hpp_fmt *fmt;
732 
733         if (current_entry) {
734                 browser->he_selection = entry;
735                 browser->selection = &entry->ms;
736         }
737 
738         if (symbol_conf.use_callchain) {
739                 hist_entry__init_have_children(entry);
740                 folded_sign = hist_entry__folded(entry);
741         }
742 
743         if (row_offset == 0) {
744                 struct hpp_arg arg = {
745                         .b              = &browser->b,
746                         .folded_sign    = folded_sign,
747                         .current_entry  = current_entry,
748                 };
749                 struct perf_hpp hpp = {
750                         .buf            = s,
751                         .size           = sizeof(s),
752                         .ptr            = &arg,
753                 };
754 
755                 ui_browser__gotorc(&browser->b, row, 0);
756 
757                 perf_hpp__for_each_format(fmt) {
758                         if (!first) {
759                                 slsmg_printf("  ");
760                                 width -= 2;
761                         }
762                         first = false;
763 
764                         if (fmt->color) {
765                                 width -= fmt->color(fmt, &hpp, entry);
766                         } else {
767                                 width -= fmt->entry(fmt, &hpp, entry);
768                                 slsmg_printf("%s", s);
769                         }
770                 }
771 
772                 /* The scroll bar isn't being used */
773                 if (!browser->b.navkeypressed)
774                         width += 1;
775 
776                 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
777                 slsmg_write_nstring(s, width);
778                 ++row;
779                 ++printed;
780         } else
781                 --row_offset;
782 
783         if (folded_sign == '-' && row != browser->b.height) {
784                 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
785                                                         1, row, &row_offset,
786                                                         &current_entry);
787                 if (current_entry)
788                         browser->he_selection = entry;
789         }
790 
791         return printed;
792 }
793 
794 static void ui_browser__hists_init_top(struct ui_browser *browser)
795 {
796         if (browser->top == NULL) {
797                 struct hist_browser *hb;
798 
799                 hb = container_of(browser, struct hist_browser, b);
800                 browser->top = rb_first(&hb->hists->entries);
801         }
802 }
803 
804 static unsigned int hist_browser__refresh(struct ui_browser *browser)
805 {
806         unsigned row = 0;
807         struct rb_node *nd;
808         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
809 
810         ui_browser__hists_init_top(browser);
811 
812         for (nd = browser->top; nd; nd = rb_next(nd)) {
813                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
814                 float percent = h->stat.period * 100.0 /
815                                         hb->hists->stats.total_period;
816 
817                 if (h->filtered)
818                         continue;
819 
820                 if (percent < hb->min_pcnt)
821                         continue;
822 
823                 row += hist_browser__show_entry(hb, h, row);
824                 if (row == browser->height)
825                         break;
826         }
827 
828         return row;
829 }
830 
831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
832                                              struct hists *hists,
833                                              float min_pcnt)
834 {
835         while (nd != NULL) {
836                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837                 float percent = h->stat.period * 100.0 /
838                                         hists->stats.total_period;
839 
840                 if (percent < min_pcnt)
841                         return NULL;
842 
843                 if (!h->filtered)
844                         return nd;
845 
846                 nd = rb_next(nd);
847         }
848 
849         return NULL;
850 }
851 
852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853                                                   struct hists *hists,
854                                                   float min_pcnt)
855 {
856         while (nd != NULL) {
857                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858                 float percent = h->stat.period * 100.0 /
859                                         hists->stats.total_period;
860 
861                 if (!h->filtered && percent >= min_pcnt)
862                         return nd;
863 
864                 nd = rb_prev(nd);
865         }
866 
867         return NULL;
868 }
869 
870 static void ui_browser__hists_seek(struct ui_browser *browser,
871                                    off_t offset, int whence)
872 {
873         struct hist_entry *h;
874         struct rb_node *nd;
875         bool first = true;
876         struct hist_browser *hb;
877 
878         hb = container_of(browser, struct hist_browser, b);
879 
880         if (browser->nr_entries == 0)
881                 return;
882 
883         ui_browser__hists_init_top(browser);
884 
885         switch (whence) {
886         case SEEK_SET:
887                 nd = hists__filter_entries(rb_first(browser->entries),
888                                            hb->hists, hb->min_pcnt);
889                 break;
890         case SEEK_CUR:
891                 nd = browser->top;
892                 goto do_offset;
893         case SEEK_END:
894                 nd = hists__filter_prev_entries(rb_last(browser->entries),
895                                                 hb->hists, hb->min_pcnt);
896                 first = false;
897                 break;
898         default:
899                 return;
900         }
901 
902         /*
903          * Moves not relative to the first visible entry invalidates its
904          * row_offset:
905          */
906         h = rb_entry(browser->top, struct hist_entry, rb_node);
907         h->row_offset = 0;
908 
909         /*
910          * Here we have to check if nd is expanded (+), if it is we can't go
911          * the next top level hist_entry, instead we must compute an offset of
912          * what _not_ to show and not change the first visible entry.
913          *
914          * This offset increments when we are going from top to bottom and
915          * decreases when we're going from bottom to top.
916          *
917          * As we don't have backpointers to the top level in the callchains
918          * structure, we need to always print the whole hist_entry callchain,
919          * skipping the first ones that are before the first visible entry
920          * and stop when we printed enough lines to fill the screen.
921          */
922 do_offset:
923         if (offset > 0) {
924                 do {
925                         h = rb_entry(nd, struct hist_entry, rb_node);
926                         if (h->ms.unfolded) {
927                                 u16 remaining = h->nr_rows - h->row_offset;
928                                 if (offset > remaining) {
929                                         offset -= remaining;
930                                         h->row_offset = 0;
931                                 } else {
932                                         h->row_offset += offset;
933                                         offset = 0;
934                                         browser->top = nd;
935                                         break;
936                                 }
937                         }
938                         nd = hists__filter_entries(rb_next(nd), hb->hists,
939                                                    hb->min_pcnt);
940                         if (nd == NULL)
941                                 break;
942                         --offset;
943                         browser->top = nd;
944                 } while (offset != 0);
945         } else if (offset < 0) {
946                 while (1) {
947                         h = rb_entry(nd, struct hist_entry, rb_node);
948                         if (h->ms.unfolded) {
949                                 if (first) {
950                                         if (-offset > h->row_offset) {
951                                                 offset += h->row_offset;
952                                                 h->row_offset = 0;
953                                         } else {
954                                                 h->row_offset += offset;
955                                                 offset = 0;
956                                                 browser->top = nd;
957                                                 break;
958                                         }
959                                 } else {
960                                         if (-offset > h->nr_rows) {
961                                                 offset += h->nr_rows;
962                                                 h->row_offset = 0;
963                                         } else {
964                                                 h->row_offset = h->nr_rows + offset;
965                                                 offset = 0;
966                                                 browser->top = nd;
967                                                 break;
968                                         }
969                                 }
970                         }
971 
972                         nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
973                                                         hb->min_pcnt);
974                         if (nd == NULL)
975                                 break;
976                         ++offset;
977                         browser->top = nd;
978                         if (offset == 0) {
979                                 /*
980                                  * Last unfiltered hist_entry, check if it is
981                                  * unfolded, if it is then we should have
982                                  * row_offset at its last entry.
983                                  */
984                                 h = rb_entry(nd, struct hist_entry, rb_node);
985                                 if (h->ms.unfolded)
986                                         h->row_offset = h->nr_rows;
987                                 break;
988                         }
989                         first = false;
990                 }
991         } else {
992                 browser->top = nd;
993                 h = rb_entry(nd, struct hist_entry, rb_node);
994                 h->row_offset = 0;
995         }
996 }
997 
998 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
999                                                         struct callchain_node *chain_node,
1000                                                         u64 total, int level,
1001                                                         FILE *fp)
1002 {
1003         struct rb_node *node;
1004         int offset = level * LEVEL_OFFSET_STEP;
1005         u64 new_total, remaining;
1006         int printed = 0;
1007 
1008         if (callchain_param.mode == CHAIN_GRAPH_REL)
1009                 new_total = chain_node->children_hit;
1010         else
1011                 new_total = total;
1012 
1013         remaining = new_total;
1014         node = rb_first(&chain_node->rb_root);
1015         while (node) {
1016                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017                 struct rb_node *next = rb_next(node);
1018                 u64 cumul = callchain_cumul_hits(child);
1019                 struct callchain_list *chain;
1020                 char folded_sign = ' ';
1021                 int first = true;
1022                 int extra_offset = 0;
1023 
1024                 remaining -= cumul;
1025 
1026                 list_for_each_entry(chain, &child->val, list) {
1027                         char bf[1024], *alloc_str;
1028                         const char *str;
1029                         bool was_first = first;
1030 
1031                         if (first)
1032                                 first = false;
1033                         else
1034                                 extra_offset = LEVEL_OFFSET_STEP;
1035 
1036                         folded_sign = callchain_list__folded(chain);
1037 
1038                         alloc_str = NULL;
1039                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040                                                        browser->show_dso);
1041                         if (was_first) {
1042                                 double percent = cumul * 100.0 / new_total;
1043 
1044                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045                                         str = "Not enough memory!";
1046                                 else
1047                                         str = alloc_str;
1048                         }
1049 
1050                         printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051                         free(alloc_str);
1052                         if (folded_sign == '+')
1053                                 break;
1054                 }
1055 
1056                 if (folded_sign == '-') {
1057                         const int new_level = level + (extra_offset ? 2 : 1);
1058                         printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059                                                                                 new_level, fp);
1060                 }
1061 
1062                 node = next;
1063         }
1064 
1065         return printed;
1066 }
1067 
1068 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069                                                 struct callchain_node *node,
1070                                                 int level, FILE *fp)
1071 {
1072         struct callchain_list *chain;
1073         int offset = level * LEVEL_OFFSET_STEP;
1074         char folded_sign = ' ';
1075         int printed = 0;
1076 
1077         list_for_each_entry(chain, &node->val, list) {
1078                 char bf[1024], *s;
1079 
1080                 folded_sign = callchain_list__folded(chain);
1081                 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082                 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1083         }
1084 
1085         if (folded_sign == '-')
1086                 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087                                                                         browser->hists->stats.total_period,
1088                                                                         level + 1,  fp);
1089         return printed;
1090 }
1091 
1092 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093                                            struct rb_root *chain, int level, FILE *fp)
1094 {
1095         struct rb_node *nd;
1096         int printed = 0;
1097 
1098         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1100 
1101                 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1102         }
1103 
1104         return printed;
1105 }
1106 
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108                                        struct hist_entry *he, FILE *fp)
1109 {
1110         char s[8192];
1111         double percent;
1112         int printed = 0;
1113         char folded_sign = ' ';
1114 
1115         if (symbol_conf.use_callchain)
1116                 folded_sign = hist_entry__folded(he);
1117 
1118         hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119         percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1120 
1121         if (symbol_conf.use_callchain)
1122                 printed += fprintf(fp, "%c ", folded_sign);
1123 
1124         printed += fprintf(fp, " %5.2f%%", percent);
1125 
1126         if (symbol_conf.show_nr_samples)
1127                 printed += fprintf(fp, " %11u", he->stat.nr_events);
1128 
1129         if (symbol_conf.show_total_period)
1130                 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1131 
1132         printed += fprintf(fp, "%s\n", rtrim(s));
1133 
1134         if (folded_sign == '-')
1135                 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136 
1137         return printed;
1138 }
1139 
1140 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1141 {
1142         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143                                                    browser->hists,
1144                                                    browser->min_pcnt);
1145         int printed = 0;
1146 
1147         while (nd) {
1148                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149 
1150                 printed += hist_browser__fprintf_entry(browser, h, fp);
1151                 nd = hists__filter_entries(rb_next(nd), browser->hists,
1152                                            browser->min_pcnt);
1153         }
1154 
1155         return printed;
1156 }
1157 
1158 static int hist_browser__dump(struct hist_browser *browser)
1159 {
1160         char filename[64];
1161         FILE *fp;
1162 
1163         while (1) {
1164                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165                 if (access(filename, F_OK))
1166                         break;
1167                 /*
1168                  * XXX: Just an arbitrary lazy upper limit
1169                  */
1170                 if (++browser->print_seq == 8192) {
1171                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172                         return -1;
1173                 }
1174         }
1175 
1176         fp = fopen(filename, "w");
1177         if (fp == NULL) {
1178                 char bf[64];
1179                 const char *err = strerror_r(errno, bf, sizeof(bf));
1180                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181                 return -1;
1182         }
1183 
1184         ++browser->print_seq;
1185         hist_browser__fprintf(browser, fp);
1186         fclose(fp);
1187         ui_helpline__fpush("%s written!", filename);
1188 
1189         return 0;
1190 }
1191 
1192 static struct hist_browser *hist_browser__new(struct hists *hists)
1193 {
1194         struct hist_browser *browser = zalloc(sizeof(*browser));
1195 
1196         if (browser) {
1197                 browser->hists = hists;
1198                 browser->b.refresh = hist_browser__refresh;
1199                 browser->b.seek = ui_browser__hists_seek;
1200                 browser->b.use_navkeypressed = true;
1201         }
1202 
1203         return browser;
1204 }
1205 
1206 static void hist_browser__delete(struct hist_browser *browser)
1207 {
1208         free(browser);
1209 }
1210 
1211 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1212 {
1213         return browser->he_selection;
1214 }
1215 
1216 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1217 {
1218         return browser->he_selection->thread;
1219 }
1220 
1221 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222                                 const char *ev_name)
1223 {
1224         char unit;
1225         int printed;
1226         const struct dso *dso = hists->dso_filter;
1227         const struct thread *thread = hists->thread_filter;
1228         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1229         u64 nr_events = hists->stats.total_period;
1230         struct perf_evsel *evsel = hists_to_evsel(hists);
1231         char buf[512];
1232         size_t buflen = sizeof(buf);
1233 
1234         if (perf_evsel__is_group_event(evsel)) {
1235                 struct perf_evsel *pos;
1236 
1237                 perf_evsel__group_desc(evsel, buf, buflen);
1238                 ev_name = buf;
1239 
1240                 for_each_group_member(pos, evsel) {
1241                         nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242                         nr_events += pos->hists.stats.total_period;
1243                 }
1244         }
1245 
1246         nr_samples = convert_unit(nr_samples, &unit);
1247         printed = scnprintf(bf, size,
1248                            "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249                            nr_samples, unit, ev_name, nr_events);
1250 
1251 
1252         if (hists->uid_filter_str)
1253                 printed += snprintf(bf + printed, size - printed,
1254                                     ", UID: %s", hists->uid_filter_str);
1255         if (thread)
1256                 printed += scnprintf(bf + printed, size - printed,
1257                                     ", Thread: %s(%d)",
1258                                     (thread->comm_set ? thread->comm : ""),
1259                                     thread->tid);
1260         if (dso)
1261                 printed += scnprintf(bf + printed, size - printed,
1262                                     ", DSO: %s", dso->short_name);
1263         return printed;
1264 }
1265 
1266 static inline void free_popup_options(char **options, int n)
1267 {
1268         int i;
1269 
1270         for (i = 0; i < n; ++i) {
1271                 free(options[i]);
1272                 options[i] = NULL;
1273         }
1274 }
1275 
1276 /* Check whether the browser is for 'top' or 'report' */
1277 static inline bool is_report_browser(void *timer)
1278 {
1279         return timer == NULL;
1280 }
1281 
1282 /*
1283  * Only runtime switching of perf data file will make "input_name" point
1284  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1285  * whether we need to call free() for current "input_name" during the switch.
1286  */
1287 static bool is_input_name_malloced = false;
1288 
1289 static int switch_data_file(void)
1290 {
1291         char *pwd, *options[32], *abs_path[32], *tmp;
1292         DIR *pwd_dir;
1293         int nr_options = 0, choice = -1, ret = -1;
1294         struct dirent *dent;
1295 
1296         pwd = getenv("PWD");
1297         if (!pwd)
1298                 return ret;
1299 
1300         pwd_dir = opendir(pwd);
1301         if (!pwd_dir)
1302                 return ret;
1303 
1304         memset(options, 0, sizeof(options));
1305         memset(options, 0, sizeof(abs_path));
1306 
1307         while ((dent = readdir(pwd_dir))) {
1308                 char path[PATH_MAX];
1309                 u64 magic;
1310                 char *name = dent->d_name;
1311                 FILE *file;
1312 
1313                 if (!(dent->d_type == DT_REG))
1314                         continue;
1315 
1316                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1317 
1318                 file = fopen(path, "r");
1319                 if (!file)
1320                         continue;
1321 
1322                 if (fread(&magic, 1, 8, file) < 8)
1323                         goto close_file_and_continue;
1324 
1325                 if (is_perf_magic(magic)) {
1326                         options[nr_options] = strdup(name);
1327                         if (!options[nr_options])
1328                                 goto close_file_and_continue;
1329 
1330                         abs_path[nr_options] = strdup(path);
1331                         if (!abs_path[nr_options]) {
1332                                 free(options[nr_options]);
1333                                 ui__warning("Can't search all data files due to memory shortage.\n");
1334                                 fclose(file);
1335                                 break;
1336                         }
1337 
1338                         nr_options++;
1339                 }
1340 
1341 close_file_and_continue:
1342                 fclose(file);
1343                 if (nr_options >= 32) {
1344                         ui__warning("Too many perf data files in PWD!\n"
1345                                     "Only the first 32 files will be listed.\n");
1346                         break;
1347                 }
1348         }
1349         closedir(pwd_dir);
1350 
1351         if (nr_options) {
1352                 choice = ui__popup_menu(nr_options, options);
1353                 if (choice < nr_options && choice >= 0) {
1354                         tmp = strdup(abs_path[choice]);
1355                         if (tmp) {
1356                                 if (is_input_name_malloced)
1357                                         free((void *)input_name);
1358                                 input_name = tmp;
1359                                 is_input_name_malloced = true;
1360                                 ret = 0;
1361                         } else
1362                                 ui__warning("Data switch failed due to memory shortage!\n");
1363                 }
1364         }
1365 
1366         free_popup_options(options, nr_options);
1367         free_popup_options(abs_path, nr_options);
1368         return ret;
1369 }
1370 
1371 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1372 {
1373         u64 nr_entries = 0;
1374         struct rb_node *nd = rb_first(&hb->hists->entries);
1375 
1376         while (nd) {
1377                 nr_entries++;
1378                 nd = hists__filter_entries(rb_next(nd), hb->hists,
1379                                            hb->min_pcnt);
1380         }
1381 
1382         hb->nr_pcnt_entries = nr_entries;
1383 }
1384 
1385 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1386                                     const char *helpline, const char *ev_name,
1387                                     bool left_exits,
1388                                     struct hist_browser_timer *hbt,
1389                                     float min_pcnt,
1390                                     struct perf_session_env *env)
1391 {
1392         struct hists *hists = &evsel->hists;
1393         struct hist_browser *browser = hist_browser__new(hists);
1394         struct branch_info *bi;
1395         struct pstack *fstack;
1396         char *options[16];
1397         int nr_options = 0;
1398         int key = -1;
1399         char buf[64];
1400         char script_opt[64];
1401         int delay_secs = hbt ? hbt->refresh : 0;
1402 
1403         if (browser == NULL)
1404                 return -1;
1405 
1406         if (min_pcnt) {
1407                 browser->min_pcnt = min_pcnt;
1408                 hist_browser__update_pcnt_entries(browser);
1409         }
1410 
1411         fstack = pstack__new(2);
1412         if (fstack == NULL)
1413                 goto out;
1414 
1415         ui_helpline__push(helpline);
1416 
1417         memset(options, 0, sizeof(options));
1418 
1419         while (1) {
1420                 const struct thread *thread = NULL;
1421                 const struct dso *dso = NULL;
1422                 int choice = 0,
1423                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1424                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1425                 int scripts_comm = -2, scripts_symbol = -2,
1426                     scripts_all = -2, switch_data = -2;
1427 
1428                 nr_options = 0;
1429 
1430                 key = hist_browser__run(browser, ev_name, hbt);
1431 
1432                 if (browser->he_selection != NULL) {
1433                         thread = hist_browser__selected_thread(browser);
1434                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1435                 }
1436                 switch (key) {
1437                 case K_TAB:
1438                 case K_UNTAB:
1439                         if (nr_events == 1)
1440                                 continue;
1441                         /*
1442                          * Exit the browser, let hists__browser_tree
1443                          * go to the next or previous
1444                          */
1445                         goto out_free_stack;
1446                 case 'a':
1447                         if (!sort__has_sym) {
1448                                 ui_browser__warning(&browser->b, delay_secs * 2,
1449                         "Annotation is only available for symbolic views, "
1450                         "include \"sym*\" in --sort to use it.");
1451                                 continue;
1452                         }
1453 
1454                         if (browser->selection == NULL ||
1455                             browser->selection->sym == NULL ||
1456                             browser->selection->map->dso->annotate_warned)
1457                                 continue;
1458                         goto do_annotate;
1459                 case 'P':
1460                         hist_browser__dump(browser);
1461                         continue;
1462                 case 'd':
1463                         goto zoom_dso;
1464                 case 'V':
1465                         browser->show_dso = !browser->show_dso;
1466                         continue;
1467                 case 't':
1468                         goto zoom_thread;
1469                 case '/':
1470                         if (ui_browser__input_window("Symbol to show",
1471                                         "Please enter the name of symbol you want to see",
1472                                         buf, "ENTER: OK, ESC: Cancel",
1473                                         delay_secs * 2) == K_ENTER) {
1474                                 hists->symbol_filter_str = *buf ? buf : NULL;
1475                                 hists__filter_by_symbol(hists);
1476                                 hist_browser__reset(browser);
1477                         }
1478                         continue;
1479                 case 'r':
1480                         if (is_report_browser(hbt))
1481                                 goto do_scripts;
1482                         continue;
1483                 case 's':
1484                         if (is_report_browser(hbt))
1485                                 goto do_data_switch;
1486                         continue;
1487                 case K_F1:
1488                 case 'h':
1489                 case '?':
1490                         ui_browser__help_window(&browser->b,
1491                                         "h/?/F1        Show this window\n"
1492                                         "UP/DOWN/PGUP\n"
1493                                         "PGDN/SPACE    Navigate\n"
1494                                         "q/ESC/CTRL+C  Exit browser\n\n"
1495                                         "For multiple event sessions:\n\n"
1496                                         "TAB/UNTAB Switch events\n\n"
1497                                         "For symbolic views (--sort has sym):\n\n"
1498                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1499                                         "<-            Zoom out\n"
1500                                         "a             Annotate current symbol\n"
1501                                         "C             Collapse all callchains\n"
1502                                         "E             Expand all callchains\n"
1503                                         "d             Zoom into current DSO\n"
1504                                         "t             Zoom into current Thread\n"
1505                                         "r             Run available scripts('perf report' only)\n"
1506                                         "s             Switch to another data file in PWD ('perf report' only)\n"
1507                                         "P             Print histograms to perf.hist.N\n"
1508                                         "V             Verbose (DSO names in callchains, etc)\n"
1509                                         "/             Filter symbol by name");
1510                         continue;
1511                 case K_ENTER:
1512                 case K_RIGHT:
1513                         /* menu */
1514                         break;
1515                 case K_LEFT: {
1516                         const void *top;
1517 
1518                         if (pstack__empty(fstack)) {
1519                                 /*
1520                                  * Go back to the perf_evsel_menu__run or other user
1521                                  */
1522                                 if (left_exits)
1523                                         goto out_free_stack;
1524                                 continue;
1525                         }
1526                         top = pstack__pop(fstack);
1527                         if (top == &browser->hists->dso_filter)
1528                                 goto zoom_out_dso;
1529                         if (top == &browser->hists->thread_filter)
1530                                 goto zoom_out_thread;
1531                         continue;
1532                 }
1533                 case K_ESC:
1534                         if (!left_exits &&
1535                             !ui_browser__dialog_yesno(&browser->b,
1536                                                "Do you really want to exit?"))
1537                                 continue;
1538                         /* Fall thru */
1539                 case 'q':
1540                 case CTRL('c'):
1541                         goto out_free_stack;
1542                 default:
1543                         continue;
1544                 }
1545 
1546                 if (!sort__has_sym)
1547                         goto add_exit_option;
1548 
1549                 if (sort__mode == SORT_MODE__BRANCH) {
1550                         bi = browser->he_selection->branch_info;
1551                         if (browser->selection != NULL &&
1552                             bi &&
1553                             bi->from.sym != NULL &&
1554                             !bi->from.map->dso->annotate_warned &&
1555                                 asprintf(&options[nr_options], "Annotate %s",
1556                                          bi->from.sym->name) > 0)
1557                                 annotate_f = nr_options++;
1558 
1559                         if (browser->selection != NULL &&
1560                             bi &&
1561                             bi->to.sym != NULL &&
1562                             !bi->to.map->dso->annotate_warned &&
1563                             (bi->to.sym != bi->from.sym ||
1564                              bi->to.map->dso != bi->from.map->dso) &&
1565                                 asprintf(&options[nr_options], "Annotate %s",
1566                                          bi->to.sym->name) > 0)
1567                                 annotate_t = nr_options++;
1568                 } else {
1569 
1570                         if (browser->selection != NULL &&
1571                             browser->selection->sym != NULL &&
1572                             !browser->selection->map->dso->annotate_warned &&
1573                                 asprintf(&options[nr_options], "Annotate %s",
1574                                          browser->selection->sym->name) > 0)
1575                                 annotate = nr_options++;
1576                 }
1577 
1578                 if (thread != NULL &&
1579                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1580                              (browser->hists->thread_filter ? "out of" : "into"),
1581                              (thread->comm_set ? thread->comm : ""),
1582                              thread->tid) > 0)
1583                         zoom_thread = nr_options++;
1584 
1585                 if (dso != NULL &&
1586                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1587                              (browser->hists->dso_filter ? "out of" : "into"),
1588                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1589                         zoom_dso = nr_options++;
1590 
1591                 if (browser->selection != NULL &&
1592                     browser->selection->map != NULL &&
1593                     asprintf(&options[nr_options], "Browse map details") > 0)
1594                         browse_map = nr_options++;
1595 
1596                 /* perf script support */
1597                 if (browser->he_selection) {
1598                         struct symbol *sym;
1599 
1600                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1601                                 browser->he_selection->thread->comm) > 0)
1602                                 scripts_comm = nr_options++;
1603 
1604                         sym = browser->he_selection->ms.sym;
1605                         if (sym && sym->namelen &&
1606                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1607                                                 sym->name) > 0)
1608                                 scripts_symbol = nr_options++;
1609                 }
1610 
1611                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1612                         scripts_all = nr_options++;
1613 
1614                 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1615                                 "Switch to another data file in PWD") > 0)
1616                         switch_data = nr_options++;
1617 add_exit_option:
1618                 options[nr_options++] = (char *)"Exit";
1619 retry_popup_menu:
1620                 choice = ui__popup_menu(nr_options, options);
1621 
1622                 if (choice == nr_options - 1)
1623                         break;
1624 
1625                 if (choice == -1) {
1626                         free_popup_options(options, nr_options - 1);
1627                         continue;
1628                 }
1629 
1630                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1631                         struct hist_entry *he;
1632                         int err;
1633 do_annotate:
1634                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1635                                 continue;
1636 
1637                         he = hist_browser__selected_entry(browser);
1638                         if (he == NULL)
1639                                 continue;
1640 
1641                         /*
1642                          * we stash the branch_info symbol + map into the
1643                          * the ms so we don't have to rewrite all the annotation
1644                          * code to use branch_info.
1645                          * in branch mode, the ms struct is not used
1646                          */
1647                         if (choice == annotate_f) {
1648                                 he->ms.sym = he->branch_info->from.sym;
1649                                 he->ms.map = he->branch_info->from.map;
1650                         }  else if (choice == annotate_t) {
1651                                 he->ms.sym = he->branch_info->to.sym;
1652                                 he->ms.map = he->branch_info->to.map;
1653                         }
1654 
1655                         /*
1656                          * Don't let this be freed, say, by hists__decay_entry.
1657                          */
1658                         he->used = true;
1659                         err = hist_entry__tui_annotate(he, evsel, hbt);
1660                         he->used = false;
1661                         /*
1662                          * offer option to annotate the other branch source or target
1663                          * (if they exists) when returning from annotate
1664                          */
1665                         if ((err == 'q' || err == CTRL('c'))
1666                             && annotate_t != -2 && annotate_f != -2)
1667                                 goto retry_popup_menu;
1668 
1669                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1670                         if (err)
1671                                 ui_browser__handle_resize(&browser->b);
1672 
1673                 } else if (choice == browse_map)
1674                         map__browse(browser->selection->map);
1675                 else if (choice == zoom_dso) {
1676 zoom_dso:
1677                         if (browser->hists->dso_filter) {
1678                                 pstack__remove(fstack, &browser->hists->dso_filter);
1679 zoom_out_dso:
1680                                 ui_helpline__pop();
1681                                 browser->hists->dso_filter = NULL;
1682                                 sort_dso.elide = false;
1683                         } else {
1684                                 if (dso == NULL)
1685                                         continue;
1686                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1687                                                    dso->kernel ? "the Kernel" : dso->short_name);
1688                                 browser->hists->dso_filter = dso;
1689                                 sort_dso.elide = true;
1690                                 pstack__push(fstack, &browser->hists->dso_filter);
1691                         }
1692                         hists__filter_by_dso(hists);
1693                         hist_browser__reset(browser);
1694                 } else if (choice == zoom_thread) {
1695 zoom_thread:
1696                         if (browser->hists->thread_filter) {
1697                                 pstack__remove(fstack, &browser->hists->thread_filter);
1698 zoom_out_thread:
1699                                 ui_helpline__pop();
1700                                 browser->hists->thread_filter = NULL;
1701                                 sort_thread.elide = false;
1702                         } else {
1703                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1704                                                    thread->comm_set ? thread->comm : "",
1705                                                    thread->tid);
1706                                 browser->hists->thread_filter = thread;
1707                                 sort_thread.elide = true;
1708                                 pstack__push(fstack, &browser->hists->thread_filter);
1709                         }
1710                         hists__filter_by_thread(hists);
1711                         hist_browser__reset(browser);
1712                 }
1713                 /* perf scripts support */
1714                 else if (choice == scripts_all || choice == scripts_comm ||
1715                                 choice == scripts_symbol) {
1716 do_scripts:
1717                         memset(script_opt, 0, 64);
1718 
1719                         if (choice == scripts_comm)
1720                                 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1721 
1722                         if (choice == scripts_symbol)
1723                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1724 
1725                         script_browse(script_opt);
1726                 }
1727                 /* Switch to another data file */
1728                 else if (choice == switch_data) {
1729 do_data_switch:
1730                         if (!switch_data_file()) {
1731                                 key = K_SWITCH_INPUT_DATA;
1732                                 break;
1733                         } else
1734                                 ui__warning("Won't switch the data files due to\n"
1735                                         "no valid data file get selected!\n");
1736                 }
1737         }
1738 out_free_stack:
1739         pstack__delete(fstack);
1740 out:
1741         hist_browser__delete(browser);
1742         free_popup_options(options, nr_options - 1);
1743         return key;
1744 }
1745 
1746 struct perf_evsel_menu {
1747         struct ui_browser b;
1748         struct perf_evsel *selection;
1749         bool lost_events, lost_events_warned;
1750         float min_pcnt;
1751         struct perf_session_env *env;
1752 };
1753 
1754 static void perf_evsel_menu__write(struct ui_browser *browser,
1755                                    void *entry, int row)
1756 {
1757         struct perf_evsel_menu *menu = container_of(browser,
1758                                                     struct perf_evsel_menu, b);
1759         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1760         bool current_entry = ui_browser__is_current_entry(browser, row);
1761         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1762         const char *ev_name = perf_evsel__name(evsel);
1763         char bf[256], unit;
1764         const char *warn = " ";
1765         size_t printed;
1766 
1767         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1768                                                        HE_COLORSET_NORMAL);
1769 
1770         if (perf_evsel__is_group_event(evsel)) {
1771                 struct perf_evsel *pos;
1772 
1773                 ev_name = perf_evsel__group_name(evsel);
1774 
1775                 for_each_group_member(pos, evsel) {
1776                         nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777                 }
1778         }
1779 
1780         nr_events = convert_unit(nr_events, &unit);
1781         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1782                            unit, unit == ' ' ? "" : " ", ev_name);
1783         slsmg_printf("%s", bf);
1784 
1785         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1786         if (nr_events != 0) {
1787                 menu->lost_events = true;
1788                 if (!current_entry)
1789                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1790                 nr_events = convert_unit(nr_events, &unit);
1791                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1792                                      nr_events, unit, unit == ' ' ? "" : " ");
1793                 warn = bf;
1794         }
1795 
1796         slsmg_write_nstring(warn, browser->width - printed);
1797 
1798         if (current_entry)
1799                 menu->selection = evsel;
1800 }
1801 
1802 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1803                                 int nr_events, const char *help,
1804                                 struct hist_browser_timer *hbt)
1805 {
1806         struct perf_evlist *evlist = menu->b.priv;
1807         struct perf_evsel *pos;
1808         const char *ev_name, *title = "Available samples";
1809         int delay_secs = hbt ? hbt->refresh : 0;
1810         int key;
1811 
1812         if (ui_browser__show(&menu->b, title,
1813                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1814                 return -1;
1815 
1816         while (1) {
1817                 key = ui_browser__run(&menu->b, delay_secs);
1818 
1819                 switch (key) {
1820                 case K_TIMER:
1821                         hbt->timer(hbt->arg);
1822 
1823                         if (!menu->lost_events_warned && menu->lost_events) {
1824                                 ui_browser__warn_lost_events(&menu->b);
1825                                 menu->lost_events_warned = true;
1826                         }
1827                         continue;
1828                 case K_RIGHT:
1829                 case K_ENTER:
1830                         if (!menu->selection)
1831                                 continue;
1832                         pos = menu->selection;
1833 browse_hists:
1834                         perf_evlist__set_selected(evlist, pos);
1835                         /*
1836                          * Give the calling tool a chance to populate the non
1837                          * default evsel resorted hists tree.
1838                          */
1839                         if (hbt)
1840                                 hbt->timer(hbt->arg);
1841                         ev_name = perf_evsel__name(pos);
1842                         key = perf_evsel__hists_browse(pos, nr_events, help,
1843                                                        ev_name, true, hbt,
1844                                                        menu->min_pcnt,
1845                                                        menu->env);
1846                         ui_browser__show_title(&menu->b, title);
1847                         switch (key) {
1848                         case K_TAB:
1849                                 if (pos->node.next == &evlist->entries)
1850                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1851                                 else
1852                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1853                                 goto browse_hists;
1854                         case K_UNTAB:
1855                                 if (pos->node.prev == &evlist->entries)
1856                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1857                                 else
1858                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1859                                 goto browse_hists;
1860                         case K_ESC:
1861                                 if (!ui_browser__dialog_yesno(&menu->b,
1862                                                 "Do you really want to exit?"))
1863                                         continue;
1864                                 /* Fall thru */
1865                         case K_SWITCH_INPUT_DATA:
1866                         case 'q':
1867                         case CTRL('c'):
1868                                 goto out;
1869                         default:
1870                                 continue;
1871                         }
1872                 case K_LEFT:
1873                         continue;
1874                 case K_ESC:
1875                         if (!ui_browser__dialog_yesno(&menu->b,
1876                                                "Do you really want to exit?"))
1877                                 continue;
1878                         /* Fall thru */
1879                 case 'q':
1880                 case CTRL('c'):
1881                         goto out;
1882                 default:
1883                         continue;
1884                 }
1885         }
1886 
1887 out:
1888         ui_browser__hide(&menu->b);
1889         return key;
1890 }
1891 
1892 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1893                                  void *entry)
1894 {
1895         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1896 
1897         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1898                 return true;
1899 
1900         return false;
1901 }
1902 
1903 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1904                                            int nr_entries, const char *help,
1905                                            struct hist_browser_timer *hbt,
1906                                            float min_pcnt,
1907                                            struct perf_session_env *env)
1908 {
1909         struct perf_evsel *pos;
1910         struct perf_evsel_menu menu = {
1911                 .b = {
1912                         .entries    = &evlist->entries,
1913                         .refresh    = ui_browser__list_head_refresh,
1914                         .seek       = ui_browser__list_head_seek,
1915                         .write      = perf_evsel_menu__write,
1916                         .filter     = filter_group_entries,
1917                         .nr_entries = nr_entries,
1918                         .priv       = evlist,
1919                 },
1920                 .min_pcnt = min_pcnt,
1921                 .env = env,
1922         };
1923 
1924         ui_helpline__push("Press ESC to exit");
1925 
1926         list_for_each_entry(pos, &evlist->entries, node) {
1927                 const char *ev_name = perf_evsel__name(pos);
1928                 size_t line_len = strlen(ev_name) + 7;
1929 
1930                 if (menu.b.width < line_len)
1931                         menu.b.width = line_len;
1932         }
1933 
1934         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1935 }
1936 
1937 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1938                                   struct hist_browser_timer *hbt,
1939                                   float min_pcnt,
1940                                   struct perf_session_env *env)
1941 {
1942         int nr_entries = evlist->nr_entries;
1943 
1944 single_entry:
1945         if (nr_entries == 1) {
1946                 struct perf_evsel *first = list_entry(evlist->entries.next,
1947                                                       struct perf_evsel, node);
1948                 const char *ev_name = perf_evsel__name(first);
1949 
1950                 return perf_evsel__hists_browse(first, nr_entries, help,
1951                                                 ev_name, false, hbt, min_pcnt,
1952                                                 env);
1953         }
1954 
1955         if (symbol_conf.event_group) {
1956                 struct perf_evsel *pos;
1957 
1958                 nr_entries = 0;
1959                 list_for_each_entry(pos, &evlist->entries, node)
1960                         if (perf_evsel__is_group_leader(pos))
1961                                 nr_entries++;
1962 
1963                 if (nr_entries == 1)
1964                         goto single_entry;
1965         }
1966 
1967         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1968                                                hbt, min_pcnt, env);
1969 }
1970 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp