blob: 4d416984c59d1891d55a80ba845331c74c7d16cc [file] [log] [blame]
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001#include <stdio.h>
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002#include "../libslang.h"
3#include <stdlib.h>
4#include <string.h>
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03005#include <linux/rbtree.h>
6
Namhyung Kimaca7a942012-04-04 00:14:26 -07007#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"
Namhyung Kim68d80752012-11-02 14:50:06 +090013#include "../../arch/common.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030014
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -020018#include "../ui.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030019#include "map.h"
20
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030021struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -030026 int print_seq;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -030027 bool show_dso;
Namhyung Kim064f1982013-05-14 11:09:04 +090028 float min_pcnt;
29 u64 nr_pcnt_entries;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030030};
31
Namhyung Kimf5951d52012-09-03 11:53:09 +090032extern void hist_browser__init_hpp(void);
33
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030034static int hists__browser_title(struct hists *hists, char *bf, size_t size,
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -020035 const char *ev_name);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -030036
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030037static void hist_browser__refresh_dimensions(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030038{
39 /* 3 == +/- toggle symbol before actual hist_entry rendering */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030040 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030041 sizeof("[k]"));
42}
43
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030044static void hist_browser__reset(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030045{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030046 browser->b.nr_entries = browser->hists->nr_entries;
47 hist_browser__refresh_dimensions(browser);
48 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030049}
50
51static char tree__folded_sign(bool unfolded)
52{
53 return unfolded ? '-' : '+';
54}
55
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030056static char map_symbol__folded(const struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030057{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030058 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030059}
60
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030061static char hist_entry__folded(const struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030062{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030063 return map_symbol__folded(&he->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030064}
65
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030066static char callchain_list__folded(const struct callchain_list *cl)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030067{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030068 return map_symbol__folded(&cl->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030069}
70
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030071static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -030072{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030073 ms->unfolded = unfold ? ms->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -030074}
75
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030076static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030077{
78 int n = 0;
79 struct rb_node *nd;
80
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030081 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030082 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
101static 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
118static 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
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300131static bool map_symbol__toggle_fold(struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300132{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300133 if (!ms)
Jiri Olsa8493fe12012-04-04 22:21:31 +0200134 return false;
135
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300136 if (!ms->has_children)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300137 return false;
138
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300139 ms->unfolded = !ms->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300140 return true;
141}
142
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300143static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300144{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300145 struct rb_node *nd = rb_first(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300146
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300147 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300148 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149 struct callchain_list *chain;
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300150 bool first = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300151
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 ||
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300156 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300157 } else
158 chain->ms.has_children = chain->list.next == &child->val &&
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300159 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300160 }
161
162 callchain_node__init_have_children_rb_tree(child);
163 }
164}
165
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300166static void callchain_node__init_have_children(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300167{
168 struct callchain_list *chain;
169
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300170 list_for_each_entry(chain, &node->val, list)
171 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300172
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300173 callchain_node__init_have_children_rb_tree(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300174}
175
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300176static void callchain__init_have_children(struct rb_root *root)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300177{
178 struct rb_node *nd;
179
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300180 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300181 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182 callchain_node__init_have_children(node);
183 }
184}
185
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300186static void hist_entry__init_have_children(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300187{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300188 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;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300192 }
193}
194
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300195static bool hist_browser__toggle_fold(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300196{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300197 if (map_symbol__toggle_fold(browser->selection)) {
198 struct hist_entry *he = browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300199
200 hist_entry__init_have_children(he);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300201 browser->hists->nr_entries -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300202
203 if (he->ms.unfolded)
204 he->nr_rows = callchain__count_rows(&he->sorted_chain);
205 else
206 he->nr_rows = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300207 browser->hists->nr_entries += he->nr_rows;
208 browser->b.nr_entries = browser->hists->nr_entries;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300209
210 return true;
211 }
212
213 /* If it doesn't have children, no toggling performed */
214 return false;
215}
216
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300217static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300218{
219 int n = 0;
220 struct rb_node *nd;
221
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300222 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300223 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
240static 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
258static 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
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300271static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300272{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300273 hist_entry__init_have_children(he);
274 map_symbol__set_folding(&he->ms, unfold);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300275
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300276 if (he->ms.has_children) {
277 int n = callchain__set_folding(&he->sorted_chain, unfold);
278 he->nr_rows = unfold ? n : 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300279 } else
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300280 he->nr_rows = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300281}
282
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300283static void hists__set_folding(struct hists *hists, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300284{
285 struct rb_node *nd;
286
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300287 hists->nr_entries = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300288
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300289 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300290 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 hist_entry__set_folding(he, unfold);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300292 hists->nr_entries += 1 + he->nr_rows;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300293 }
294}
295
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300296static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300297{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300298 hists__set_folding(browser->hists, unfold);
299 browser->b.nr_entries = browser->hists->nr_entries;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300300 /* Go to the start, we may be way after valid entries after a collapse */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300301 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300302}
303
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200304static 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
Namhyung Kimfa5df942013-05-14 11:09:05 +0900313static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
314
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300315static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
Namhyung Kim9783adf2012-11-02 14:50:05 +0900316 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300317{
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300318 int key;
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300319 char title[160];
Namhyung Kim9783adf2012-11-02 14:50:05 +0900320 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300321
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300322 browser->b.entries = &browser->hists->entries;
323 browser->b.nr_entries = browser->hists->nr_entries;
Namhyung Kim064f1982013-05-14 11:09:04 +0900324 if (browser->min_pcnt)
325 browser->b.nr_entries = browser->nr_pcnt_entries;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300326
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300327 hist_browser__refresh_dimensions(browser);
328 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300329
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300330 if (ui_browser__show(&browser->b, title,
Arnaldo Carvalho de Melo59e8fe32010-08-10 15:44:20 -0300331 "Press '?' for help on key bindings") < 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300332 return -1;
333
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300334 while (1) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300335 key = ui_browser__run(&browser->b, delay_secs);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300336
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300337 switch (key) {
Namhyung Kimfa5df942013-05-14 11:09:05 +0900338 case K_TIMER: {
339 u64 nr_entries;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900340 hbt->timer(hbt->arg);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900341
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);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200350
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300351 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);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200356 }
357
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300358 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
359 ui_browser__show_title(&browser->b, title);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300360 continue;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900361 }
Arnaldo Carvalho de Melo46941532010-08-10 15:50:07 -0300362 case 'D': { /* Debug */
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300363 static int seq;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300364 struct hist_entry *h = rb_entry(browser->b.top,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300365 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",
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300368 seq++, browser->b.nr_entries,
369 browser->hists->nr_entries,
370 browser->b.height,
371 browser->b.index,
372 browser->b.top_idx,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300373 h->row_offset, h->nr_rows);
374 }
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300375 break;
376 case 'C':
377 /* Collapse the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300378 hist_browser__set_folding(browser, false);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300379 break;
380 case 'E':
381 /* Expand the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300382 hist_browser__set_folding(browser, true);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300383 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -0200384 case K_ENTER:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300385 if (hist_browser__toggle_fold(browser))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300386 break;
387 /* fall thru */
388 default:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300389 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300390 }
391 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300392out:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300393 ui_browser__hide(&browser->b);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300394 return key;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300395}
396
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300397static char *callchain_list__sym_name(struct callchain_list *cl,
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300398 char *bf, size_t bfsize, bool show_dso)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300399{
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300400 int printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300401
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300402 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
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300411 return bf;
412}
413
414#define LEVEL_OFFSET_STEP 3
415
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300416static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300417 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);
Frederic Weisbeckerf08c3152011-01-14 04:51:59 +0100437 u64 cumul = callchain_cumul_hits(child);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300438 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) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300446 char bf[1024], *alloc_str;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300447 const char *str;
448 int color;
449 bool was_first = first;
450
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300451 if (first)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300452 first = false;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300453 else
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300454 extra_offset = LEVEL_OFFSET_STEP;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300455
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;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300463 str = callchain_list__sym_name(chain, bf, sizeof(bf),
464 browser->show_dso);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300465 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;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300475 width = browser->b.width - (offset + extra_offset + 2);
476 if (ui_browser__is_current_entry(&browser->b, row)) {
477 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
480 }
481
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300482 ui_browser__set_color(&browser->b, color);
483 ui_browser__gotorc(&browser->b, row, 0);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300484 slsmg_write_nstring(" ", offset + extra_offset);
485 slsmg_printf("%c ", folded_sign);
486 slsmg_write_nstring(str, width);
487 free(alloc_str);
488
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300489 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300490 goto out;
491do_next:
492 if (folded_sign == '+')
493 break;
494 }
495
496 if (folded_sign == '-') {
497 const int new_level = level + (extra_offset ? 2 : 1);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300498 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300499 new_level, row, row_offset,
500 is_current_entry);
501 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300502 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300503 goto out;
504 node = next;
505 }
506out:
507 return row - first_row;
508}
509
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300510static int hist_browser__show_callchain_node(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300511 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,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300519 width = browser->b.width - offset;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300520 char folded_sign = ' ';
521
522 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300523 char bf[1024], *s;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300524 int color;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300525
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300526 folded_sign = callchain_list__folded(chain);
527
528 if (*row_offset != 0) {
529 --*row_offset;
530 continue;
531 }
532
533 color = HE_COLORSET_NORMAL;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300534 if (ui_browser__is_current_entry(&browser->b, row)) {
535 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300536 color = HE_COLORSET_SELECTED;
537 *is_current_entry = true;
538 }
539
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300540 s = callchain_list__sym_name(chain, bf, sizeof(bf),
541 browser->show_dso);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300542 ui_browser__gotorc(&browser->b, row, 0);
543 ui_browser__set_color(&browser->b, color);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300544 slsmg_write_nstring(" ", offset);
545 slsmg_printf("%c ", folded_sign);
546 slsmg_write_nstring(s, width - 2);
547
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300548 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300549 goto out;
550 }
551
552 if (folded_sign == '-')
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300553 row += hist_browser__show_callchain_node_rb_tree(browser, node,
554 browser->hists->stats.total_period,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300555 level + 1, row,
556 row_offset,
557 is_current_entry);
558out:
559 return row - first_row;
560}
561
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300562static int hist_browser__show_callchain(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300563 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
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300574 row += hist_browser__show_callchain_node(browser, node, level,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300575 row, row_offset,
576 is_current_entry);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300577 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300578 break;
579 }
580
581 return row - first_row;
582}
583
Namhyung Kim89701462013-01-22 18:09:38 +0900584struct hpp_arg {
585 struct ui_browser *b;
586 char folded_sign;
587 bool current_entry;
588};
589
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900590static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900591{
Namhyung Kim89701462013-01-22 18:09:38 +0900592 struct hpp_arg *arg = hpp->ptr;
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900593
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900594 if (arg->current_entry && arg->b->navkeypressed)
595 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
596 else
597 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
598
599 if (front) {
600 if (!symbol_conf.use_callchain)
601 return 0;
602
603 slsmg_printf("%c ", arg->folded_sign);
604 return 2;
605 }
606
607 return 0;
608}
609
610static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
611{
612 struct hpp_arg *arg = hpp->ptr;
613
614 if (!arg->current_entry || !arg->b->navkeypressed)
615 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
616 return 0;
617}
618
619static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
620{
621 struct hpp_arg *arg = hpp->ptr;
622 int ret;
623 va_list args;
624 double percent;
625
626 va_start(args, fmt);
627 percent = va_arg(args, double);
628 va_end(args);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900629
Namhyung Kim89701462013-01-22 18:09:38 +0900630 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900631
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900632 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
Namhyung Kim89701462013-01-22 18:09:38 +0900633 slsmg_printf("%s", hpp->buf);
634
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900635 advance_hpp(hpp, ret);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900636 return ret;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900637}
638
Namhyung Kim89701462013-01-22 18:09:38 +0900639#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900640static u64 __hpp_get_##_field(struct hist_entry *he) \
641{ \
642 return he->stat._field; \
643} \
644 \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100645static int \
646hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
647 struct perf_hpp *hpp, \
648 struct hist_entry *he) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900649{ \
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900650 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
651 __hpp__slsmg_color_printf, true); \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900652}
Namhyung Kimf5951d52012-09-03 11:53:09 +0900653
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900654__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
655__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
656__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
657__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
658__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900659
660#undef __HPP_COLOR_PERCENT_FN
Namhyung Kimf5951d52012-09-03 11:53:09 +0900661
662void hist_browser__init_hpp(void)
663{
Jiri Olsa1d778222012-10-04 21:49:39 +0900664 perf_hpp__init();
Namhyung Kimf5951d52012-09-03 11:53:09 +0900665
666 perf_hpp__format[PERF_HPP__OVERHEAD].color =
667 hist_browser__hpp_color_overhead;
668 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
669 hist_browser__hpp_color_overhead_sys;
670 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
671 hist_browser__hpp_color_overhead_us;
672 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
673 hist_browser__hpp_color_overhead_guest_sys;
674 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
675 hist_browser__hpp_color_overhead_guest_us;
676}
677
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300678static int hist_browser__show_entry(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300679 struct hist_entry *entry,
680 unsigned short row)
681{
682 char s[256];
Jiri Olsa12400052012-10-13 00:06:16 +0200683 int printed = 0;
Namhyung Kim67d25912012-09-12 15:35:06 +0900684 int width = browser->b.width;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300685 char folded_sign = ' ';
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300686 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300687 off_t row_offset = entry->row_offset;
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300688 bool first = true;
Jiri Olsa12400052012-10-13 00:06:16 +0200689 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300690
691 if (current_entry) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300692 browser->he_selection = entry;
693 browser->selection = &entry->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300694 }
695
696 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300697 hist_entry__init_have_children(entry);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300698 folded_sign = hist_entry__folded(entry);
699 }
700
701 if (row_offset == 0) {
Namhyung Kim89701462013-01-22 18:09:38 +0900702 struct hpp_arg arg = {
703 .b = &browser->b,
704 .folded_sign = folded_sign,
705 .current_entry = current_entry,
706 };
Namhyung Kimf5951d52012-09-03 11:53:09 +0900707 struct perf_hpp hpp = {
708 .buf = s,
709 .size = sizeof(s),
Namhyung Kim89701462013-01-22 18:09:38 +0900710 .ptr = &arg,
Namhyung Kimf5951d52012-09-03 11:53:09 +0900711 };
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300712
Namhyung Kim67d25912012-09-12 15:35:06 +0900713 ui_browser__gotorc(&browser->b, row, 0);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900714
Jiri Olsa12400052012-10-13 00:06:16 +0200715 perf_hpp__for_each_format(fmt) {
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300716 if (!first) {
Namhyung Kimf5951d52012-09-03 11:53:09 +0900717 slsmg_printf(" ");
718 width -= 2;
719 }
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300720 first = false;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900721
Jiri Olsa12400052012-10-13 00:06:16 +0200722 if (fmt->color) {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100723 width -= fmt->color(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900724 } else {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100725 width -= fmt->entry(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900726 slsmg_printf("%s", s);
727 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300728 }
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200729
730 /* The scroll bar isn't being used */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300731 if (!browser->b.navkeypressed)
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200732 width += 1;
733
Namhyung Kimf5951d52012-09-03 11:53:09 +0900734 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300735 slsmg_write_nstring(s, width);
736 ++row;
737 ++printed;
738 } else
739 --row_offset;
740
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300741 if (folded_sign == '-' && row != browser->b.height) {
742 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300743 1, row, &row_offset,
744 &current_entry);
745 if (current_entry)
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300746 browser->he_selection = entry;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300747 }
748
749 return printed;
750}
751
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300752static void ui_browser__hists_init_top(struct ui_browser *browser)
753{
754 if (browser->top == NULL) {
755 struct hist_browser *hb;
756
757 hb = container_of(browser, struct hist_browser, b);
758 browser->top = rb_first(&hb->hists->entries);
759 }
760}
761
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300762static unsigned int hist_browser__refresh(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300763{
764 unsigned row = 0;
765 struct rb_node *nd;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300766 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300767
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300768 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300769
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300770 for (nd = browser->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300771 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900772 u64 total = hists__total_period(h->hists);
773 float percent = 0.0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300774
775 if (h->filtered)
776 continue;
777
Namhyung Kimf2148332014-01-14 11:52:48 +0900778 if (total)
779 percent = h->stat.period * 100.0 / total;
780
Namhyung Kim064f1982013-05-14 11:09:04 +0900781 if (percent < hb->min_pcnt)
782 continue;
783
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300784 row += hist_browser__show_entry(hb, h, row);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300785 if (row == browser->height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300786 break;
787 }
788
789 return row;
790}
791
Namhyung Kim064f1982013-05-14 11:09:04 +0900792static struct rb_node *hists__filter_entries(struct rb_node *nd,
793 struct hists *hists,
794 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300795{
796 while (nd != NULL) {
797 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900798 u64 total = hists__total_period(hists);
799 float percent = 0.0;
800
801 if (total)
802 percent = h->stat.period * 100.0 / total;
Namhyung Kim064f1982013-05-14 11:09:04 +0900803
804 if (percent < min_pcnt)
805 return NULL;
806
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300807 if (!h->filtered)
808 return nd;
809
810 nd = rb_next(nd);
811 }
812
813 return NULL;
814}
815
Namhyung Kim064f1982013-05-14 11:09:04 +0900816static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
817 struct hists *hists,
818 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300819{
820 while (nd != NULL) {
821 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900822 u64 total = hists__total_period(hists);
823 float percent = 0.0;
824
825 if (total)
826 percent = h->stat.period * 100.0 / total;
Namhyung Kim064f1982013-05-14 11:09:04 +0900827
828 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300829 return nd;
830
831 nd = rb_prev(nd);
832 }
833
834 return NULL;
835}
836
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300837static void ui_browser__hists_seek(struct ui_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300838 off_t offset, int whence)
839{
840 struct hist_entry *h;
841 struct rb_node *nd;
842 bool first = true;
Namhyung Kim064f1982013-05-14 11:09:04 +0900843 struct hist_browser *hb;
844
845 hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300846
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300847 if (browser->nr_entries == 0)
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -0300848 return;
849
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300850 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300851
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300852 switch (whence) {
853 case SEEK_SET:
Namhyung Kim064f1982013-05-14 11:09:04 +0900854 nd = hists__filter_entries(rb_first(browser->entries),
855 hb->hists, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300856 break;
857 case SEEK_CUR:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300858 nd = browser->top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300859 goto do_offset;
860 case SEEK_END:
Namhyung Kim064f1982013-05-14 11:09:04 +0900861 nd = hists__filter_prev_entries(rb_last(browser->entries),
862 hb->hists, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300863 first = false;
864 break;
865 default:
866 return;
867 }
868
869 /*
870 * Moves not relative to the first visible entry invalidates its
871 * row_offset:
872 */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300873 h = rb_entry(browser->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300874 h->row_offset = 0;
875
876 /*
877 * Here we have to check if nd is expanded (+), if it is we can't go
878 * the next top level hist_entry, instead we must compute an offset of
879 * what _not_ to show and not change the first visible entry.
880 *
881 * This offset increments when we are going from top to bottom and
882 * decreases when we're going from bottom to top.
883 *
884 * As we don't have backpointers to the top level in the callchains
885 * structure, we need to always print the whole hist_entry callchain,
886 * skipping the first ones that are before the first visible entry
887 * and stop when we printed enough lines to fill the screen.
888 */
889do_offset:
890 if (offset > 0) {
891 do {
892 h = rb_entry(nd, struct hist_entry, rb_node);
893 if (h->ms.unfolded) {
894 u16 remaining = h->nr_rows - h->row_offset;
895 if (offset > remaining) {
896 offset -= remaining;
897 h->row_offset = 0;
898 } else {
899 h->row_offset += offset;
900 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300901 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300902 break;
903 }
904 }
Namhyung Kim064f1982013-05-14 11:09:04 +0900905 nd = hists__filter_entries(rb_next(nd), hb->hists,
906 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300907 if (nd == NULL)
908 break;
909 --offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300910 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300911 } while (offset != 0);
912 } else if (offset < 0) {
913 while (1) {
914 h = rb_entry(nd, struct hist_entry, rb_node);
915 if (h->ms.unfolded) {
916 if (first) {
917 if (-offset > h->row_offset) {
918 offset += h->row_offset;
919 h->row_offset = 0;
920 } else {
921 h->row_offset += offset;
922 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300923 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300924 break;
925 }
926 } else {
927 if (-offset > h->nr_rows) {
928 offset += h->nr_rows;
929 h->row_offset = 0;
930 } else {
931 h->row_offset = h->nr_rows + offset;
932 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300933 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300934 break;
935 }
936 }
937 }
938
Namhyung Kim064f1982013-05-14 11:09:04 +0900939 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
940 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300941 if (nd == NULL)
942 break;
943 ++offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300944 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300945 if (offset == 0) {
946 /*
947 * Last unfiltered hist_entry, check if it is
948 * unfolded, if it is then we should have
949 * row_offset at its last entry.
950 */
951 h = rb_entry(nd, struct hist_entry, rb_node);
952 if (h->ms.unfolded)
953 h->row_offset = h->nr_rows;
954 break;
955 }
956 first = false;
957 }
958 } else {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300959 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300960 h = rb_entry(nd, struct hist_entry, rb_node);
961 h->row_offset = 0;
962 }
963}
964
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -0300965static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
966 struct callchain_node *chain_node,
967 u64 total, int level,
968 FILE *fp)
969{
970 struct rb_node *node;
971 int offset = level * LEVEL_OFFSET_STEP;
972 u64 new_total, remaining;
973 int printed = 0;
974
975 if (callchain_param.mode == CHAIN_GRAPH_REL)
976 new_total = chain_node->children_hit;
977 else
978 new_total = total;
979
980 remaining = new_total;
981 node = rb_first(&chain_node->rb_root);
982 while (node) {
983 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
984 struct rb_node *next = rb_next(node);
985 u64 cumul = callchain_cumul_hits(child);
986 struct callchain_list *chain;
987 char folded_sign = ' ';
988 int first = true;
989 int extra_offset = 0;
990
991 remaining -= cumul;
992
993 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300994 char bf[1024], *alloc_str;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -0300995 const char *str;
996 bool was_first = first;
997
998 if (first)
999 first = false;
1000 else
1001 extra_offset = LEVEL_OFFSET_STEP;
1002
1003 folded_sign = callchain_list__folded(chain);
1004
1005 alloc_str = NULL;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001006 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1007 browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001008 if (was_first) {
1009 double percent = cumul * 100.0 / new_total;
1010
1011 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1012 str = "Not enough memory!";
1013 else
1014 str = alloc_str;
1015 }
1016
1017 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1018 free(alloc_str);
1019 if (folded_sign == '+')
1020 break;
1021 }
1022
1023 if (folded_sign == '-') {
1024 const int new_level = level + (extra_offset ? 2 : 1);
1025 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1026 new_level, fp);
1027 }
1028
1029 node = next;
1030 }
1031
1032 return printed;
1033}
1034
1035static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1036 struct callchain_node *node,
1037 int level, FILE *fp)
1038{
1039 struct callchain_list *chain;
1040 int offset = level * LEVEL_OFFSET_STEP;
1041 char folded_sign = ' ';
1042 int printed = 0;
1043
1044 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001045 char bf[1024], *s;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001046
1047 folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001048 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001049 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1050 }
1051
1052 if (folded_sign == '-')
1053 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1054 browser->hists->stats.total_period,
1055 level + 1, fp);
1056 return printed;
1057}
1058
1059static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1060 struct rb_root *chain, int level, FILE *fp)
1061{
1062 struct rb_node *nd;
1063 int printed = 0;
1064
1065 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1066 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1067
1068 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1069 }
1070
1071 return printed;
1072}
1073
1074static int hist_browser__fprintf_entry(struct hist_browser *browser,
1075 struct hist_entry *he, FILE *fp)
1076{
1077 char s[8192];
1078 double percent;
1079 int printed = 0;
1080 char folded_sign = ' ';
1081
1082 if (symbol_conf.use_callchain)
1083 folded_sign = hist_entry__folded(he);
1084
Namhyung Kim000078b2012-08-20 13:52:06 +09001085 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001086 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001087
1088 if (symbol_conf.use_callchain)
1089 printed += fprintf(fp, "%c ", folded_sign);
1090
1091 printed += fprintf(fp, " %5.2f%%", percent);
1092
1093 if (symbol_conf.show_nr_samples)
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001094 printed += fprintf(fp, " %11u", he->stat.nr_events);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001095
1096 if (symbol_conf.show_total_period)
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001097 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001098
1099 printed += fprintf(fp, "%s\n", rtrim(s));
1100
1101 if (folded_sign == '-')
1102 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1103
1104 return printed;
1105}
1106
1107static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1108{
Namhyung Kim064f1982013-05-14 11:09:04 +09001109 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1110 browser->hists,
1111 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001112 int printed = 0;
1113
1114 while (nd) {
1115 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1116
1117 printed += hist_browser__fprintf_entry(browser, h, fp);
Namhyung Kim064f1982013-05-14 11:09:04 +09001118 nd = hists__filter_entries(rb_next(nd), browser->hists,
1119 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001120 }
1121
1122 return printed;
1123}
1124
1125static int hist_browser__dump(struct hist_browser *browser)
1126{
1127 char filename[64];
1128 FILE *fp;
1129
1130 while (1) {
1131 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1132 if (access(filename, F_OK))
1133 break;
1134 /*
1135 * XXX: Just an arbitrary lazy upper limit
1136 */
1137 if (++browser->print_seq == 8192) {
1138 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1139 return -1;
1140 }
1141 }
1142
1143 fp = fopen(filename, "w");
1144 if (fp == NULL) {
1145 char bf[64];
Kirill A. Shutemov4cc49d42012-07-24 00:06:54 +03001146 const char *err = strerror_r(errno, bf, sizeof(bf));
1147 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001148 return -1;
1149 }
1150
1151 ++browser->print_seq;
1152 hist_browser__fprintf(browser, fp);
1153 fclose(fp);
1154 ui_helpline__fpush("%s written!", filename);
1155
1156 return 0;
1157}
1158
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001159static struct hist_browser *hist_browser__new(struct hists *hists)
1160{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001161 struct hist_browser *browser = zalloc(sizeof(*browser));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001162
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001163 if (browser) {
1164 browser->hists = hists;
1165 browser->b.refresh = hist_browser__refresh;
1166 browser->b.seek = ui_browser__hists_seek;
1167 browser->b.use_navkeypressed = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001168 }
1169
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001170 return browser;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001171}
1172
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001173static void hist_browser__delete(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001174{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001175 free(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001176}
1177
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001178static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001179{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001180 return browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001181}
1182
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001183static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001184{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001185 return browser->he_selection->thread;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001186}
1187
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001188static int hists__browser_title(struct hists *hists, char *bf, size_t size,
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001189 const char *ev_name)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001190{
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001191 char unit;
1192 int printed;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001193 const struct dso *dso = hists->dso_filter;
1194 const struct thread *thread = hists->thread_filter;
1195 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1196 u64 nr_events = hists->stats.total_period;
Namhyung Kim717e2632013-01-22 18:09:44 +09001197 struct perf_evsel *evsel = hists_to_evsel(hists);
1198 char buf[512];
1199 size_t buflen = sizeof(buf);
1200
Namhyung Kimf2148332014-01-14 11:52:48 +09001201 if (symbol_conf.filter_relative) {
1202 nr_samples = hists->stats.nr_non_filtered_samples;
1203 nr_events = hists->stats.total_non_filtered_period;
1204 }
1205
Namhyung Kim759ff492013-03-05 14:53:26 +09001206 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001207 struct perf_evsel *pos;
1208
1209 perf_evsel__group_desc(evsel, buf, buflen);
1210 ev_name = buf;
1211
1212 for_each_group_member(pos, evsel) {
Namhyung Kimf2148332014-01-14 11:52:48 +09001213 if (symbol_conf.filter_relative) {
1214 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1215 nr_events += pos->hists.stats.total_non_filtered_period;
1216 } else {
1217 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1218 nr_events += pos->hists.stats.total_period;
1219 }
Namhyung Kim717e2632013-01-22 18:09:44 +09001220 }
1221 }
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001222
Ashay Ranecc6862802012-04-05 21:01:01 -05001223 nr_samples = convert_unit(nr_samples, &unit);
1224 printed = scnprintf(bf, size,
1225 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1226 nr_samples, unit, ev_name, nr_events);
1227
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001228
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001229 if (hists->uid_filter_str)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02001230 printed += snprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001231 ", UID: %s", hists->uid_filter_str);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001232 if (thread)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001233 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001234 ", Thread: %s(%d)",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001235 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001236 thread->tid);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001237 if (dso)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001238 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001239 ", DSO: %s", dso->short_name);
1240 return printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001241}
1242
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001243static inline void free_popup_options(char **options, int n)
1244{
1245 int i;
1246
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -03001247 for (i = 0; i < n; ++i)
1248 zfree(&options[i]);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001249}
1250
Feng Tangc77d8d72012-11-01 00:00:55 +08001251/* Check whether the browser is for 'top' or 'report' */
1252static inline bool is_report_browser(void *timer)
1253{
1254 return timer == NULL;
1255}
1256
Feng Tang341487ab2013-02-03 14:38:20 +08001257/*
1258 * Only runtime switching of perf data file will make "input_name" point
1259 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1260 * whether we need to call free() for current "input_name" during the switch.
1261 */
1262static bool is_input_name_malloced = false;
1263
1264static int switch_data_file(void)
1265{
1266 char *pwd, *options[32], *abs_path[32], *tmp;
1267 DIR *pwd_dir;
1268 int nr_options = 0, choice = -1, ret = -1;
1269 struct dirent *dent;
1270
1271 pwd = getenv("PWD");
1272 if (!pwd)
1273 return ret;
1274
1275 pwd_dir = opendir(pwd);
1276 if (!pwd_dir)
1277 return ret;
1278
1279 memset(options, 0, sizeof(options));
1280 memset(options, 0, sizeof(abs_path));
1281
1282 while ((dent = readdir(pwd_dir))) {
1283 char path[PATH_MAX];
1284 u64 magic;
1285 char *name = dent->d_name;
1286 FILE *file;
1287
1288 if (!(dent->d_type == DT_REG))
1289 continue;
1290
1291 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1292
1293 file = fopen(path, "r");
1294 if (!file)
1295 continue;
1296
1297 if (fread(&magic, 1, 8, file) < 8)
1298 goto close_file_and_continue;
1299
1300 if (is_perf_magic(magic)) {
1301 options[nr_options] = strdup(name);
1302 if (!options[nr_options])
1303 goto close_file_and_continue;
1304
1305 abs_path[nr_options] = strdup(path);
1306 if (!abs_path[nr_options]) {
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -03001307 zfree(&options[nr_options]);
Feng Tang341487ab2013-02-03 14:38:20 +08001308 ui__warning("Can't search all data files due to memory shortage.\n");
1309 fclose(file);
1310 break;
1311 }
1312
1313 nr_options++;
1314 }
1315
1316close_file_and_continue:
1317 fclose(file);
1318 if (nr_options >= 32) {
1319 ui__warning("Too many perf data files in PWD!\n"
1320 "Only the first 32 files will be listed.\n");
1321 break;
1322 }
1323 }
1324 closedir(pwd_dir);
1325
1326 if (nr_options) {
1327 choice = ui__popup_menu(nr_options, options);
1328 if (choice < nr_options && choice >= 0) {
1329 tmp = strdup(abs_path[choice]);
1330 if (tmp) {
1331 if (is_input_name_malloced)
1332 free((void *)input_name);
1333 input_name = tmp;
1334 is_input_name_malloced = true;
1335 ret = 0;
1336 } else
1337 ui__warning("Data switch failed due to memory shortage!\n");
1338 }
1339 }
1340
1341 free_popup_options(options, nr_options);
1342 free_popup_options(abs_path, nr_options);
1343 return ret;
1344}
1345
Namhyung Kim064f1982013-05-14 11:09:04 +09001346static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1347{
1348 u64 nr_entries = 0;
1349 struct rb_node *nd = rb_first(&hb->hists->entries);
1350
1351 while (nd) {
1352 nr_entries++;
1353 nd = hists__filter_entries(rb_next(nd), hb->hists,
1354 hb->min_pcnt);
1355 }
1356
1357 hb->nr_pcnt_entries = nr_entries;
1358}
Feng Tang341487ab2013-02-03 14:38:20 +08001359
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001360static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001361 const char *helpline, const char *ev_name,
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001362 bool left_exits,
Namhyung Kim68d80752012-11-02 14:50:06 +09001363 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001364 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001365 struct perf_session_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001366{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001367 struct hists *hists = &evsel->hists;
1368 struct hist_browser *browser = hist_browser__new(hists);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001369 struct branch_info *bi;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001370 struct pstack *fstack;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001371 char *options[16];
1372 int nr_options = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001373 int key = -1;
Namhyung Kim938a23a2012-03-16 17:50:53 +09001374 char buf[64];
Feng Tangcdbab7c2012-10-30 11:56:06 +08001375 char script_opt[64];
Namhyung Kim9783adf2012-11-02 14:50:05 +09001376 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001377
Namhyung Kime8e684a2013-12-26 14:37:58 +09001378#define HIST_BROWSER_HELP_COMMON \
1379 "h/?/F1 Show this window\n" \
1380 "UP/DOWN/PGUP\n" \
1381 "PGDN/SPACE Navigate\n" \
1382 "q/ESC/CTRL+C Exit browser\n\n" \
1383 "For multiple event sessions:\n\n" \
1384 "TAB/UNTAB Switch events\n\n" \
1385 "For symbolic views (--sort has sym):\n\n" \
1386 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1387 "<- Zoom out\n" \
1388 "a Annotate current symbol\n" \
1389 "C Collapse all callchains\n" \
1390 "d Zoom into current DSO\n" \
1391 "E Expand all callchains\n" \
Namhyung Kim105eb302014-02-10 11:20:10 +09001392 "F Toggle percentage of filtered entries\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09001393
1394 /* help messages are sorted by lexical order of the hotkey */
1395 const char report_help[] = HIST_BROWSER_HELP_COMMON
Namhyung Kim6dd60132013-12-26 14:37:59 +09001396 "i Show header information\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09001397 "P Print histograms to perf.hist.N\n"
1398 "r Run available scripts\n"
1399 "s Switch to another data file in PWD\n"
1400 "t Zoom into current Thread\n"
1401 "V Verbose (DSO names in callchains, etc)\n"
1402 "/ Filter symbol by name";
1403 const char top_help[] = HIST_BROWSER_HELP_COMMON
1404 "P Print histograms to perf.hist.N\n"
1405 "t Zoom into current Thread\n"
1406 "V Verbose (DSO names in callchains, etc)\n"
1407 "/ Filter symbol by name";
1408
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001409 if (browser == NULL)
1410 return -1;
1411
Namhyung Kim064f1982013-05-14 11:09:04 +09001412 if (min_pcnt) {
1413 browser->min_pcnt = min_pcnt;
1414 hist_browser__update_pcnt_entries(browser);
1415 }
1416
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001417 fstack = pstack__new(2);
1418 if (fstack == NULL)
1419 goto out;
1420
1421 ui_helpline__push(helpline);
1422
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001423 memset(options, 0, sizeof(options));
1424
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001425 while (1) {
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001426 const struct thread *thread = NULL;
1427 const struct dso *dso = NULL;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001428 int choice = 0,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001429 annotate = -2, zoom_dso = -2, zoom_thread = -2,
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001430 annotate_f = -2, annotate_t = -2, browse_map = -2;
Feng Tang341487ab2013-02-03 14:38:20 +08001431 int scripts_comm = -2, scripts_symbol = -2,
1432 scripts_all = -2, switch_data = -2;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001433
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001434 nr_options = 0;
1435
Namhyung Kim9783adf2012-11-02 14:50:05 +09001436 key = hist_browser__run(browser, ev_name, hbt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001437
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001438 if (browser->he_selection != NULL) {
1439 thread = hist_browser__selected_thread(browser);
1440 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1441 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001442 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001443 case K_TAB:
1444 case K_UNTAB:
David Aherne4419b82011-10-19 11:37:47 -06001445 if (nr_events == 1)
1446 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001447 /*
1448 * Exit the browser, let hists__browser_tree
1449 * go to the next or previous
1450 */
1451 goto out_free_stack;
1452 case 'a':
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001453 if (!sort__has_sym) {
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001454 ui_browser__warning(&browser->b, delay_secs * 2,
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001455 "Annotation is only available for symbolic views, "
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001456 "include \"sym*\" in --sort to use it.");
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001457 continue;
1458 }
1459
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001460 if (browser->selection == NULL ||
Lin Mingdb9a9cbc2011-04-08 14:31:26 +08001461 browser->selection->sym == NULL ||
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001462 browser->selection->map->dso->annotate_warned)
1463 continue;
1464 goto do_annotate;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001465 case 'P':
1466 hist_browser__dump(browser);
1467 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001468 case 'd':
1469 goto zoom_dso;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001470 case 'V':
1471 browser->show_dso = !browser->show_dso;
1472 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001473 case 't':
1474 goto zoom_thread;
Arnaldo Carvalho de Melo5a5626b2012-05-09 12:21:22 -03001475 case '/':
Namhyung Kim938a23a2012-03-16 17:50:53 +09001476 if (ui_browser__input_window("Symbol to show",
1477 "Please enter the name of symbol you want to see",
1478 buf, "ENTER: OK, ESC: Cancel",
1479 delay_secs * 2) == K_ENTER) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001480 hists->symbol_filter_str = *buf ? buf : NULL;
1481 hists__filter_by_symbol(hists);
Namhyung Kim938a23a2012-03-16 17:50:53 +09001482 hist_browser__reset(browser);
1483 }
1484 continue;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001485 case 'r':
Namhyung Kim9783adf2012-11-02 14:50:05 +09001486 if (is_report_browser(hbt))
Feng Tangc77d8d72012-11-01 00:00:55 +08001487 goto do_scripts;
1488 continue;
Feng Tang341487ab2013-02-03 14:38:20 +08001489 case 's':
1490 if (is_report_browser(hbt))
1491 goto do_data_switch;
1492 continue;
Namhyung Kim6dd60132013-12-26 14:37:59 +09001493 case 'i':
1494 /* env->arch is NULL for live-mode (i.e. perf top) */
1495 if (env->arch)
1496 tui__header_window(env);
1497 continue;
Namhyung Kim105eb302014-02-10 11:20:10 +09001498 case 'F':
1499 symbol_conf.filter_relative ^= 1;
1500 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001501 case K_F1:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001502 case 'h':
1503 case '?':
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001504 ui_browser__help_window(&browser->b,
Namhyung Kime8e684a2013-12-26 14:37:58 +09001505 is_report_browser(hbt) ? report_help : top_help);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001506 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001507 case K_ENTER:
1508 case K_RIGHT:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001509 /* menu */
1510 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001511 case K_LEFT: {
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001512 const void *top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001513
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001514 if (pstack__empty(fstack)) {
1515 /*
1516 * Go back to the perf_evsel_menu__run or other user
1517 */
1518 if (left_exits)
1519 goto out_free_stack;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001520 continue;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001521 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001522 top = pstack__pop(fstack);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001523 if (top == &browser->hists->dso_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001524 goto zoom_out_dso;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001525 if (top == &browser->hists->thread_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001526 goto zoom_out_thread;
1527 continue;
1528 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001529 case K_ESC:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001530 if (!left_exits &&
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001531 !ui_browser__dialog_yesno(&browser->b,
1532 "Do you really want to exit?"))
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001533 continue;
1534 /* Fall thru */
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001535 case 'q':
1536 case CTRL('c'):
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001537 goto out_free_stack;
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001538 default:
1539 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001540 }
1541
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001542 if (!sort__has_sym)
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001543 goto add_exit_option;
1544
Namhyung Kim55369fc2013-04-01 20:35:20 +09001545 if (sort__mode == SORT_MODE__BRANCH) {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001546 bi = browser->he_selection->branch_info;
1547 if (browser->selection != NULL &&
1548 bi &&
1549 bi->from.sym != NULL &&
1550 !bi->from.map->dso->annotate_warned &&
1551 asprintf(&options[nr_options], "Annotate %s",
1552 bi->from.sym->name) > 0)
1553 annotate_f = nr_options++;
1554
1555 if (browser->selection != NULL &&
1556 bi &&
1557 bi->to.sym != NULL &&
1558 !bi->to.map->dso->annotate_warned &&
Stephane Eranian8bcd65f2012-03-12 16:13:29 +01001559 (bi->to.sym != bi->from.sym ||
1560 bi->to.map->dso != bi->from.map->dso) &&
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001561 asprintf(&options[nr_options], "Annotate %s",
1562 bi->to.sym->name) > 0)
1563 annotate_t = nr_options++;
1564 } else {
1565
1566 if (browser->selection != NULL &&
1567 browser->selection->sym != NULL &&
1568 !browser->selection->map->dso->annotate_warned &&
1569 asprintf(&options[nr_options], "Annotate %s",
1570 browser->selection->sym->name) > 0)
1571 annotate = nr_options++;
1572 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001573
1574 if (thread != NULL &&
1575 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001576 (browser->hists->thread_filter ? "out of" : "into"),
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001577 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001578 thread->tid) > 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001579 zoom_thread = nr_options++;
1580
1581 if (dso != NULL &&
1582 asprintf(&options[nr_options], "Zoom %s %s DSO",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001583 (browser->hists->dso_filter ? "out of" : "into"),
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001584 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1585 zoom_dso = nr_options++;
1586
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001587 if (browser->selection != NULL &&
1588 browser->selection->map != NULL &&
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001589 asprintf(&options[nr_options], "Browse map details") > 0)
1590 browse_map = nr_options++;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001591
1592 /* perf script support */
1593 if (browser->he_selection) {
1594 struct symbol *sym;
1595
1596 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001597 thread__comm_str(browser->he_selection->thread)) > 0)
Feng Tangcdbab7c2012-10-30 11:56:06 +08001598 scripts_comm = nr_options++;
1599
1600 sym = browser->he_selection->ms.sym;
1601 if (sym && sym->namelen &&
1602 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1603 sym->name) > 0)
1604 scripts_symbol = nr_options++;
1605 }
1606
1607 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1608 scripts_all = nr_options++;
1609
Feng Tang341487ab2013-02-03 14:38:20 +08001610 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1611 "Switch to another data file in PWD") > 0)
1612 switch_data = nr_options++;
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001613add_exit_option:
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001614 options[nr_options++] = (char *)"Exit";
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001615retry_popup_menu:
Arnaldo Carvalho de Melo1e6dd072010-08-10 15:58:50 -03001616 choice = ui__popup_menu(nr_options, options);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001617
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001618 if (choice == nr_options - 1)
1619 break;
1620
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001621 if (choice == -1) {
1622 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001623 continue;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001624 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001625
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001626 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001627 struct hist_entry *he;
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001628 int err;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001629do_annotate:
Namhyung Kim68d80752012-11-02 14:50:06 +09001630 if (!objdump_path && perf_session_env__lookup_objdump(env))
1631 continue;
1632
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001633 he = hist_browser__selected_entry(browser);
1634 if (he == NULL)
1635 continue;
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001636
1637 /*
1638 * we stash the branch_info symbol + map into the
1639 * the ms so we don't have to rewrite all the annotation
1640 * code to use branch_info.
1641 * in branch mode, the ms struct is not used
1642 */
1643 if (choice == annotate_f) {
1644 he->ms.sym = he->branch_info->from.sym;
1645 he->ms.map = he->branch_info->from.map;
1646 } else if (choice == annotate_t) {
1647 he->ms.sym = he->branch_info->to.sym;
1648 he->ms.map = he->branch_info->to.map;
1649 }
1650
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001651 /*
1652 * Don't let this be freed, say, by hists__decay_entry.
1653 */
1654 he->used = true;
Namhyung Kimdb8fd072013-03-05 14:53:21 +09001655 err = hist_entry__tui_annotate(he, evsel, hbt);
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001656 he->used = false;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001657 /*
1658 * offer option to annotate the other branch source or target
1659 * (if they exists) when returning from annotate
1660 */
1661 if ((err == 'q' || err == CTRL('c'))
1662 && annotate_t != -2 && annotate_f != -2)
1663 goto retry_popup_menu;
1664
Arnaldo Carvalho de Melo900e14a2011-10-11 16:15:39 -03001665 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001666 if (err)
1667 ui_browser__handle_resize(&browser->b);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001668
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001669 } else if (choice == browse_map)
1670 map__browse(browser->selection->map);
1671 else if (choice == zoom_dso) {
1672zoom_dso:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001673 if (browser->hists->dso_filter) {
1674 pstack__remove(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001675zoom_out_dso:
1676 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001677 browser->hists->dso_filter = NULL;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001678 sort_dso.elide = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001679 } else {
1680 if (dso == NULL)
1681 continue;
1682 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1683 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001684 browser->hists->dso_filter = dso;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001685 sort_dso.elide = true;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001686 pstack__push(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001687 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001688 hists__filter_by_dso(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001689 hist_browser__reset(browser);
1690 } else if (choice == zoom_thread) {
1691zoom_thread:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001692 if (browser->hists->thread_filter) {
1693 pstack__remove(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001694zoom_out_thread:
1695 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001696 browser->hists->thread_filter = NULL;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001697 sort_thread.elide = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001698 } else {
1699 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001700 thread->comm_set ? thread__comm_str(thread) : "",
Adrian Hunter38051232013-07-04 16:20:31 +03001701 thread->tid);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001702 browser->hists->thread_filter = thread;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001703 sort_thread.elide = true;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001704 pstack__push(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001705 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001706 hists__filter_by_thread(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001707 hist_browser__reset(browser);
1708 }
Feng Tangcdbab7c2012-10-30 11:56:06 +08001709 /* perf scripts support */
1710 else if (choice == scripts_all || choice == scripts_comm ||
1711 choice == scripts_symbol) {
1712do_scripts:
1713 memset(script_opt, 0, 64);
1714
1715 if (choice == scripts_comm)
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001716 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
Feng Tangcdbab7c2012-10-30 11:56:06 +08001717
1718 if (choice == scripts_symbol)
1719 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1720
1721 script_browse(script_opt);
1722 }
Feng Tang341487ab2013-02-03 14:38:20 +08001723 /* Switch to another data file */
1724 else if (choice == switch_data) {
1725do_data_switch:
1726 if (!switch_data_file()) {
1727 key = K_SWITCH_INPUT_DATA;
1728 break;
1729 } else
1730 ui__warning("Won't switch the data files due to\n"
1731 "no valid data file get selected!\n");
1732 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001733 }
1734out_free_stack:
1735 pstack__delete(fstack);
1736out:
1737 hist_browser__delete(browser);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001738 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001739 return key;
1740}
1741
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001742struct perf_evsel_menu {
1743 struct ui_browser b;
1744 struct perf_evsel *selection;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001745 bool lost_events, lost_events_warned;
Namhyung Kim064f1982013-05-14 11:09:04 +09001746 float min_pcnt;
Namhyung Kim68d80752012-11-02 14:50:06 +09001747 struct perf_session_env *env;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001748};
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001749
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001750static void perf_evsel_menu__write(struct ui_browser *browser,
1751 void *entry, int row)
1752{
1753 struct perf_evsel_menu *menu = container_of(browser,
1754 struct perf_evsel_menu, b);
1755 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1756 bool current_entry = ui_browser__is_current_entry(browser, row);
1757 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001758 const char *ev_name = perf_evsel__name(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001759 char bf[256], unit;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001760 const char *warn = " ";
1761 size_t printed;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001762
1763 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1764 HE_COLORSET_NORMAL);
1765
Namhyung Kim759ff492013-03-05 14:53:26 +09001766 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001767 struct perf_evsel *pos;
1768
1769 ev_name = perf_evsel__group_name(evsel);
1770
1771 for_each_group_member(pos, evsel) {
1772 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1773 }
1774 }
1775
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001776 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001777 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001778 unit, unit == ' ' ? "" : " ", ev_name);
1779 slsmg_printf("%s", bf);
1780
1781 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1782 if (nr_events != 0) {
1783 menu->lost_events = true;
1784 if (!current_entry)
1785 ui_browser__set_color(browser, HE_COLORSET_TOP);
1786 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001787 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1788 nr_events, unit, unit == ' ' ? "" : " ");
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001789 warn = bf;
1790 }
1791
1792 slsmg_write_nstring(warn, browser->width - printed);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001793
1794 if (current_entry)
1795 menu->selection = evsel;
1796}
1797
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001798static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1799 int nr_events, const char *help,
Namhyung Kim9783adf2012-11-02 14:50:05 +09001800 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001801{
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001802 struct perf_evlist *evlist = menu->b.priv;
1803 struct perf_evsel *pos;
1804 const char *ev_name, *title = "Available samples";
Namhyung Kim9783adf2012-11-02 14:50:05 +09001805 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001806 int key;
1807
1808 if (ui_browser__show(&menu->b, title,
1809 "ESC: exit, ENTER|->: Browse histograms") < 0)
1810 return -1;
1811
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001812 while (1) {
Arnaldo Carvalho de Melo3af6e332011-10-13 08:52:46 -03001813 key = ui_browser__run(&menu->b, delay_secs);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001814
1815 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001816 case K_TIMER:
Namhyung Kim9783adf2012-11-02 14:50:05 +09001817 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001818
1819 if (!menu->lost_events_warned && menu->lost_events) {
1820 ui_browser__warn_lost_events(&menu->b);
1821 menu->lost_events_warned = true;
1822 }
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001823 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001824 case K_RIGHT:
1825 case K_ENTER:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001826 if (!menu->selection)
1827 continue;
1828 pos = menu->selection;
1829browse_hists:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001830 perf_evlist__set_selected(evlist, pos);
1831 /*
1832 * Give the calling tool a chance to populate the non
1833 * default evsel resorted hists tree.
1834 */
Namhyung Kim9783adf2012-11-02 14:50:05 +09001835 if (hbt)
1836 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001837 ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001838 key = perf_evsel__hists_browse(pos, nr_events, help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001839 ev_name, true, hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001840 menu->min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001841 menu->env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001842 ui_browser__show_title(&menu->b, title);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001843 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001844 case K_TAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001845 if (pos->node.next == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001846 pos = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001847 else
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001848 pos = perf_evsel__next(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001849 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001850 case K_UNTAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001851 if (pos->node.prev == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001852 pos = perf_evlist__last(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001853 else
Arnaldo Carvalho de Melod87fcb42013-11-13 15:56:40 -03001854 pos = perf_evsel__prev(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001855 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001856 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001857 if (!ui_browser__dialog_yesno(&menu->b,
1858 "Do you really want to exit?"))
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001859 continue;
1860 /* Fall thru */
Feng Tang341487ab2013-02-03 14:38:20 +08001861 case K_SWITCH_INPUT_DATA:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001862 case 'q':
1863 case CTRL('c'):
1864 goto out;
1865 default:
1866 continue;
1867 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001868 case K_LEFT:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001869 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001870 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001871 if (!ui_browser__dialog_yesno(&menu->b,
1872 "Do you really want to exit?"))
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001873 continue;
1874 /* Fall thru */
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001875 case 'q':
1876 case CTRL('c'):
1877 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001878 default:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001879 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001880 }
1881 }
1882
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001883out:
1884 ui_browser__hide(&menu->b);
1885 return key;
1886}
1887
Arnaldo Carvalho de Melo316c7132013-11-05 15:32:36 -03001888static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001889 void *entry)
1890{
1891 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1892
1893 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1894 return true;
1895
1896 return false;
1897}
1898
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001899static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001900 int nr_entries, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001901 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001902 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001903 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001904{
1905 struct perf_evsel *pos;
1906 struct perf_evsel_menu menu = {
1907 .b = {
1908 .entries = &evlist->entries,
1909 .refresh = ui_browser__list_head_refresh,
1910 .seek = ui_browser__list_head_seek,
1911 .write = perf_evsel_menu__write,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001912 .filter = filter_group_entries,
1913 .nr_entries = nr_entries,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001914 .priv = evlist,
1915 },
Namhyung Kim064f1982013-05-14 11:09:04 +09001916 .min_pcnt = min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001917 .env = env,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001918 };
1919
1920 ui_helpline__push("Press ESC to exit");
1921
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001922 evlist__for_each(evlist, pos) {
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001923 const char *ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001924 size_t line_len = strlen(ev_name) + 7;
1925
1926 if (menu.b.width < line_len)
1927 menu.b.width = line_len;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001928 }
1929
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001930 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001931}
1932
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001933int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001934 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001935 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001936 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001937{
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001938 int nr_entries = evlist->nr_entries;
1939
1940single_entry:
1941 if (nr_entries == 1) {
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001942 struct perf_evsel *first = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001943 const char *ev_name = perf_evsel__name(first);
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001944
1945 return perf_evsel__hists_browse(first, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09001946 ev_name, false, hbt, min_pcnt,
1947 env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001948 }
1949
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001950 if (symbol_conf.event_group) {
1951 struct perf_evsel *pos;
1952
1953 nr_entries = 0;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001954 evlist__for_each(evlist, pos) {
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001955 if (perf_evsel__is_group_leader(pos))
1956 nr_entries++;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001957 }
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001958
1959 if (nr_entries == 1)
1960 goto single_entry;
1961 }
1962
1963 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09001964 hbt, min_pcnt, env);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001965}